Issue #3255809 by nod_, lauriii, xjm, hooroomoo: Add nightwatch tests for toolbar

merge-requests/1795/merge
bnjmnm 2022-02-17 10:25:13 -05:00
parent a2c7e7b00e
commit 18e36fa471
4 changed files with 670 additions and 1 deletions

View File

@ -0,0 +1,264 @@
/**
* @file
* Tests of the existing Toolbar JS Api.
*/
module.exports = {
'@tags': ['core'],
before(browser) {
browser
.drupalInstall()
.drupalInstallModule('breakpoint')
.drupalInstallModule('toolbar')
.drupalCreateUser({
name: 'user',
password: '123',
permissions: [
'access site reports',
'access toolbar',
'administer menu',
'administer modules',
'administer site configuration',
'administer account settings',
'administer software updates',
'access content',
'administer permissions',
'administer users',
],
})
.drupalLogin({ name: 'user', password: '123' })
.drupalRelativeURL('/')
.waitForElementPresent('#toolbar-administration', 10000);
},
beforeEach(browser) {
// Set the resolution to the default desktop resolution. Ensure the default
// toolbar is horizontal in headless mode.
browser.resizeWindow(1920, 1080);
// To clear active tab/tray from previous tests
browser.execute(function () {
localStorage.clear();
// Clear escapeAdmin url values.
sessionStorage.clear();
});
browser.drupalRelativeURL('/');
},
after(browser) {
browser.drupalUninstall();
},
'Drupal.Toolbar.models': (browser) => {
browser.execute(
function () {
const toReturn = {};
const { models } = Drupal.toolbar;
toReturn.hasMenuModel = models.hasOwnProperty('menuModel');
toReturn.menuModelType = typeof models.menuModel === 'object';
toReturn.hasToolbarModel = models.hasOwnProperty('toolbarModel');
toReturn.toolbarModelType = typeof models.toolbarModel === 'object';
toReturn.toolbarModelActiveTab =
models.toolbarModel.get('activeTab').id ===
'toolbar-item-administration';
toReturn.toolbarModelActiveTray =
models.toolbarModel.get('activeTray').id ===
'toolbar-item-administration-tray';
toReturn.toolbarModelIsOriented =
models.toolbarModel.get('isOriented') === true;
toReturn.toolbarModelIsFixed =
models.toolbarModel.get('isFixed') === true;
toReturn.toolbarModelAreSubtreesLoaded =
models.toolbarModel.get('areSubtreesLoaded') === false;
toReturn.toolbarModelIsViewportOverflowConstrained =
models.toolbarModel.get('isViewportOverflowConstrained') === false;
toReturn.toolbarModelOrientation =
models.toolbarModel.get('orientation') === 'horizontal';
toReturn.toolbarModelLocked =
models.toolbarModel.get('locked') === null;
toReturn.toolbarModelIsTrayToggleVisible =
models.toolbarModel.get('isTrayToggleVisible') === true;
toReturn.toolbarModelHeight = models.toolbarModel.get('height') === 79;
toReturn.toolbarModelOffsetsBottom =
models.toolbarModel.get('offsets').bottom === 0;
toReturn.toolbarModelOffsetsLeft =
models.toolbarModel.get('offsets').left === 0;
toReturn.toolbarModelOffsetsRight =
models.toolbarModel.get('offsets').right === 0;
toReturn.toolbarModelOffsetsTop =
models.toolbarModel.get('offsets').top === 79;
toReturn.toolbarModelSubtrees =
Object.keys(models.menuModel.get('subtrees')).length === 0;
return toReturn;
},
[],
(result) => {
const expectedTrue = {
hasMenuModel: 'has menu model',
menuModelType: 'menu model is an object',
hasToolbarModel: 'has toolbar model',
toolbarModelType: 'toolbar model is an object',
toolbarModelActiveTab: 'get("activeTab") has expected result',
toolbarModelActiveTray: 'get("activeTray") has expected result',
toolbarModelIsOriented: 'get("isOriented") has expected result',
toolbarModelIsFixed: 'get("isFixed") has expected result',
toolbarModelAreSubtreesLoaded:
'get("areSubtreesLoaded") has expected result',
toolbarModelIsViewportOverflowConstrained:
'get("isViewportOverflowConstrained") has expected result',
toolbarModelOrientation: 'get("orientation") has expected result',
toolbarModelLocked: 'get("locked") has expected result',
toolbarModelIsTrayToggleVisible:
'get("isTrayToggleVisible") has expected result',
toolbarModelHeight: 'get("height") has expected result',
toolbarModelOffsetsBottom:
'get("offsets") bottom has expected result',
toolbarModelOffsetsLeft: 'get("offsets") left has expected result',
toolbarModelOffsetsRight: 'get("offsets") right has expected result',
toolbarModelOffsetsTop: 'get("offsets") top has expected result',
toolbarModelSubtrees: 'get("subtrees") has expected result',
};
browser.assert.deepEqual(
Object.keys(expectedTrue).sort(),
Object.keys(result.value).sort(),
'Keys to check match',
);
Object.keys(expectedTrue).forEach((property) => {
browser.assert.equal(
result.value[property],
true,
expectedTrue[property],
);
});
},
);
},
'Change tab': (browser) => {
browser.execute(
function () {
const toReturn = {};
const { models } = Drupal.toolbar;
toReturn.hasMenuModel = models.hasOwnProperty('menuModel');
toReturn.menuModelType = typeof models.menuModel === 'object';
toReturn.hasToolbarModel = models.hasOwnProperty('toolbarModel');
toReturn.toolbarModelType = typeof models.toolbarModel === 'object';
const tab = document.querySelector('#toolbar-item-user');
tab.dispatchEvent(new MouseEvent('click', { bubbles: true }));
toReturn.toolbarModelChangedTab =
models.toolbarModel.get('activeTab').id === 'toolbar-item-user';
toReturn.toolbarModelChangedTray =
models.toolbarModel.get('activeTray').id === 'toolbar-item-user-tray';
return toReturn;
},
[],
(result) => {
const expectedTrue = {
hasMenuModel: 'has menu model',
menuModelType: 'menu model is an object',
hasToolbarModel: 'has toolbar model',
toolbarModelType: 'toolbar model is an object',
toolbarModelChangedTab: 'get("activeTab") has expected result',
toolbarModelChangedTray: 'get("activeTray") has expected result',
};
browser.assert.deepEqual(
Object.keys(expectedTrue).sort(),
Object.keys(result.value).sort(),
'Keys to check match',
);
Object.keys(expectedTrue).forEach((property) => {
browser.assert.equal(
result.value[property],
true,
expectedTrue[property],
);
});
},
);
},
'Change orientation': (browser) => {
browser.executeAsync(
function (done) {
const toReturn = {};
const { models } = Drupal.toolbar;
const orientationToggle = document.querySelector(
'#toolbar-item-administration-tray .toolbar-toggle-orientation button',
);
toReturn.toolbarOrientation =
models.toolbarModel.get('orientation') === 'horizontal';
orientationToggle.dispatchEvent(
new MouseEvent('click', { bubbles: true }),
);
setTimeout(() => {
toReturn.toolbarChangeOrientation =
models.toolbarModel.get('orientation') === 'vertical';
done(toReturn);
}, 100);
},
[],
(result) => {
const expectedTrue = {
toolbarOrientation: 'get("orientation") has expected result',
toolbarChangeOrientation: 'changing orientation has expected result',
};
browser.assert.deepEqual(
Object.keys(expectedTrue).sort(),
Object.keys(result.value).sort(),
'Keys to check match',
);
Object.keys(expectedTrue).forEach((property) => {
browser.assert.equal(
result.value[property],
true,
expectedTrue[property],
);
});
},
);
},
'Open submenu': (browser) => {
browser.executeAsync(
function (done) {
const toReturn = {};
const { models } = Drupal.toolbar;
Drupal.toolbar.models.toolbarModel.set('orientation', 'vertical');
toReturn.toolbarOrientation =
models.toolbarModel.get('orientation') === 'vertical';
const manageTab = document.querySelector(
'#toolbar-item-administration',
);
Drupal.toolbar.models.toolbarModel.set('activeTab', manageTab);
const menuDropdown = document.querySelector(
'#toolbar-item-administration-tray button',
);
menuDropdown.dispatchEvent(new MouseEvent('click', { bubbles: true }));
setTimeout(() => {
const statReportElement = document.querySelector(
'#toolbar-link-system-status',
);
toReturn.submenuItem =
statReportElement.textContent === 'Status report';
done(toReturn);
}, 100);
},
[],
(result) => {
const expectedTrue = {
toolbarOrientation: 'get("orientation") has expected result',
submenuItem: 'opening submenu has expected result',
};
browser.assert.deepEqual(
Object.keys(expectedTrue).sort(),
Object.keys(result.value).sort(),
'Keys to check match',
);
Object.keys(expectedTrue).forEach((property) => {
browser.assert.equal(
result.value[property],
true,
expectedTrue[property],
);
});
},
);
},
};

