Unverified Commit c5782b95 authored by alexpott's avatar alexpott

Issue #2973879 by jibran, lauriii, drpal, samuel.mortenson, dawehner, Wim...

Issue #2973879 by jibran, lauriii, drpal, samuel.mortenson, dawehner, Wim Leers: Add login/logout Nightwatch commands (and a Drupal "login" command to allow for that)

(cherry picked from commit 0083ba97)
parent e0feb5b1
/**
* 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;
};
/**
* 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;
};
......@@ -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;
};
/**
* 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;
};
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;
};
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;
};
/**
* 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;
};
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');
},
};
......@@ -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],
]);
}
}
......
<?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);
}
}
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment