diff --git a/ext.php b/ext.php index dc6c9b0..f085e0d 100644 --- a/ext.php +++ b/ext.php @@ -19,6 +19,10 @@ class ext extends \phpbb\extension\base const DEFAULT_PHPBB_MIN = '3.3.0'; const DEFAULT_PHPBB_MAX = '4.0.0@dev'; + const REQUIRE_PHPBB_MIN = '3.2.3'; + const REQUIRE_PHPBB_MAX = '4.0.0-dev'; + const REQUIRE_PHP = 50600; + /** * @var array An array of installation error messages */ @@ -40,31 +44,32 @@ public function is_enableable() } /** - * Check phpBB 3.2.3 minimum requirement. + * Check phpBB requirements. * + * @param string $phpBB_version * @return void */ - protected function phpbb_requirement() + protected function phpbb_requirement($phpBB_version = PHPBB_VERSION) { - if (phpbb_version_compare(PHPBB_VERSION, '3.2.3', '<')) + if (phpbb_version_compare($phpBB_version, self::REQUIRE_PHPBB_MIN, '<')) { $this->errors[] = 'PHPBB_VERSION_MIN_ERROR'; } - - else if (phpbb_version_compare(PHPBB_VERSION, '4.0.0-dev', '>=')) + else if (phpbb_version_compare($phpBB_version, self::REQUIRE_PHPBB_MAX, '>=')) { $this->errors[] = 'PHPBB_VERSION_MAX_ERROR'; } } /** - * Check PHP 5.6.0 minimum requirement. + * Check PHP minimum requirement. * + * @param int $php_version * @return void */ - protected function php_requirement() + protected function php_requirement($php_version = PHP_VERSION_ID) { - if (PHP_VERSION_ID < 50600) + if ($php_version < self::REQUIRE_PHP) { $this->errors[] = 'PHP_VERSION_ERROR'; } @@ -73,11 +78,12 @@ protected function php_requirement() /** * Check PHP ZipArchive binary requirement. * + * @param string $zip_class * @return void */ - protected function ziparchive_exists() + protected function ziparchive_exists($zip_class = 'ZipArchive') { - if (!class_exists('ZipArchive')) + if (!class_exists($zip_class, false)) { $this->errors[] = 'NO_ZIPARCHIVE_ERROR'; } diff --git a/skeleton/notification/type/sample.php.twig b/skeleton/notification/type/sample.php.twig index 462bd5b..9afebe4 100644 --- a/skeleton/notification/type/sample.php.twig +++ b/skeleton/notification/type/sample.php.twig @@ -1,3 +1,4 @@ +{% set is_phpbb_pre_32 = skeleton_version_compare(REQUIREMENTS.phpbb_version_max, "3.2", "<") %} = 3.2.x #} @@ -166,7 +167,7 @@ class sample extends \phpbb\notification\type\base { $this->set_data('{{ EXTENSION.extension_name|lower }}_sample_name', $data['{{ EXTENSION.extension_name|lower }}_sample_name']); -{% if skeleton_version_compare(REQUIREMENTS.phpbb_version_max, "3.2", "<") %}{# for phpBB 3.1.x only #} +{% if is_phpbb_pre_32 %}{# for phpBB 3.1.x only #} return parent::create_insert_array($data, $pre_create_data); {% else %}{# for phpBB >= 3.2.x #} parent::create_insert_array($data, $pre_create_data); diff --git a/skeleton/phpunit.xml.dist.twig b/skeleton/phpunit.xml.dist.twig index e40aa26..cfd5efe 100644 --- a/skeleton/phpunit.xml.dist.twig +++ b/skeleton/phpunit.xml.dist.twig @@ -1,3 +1,4 @@ +{% set is_phpbb_pre_33 = skeleton_version_compare(REQUIREMENTS.phpbb_version_max, "3.3", "<") %} ./tests/functional - ./tests/functional/ + ./tests/functional/ -{% if skeleton_version_compare(REQUIREMENTS.phpbb_version_max, "3.3", "<") %} +{% if is_phpbb_pre_33 %} ./tests/ diff --git a/tests/ext_test.php b/tests/ext_test.php new file mode 100644 index 0000000..225580f --- /dev/null +++ b/tests/ext_test.php @@ -0,0 +1,164 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\skeleton\tests; + +use phpbb\db\migrator; +use phpbb\finder; +use phpbb\language\language; +use phpbb\skeleton\ext; +use Symfony\Component\DependencyInjection\ContainerInterface; + +class ext_test extends \phpbb_test_case +{ + protected $ext; + + protected function setUp(): void + { + $this->ext = new ext( + $this->createMock(ContainerInterface::class), + $this->createMock(finder::class), + $this->createMock(migrator::class), + 'phpbb/skeleton', + '' + ); + } + + public function test_ext_is_instance_of_base() + { + $this->assertInstanceOf(ext::class, $this->ext); + } + + public function test_is_enableable_true() + { + $ext = $this->getMockBuilder(ext::class) + ->disableOriginalConstructor() + ->setMethods(['ziparchive_exists', 'phpbb_requirement', 'php_requirement']) + ->getMock(); + + $ext->method('ziparchive_exists')->willReturn(null); + $ext->method('phpbb_requirement')->willReturn(null); + $ext->method('php_requirement')->willReturn(null); + + $this->setExtErrors($ext, []); + $this->assertTrue($ext->is_enableable()); + } + + public function test_is_enableable_with_errors() + { + $ext = $this->getMockBuilder(ext::class) + ->disableOriginalConstructor() + ->setMethods(['ziparchive_exists', 'phpbb_requirement', 'php_requirement', 'enable_failed']) + ->getMock(); + + $ext->method('ziparchive_exists')->willReturnCallback(function () use ($ext) { + $this->appendExtError($ext, 'NO_ZIPARCHIVE_ERROR'); + }); + $ext->method('phpbb_requirement')->willReturn(null); + $ext->method('php_requirement')->willReturn(null); + $ext->method('enable_failed')->willReturn(['NO_ZIPARCHIVE_ERROR']); + + $this->setExtErrors($ext, ['NO_ZIPARCHIVE_ERROR']); + $this->assertEquals(['NO_ZIPARCHIVE_ERROR'], $ext->is_enableable()); + } + + public function test_enable_failed_returns_expected() + { + $ext = $this->getMockBuilder(ext::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->setExtErrors($ext, ['SOME_ERROR']); + + $languageMock = $this->createMock(language::class); + $languageMock->method('add_lang')->willReturn(null); + $languageMock->method('lang')->willReturnCallback(function ($msg) { + return "LANG: $msg"; + }); + + $containerMock = $this->createMock(ContainerInterface::class); + $containerMock->method('get')->with('language')->willReturn($languageMock); + + $this->setProperty($ext, 'container', $containerMock); + + $method = (new \ReflectionClass($ext))->getMethod('enable_failed'); + $method->setAccessible(true); + + $this->assertEquals(['LANG: SOME_ERROR'], $method->invoke($ext)); + } + + public function test_phpbb_requirement_min_error() + { + $this->setExtErrors($this->ext, []); + $this->invokeProtectedMethod($this->ext, 'phpbb_requirement', ['3.2.2']); + $this->assertContains('PHPBB_VERSION_MIN_ERROR', $this->getExtErrors($this->ext)); + } + + public function test_phpbb_requirement_max_error() + { + $this->setExtErrors($this->ext, []); + $this->invokeProtectedMethod($this->ext, 'phpbb_requirement', ['4.0.0-dev']); + $this->assertContains('PHPBB_VERSION_MAX_ERROR', $this->getExtErrors($this->ext)); + } + + public function test_php_requirement_error() + { + $this->setExtErrors($this->ext, []); + $this->invokeProtectedMethod($this->ext, 'php_requirement', [50500]); + $this->assertContains('PHP_VERSION_ERROR', $this->getExtErrors($this->ext)); + } + + public function test_ziparchive_exists_error() + { + $this->setExtErrors($this->ext, []); + $this->invokeProtectedMethod($this->ext, 'ziparchive_exists', ['NotZipArchive']); + $this->assertContains('NO_ZIPARCHIVE_ERROR', $this->getExtErrors($this->ext)); + } + + // --- Helpers --- + + protected function invokeProtectedMethod($object, string $methodName, array $args = []) + { + $method = (new \ReflectionClass($object))->getMethod($methodName); + $method->setAccessible(true); + return $method->invokeArgs($object, $args); + } + + protected function getExtErrors($ext): array + { + $prop = (new \ReflectionClass($ext))->getProperty('errors'); + $prop->setAccessible(true); + return $prop->getValue($ext); + } + + protected function setExtErrors($ext, array $errors): void + { + $prop = (new \ReflectionClass($ext))->getProperty('errors'); + $prop->setAccessible(true); + $prop->setValue($ext, $errors); + } + + protected function appendExtError($ext, string $error): void + { + $errors = $this->getExtErrors($ext); + $errors[] = $error; + $this->setExtErrors($ext, $errors); + } + + protected function setProperty($object, string $property, $value): void + { + $prop = (new \ReflectionClass($object))->getProperty($property); + $prop->setAccessible(true); + $prop->setValue($object, $value); + } +} diff --git a/tests/helper/packager_test.php b/tests/helper/packager_test.php new file mode 100644 index 0000000..d9b87e5 --- /dev/null +++ b/tests/helper/packager_test.php @@ -0,0 +1,184 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\skeleton\tests\helper; + +use phpbb\skeleton\helper\packager; +use phpbb\di\service_collection; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\DependencyInjection\ContainerInterface; + +class packager_test extends \phpbb_test_case +{ + /** @var ContainerInterface|MockObject */ + protected $container; + + /** @var service_collection|MockObject */ + protected $collection; + + /** @var packager */ + protected $packager; + + /** @var string */ + protected $root_path = '/tmp/phpbb/'; + + /** + * @return void + */ + public function setUpTheCollectionToReturnAFakeSkeletonClass(): void + { + // Set up the collection to return a fake skeleton class + $skeleton = $this->getMockBuilder(\phpbb\skeleton\skeleton::class) + ->disableOriginalConstructor() + ->getMock(); + $skeleton->method('get_name')->willReturn('phplistener'); + $skeleton->method('get_default')->willReturn(false); + $skeleton->method('get_dependencies')->willReturn([]); + $skeleton->method('get_files')->willReturn(['test.php']); + $skeleton->method('get_group')->willReturn('BACK_END'); + + $this->collection->method('getIterator')->willReturn(new \ArrayIterator([$skeleton])); + } + + protected function setUp(): void + { + $this->container = $this->createMock(ContainerInterface::class); + $this->collection = $this->createMock(service_collection::class); + + $this->packager = new packager($this->container, $this->collection, $this->root_path); + } + + public function test_get_composer_dialog_values_returns_expected_structure() + { + $result = $this->packager->get_composer_dialog_values(); + $this->assertArrayHasKey('author', $result); + $this->assertArrayHasKey('extension', $result); + $this->assertArrayHasKey('requirements', $result); + } + + public function test_get_component_dialog_values_calls_collection() + { + $this->setUpTheCollectionToReturnAFakeSkeletonClass(); + + $result = $this->packager->get_component_dialog_values(); + $this->assertArrayHasKey('phplistener', $result); + $this->assertSame([ + 'default' => false, + 'dependencies' => [], + 'files' => ['test.php'], + 'group' => 'BACK_END' + ], $result['phplistener']); + } + + public function test_create_extension_runs_without_exception() + { + // Create a partial mock for packager, stubbing get_template_engine + $packager = $this->getMockBuilder(packager::class) + ->setConstructorArgs([$this->container, $this->collection, $this->root_path]) + ->setMethods(['get_template_engine']) + ->getMock(); + + // Mock the template engine + $templateMock = $this->createMock(\phpbb\template\twig\twig::class); + $templateMock->method('set_custom_style')->willReturnSelf(); + $templateMock->method('assign_vars')->willReturnSelf(); + $templateMock->method('set_filenames')->willReturnSelf(); + $templateMock->method('assign_display')->willReturn('dummy'); + $packager->method('get_template_engine')->willReturn($templateMock); + + $this->setUpTheCollectionToReturnAFakeSkeletonClass(); + + // You can mock dependencies further as needed + $data = [ + 'extension' => [ + 'vendor_name' => 'testvendor', + 'extension_name' => 'testext', + 'extension_display_name' => 'Test Ext', + 'extension_version' => '1.0.0', + ], + 'requirements' => [ + 'phpbb_version_min' => '3.2.0', + ], + 'components' => [], + 'authors' => [], + ]; + // No assertions, just ensure no exceptions + $packager->create_extension($data); + $this->assertTrue(true); + } + + public function test_create_zip_creates_zip_file() + { + $data = [ + 'extension' => [ + 'vendor_name' => 'testvendor', + 'extension_name' => 'testext', + 'extension_version' => '1.0.0', + ], + ]; + // You might want to mock filesystem interactions or use vfsStream + $zipPath = $this->packager->create_zip($data); + $this->assertIsString($zipPath); + // Clean up or further assertions here + } + + public function test_get_language_version_data_returns_expected() + { + $data_31 = $this->invokeMethod($this->packager, 'get_language_version_data', [true]); + $data_32 = $this->invokeMethod($this->packager, 'get_language_version_data', [false]); + $this->assertArrayHasKey('class', $data_31); + $this->assertArrayHasKey('class', $data_32); + } + + public function test_get_template_engine_returns_twig_instance() + { + // Mock all required dependencies for the container + $filesystem = $this->createMock(\phpbb\filesystem\filesystem::class); + $path_helper = $this->createMock(\phpbb\path_helper::class); + $ext_manager = $this->createMock(\phpbb\extension\manager::class); + $user = $this->createMock(\phpbb\user::class); + + $this->container->expects($this->exactly(4)) + ->method('get') + ->withConsecutive( + ['path_helper'], + ['filesystem'], + ['ext.manager'], + ['user'] + ) + ->willReturnOnConsecutiveCalls( + $path_helper, + $filesystem, + $ext_manager, + $user + ); + + $this->container->expects($this->exactly(2)) + ->method('getParameter') + ->with('core.cache_dir') + ->willReturn(false); + + // Call the protected method using your invokeMethod helper + $twig = $this->invokeMethod($this->packager, 'get_template_engine'); + $this->assertInstanceOf(\phpbb\template\twig\twig::class, $twig); + } + + // Helper for protected/private method invocation + protected function invokeMethod($object, $methodName, array $parameters = []) + { + $reflection = new \ReflectionClass(get_class($object)); + $method = $reflection->getMethod($methodName); + $method->setAccessible(true); + return $method->invokeArgs($object, $parameters); + } +} diff --git a/tests/validator_test.php b/tests/helper/validator_test.php similarity index 99% rename from tests/validator_test.php rename to tests/helper/validator_test.php index ddba5a5..5c4e74e 100644 --- a/tests/validator_test.php +++ b/tests/helper/validator_test.php @@ -11,7 +11,7 @@ * */ -namespace phpbb\skeleton\tests; +namespace phpbb\skeleton\tests\helper; use phpbb\exception\runtime_exception;