View File

@ -0,0 +1,379 @@
/**
* @file
* Test the expected toolbar functionality.
*/
const itemAdministration = '#toolbar-item-administration';
const itemAdministrationTray = '#toolbar-item-administration-tray';
const adminOrientationButton = `${itemAdministrationTray} .toolbar-toggle-orientation button`;
const itemUser = '#toolbar-item-user';
const itemUserTray = '#toolbar-item-user-tray';
const userOrientationBtn = `${itemUserTray} .toolbar-toggle-orientation button`;
module.exports = {
'@tags': ['core'],
before(browser) {
browser
.drupalInstall()
.drupalInstallModule('breakpoint')
.drupalInstallModule('toolbar')
.drupalCreateUser({
name: 'user',
password: '123',
permissions: [
'access site reports',
'access toolbar',
'access administration pages',
'administer menu',
'administer modules',
'administer site configuration',
'administer account settings',
'administer software updates',
'access content',
'administer permissions',
'administer users',
],
})
.drupalLogin({ name: 'user', password: '123' })
.drupalRelativeURL('/')
.waitForElementPresent('#toolbar-administration', 10000);
},
beforeEach(browser) {
browser.resizeWindow(1920, 1080);
browser.execute(function () {
// To clear active tab/tray from previous tests.
localStorage.clear();
// Clear escapeAdmin URL values.
sessionStorage.clear();
});
browser.drupalRelativeURL('/');
},
after(browser) {
browser.drupalUninstall();
},
'Change tab': (browser) => {
browser.waitForElementPresent(itemUserTray);
browser.assert.not.cssClassPresent(itemUser, 'is-active');
browser.assert.not.cssClassPresent(itemUserTray, 'is-active');
browser.click(itemUser);
browser.assert.cssClassPresent(itemUser, 'is-active');
browser.assert.cssClassPresent(itemUserTray, 'is-active');
},
'Change orientation': (browser) => {
browser.waitForElementPresent(adminOrientationButton);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-horizontal',
);
browser.click(adminOrientationButton);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-vertical',
);
browser.click(adminOrientationButton);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-horizontal',
);
},
'Toggle tray': (browser) => {
browser.waitForElementPresent(itemUserTray);
browser.click(itemUser);
browser.assert.cssClassPresent(itemUserTray, 'is-active');
browser.click(itemUser);
browser.assert.not.cssClassPresent(itemUserTray, 'is-active');
browser.click(itemUser);
browser.assert.cssClassPresent(itemUserTray, 'is-active');
},
'Toggle submenu and sub-submenu': (browser) => {
browser.waitForElementPresent(adminOrientationButton);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-horizontal',
);
browser.click(adminOrientationButton);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-vertical',
);
browser.waitForElementPresent(
'#toolbar-item-administration-tray li:nth-child(4) button',
);
browser.assert.not.cssClassPresent(
'#toolbar-item-administration-tray li:nth-child(4)',
'open',
);
browser.assert.not.cssClassPresent(
'#toolbar-item-administration-tray li:nth-child(4) button',
'open',
);
browser.click('#toolbar-item-administration-tray li:nth-child(4) button');
browser.assert.cssClassPresent(
'#toolbar-item-administration-tray li:nth-child(4)',
'open',
);
browser.assert.cssClassPresent(
'#toolbar-item-administration-tray li:nth-child(4) button',
'open',
);
browser.expect
.element('#toolbar-link-user-admin_index')
.text.to.equal('People');
browser.expect
.element('#toolbar-link-system-admin_config_system')
.text.to.equal('System');
// Check sub-submenu.
browser.waitForElementPresent(
'#toolbar-item-administration-tray li.menu-item.level-2',
);
browser.assert.not.cssClassPresent(
'#toolbar-item-administration-tray li.menu-item.level-2',
'open',
);
browser.assert.not.cssClassPresent(
'#toolbar-item-administration-tray li.menu-item.level-2 button',
'open',
);
browser.click(
'#toolbar-item-administration-tray li.menu-item.level-2 button',
);
browser.assert.cssClassPresent(
'#toolbar-item-administration-tray li.menu-item.level-2',
'open',
);
browser.assert.cssClassPresent(
'#toolbar-item-administration-tray li.menu-item.level-2 button',
'open',
);
browser.expect
.element('#toolbar-link-entity-user-admin_form')
.text.to.equal('Account settings');
},
'Narrow toolbar width breakpoint': (browser) => {
browser.waitForElementPresent(adminOrientationButton);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-horizontal',
);
browser.assert.cssClassPresent(
'#toolbar-administration',
'toolbar-oriented',
);
browser.resizeWindow(263, 900);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-vertical',
);
browser.assert.not.cssClassPresent(itemAdministration, 'toolbar-oriented');
},
'Standard width toolbar breakpoint': (browser) => {
browser.resizeWindow(1000, 900);
browser.waitForElementPresent(adminOrientationButton);
browser.assert.cssClassPresent('body', 'toolbar-fixed');
browser.resizeWindow(609, 900);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-vertical',
);
browser.assert.not.cssClassPresent('body', 'toolbar-fixed');
},
'Wide toolbar breakpoint': (browser) => {
browser.waitForElementPresent(adminOrientationButton);
browser.resizeWindow(975, 900);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-vertical',
);
},
'Back to site link': (browser) => {
const escapeSelector = '[data-toolbar-escape-admin]';
browser.drupalRelativeURL('/user');
browser.drupalRelativeURL('/admin');
// Don't check the visibility as stark doesn't add the .path-admin class
// to the <body> required to display the button.
browser.assert.attributeContains(escapeSelector, 'href', '/user/2');
},
'Aural view test: tray orientation': (browser) => {
browser.waitForElementPresent(
'#toolbar-item-administration-tray .toolbar-toggle-orientation button',
);
browser.executeAsync(
function (done) {
Drupal.announce = done;
const orientationButton = document.querySelector(
'#toolbar-item-administration-tray .toolbar-toggle-orientation button',
);
orientationButton.dispatchEvent(
new MouseEvent('click', { bubbles: true }),
);
},
(result) => {
browser.assert.equal(
result.value,
'Tray orientation changed to vertical.',
);
},
);
browser.executeAsync(
function (done) {
Drupal.announce = done;
const orientationButton = document.querySelector(
'#toolbar-item-administration-tray .toolbar-toggle-orientation button',
);
orientationButton.dispatchEvent(
new MouseEvent('click', { bubbles: true }),
);
},
(result) => {
browser.assert.equal(
result.value,
'Tray orientation changed to horizontal.',
);
},
);
},
'Aural view test: tray toggle': (browser) => {
browser.executeAsync(
function (done) {
Drupal.announce = done;
const $adminButton = jQuery('#toolbar-item-administration');
$adminButton.trigger('click');
},
(result) => {
browser.assert.equal(
result.value,
'Tray "Administration menu" closed.',
);
},
);
browser.executeAsync(
function (done) {
Drupal.announce = done;
const $adminButton = jQuery('#toolbar-item-administration');
$adminButton.trigger('click');
},
(result) => {
browser.assert.equal(
result.value,
'Tray "Administration menu" opened.',
);
},
);
},
'Toolbar event: drupalToolbarOrientationChange': (browser) => {
browser.executeAsync(
function (done) {
jQuery(document).on(
'drupalToolbarOrientationChange',
function (event, orientation) {
done(orientation);
},
);
const orientationButton = document.querySelector(
'#toolbar-item-administration-tray .toolbar-toggle-orientation button',
);
orientationButton.dispatchEvent(
new MouseEvent('click', { bubbles: true }),
);
},
(result) => {
browser.assert.equal(result.value, 'vertical');
},
);
},
'Toolbar event: drupalToolbarTabChange': (browser) => {
browser.executeAsync(
function (done) {
jQuery(document).on('drupalToolbarTabChange', function (event, tab) {
done(tab.id);
});
jQuery('#toolbar-item-user').trigger('click');
},
(result) => {
browser.assert.equal(result.value, 'toolbar-item-user');
},
);
},
'Toolbar event: drupalToolbarTrayChange': (browser) => {
browser.executeAsync(
function (done) {
const $adminButton = jQuery('#toolbar-item-administration');
// Hide the admin menu first, this event is not firing reliably
// otherwise.
$adminButton.trigger('click');
jQuery(document).on('drupalToolbarTrayChange', function (event, tray) {
done(tray.id);
});
$adminButton.trigger('click');
},
(result) => {
browser.assert.equal(result.value, 'toolbar-item-administration-tray');
},
);
},
'Locked toolbar vertical wide viewport': (browser) => {
browser.resizeWindow(1000, 900);
browser.waitForElementPresent(adminOrientationButton);
// eslint-disable-next-line no-unused-expressions
browser.expect.element(adminOrientationButton).to.be.visible;
browser.resizeWindow(975, 900);
browser.assert.cssClassPresent(
itemAdministrationTray,
'is-active toolbar-tray-vertical',
);
// eslint-disable-next-line no-unused-expressions
browser.expect.element(adminOrientationButton).to.not.be.visible;
},
'Settings are retained on refresh': (browser) => {
browser.waitForElementPresent(itemUser);
// Set user as active tab.
browser.assert.not.cssClassPresent(itemUser, 'is-active');
browser.assert.not.cssClassPresent(itemUserTray, 'is-active');
browser.click(itemUser);
// Check tab and tray are open.
browser.assert.cssClassPresent(itemUser, 'is-active');
browser.assert.cssClassPresent(itemUserTray, 'is-active');
// Set orientation to vertical.
browser.waitForElementPresent(userOrientationBtn);
browser.assert.cssClassPresent(
itemUserTray,
'is-active toolbar-tray-horizontal',
);
browser.click(userOrientationBtn);
browser.assert.cssClassPresent(
itemUserTray,
'is-active toolbar-tray-vertical',
);
browser.refresh();
// Check user tab is active.
browser.assert.cssClassPresent(itemUser, 'is-active');
// Check tray is active and orientation is vertical.
browser.assert.cssClassPresent(
itemUserTray,
'is-active toolbar-tray-vertical',
);
},
'Check toolbar overlap with page content': (browser) => {
browser.assert.cssClassPresent('body', 'toolbar-horizontal');
browser.execute(
() => {
const toolbar = document.querySelector('#toolbar-administration');
const nextElement = toolbar.nextElementSibling.getBoundingClientRect();
const tray = document
.querySelector('#toolbar-item-administration-tray')
.getBoundingClientRect();
// Page content should start after the toolbar height to not overlap.
return nextElement.top > tray.top + tray.height;
},
(result) => {
browser.assert.equal(
result.value,
true,
'Toolbar and page content do not overlap',
);
},
);
},
};

