<?php declare(strict_types=1); namespace Tests\Modules\Plugins; use App\Entities\Episode; use App\Entities\Podcast; use App\Libraries\HtmlHead; use App\Libraries\RssFeed; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use Modules\Plugins\Config\Plugins as PluginsConfig; use Modules\Plugins\Core\Plugins; use Modules\Plugins\Core\PluginStatus; use Override; /** * @internal */ final class PluginsTest extends CIUnitTestCase { use DatabaseTestTrait; protected $migrate = true; protected $migrateOnce = false; protected $refresh = true; /** * @var string|null */ protected $namespace; protected static Plugins $plugins; #[Override] public static function setUpBeforeClass(): void { $pluginsConfig = new PluginsConfig(); $pluginsConfig->folder = __DIR__ . '/mocks/plugins' . DIRECTORY_SEPARATOR; self::$plugins = new Plugins($pluginsConfig); } public function testRegister(): void { $this->assertCount(7, self::$plugins->getAllPlugins()); $this->assertEquals(7, self::$plugins->getInstalledCount()); $this->assertEquals(0, self::$plugins->getActiveCount()); } public function testActivateDeactivate(): void { $this->assertEquals(0, self::$plugins->getActiveCount()); $plugin = self::$plugins->getAllPlugins()[0]; // get first plugin and activate it self::$plugins->activate($plugin); $this->assertEquals(1, self::$plugins->getActiveCount()); $this->assertEquals(PluginStatus::ACTIVE, $plugin->getStatus()); $this->seeInDatabase('settings', [ 'class' => PluginsConfig::class, 'key' => 'active', 'value' => '1', 'type' => 'boolean', 'context' => 'plugin:' . $plugin->getKey(), ]); // get first plugin and deactivate it self::$plugins->deactivate($plugin); $this->assertEquals(0, self::$plugins->getActiveCount()); $this->assertEquals(PluginStatus::INACTIVE, $plugin->getStatus()); $this->seeInDatabase('settings', [ 'class' => PluginsConfig::class, 'key' => 'active', 'value' => '0', 'type' => 'boolean', 'context' => 'plugin:' . $plugin->getKey(), ]); } public function testRunHooksActive(): void { $acmeAllHooksPlugin = self::$plugins->getPlugin('acme', 'all-hooks'); self::$plugins->activate($acmeAllHooksPlugin); $this->assertEquals(1, self::$plugins->getActiveCount()); $podcast = new Podcast(); $this->assertEquals('', $podcast->title); self::$plugins->runHook('rssBeforeChannel', [$podcast]); $this->assertEquals('Podcast test', $podcast->title); $channel = new RssFeed('<channel></channel>'); $this->assertTrue(empty($channel->foo)); self::$plugins->runHook('rssAfterChannel', [$podcast, $channel]); $this->assertFalse(empty($channel->foo)); $episode = new Episode(); $this->assertEquals('', $episode->title); self::$plugins->runHook('rssBeforeItem', [$episode]); $this->assertEquals('Episode test', $episode->title); $item = new RssFeed('<item></item>'); $this->assertTrue(empty($item->efoo)); self::$plugins->runHook('rssAfterItem', [$episode, $item]); $this->assertFalse(empty($item->efoo)); $head = new HtmlHead(); self::$plugins->runHook('siteHead', [$head]); $this->assertEquals( (string) $head, '<head> <title >foo</title> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <link rel="icon" type="image/x-icon" href="' . base_url( 'favicon.ico' ) . '"/> <link rel="apple-touch-icon" href="' . base_url( 'icon-180.png' ) . '"/> <link rel="manifest" href="/manifest.webmanifest"/> <meta name="theme-color" content="#009486"/> <link rel="stylesheet" type="text/css" href="/themes/colors"/> <title >foo</title> <script> // Check that service workers are supported if (\'serviceWorker\' in navigator) { // Use the window load event to keep the page load performant window.addEventListener(\'load\', () => { navigator.serviceWorker.register(\'/sw.js\'); }); } </script></head>' ); } public function testRunHooksInactive(): void { $acmeAllHooksPlugin = self::$plugins->getPlugin('acme', 'all-hooks'); self::$plugins->deactivate($acmeAllHooksPlugin); $this->assertEquals(0, self::$plugins->getActiveCount()); // nothing should change when running hooks as the plugin is inactive $podcast = new Podcast(); $this->assertEquals('', $podcast->title); self::$plugins->runHook('rssBeforeChannel', [$podcast]); $this->assertEquals('', $podcast->title); $channel = new RssFeed('<channel></channel>'); $this->assertTrue(empty($channel->foo)); self::$plugins->runHook('rssAfterChannel', [$podcast, $channel]); $this->assertTrue(empty($channel->foo)); $episode = new Episode(); $this->assertEquals('', $episode->title); self::$plugins->runHook('rssBeforeItem', [$episode]); $this->assertEquals('', $episode->title); $item = new RssFeed('<item></item>'); $this->assertTrue(empty($item->efoo)); self::$plugins->runHook('rssAfterItem', [$episode, $item]); $this->assertTrue(empty($item->efoo)); ob_start(); self::$plugins->runHook('siteHead', []); $result = ob_get_contents(); ob_end_clean(); //Discard output buffer $this->assertEquals('', $result); } public function testRunUndeclaredHook(): void { $acmeUndeclaredHookPlugin = self::$plugins->getPlugin('acme', 'undeclared-hook'); self::$plugins->activate($acmeUndeclaredHookPlugin); $podcast = new Podcast(); $this->assertEquals('', $podcast->title); self::$plugins->runHook('rssBeforeChannel', [$podcast]); $this->assertEquals('Podcast test undeclared', $podcast->title); // rssAfterChannel has not been declared in plugin manifest, should not be running $channel = new RssFeed('<channel></channel>'); $this->assertTrue(empty($channel->foo)); self::$plugins->runHook('rssAfterChannel', [$podcast, $channel]); $this->assertTrue(empty($channel->foo)); } }