Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
|
@ -0,0 +1,249 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\TestSite\Commands;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Test\FunctionalTestSetupTrait;
|
||||
use Drupal\Core\Test\TestDatabase;
|
||||
use Drupal\Core\Test\TestSetupTrait;
|
||||
use Drupal\TestSite\TestSetupInterface;
|
||||
use Drupal\Tests\RandomGeneratorTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* Command to create a test Drupal site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TestSiteInstallCommand extends Command {
|
||||
|
||||
use FunctionalTestSetupTrait {
|
||||
installParameters as protected installParametersTrait;
|
||||
}
|
||||
use RandomGeneratorTrait;
|
||||
use TestSetupTrait {
|
||||
changeDatabasePrefix as protected changeDatabasePrefixTrait;
|
||||
}
|
||||
|
||||
/**
|
||||
* The install profile to use.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Time limit in seconds for the test.
|
||||
*
|
||||
* Used by \Drupal\Core\Test\FunctionalTestSetupTrait::prepareEnvironment().
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $timeLimit = 500;
|
||||
|
||||
/**
|
||||
* The database prefix of this test run.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $databasePrefix;
|
||||
|
||||
/**
|
||||
* The language to install the site in.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $langcode = 'en';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this->setName('install')
|
||||
->setDescription('Creates a test Drupal site')
|
||||
->setHelp('The details to connect to the test site created will be displayed upon success. It will contain the database prefix and the user agent.')
|
||||
->addOption('setup-file', NULL, InputOption::VALUE_OPTIONAL, 'The path to a PHP file containing a class to setup configuration used by the test, for example, core/tests/Drupal/TestSite/TestSiteInstallTestScript.php.')
|
||||
->addOption('db-url', NULL, InputOption::VALUE_OPTIONAL, 'URL for database. Defaults to the environment variable SIMPLETEST_DB.', getenv('SIMPLETEST_DB'))
|
||||
->addOption('base-url', NULL, InputOption::VALUE_OPTIONAL, 'Base URL for site under test. Defaults to the environment variable SIMPLETEST_BASE_URL.', getenv('SIMPLETEST_BASE_URL'))
|
||||
->addOption('install-profile', NULL, InputOption::VALUE_OPTIONAL, 'Install profile to install the site in. Defaults to testing.', 'testing')
|
||||
->addOption('langcode', NULL, InputOption::VALUE_OPTIONAL, 'The language to install the site in. Defaults to en.', 'en')
|
||||
->addOption('json', NULL, InputOption::VALUE_NONE, 'Output test site connection details in JSON.')
|
||||
->addUsage('--setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --json')
|
||||
->addUsage('--install-profile demo_umami --langcode fr')
|
||||
->addUsage('--base-url "http://example.com" --db-url "mysql://username:password@localhost/databasename#table_prefix"');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
// Determines and validates the setup class prior to installing a database
|
||||
// to avoid creating unnecessary sites.
|
||||
$root = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
|
||||
chdir($root);
|
||||
$class_name = $this->getSetupClass($input->getOption('setup-file'));
|
||||
// Ensure we can install a site in the sites/simpletest directory.
|
||||
$this->ensureDirectory($root);
|
||||
|
||||
$db_url = $input->getOption('db-url');
|
||||
$base_url = $input->getOption('base-url');
|
||||
putenv("SIMPLETEST_DB=$db_url");
|
||||
putenv("SIMPLETEST_BASE_URL=$base_url");
|
||||
|
||||
// Manage site fixture.
|
||||
$this->setup($input->getOption('install-profile'), $class_name, $input->getOption('langcode'));
|
||||
|
||||
$user_agent = drupal_generate_test_ua($this->databasePrefix);
|
||||
if ($input->getOption('json')) {
|
||||
$output->writeln(json_encode([
|
||||
'db_prefix' => $this->databasePrefix,
|
||||
'user_agent' => $user_agent,
|
||||
'site_path' => $this->siteDirectory,
|
||||
]));
|
||||
}
|
||||
else {
|
||||
$output->writeln('<info>Successfully installed a test site</info>');
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->table([], [
|
||||
['Database prefix', $this->databasePrefix],
|
||||
['User agent', $user_agent],
|
||||
['Site path', $this->siteDirectory],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the setup class.
|
||||
*
|
||||
* @param string|null $file
|
||||
* The file to get the setup class from.
|
||||
*
|
||||
* @return string|null
|
||||
* The setup class contained in the provided $file.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* Thrown if the file does not exist, does not contain a class or the class
|
||||
* does not implement \Drupal\TestSite\TestSetupInterface.
|
||||
*/
|
||||
protected function getSetupClass($file) {
|
||||
if ($file === NULL) {
|
||||
return;
|
||||
}
|
||||
if (!file_exists($file)) {
|
||||
throw new \InvalidArgumentException("The file $file does not exist.");
|
||||
}
|
||||
|
||||
$classes = get_declared_classes();
|
||||
include_once $file;
|
||||
$new_classes = array_values(array_diff(get_declared_classes(), $classes));
|
||||
if (empty($new_classes)) {
|
||||
throw new \InvalidArgumentException("The file $file does not contain a class.");
|
||||
}
|
||||
$class = array_pop($new_classes);
|
||||
|
||||
if (!is_subclass_of($class, TestSetupInterface::class)) {
|
||||
throw new \InvalidArgumentException("The class $class contained in $file needs to implement \Drupal\TestSite\TestSetupInterface");
|
||||
}
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the sites/simpletest directory exists and is writable.
|
||||
*
|
||||
* @param string $root
|
||||
* The Drupal root.
|
||||
*/
|
||||
protected function ensureDirectory($root) {
|
||||
if (!is_writable($root . '/sites/simpletest')) {
|
||||
if (!@mkdir($root . '/sites/simpletest')) {
|
||||
throw new \RuntimeException($root . '/sites/simpletest must exist and be writable to install a test site');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a test drupal installation.
|
||||
*
|
||||
* @param string $profile
|
||||
* (optional) The installation profile to use.
|
||||
* @param string $setup_class
|
||||
* (optional) Setup class. A PHP class to setup configuration used by the
|
||||
* test.
|
||||
* @param string $langcode
|
||||
* (optional) The language to install the site in.
|
||||
*/
|
||||
public function setup($profile = 'testing', $setup_class = NULL, $langcode = 'en') {
|
||||
$this->profile = $profile;
|
||||
$this->langcode = $langcode;
|
||||
$this->setupBaseUrl();
|
||||
$this->prepareEnvironment();
|
||||
$this->installDrupal();
|
||||
|
||||
if ($setup_class) {
|
||||
$this->executeSetupClass($setup_class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs Drupal into the test site.
|
||||
*/
|
||||
protected function installDrupal() {
|
||||
$this->initUserSession();
|
||||
$this->prepareSettings();
|
||||
$this->doInstall();
|
||||
$this->initSettings();
|
||||
$container = $this->initKernel(\Drupal::request());
|
||||
$this->initConfig($container);
|
||||
$this->installModulesFromClassProperty($container);
|
||||
$this->rebuildAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the setup file to configure Drupal.
|
||||
*
|
||||
* @param string $class
|
||||
* The fully qualified class name, which should set up Drupal for tests. For
|
||||
* example this class could create content types and fields or install
|
||||
* modules. The class needs to implement TestSetupInterface.
|
||||
*
|
||||
* @see \Drupal\TestSite\TestSetupInterface
|
||||
*/
|
||||
protected function executeSetupClass($class) {
|
||||
/** @var \Drupal\TestSite\TestSetupInterface $instance */
|
||||
$instance = new $class();
|
||||
$instance->setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function installParameters() {
|
||||
$parameters = $this->installParametersTrait();
|
||||
$parameters['parameters']['langcode'] = $this->langcode;
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function changeDatabasePrefix() {
|
||||
// Ensure that we use the database from SIMPLETEST_DB environment variable.
|
||||
Database::removeConnection('default');
|
||||
$this->changeDatabasePrefixTrait();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepareDatabasePrefix() {
|
||||
// Override this method so that we can force a lock to be created.
|
||||
$test_db = new TestDatabase(NULL, TRUE);
|
||||
$this->siteDirectory = $test_db->getTestSitePath();
|
||||
$this->databasePrefix = $test_db->getDatabasePrefix();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\TestSite\Commands;
|
||||
|
||||
use Drupal\Core\Test\TestDatabase;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Command to release all test site database prefix locks.
|
||||
*
|
||||
* Note that this command can't be safely tested by DrupalCI without potentially
|
||||
* causing random failures.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TestSiteReleaseLocksCommand extends Command {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this->setName('release-locks')
|
||||
->setDescription('Releases all test site locks')
|
||||
->setHelp('The locks ensure test site database prefixes are not reused.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
TestDatabase::releaseAllTestLocks();
|
||||
$output->writeln('<info>Successfully released all the test database locks</info>');
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\TestSite\Commands;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Test\TestDatabase;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* Command to tear down a test Drupal site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TestSiteTearDownCommand extends Command {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this->setName('tear-down')
|
||||
->setDescription('Removes a test site added by the install command')
|
||||
->setHelp('All the database tables and files will be removed.')
|
||||
->addArgument('db-prefix', InputArgument::REQUIRED, 'The database prefix for the test site.')
|
||||
->addOption('db-url', NULL, InputOption::VALUE_OPTIONAL, 'URL for database. Defaults to the environment variable SIMPLETEST_DB.', getenv('SIMPLETEST_DB'))
|
||||
->addOption('keep-lock', NULL, InputOption::VALUE_NONE, 'Keeps the database prefix lock. Useful for ensuring test isolation when running concurrent tests.')
|
||||
->addUsage('test12345678')
|
||||
->addUsage('test12345678 --db-url "mysql://username:password@localhost/databasename#table_prefix"')
|
||||
->addUsage('test12345678 --keep-lock');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$db_prefix = $input->getArgument('db-prefix');
|
||||
// Validate the db_prefix argument.
|
||||
try {
|
||||
$test_database = new TestDatabase($db_prefix);
|
||||
}
|
||||
catch (\InvalidArgumentException $e) {
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
$io->getErrorStyle()->error("Invalid database prefix: $db_prefix\n\nValid database prefixes match the regular expression '/test(\d+)$/'. For example, 'test12345678'.");
|
||||
// Display the synopsis of the command like Composer does.
|
||||
$output->writeln(sprintf('<info>%s</info>', sprintf($this->getSynopsis(), $this->getName())), OutputInterface::VERBOSITY_QUIET);
|
||||
return 1;
|
||||
}
|
||||
|
||||
$db_url = $input->getOption('db-url');
|
||||
putenv("SIMPLETEST_DB=$db_url");
|
||||
|
||||
// Handle the cleanup of the test site.
|
||||
$this->tearDown($test_database, $db_url);
|
||||
|
||||
// Release the test database prefix lock.
|
||||
if (!$input->getOption('keep-lock')) {
|
||||
$test_database->releaseLock();
|
||||
}
|
||||
|
||||
$output->writeln("<info>Successfully uninstalled $db_prefix test site</info>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a given instance by deleting all the database tables and files.
|
||||
*
|
||||
* @param \Drupal\Core\Test\TestDatabase $test_database
|
||||
* The test database object.
|
||||
* @param string $db_url
|
||||
* The database URL.
|
||||
*
|
||||
* @see \Drupal\Tests\BrowserTestBase::cleanupEnvironment()
|
||||
*/
|
||||
protected function tearDown(TestDatabase $test_database, $db_url) {
|
||||
// Connect to the test database.
|
||||
$root = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
|
||||
$database = Database::convertDbUrlToConnectionInfo($db_url, $root);
|
||||
$database['prefix'] = ['default' => $test_database->getDatabasePrefix()];
|
||||
Database::addConnectionInfo(__CLASS__, 'default', $database);
|
||||
|
||||
// Remove all the tables.
|
||||
$schema = Database::getConnection('default', __CLASS__)->schema();
|
||||
$tables = $schema->findTables('%');
|
||||
array_walk($tables, [$schema, 'dropTable']);
|
||||
|
||||
// Delete test site directory.
|
||||
$this->fileUnmanagedDeleteRecursive($root . DIRECTORY_SEPARATOR . $test_database->getTestSitePath(), [BrowserTestBase::class, 'filePreDeleteCallback']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all files and directories in the specified path recursively.
|
||||
*
|
||||
* Note this method has no dependencies on Drupal core to ensure that the
|
||||
* test site can be torn down even if something in the test site is broken.
|
||||
*
|
||||
* @param string $path
|
||||
* A string containing either an URI or a file or directory path.
|
||||
* @param callable $callback
|
||||
* (optional) Callback function to run on each file prior to deleting it and
|
||||
* on each directory prior to traversing it. For example, can be used to
|
||||
* modify permissions.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE for success or if path does not exist, FALSE in the event of an
|
||||
* error.
|
||||
*
|
||||
* @see file_unmanaged_delete_recursive()
|
||||
*/
|
||||
protected function fileUnmanagedDeleteRecursive($path, $callback = NULL) {
|
||||
if (isset($callback)) {
|
||||
call_user_func($callback, $path);
|
||||
}
|
||||
if (is_dir($path)) {
|
||||
$dir = dir($path);
|
||||
while (($entry = $dir->read()) !== FALSE) {
|
||||
if ($entry == '.' || $entry == '..') {
|
||||
continue;
|
||||
}
|
||||
$entry_path = $path . '/' . $entry;
|
||||
$this->fileUnmanagedDeleteRecursive($entry_path, $callback);
|
||||
}
|
||||
$dir->close();
|
||||
|
||||
return rmdir($path);
|
||||
}
|
||||
return unlink($path);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\TestSite\Commands;
|
||||
|
||||
use Drupal\Core\DrupalKernel;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* Command to generate a login link for the test site.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TestSiteUserLoginCommand extends Command {
|
||||
|
||||
/**
|
||||
* The class loader to use for installation and initialization of setup.
|
||||
*
|
||||
* @var \Symfony\Component\Classloader\Classloader
|
||||
*/
|
||||
protected $classLoader;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure() {
|
||||
$this->setName('user-login')
|
||||
->setDescription('Generate a one time login link for an user.')
|
||||
->addArgument('uid', InputArgument::REQUIRED, 'The ID of the user for whom the link will be generated')
|
||||
->addOption('site-path', NULL, InputOption::VALUE_REQUIRED, 'The path for the test site.');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws \Symfony\Component\Console\Exception\InvalidArgumentException
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output) {
|
||||
$root = dirname(dirname(dirname(dirname(dirname(__DIR__)))));
|
||||
chdir($root);
|
||||
|
||||
$this->classLoader = require 'autoload.php';
|
||||
$kernel = new DrupalKernel('prod', $this->classLoader, FALSE);
|
||||
$kernel::bootEnvironment();
|
||||
$kernel->setSitePath($input->getOption('site-path'));
|
||||
Settings::initialize($kernel->getAppRoot(), $kernel->getSitePath(), $this->classLoader);
|
||||
|
||||
$request = Request::createFromGlobals();
|
||||
$kernel->prepareLegacyRequest($request);
|
||||
|
||||
$kernel->boot();
|
||||
|
||||
$container = $kernel->getContainer();
|
||||
$uid = $input->getArgument('uid');
|
||||
if (!is_numeric($uid)) {
|
||||
throw new InvalidArgumentException(sprintf('The "uid" argument needs to be an integer, but it is "%s".', $uid));
|
||||
}
|
||||
$userEntity = $container->get('entity_type.manager')
|
||||
->getStorage('user')
|
||||
->load($uid);
|
||||
$url = user_pass_reset_url($userEntity) . '/login';
|
||||
$output->writeln($url);
|
||||
}
|
||||
|
||||
}
|
27
web/core/tests/Drupal/TestSite/TestSetupInterface.php
Normal file
27
web/core/tests/Drupal/TestSite/TestSetupInterface.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\TestSite;
|
||||
|
||||
/**
|
||||
* Allows setting up an environment as part of a test site install.
|
||||
*
|
||||
* @see \Drupal\TestSite\Commands\TestSiteInstallCommand
|
||||
*/
|
||||
interface TestSetupInterface {
|
||||
|
||||
/**
|
||||
* Run the code to setup the test environment.
|
||||
*
|
||||
* You have access to any API provided by any installed module. For example,
|
||||
* to install modules use:
|
||||
* @code
|
||||
* \Drupal::service('module_installer')->install(['my_module'])
|
||||
* @endcode
|
||||
*
|
||||
* Check out TestSiteInstallTestScript for an example.
|
||||
*
|
||||
* @see \Drupal\TestSite\TestSiteInstallTestScript
|
||||
*/
|
||||
public function setup();
|
||||
|
||||
}
|
33
web/core/tests/Drupal/TestSite/TestSiteApplication.php
Normal file
33
web/core/tests/Drupal/TestSite/TestSiteApplication.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\TestSite;
|
||||
|
||||
use Drupal\TestSite\Commands\TestSiteInstallCommand;
|
||||
use Drupal\TestSite\Commands\TestSiteReleaseLocksCommand;
|
||||
use Drupal\TestSite\Commands\TestSiteTearDownCommand;
|
||||
use Drupal\TestSite\Commands\TestSiteUserLoginCommand;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
/**
|
||||
* Application wrapper for test site commands.
|
||||
*
|
||||
* In order to see what commands are available and how to use them run
|
||||
* "php core/scripts/test-site.php" from command line and use the help system.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TestSiteApplication extends Application {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getDefaultCommands() {
|
||||
$default_commands = parent::getDefaultCommands();
|
||||
$default_commands[] = new TestSiteInstallCommand();
|
||||
$default_commands[] = new TestSiteTearDownCommand();
|
||||
$default_commands[] = new TestSiteReleaseLocksCommand();
|
||||
$default_commands[] = new TestSiteUserLoginCommand();
|
||||
return $default_commands;
|
||||
}
|
||||
|
||||
}
|
19
web/core/tests/Drupal/TestSite/TestSiteInstallTestScript.php
Normal file
19
web/core/tests/Drupal/TestSite/TestSiteInstallTestScript.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\TestSite;
|
||||
|
||||
/**
|
||||
* Setup file used by TestSiteApplicationTest.
|
||||
*
|
||||
* @see \Drupal\Tests\Scripts\TestSiteApplicationTest
|
||||
*/
|
||||
class TestSiteInstallTestScript implements TestSetupInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setup() {
|
||||
\Drupal::service('module_installer')->install(['test_page_test']);
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue