mirror of
https://github.com/wintercms/winter.git
synced 2024-06-28 05:33:29 +02:00
Clean up of base plugin test case class (#672)
- Deprecate "runPluginRefreshCommand" method and replace with "instantiatePlugin", to better describe the functionality within. - Use namespace to guess plugin being detected, falling back to path if there is no namespace given - this fixes testing for symlinked plugins. - Add type hints and return types for most methods
This commit is contained in:
parent
e5fa59efab
commit
a795659fc8
@ -2,29 +2,38 @@
|
||||
|
||||
namespace System\Tests\Bootstrap;
|
||||
|
||||
use Backend\Classes\AuthManager;
|
||||
use System\Classes\UpdateManager;
|
||||
use System\Classes\PluginManager;
|
||||
use Winter\Storm\Database\Model as ActiveRecord;
|
||||
use Backend\Tests\Concerns\InteractsWithAuthentication;
|
||||
use Config;
|
||||
use Mail;
|
||||
use Config;
|
||||
use Artisan;
|
||||
use ReflectionClass;
|
||||
use Exception;
|
||||
use ReflectionClass;
|
||||
use Backend\Classes\AuthManager;
|
||||
use Backend\Tests\Concerns\InteractsWithAuthentication;
|
||||
use System\Classes\PluginBase;
|
||||
use System\Classes\PluginManager;
|
||||
use System\Classes\UpdateManager;
|
||||
use Winter\Storm\Database\Model as BaseModel;
|
||||
|
||||
/**
|
||||
* Plugin test case.
|
||||
*
|
||||
* The base test case that should be used for plugin tests. It instantiates the given plugin and
|
||||
* its dependencies, and ensures that the plugin is available for use within the tests.
|
||||
*
|
||||
* @package winter/wn-system-module
|
||||
*/
|
||||
abstract class PluginTestCase extends TestCase
|
||||
{
|
||||
use InteractsWithAuthentication;
|
||||
|
||||
/**
|
||||
* @var array Cache for storing which plugins have been loaded
|
||||
* and refreshed.
|
||||
* @var array Cache for storing which plugins have been loaded.
|
||||
*/
|
||||
protected $pluginTestCaseLoadedPlugins = [];
|
||||
|
||||
/**
|
||||
* Creates the application.
|
||||
*
|
||||
* @return \Symfony\Component\HttpKernel\HttpKernelInterface
|
||||
*/
|
||||
public function createApplication()
|
||||
@ -41,9 +50,7 @@ abstract class PluginTestCase extends TestCase
|
||||
return AuthManager::instance();
|
||||
});
|
||||
|
||||
/*
|
||||
* Store database in memory by default unless specified otherwise
|
||||
*/
|
||||
// Store database in memory by default unless otherwise specified
|
||||
if (!file_exists(base_path('config/testing/database.php'))) {
|
||||
$app['config']->set('database.connections.testing', [
|
||||
'driver' => 'sqlite',
|
||||
@ -55,9 +62,7 @@ abstract class PluginTestCase extends TestCase
|
||||
// Set random encryption key
|
||||
$app['config']->set('app.key', bin2hex(random_bytes(16)));
|
||||
|
||||
/*
|
||||
* Modify the plugin path away from the test context
|
||||
*/
|
||||
// Modify the plugin path away from the test context
|
||||
$app->setPluginsPath(realpath(base_path() . Config::get('cms.pluginsPath')));
|
||||
|
||||
return $app;
|
||||
@ -65,47 +70,35 @@ abstract class PluginTestCase extends TestCase
|
||||
|
||||
/**
|
||||
* Perform test case set up.
|
||||
* @return void
|
||||
*/
|
||||
public function setUp() : void
|
||||
public function setUp(): void
|
||||
{
|
||||
/*
|
||||
* Force reload of Winter singletons
|
||||
*/
|
||||
// Reload the plugin and update manager singletons
|
||||
PluginManager::forgetInstance();
|
||||
UpdateManager::forgetInstance();
|
||||
|
||||
/*
|
||||
* Create application instance
|
||||
*/
|
||||
parent::setUp();
|
||||
|
||||
/*
|
||||
* Ensure system is up to date
|
||||
*/
|
||||
$this->runWinterUpCommand();
|
||||
// Run all migrations
|
||||
Artisan::call('winter:up');
|
||||
|
||||
/*
|
||||
* Detect plugin from test and autoload it
|
||||
*/
|
||||
// Reset loaded plugins for this test
|
||||
$this->pluginTestCaseLoadedPlugins = [];
|
||||
$pluginCode = $this->guessPluginCodeFromTest();
|
||||
|
||||
if ($pluginCode !== false) {
|
||||
$this->runPluginRefreshCommand($pluginCode, false);
|
||||
// Detect the plugin being tested and load it automatically
|
||||
$pluginCode = $this->guessPluginCode();
|
||||
if (!is_null($pluginCode)) {
|
||||
$this->instantiatePlugin($pluginCode, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable mailer
|
||||
*/
|
||||
// Disable mailing
|
||||
Mail::pretend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush event listeners and collect garbage.
|
||||
* @return void
|
||||
* Flush event listeners and tear down.
|
||||
*/
|
||||
public function tearDown() : void
|
||||
public function tearDown(): void
|
||||
{
|
||||
$this->flushModelEventListeners();
|
||||
parent::tearDown();
|
||||
@ -113,24 +106,30 @@ abstract class PluginTestCase extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate database using winter:up command.
|
||||
* @return void
|
||||
*/
|
||||
protected function runWinterUpCommand()
|
||||
{
|
||||
Artisan::call('winter:up');
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the test environment has loaded all the test plugins
|
||||
* natively, this method will ensure the desired plugin is
|
||||
* loaded in the system before proceeding to migrate it.
|
||||
* Refreshes a plugin for testing.
|
||||
*
|
||||
* Since the test environment has loaded all the test plugins natively, this method will ensure
|
||||
* the desired plugin is loaded in the system before proceeding to migrate it.
|
||||
*
|
||||
* @deprecated v1.2.1 Use `instantiatePlugin()` instead.
|
||||
* @return void
|
||||
*/
|
||||
protected function runPluginRefreshCommand($code, $throwException = true)
|
||||
{
|
||||
$this->instantiatePlugin((string) $code, (bool) $throwException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a plugin for testing.
|
||||
*
|
||||
* @param string $code Plugin code.
|
||||
* @param boolean $throw Throw an exception if the plugin cannot be found.
|
||||
*/
|
||||
protected function instantiatePlugin(string $code, bool $throw = true): void
|
||||
{
|
||||
// Check plugin code is valid
|
||||
if (!preg_match('/^[\w+]*\.[\w+]*$/', $code)) {
|
||||
if (!$throwException) {
|
||||
if (!$throw) {
|
||||
return;
|
||||
}
|
||||
throw new Exception(sprintf('Invalid plugin code: "%s"', $code));
|
||||
@ -138,16 +137,15 @@ abstract class PluginTestCase extends TestCase
|
||||
|
||||
$manager = PluginManager::instance();
|
||||
$plugin = $manager->findByIdentifier($code);
|
||||
$firstLoad = !$plugin;
|
||||
|
||||
/*
|
||||
* First time seeing this plugin, load it up
|
||||
*/
|
||||
if (!$plugin) {
|
||||
// First time seeing this plugin, load it up
|
||||
if ($firstLoad) {
|
||||
$namespace = '\\'.str_replace('.', '\\', strtolower($code));
|
||||
$path = array_get($manager->getPluginNamespaces(), $namespace);
|
||||
|
||||
if (!$path) {
|
||||
if (!$throwException) {
|
||||
if (!$throw) {
|
||||
return;
|
||||
}
|
||||
throw new Exception(sprintf('Unable to find plugin with code: "%s"', $code));
|
||||
@ -157,49 +155,47 @@ abstract class PluginTestCase extends TestCase
|
||||
$manager->registerPlugin($plugin);
|
||||
}
|
||||
|
||||
/*
|
||||
* Spin over dependencies and refresh them too
|
||||
*/
|
||||
$this->pluginTestCaseLoadedPlugins[$code] = $plugin;
|
||||
|
||||
// Load any dependencies
|
||||
if (!empty($plugin->require)) {
|
||||
foreach ((array) $plugin->require as $dependency) {
|
||||
if (isset($this->pluginTestCaseLoadedPlugins[$dependency])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->runPluginRefreshCommand($dependency);
|
||||
$this->instantiatePlugin($dependency);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the command
|
||||
*/
|
||||
// Refresh the plugin's tables
|
||||
Artisan::call('plugin:refresh', ['plugin' => $code, '--force' => true]);
|
||||
|
||||
// Boot the plugin if this is the first load
|
||||
if ($firstLoad) {
|
||||
$manager->bootPlugin($plugin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a plugin object from its code, useful for registering events, etc.
|
||||
* @return PluginBase
|
||||
*/
|
||||
protected function getPluginObject($code = null)
|
||||
protected function getPluginObject($code = null): ?PluginBase
|
||||
{
|
||||
if ($code === null) {
|
||||
$code = $this->guessPluginCodeFromTest();
|
||||
}
|
||||
|
||||
if (isset($this->pluginTestCaseLoadedPlugins[$code])) {
|
||||
return $this->pluginTestCaseLoadedPlugins[$code];
|
||||
}
|
||||
return $this->pluginTestCaseLoadedPlugins[$code]
|
||||
?? $this->pluginTestCaseLoadedPlugins[$this->guessPluginCode()]
|
||||
?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The models in Winter use a static property to store their events, these
|
||||
* will need to be targeted and reset ready for a new test cycle.
|
||||
* Flush model event listeners.
|
||||
*
|
||||
* The models in Winter use a static property to store their events. These will need to be
|
||||
* targeted and reset, ready for a new test cycle.
|
||||
*
|
||||
* Pivot models are an exception since they are internally managed.
|
||||
* @return void
|
||||
*/
|
||||
protected function flushModelEventListeners()
|
||||
protected function flushModelEventListeners(): void
|
||||
{
|
||||
foreach (get_declared_classes() as $class) {
|
||||
if ($class === 'Winter\Storm\Database\Pivot' || strtolower($class) === 'october\rain\database\pivot') {
|
||||
@ -218,26 +214,35 @@ abstract class PluginTestCase extends TestCase
|
||||
$class::flushEventListeners();
|
||||
}
|
||||
|
||||
ActiveRecord::flushEventListeners();
|
||||
BaseModel::flushEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the plugin code based on the test file location.
|
||||
* @return string|bool
|
||||
* Guesses the plugin code being tested.
|
||||
*/
|
||||
protected function guessPluginCodeFromTest()
|
||||
protected function guessPluginCode(): ?string
|
||||
{
|
||||
$reflect = new ReflectionClass($this);
|
||||
$path = $reflect->getFilename();
|
||||
$basePath = $this->app->pluginsPath();
|
||||
$fqClass = $reflect->getName();
|
||||
$namespace = $reflect->getNamespaceName();
|
||||
|
||||
$result = false;
|
||||
if (empty($namespace)) {
|
||||
// Try to determine from the path instead
|
||||
$path = $reflect->getFilename();
|
||||
$basePath = $this->app->pluginsPath();
|
||||
|
||||
if (strpos($path, $basePath) === 0) {
|
||||
$result = ltrim(str_replace('\\', '/', substr($path, strlen($basePath))), '/');
|
||||
$result = implode('.', array_slice(explode('/', $result), 0, 2));
|
||||
if (!strpos($path, $basePath) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pluginCode = ltrim(str_replace('\\', '/', substr($path, strlen($basePath))), '/');
|
||||
$pluginCode = implode('.', array_slice(explode('/', $pluginCode), 0, 2));
|
||||
} else {
|
||||
// Determine code from namespace
|
||||
$manager = PluginManager::instance();
|
||||
$pluginCode = $manager->getIdentifier($fqClass);
|
||||
}
|
||||
|
||||
return $result;
|
||||
return $pluginCode;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user