diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalCreateRole.js b/core/tests/Drupal/Nightwatch/Commands/drupalCreateRole.js new file mode 100644 index 0000000000000000000000000000000000000000..e3903ae48755aea94ef60095a811c4878c4acca3 --- /dev/null +++ b/core/tests/Drupal/Nightwatch/Commands/drupalCreateRole.js @@ -0,0 +1,59 @@ +/** + * Creates role with given permissions. + * + * @param {object} settings + * Settings object + * @param {array} settings.permissions + * The list of roles granted for the user. + * @param {string} [settings.name=null] + * The role name. + * @param {function} callback + * A callback which will be called, when creating the role is finished. + * @return {object} + * The drupalCreateRole command. + */ +exports.command = function drupalCreateRole({ permissions, name = null }, callback) { + const self = this; + const roleName = name || Math.random().toString(36).substring(2, 15); + + let machineName; + this + .drupalLoginAsAdmin(() => { + this + .drupalRelativeURL('/admin/people/roles/add') + .setValue('input[name="label"]', roleName) + // Wait for the machine name to appear so that it can be used later to + // select the permissions from the permission page. + .expect.element('.user-role-form .machine-name-value').to.be.visible.before(2000); + + this.perform((done) => { + this.getText('.user-role-form .machine-name-value', (element) => { + machineName = element.value; + done(); + }); + }) + .submitForm('#user-role-form') + .drupalRelativeURL('/admin/people/permissions') + .perform((client, done) => { + Promise.all( + permissions.map(permission => ( + new Promise((resolve) => { + client.click(`input[name="${machineName}[${permission}]"]`, () => { + resolve(); + }); + }) + )), + ).then(() => { + done(); + }); + }) + .submitForm('#user-admin-permissions'); + }) + .perform(() => { + if (typeof callback === 'function') { + callback.call(self, machineName); + } + }); + + return this; +}; diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalCreateUser.js b/core/tests/Drupal/Nightwatch/Commands/drupalCreateUser.js new file mode 100644 index 0000000000000000000000000000000000000000..7297cdf0c79492167bd68dbeb2a6759565ad82c1 --- /dev/null +++ b/core/tests/Drupal/Nightwatch/Commands/drupalCreateUser.js @@ -0,0 +1,58 @@ +/** + * Logs into Drupal as the given user. + * + * @param {object} settings + * Settings object + * @param {string} settings.name + * The user name. + * @param {string} settings.password + * The user password. + * @param {array} [settings.permissions=[]] + * The list of permissions granted for the user. + * @param {function} callback + * A callback which will be called, when the creating the use is finished. + * @return {object} + * The drupalCreateUser command. + */ +exports.command = function drupalCreateUser({ name, password, permissions = [] }, callback) { + const self = this; + + let role; + this + .perform((client, done) => { + if (permissions) { + client.drupalCreateRole({ permissions, name: null }, (newRole) => { + role = newRole; + done(); + }); + } + else { + done(); + } + }) + .drupalLoginAsAdmin(() => { + this + .drupalRelativeURL('/admin/people/create') + .setValue('input[name="name"]', name) + .setValue('input[name="pass[pass1]"]', password) + .setValue('input[name="pass[pass2]"]', password) + .perform((client, done) => { + if (role) { + client.click(`input[name="roles[${role}]`, () => { + done(); + }); + } + else { + done(); + } + }) + .submitForm('#user-register-form') + .assert.containsText('.messages', 'Created a new user account', `User "${name}" was created succesfully.`); + }); + + if (typeof callback === 'function') { + callback.call(self); + } + + return this; +}; diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalInstall.js b/core/tests/Drupal/Nightwatch/Commands/drupalInstall.js index 2f35adfab5552bbd67a96c0b55f86a7db0e20c8e..176eeef0f00065c5bc417ebced57b6e568602867 100644 --- a/core/tests/Drupal/Nightwatch/Commands/drupalInstall.js +++ b/core/tests/Drupal/Nightwatch/Commands/drupalInstall.js @@ -5,16 +5,16 @@ import { commandAsWebserver } from '../globals'; /** * Installs a Drupal test site. * - * @param {Object} - * (optional) Settings object - * @param setupFile - * (optional) Setup file used by TestSiteApplicationTest + * @param {oject} [settings={}] + * Settings object + * @param {string} [settings.setupFile=''] + * Setup file used by TestSiteApplicationTest * @param {function} callback * A callback which will be called, when the installation is finished. * @return {object} * The 'browser' object. */ -exports.command = function drupalInstall({ setupFile = '' }, callback) { +exports.command = function drupalInstall({ setupFile = '' } = {}, callback) { const self = this; try { @@ -23,6 +23,7 @@ exports.command = function drupalInstall({ setupFile = '' }, callback) { const install = execSync(commandAsWebserver(`php ./scripts/test-site.php install ${setupFile} --base-url ${process.env.DRUPAL_TEST_BASE_URL} ${dbOption} --json`)); const installData = JSON.parse(install.toString()); this.drupalDbPrefix = installData.db_prefix; + this.drupalSitePath = installData.site_path; const url = new URL(process.env.DRUPAL_TEST_BASE_URL); this .url(process.env.DRUPAL_TEST_BASE_URL) @@ -45,5 +46,6 @@ exports.command = function drupalInstall({ setupFile = '' }, callback) { if (typeof callback === 'function') { callback.call(self); } + return this; }; diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalLogin.js b/core/tests/Drupal/Nightwatch/Commands/drupalLogin.js new file mode 100644 index 0000000000000000000000000000000000000000..c6785ec814ce90483b5e8a9346347055a41fdea4 --- /dev/null +++ b/core/tests/Drupal/Nightwatch/Commands/drupalLogin.js @@ -0,0 +1,30 @@ +/** + * Logs into Drupal as the given user. + * + * @param {string} name + * The user name. + * @param {string} password + * The user password. + * @return {object} + * The drupalUserIsLoggedIn command. + */ +exports.command = function drupalLogin({ name, password }) { + this.drupalUserIsLoggedIn((sessionExists) => { + // Log the current user out if necessary. + if (sessionExists) { + this.drupalLogout(); + } + // Log in with the given credentials. + this + .drupalRelativeURL('/user/login') + .setValue('input[name="name"]', name) + .setValue('input[name="pass"]', password) + .submitForm('#user-login-form'); + // Assert that a user is logged in. + this.drupalUserIsLoggedIn((sessionExists) => { + this.assert.equal(sessionExists, true, `The user "${name}" was logged in.`); + }); + }); + + return this; +}; diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalLoginAsAdmin.js b/core/tests/Drupal/Nightwatch/Commands/drupalLoginAsAdmin.js new file mode 100644 index 0000000000000000000000000000000000000000..8bc0e4e632f9370af3c70b5402020b6086db924a --- /dev/null +++ b/core/tests/Drupal/Nightwatch/Commands/drupalLoginAsAdmin.js @@ -0,0 +1,37 @@ +import { execSync } from 'child_process'; +import { URL } from 'url'; +import { commandAsWebserver } from '../globals'; + +/** + * Logs in as the admin user. + * + * @param {function} callback + * A callback which will allow running commands as an administrator. + * @return {object} + * The drupalLoginAsAdmin command. + */ +exports.command = function drupalLoginAsAdmin(callback) { + const self = this; + this.drupalUserIsLoggedIn((sessionExists) => { + if (sessionExists) { + this.drupalLogout(); + } + const userLink = execSync(commandAsWebserver(`php ./scripts/test-site.php user-login 1 --site-path ${this.drupalSitePath}`)); + + this.drupalRelativeURL(userLink.toString()); + + this.drupalUserIsLoggedIn((sessionExists) => { + if (!sessionExists) { + throw new Error('Logging in as an admin user failed.'); + } + }); + }); + + if (typeof callback === 'function') { + callback.call(self); + } + + this.drupalLogout({ silent: true }); + + return this; +}; diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalLogout.js b/core/tests/Drupal/Nightwatch/Commands/drupalLogout.js new file mode 100644 index 0000000000000000000000000000000000000000..f96ce475e1584c5f750961696a0cda3d28789415 --- /dev/null +++ b/core/tests/Drupal/Nightwatch/Commands/drupalLogout.js @@ -0,0 +1,37 @@ +import { execSync } from 'child_process'; +import { URL } from 'url'; + +/** + * Logs out from a Drupal site. + * + * @param {object} [settings={}] + * The settings object. + * @param {boolean} [settings.silent=false] + * If the command should be run silently. + * @param {function} callback + * A callback which will be called, when the logout is finished. + * @return {object} + * The drupalLogout command. + */ +exports.command = function drupalLogout({ silent = false } = {}, callback) { + const self = this; + + this.drupalRelativeURL('/user/logout'); + + this.drupalUserIsLoggedIn((sessionExists) => { + if (silent) { + if (sessionExists) { + throw new Error('Logging out failed.'); + } + } + else { + this.assert.equal(sessionExists, false, 'The user was logged out.'); + } + }); + + if (typeof callback === 'function') { + callback.call(self); + } + + return this; +}; diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalUserIsLoggedIn.js b/core/tests/Drupal/Nightwatch/Commands/drupalUserIsLoggedIn.js new file mode 100644 index 0000000000000000000000000000000000000000..e5db1c1edd0d54c4775ffb0c863b4941ebc655fa --- /dev/null +++ b/core/tests/Drupal/Nightwatch/Commands/drupalUserIsLoggedIn.js @@ -0,0 +1,19 @@ +/** + * Checks if a user is logged in. + * + * @param {function} callback + * A callback which will be called, when the login status has been checked. + * @return {object} + * The drupalUserIsLoggedIn command. + */ +exports.command = function drupalUserIsLoggedIn(callback) { + if (typeof callback === 'function') { + this.getCookies((cookies) => { + const sessionExists = cookies.value.some(cookie => cookie.name.match(/^SESS/)); + + callback.call(this, sessionExists); + }); + } + + return this; +}; diff --git a/core/tests/Drupal/Nightwatch/Tests/loginTest.js b/core/tests/Drupal/Nightwatch/Tests/loginTest.js new file mode 100644 index 0000000000000000000000000000000000000000..3c62d737c9ecec95c5186685816e2443109407d2 --- /dev/null +++ b/core/tests/Drupal/Nightwatch/Tests/loginTest.js @@ -0,0 +1,24 @@ +module.exports = { + '@tags': ['core'], + + before(browser) { + browser + .drupalInstall(); + }, + after(browser) { + browser + .drupalUninstall(); + }, + + 'Test login': (browser) => { + browser + .drupalCreateUser({ + name: 'user', + password: '123', + permissions: ['access site reports'], + }) + .drupalLogin({ name: 'user', password: '123' }) + .drupalRelativeURL('/admin/reports') + .expect.element('h1.page-title').text.to.contain('Reports'); + }, +}; diff --git a/core/tests/Drupal/TestSite/Commands/TestSiteInstallCommand.php b/core/tests/Drupal/TestSite/Commands/TestSiteInstallCommand.php index 50167bff3b2a25d887fbcae06dadc3c2f2ec969d..d020304018ab36c9ce64a1d68c322b578534ac17 100644 --- a/core/tests/Drupal/TestSite/Commands/TestSiteInstallCommand.php +++ b/core/tests/Drupal/TestSite/Commands/TestSiteInstallCommand.php @@ -102,6 +102,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { $output->writeln(json_encode([ 'db_prefix' => $this->databasePrefix, 'user_agent' => $user_agent, + 'site_path' => $this->siteDirectory, ])); } else { @@ -110,6 +111,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { $io->table([], [ ['Database prefix', $this->databasePrefix], ['User agent', $user_agent], + ['Site path', $this->siteDirectory], ]); } } diff --git a/core/tests/Drupal/TestSite/Commands/TestSiteUserLoginCommand.php b/core/tests/Drupal/TestSite/Commands/TestSiteUserLoginCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..55c92032cdce7198e30b94a54e2d77d1b8a3aa23 --- /dev/null +++ b/core/tests/Drupal/TestSite/Commands/TestSiteUserLoginCommand.php @@ -0,0 +1,71 @@ +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); + } + +} diff --git a/core/tests/Drupal/TestSite/TestSiteApplication.php b/core/tests/Drupal/TestSite/TestSiteApplication.php index eba29d8d3f8099f49f921afde88d693302fe4a0f..d67c4853ed8131b72cf4cc6b17884e02bc2b76a7 100644 --- a/core/tests/Drupal/TestSite/TestSiteApplication.php +++ b/core/tests/Drupal/TestSite/TestSiteApplication.php @@ -5,6 +5,7 @@ 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; /** @@ -25,6 +26,7 @@ protected function getDefaultCommands() { $default_commands[] = new TestSiteInstallCommand(); $default_commands[] = new TestSiteTearDownCommand(); $default_commands[] = new TestSiteReleaseLocksCommand(); + $default_commands[] = new TestSiteUserLoginCommand(); return $default_commands; } diff --git a/core/tests/Drupal/Tests/Scripts/TestSiteApplicationTest.php b/core/tests/Drupal/Tests/Scripts/TestSiteApplicationTest.php index 50d9f99640ad363dceacd10002aff39749304e1d..d3c644468609d67413a74776879b6448cceec499 100644 --- a/core/tests/Drupal/Tests/Scripts/TestSiteApplicationTest.php +++ b/core/tests/Drupal/Tests/Scripts/TestSiteApplicationTest.php @@ -28,11 +28,20 @@ */ class TestSiteApplicationTest extends UnitTestCase { + /** + * The PHP executable path. + * + * @var string + */ + protected $php; + /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); + $php_executable_finder = new PhpExecutableFinder(); + $this->php = $php_executable_finder->find(); $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__)))); } @@ -40,14 +49,12 @@ protected function setUp() { * @coversNothing */ public function testInstallWithNonExistingFile() { - $php_binary_finder = new PhpExecutableFinder(); - $php_binary_path = $php_binary_finder->find(); // Create a connection to the DB configured in SIMPLETEST_DB. $connection = Database::getConnection('default', $this->addTestDatabase('')); $table_count = count($connection->schema()->findTables('%')); - $command_line = $php_binary_path . ' core/scripts/test-site.php install --setup-file "this-class-does-not-exist" --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php install --setup-file "this-class-does-not-exist" --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root); $process->run(); @@ -60,14 +67,12 @@ public function testInstallWithNonExistingFile() { * @coversNothing */ public function testInstallWithFileWithNoClass() { - $php_binary_finder = new PhpExecutableFinder(); - $php_binary_path = $php_binary_finder->find(); // Create a connection to the DB configured in SIMPLETEST_DB. $connection = Database::getConnection('default', $this->addTestDatabase('')); $table_count = count($connection->schema()->findTables('%')); - $command_line = $php_binary_path . ' core/scripts/test-site.php install --setup-file core/tests/fixtures/empty_file.php.module --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php install --setup-file core/tests/fixtures/empty_file.php.module --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root); $process->run(); @@ -80,15 +85,13 @@ public function testInstallWithFileWithNoClass() { * @coversNothing */ public function testInstallWithNonSetupClass() { - $php_binary_finder = new PhpExecutableFinder(); - $php_binary_path = $php_binary_finder->find(); // Create a connection to the DB configured in SIMPLETEST_DB. $connection = Database::getConnection('default', $this->addTestDatabase('')); $table_count = count($connection->schema()->findTables('%')); // Use __FILE__ to test absolute paths. - $command_line = $php_binary_path . ' core/scripts/test-site.php install --setup-file "' . __FILE__ . '" --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php install --setup-file "' . __FILE__ . '" --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root, ['COLUMNS' => PHP_INT_MAX]); $process->run(); @@ -106,11 +109,9 @@ public function testInstallScript() { if (!is_writable($simpletest_path)) { $this->markTestSkipped("Requires the directory $simpletest_path to exist and be writable"); } - $php_binary_finder = new PhpExecutableFinder(); - $php_binary_path = $php_binary_finder->find(); // Install a site using the JSON output. - $command_line = $php_binary_path . ' core/scripts/test-site.php install --json --setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php install --json --setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root); // Set the timeout to a value that allows debugging. $process->setTimeout(500); @@ -142,7 +143,7 @@ public function testInstallScript() { // Install another site so we can ensure the tear down command only removes // one site at a time. Use the regular output. - $command_line = $php_binary_path . ' core/scripts/test-site.php install --setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php install --setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root); // Set the timeout to a value that allows debugging. $process->setTimeout(500); @@ -160,7 +161,7 @@ public function testInstallScript() { $this->assertFileExists($this->getTestLockFile($other_db_prefix)); // Now test the tear down process as well, but keep the lock. - $command_line = $php_binary_path . ' core/scripts/test-site.php tear-down ' . $db_prefix . ' --keep-lock --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php tear-down ' . $db_prefix . ' --keep-lock --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root); // Set the timeout to a value that allows debugging. $process->setTimeout(500); @@ -182,7 +183,7 @@ public function testInstallScript() { // broken. Prove this by removing its settings.php. $test_site_settings = $this->root . DIRECTORY_SEPARATOR . $test_database->getTestSitePath() . DIRECTORY_SEPARATOR . 'settings.php'; $this->assertTrue(unlink($test_site_settings)); - $command_line = $php_binary_path . ' core/scripts/test-site.php tear-down ' . $other_db_prefix . ' --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php tear-down ' . $other_db_prefix . ' --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root); // Set the timeout to a value that allows debugging. $process->setTimeout(500); @@ -208,10 +209,8 @@ public function testInstallInDifferentLanguage() { if (!is_writable($simpletest_path)) { $this->markTestSkipped("Requires the directory $simpletest_path to exist and be writable"); } - $php_binary_finder = new PhpExecutableFinder(); - $php_binary_path = $php_binary_finder->find(); - $command_line = $php_binary_path . ' core/scripts/test-site.php install --json --langcode fr --setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php install --json --langcode fr --setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root); $process->setTimeout(500); $process->run(); @@ -229,7 +228,7 @@ public function testInstallInDifferentLanguage() { $this->assertContains('lang="fr"', (string) $response->getBody()); // Now test the tear down process as well. - $command_line = $php_binary_path . ' core/scripts/test-site.php tear-down ' . $db_prefix . ' --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $command_line = $this->php . ' core/scripts/test-site.php tear-down ' . $db_prefix . ' --db-url "' . getenv('SIMPLETEST_DB') . '"'; $process = new Process($command_line, $this->root); $process->setTimeout(500); $process->run(); @@ -243,10 +242,7 @@ public function testInstallInDifferentLanguage() { * @coversNothing */ public function testTearDownDbPrefixValidation() { - $php_binary_finder = new PhpExecutableFinder(); - $php_binary_path = $php_binary_finder->find(); - - $command_line = $php_binary_path . ' core/scripts/test-site.php tear-down not-a-valid-prefix'; + $command_line = $this->php . ' core/scripts/test-site.php tear-down not-a-valid-prefix'; $process = new Process($command_line, $this->root); $process->setTimeout(500); $process->run(); @@ -254,6 +250,61 @@ public function testTearDownDbPrefixValidation() { $this->assertContains('Invalid database prefix: not-a-valid-prefix', $process->getErrorOutput()); } + /** + * @coversNothing + */ + public function testUserLogin() { + $simpletest_path = $this->root . DIRECTORY_SEPARATOR . 'sites' . DIRECTORY_SEPARATOR . 'simpletest'; + if (!is_writable($simpletest_path)) { + $this->markTestSkipped("Requires the directory $simpletest_path to exist and be writable"); + } + + // Install a site using the JSON output. + $command_line = $this->php . ' core/scripts/test-site.php install --json --setup-file core/tests/Drupal/TestSite/TestSiteInstallTestScript.php --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $process = new Process($command_line, $this->root); + // Set the timeout to a value that allows debugging. + $process->setTimeout(500); + $process->run(); + + $this->assertSame(0, $process->getExitCode()); + $result = json_decode($process->getOutput(), TRUE); + $db_prefix = $result['db_prefix']; + $site_path = $result['site_path']; + $this->assertSame('sites/simpletest/' . str_replace('test', '', $db_prefix), $site_path); + + // Test the user login command with valid uid. + $command_line = $this->php . ' core/scripts/test-site.php user-login 1 --site-path ' . $site_path; + $process = new Process($command_line, $this->root); + $process->run(); + $this->assertSame(0, $process->getExitCode()); + $this->assertContains('/user/reset/1/', $process->getOutput()); + + $http_client = new Client(); + $request = (new Request('GET', getenv('SIMPLETEST_BASE_URL') . trim($process->getOutput()))) + ->withHeader('User-Agent', trim($result['user_agent'])); + + $response = $http_client->send($request); + + // Ensure the response sets a new session. + $this->assertTrue($response->getHeader('Set-Cookie')); + + // Test the user login command with invalid uid. + $command_line = $this->php . ' core/scripts/test-site.php user-login invalid-uid --site-path ' . $site_path; + $process = new Process($command_line, $this->root); + $process->run(); + $this->assertSame(1, $process->getExitCode()); + $this->assertContains('The "uid" argument needs to be an integer, but it is "invalid-uid".', $process->getErrorOutput()); + + // Now tear down the test site. + $command_line = $this->php . ' core/scripts/test-site.php tear-down ' . $db_prefix . ' --db-url "' . getenv('SIMPLETEST_DB') . '"'; + $process = new Process($command_line, $this->root); + // Set the timeout to a value that allows debugging. + $process->setTimeout(500); + $process->run(); + $this->assertSame(0, $process->getExitCode()); + $this->assertContains("Successfully uninstalled $db_prefix test site", $process->getOutput()); + } + /** * Adds the installed test site to the database connection info. *