View File

@ -320,7 +320,7 @@ for FILE in $FILES; do
############################################################################
### JAVASCRIPT FILES
############################################################################
if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ ! $FILE =~ ^core/tests/Drupal/Nightwatch ]] && [[ ! $FILE =~ ^core/assets/vendor/jquery.ui/ui ]] && [[ ! $FILE =~ ^core/modules/ckeditor5/js/ckeditor5_plugins ]]; then
if [[ -f "$TOP_LEVEL/$FILE" ]] && [[ $FILE =~ \.js$ ]] && [[ ! $FILE =~ ^core/tests/Drupal/Nightwatch ]] && [[ ! $FILE =~ /tests/src/Nightwatch/ ]] && [[ ! $FILE =~ ^core/assets/vendor/jquery.ui/ui ]] && [[ ! $FILE =~ ^core/modules/ckeditor5/js/ckeditor5_plugins ]]; then
# Work out the root name of the JavaScript so we can ensure that the ES6
# version has been compiled correctly.
if [[ $FILE =~ \.es6\.js$ ]]; then

View File

@ -0,0 +1,26 @@
/**
* Install the given module.
*
* @param {string} module
* The module machine name to enable.
* @param {function} callback
* A callback which will be called, when the module has been enabled.
* @return {object}
* The drupalInstallModule command.
*/
exports.command = function drupalInstallModule(module, callback) {
const self = this;
this.drupalLoginAsAdmin(() => {
this.drupalRelativeURL('/admin/modules')
.click(`input[data-drupal-selector="edit-modules-${module}-enable"]`)
.click('input[data-drupal-selector="edit-submit"]')
// Wait for the install message to show up.
.waitForElementVisible('.system-modules', 10000);
}).perform(() => {
if (typeof callback === 'function') {
callback.call(self);
}
});
return this;
};