diff --git a/cypress/support/atk_utilities.js b/cypress/support/atk_utilities.js index b56113af5706a13bb79d8b9bd4f14c9d032296b7..fe97687ea667da2e08fabbabfaa0073e71db50e1 100644 --- a/cypress/support/atk_utilities.js +++ b/cypress/support/atk_utilities.js @@ -1,14 +1,14 @@ /// <reference types='Cypress' /> -import fs from 'fs'; -import YAML from 'yaml'; +import fs from 'fs' +import YAML from 'yaml' /** * Return a string of random characters of specified length. * * @param {length} int Length of string to return. */ -function createRandomString (length) { +function createRandomString(length) { let result = '' const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' const charactersLength = characters.length @@ -25,8 +25,8 @@ function createRandomString (length) { * @return {{userRoles: *[], userPassword: string, userEmail: string, userName: string}} */ function createRandomUser() { - const name1 = createRandomString(6); - const name2 = createRandomString(6); + const name1 = createRandomString(6) + const name2 = createRandomString(6) return { userName: `${name1} ${name2}`, userEmail: `${name1.toLowerCase()}.${name2.toLowerCase()}@ethereal.email`, @@ -42,8 +42,33 @@ function createRandomUser() { * @return {object} */ function readYAML(filename) { - return cy.readFile(`cypress/data/${filename}`).then((text) => YAML.parse(text)); + return cy.readFile(`cypress/data/${filename}`).then((text) => YAML.parse(text)) } +/** + * Get multi-level property from an object. + * E.g. if object is {"foo":{"bar":"buzz"}} and key is "foo.bar", + * "buzz" will be returned. + * If key at some level does not exist, null is returned. + * + * @param object {*} Initial object. + * @param key {string} Property path. + * @return {*} + */ +function getProperty(object, key) { + let result + result = object + for (const p of key.split('.')) { + if (result === undefined) { + return null + } + result = result[p] + } + + if (result === undefined) { + return null + } + return result +} -export { createRandomString, createRandomUser, readYAML } +export { createRandomString, createRandomUser, readYAML, getProperty } diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js new file mode 100644 index 0000000000000000000000000000000000000000..3f03cc6d9f8753038b859bb115b4983b477dd764 --- /dev/null +++ b/cypress/support/e2e.js @@ -0,0 +1,50 @@ +// *********************************************************** +// This example support/e2e.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './atk_commands' +import { getProperty, readYAML } from './atk_utilities' + +// Do environment check before all tests. +before(() => { + readYAML('atk_prerequisites.yml').then((prerequisites) => { + for (const prerequisite of prerequisites) { + if ('command' in prerequisite) { + cy.execDrush(prerequisite.command).then((output) => { + if (prerequisite.json) { + const outputJson = JSON.parse(output) + // Each property of prerequisite.json is a condition. + for (const [key, condition] of Object.entries(prerequisite.json)) { + const value = getProperty(outputJson, key) + if (typeof condition !== 'object') { + throw new Error('Condition must be {"eq":...} or ...') + } + for (const [conditionType, conditionValue] of Object.entries(condition)) { + switch (conditionType) { + case 'eq': + expect(value).to.eq(conditionValue) + break + // ... + default: + throw new Error(`Condition ${conditionType} is not implemented`) + } + } + } + } + }) + } + } + }) +}) diff --git a/data/atk_prerequisites.yml b/data/atk_prerequisites.yml new file mode 100644 index 0000000000000000000000000000000000000000..ce7f181152ca3b0cfff8a3b782642ace5d88f8b4 --- /dev/null +++ b/data/atk_prerequisites.yml @@ -0,0 +1,7 @@ +- command: 'pm:list --format=json' + json: + automated_testing_kit.status: + eq: 'Enabled' + qa_accounts.status: + eq: 'Enabled' + diff --git a/playwright/e2e/atk_caching/atk_caching.spec.js b/playwright/e2e/atk_caching/atk_caching.spec.js index 6bd1cf8364cc6a9296999364c1652459ec3a53a2..8218696e81d327eba3edfc4a6c25493f31cc441a 100644 --- a/playwright/e2e/atk_caching/atk_caching.spec.js +++ b/playwright/e2e/atk_caching/atk_caching.spec.js @@ -5,15 +5,15 @@ * */ +// Set up Playwright. +import { expect, test } from '@playwright/test' + /** ESLint directives */ /* eslint-disable import/first */ import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test, expect } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL diff --git a/playwright/e2e/atk_contact_us/atk_contact_us.spec.js b/playwright/e2e/atk_contact_us/atk_contact_us.spec.js index cc339673d887e8cd871fa71d12f6a1c44446f44b..29b1f3d9c431b7be5dcd5c7435971dedd4843d17 100644 --- a/playwright/e2e/atk_contact_us/atk_contact_us.spec.js +++ b/playwright/e2e/atk_contact_us/atk_contact_us.spec.js @@ -8,12 +8,12 @@ /** ESLint directives */ /* eslint-disable import/first */ +// Set up Playwright. +import { expect, test } from '@playwright/test' + import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test, expect } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL diff --git a/playwright/e2e/atk_entity/atk_media.spec.js b/playwright/e2e/atk_entity/atk_media.spec.js index 23a7f8cf5a72543b6f99569f2baffb4f34243e90..a0bfc181a15b7537cbf0b5b9ea95db46d570e228 100644 --- a/playwright/e2e/atk_entity/atk_media.spec.js +++ b/playwright/e2e/atk_entity/atk_media.spec.js @@ -8,12 +8,12 @@ /** ESLint directives */ /* eslint-disable import/first */ +// Set up Playwright. +import { expect, test } from '@playwright/test' + import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test, expect } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL diff --git a/playwright/e2e/atk_entity/atk_node.spec.js b/playwright/e2e/atk_entity/atk_node.spec.js index bc849e5ddd068c5927f51fe69130f492d3fa7c54..e48bad1a9b211966f3d464c73c78132d443f44c4 100644 --- a/playwright/e2e/atk_entity/atk_node.spec.js +++ b/playwright/e2e/atk_entity/atk_node.spec.js @@ -8,12 +8,12 @@ /** ESLint directives */ /* eslint-disable import/first */ +// Set up Playwright. +import { expect, test } from '@playwright/test' + import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test, expect } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL diff --git a/playwright/e2e/atk_entity/atk_taxonomy.spec.js b/playwright/e2e/atk_entity/atk_taxonomy.spec.js index 3c7b914f9f0feafa68317e5702bb280c9a44820e..c4c950e19a7e24f835c6afc3126199c76c6e9f2a 100644 --- a/playwright/e2e/atk_entity/atk_taxonomy.spec.js +++ b/playwright/e2e/atk_entity/atk_taxonomy.spec.js @@ -8,12 +8,12 @@ /** ESLint directives */ /* eslint-disable import/first */ +// Set up Playwright. +import { expect, test } from '@playwright/test' + import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test, expect } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL diff --git a/playwright/e2e/atk_entity/atk_user.spec.js b/playwright/e2e/atk_entity/atk_user.spec.js index ac7d40d1634bb8ef20574cf8ab247252145c823f..459e762619f8d7e04ea57300cac0b16cfbc0d97f 100644 --- a/playwright/e2e/atk_entity/atk_user.spec.js +++ b/playwright/e2e/atk_entity/atk_user.spec.js @@ -8,12 +8,12 @@ /** ESLint directives */ /* eslint-disable import/first */ +// Set up Playwright. +import { test } from '@playwright/test' + import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL // eslint-disable-line no-unused-vars diff --git a/playwright/e2e/atk_menu/atk_menu.spec.js b/playwright/e2e/atk_menu/atk_menu.spec.js index 3dfba02156267a7c6b629f839305f6869944d8c6..dbb5650b26a1395f7922e5c0274136f8855c3906 100644 --- a/playwright/e2e/atk_menu/atk_menu.spec.js +++ b/playwright/e2e/atk_menu/atk_menu.spec.js @@ -7,12 +7,12 @@ /** ESLint directives */ /* eslint-disable import/first */ +// Set up Playwright. +import { test } from '@playwright/test' + import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL diff --git a/playwright/e2e/atk_page_error/atk_page_error.spec.js b/playwright/e2e/atk_page_error/atk_page_error.spec.js index 249a4c01eb99203b7015ff2b38e1cf4309ca7664..db84d4101434abde4eab64edb07d70ed139f2f9e 100644 --- a/playwright/e2e/atk_page_error/atk_page_error.spec.js +++ b/playwright/e2e/atk_page_error/atk_page_error.spec.js @@ -8,12 +8,12 @@ /** ESLint directives */ /* eslint-disable import/first */ +// Set up Playwright. +import { expect, test } from '@playwright/test' + import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test, expect } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL diff --git a/playwright/e2e/atk_register_login/atk_register_login.spec.js b/playwright/e2e/atk_register_login/atk_register_login.spec.js index ccba0b866633be009a0b0a5748d30909890c87b7..79ca89042095d6cddd97dcbac3ae6a336506b668 100644 --- a/playwright/e2e/atk_register_login/atk_register_login.spec.js +++ b/playwright/e2e/atk_register_login/atk_register_login.spec.js @@ -7,12 +7,12 @@ /** ESLint directives */ /* eslint-disable import/first */ +// Set up Playwright. +import { test } from '@playwright/test' + import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL diff --git a/playwright/e2e/atk_search/atk_search.spec.js b/playwright/e2e/atk_search/atk_search.spec.js index 7033c366eaede7badb77fdc1043ca6b4e6c9a8b1..d4033b7ade512ddce28adf8d3d8173cd5bdb127a 100644 --- a/playwright/e2e/atk_search/atk_search.spec.js +++ b/playwright/e2e/atk_search/atk_search.spec.js @@ -5,15 +5,15 @@ * */ +// Set up Playwright. +import { expect, test } from '@playwright/test' + /** ESLint directives */ /* eslint-disable import/first */ import * as atkCommands from '../support/atk_commands' import * as atkUtilities from '../support/atk_utilities' -// Set up Playwright. -const { test, expect } = require('@playwright/test') - import playwrightConfig from '../../playwright.config' const baseUrl = playwrightConfig.use.baseURL @@ -36,14 +36,14 @@ test.describe('Search tests.', () => { await page.goto(baseUrl) - const searchForm = page.getByLabel('Search Form'); - const isSearchFormVisible = await searchForm.isVisible(); + const searchForm = page.getByLabel('Search Form') + const isSearchFormVisible = await searchForm.isVisible() if (!isSearchFormVisible) { - await page.getByLabel('Main Menu').click(); + await page.getByLabel('Main Menu').click() } for (const item of searchData.simple) { - await openSearchForm(page); + await openSearchForm(page) const keyInput = page.getByRole('searchbox', { name: 'Search' }) await keyInput.fill(item.keyword) await keyInput.press('Enter') @@ -106,13 +106,13 @@ test.describe('Search tests.', () => { await page.goto(baseUrl) - const searchForm = page.getByLabel('Search Form'); - const isSearchFormVisible = await searchForm.isVisible(); + const searchForm = page.getByLabel('Search Form') + const isSearchFormVisible = await searchForm.isVisible() if (!isSearchFormVisible) { - await page.getByLabel('Main Menu').click(); + await page.getByLabel('Main Menu').click() } - await openSearchForm(page); + await openSearchForm(page) const searchInput = page.getByRole('searchbox', { name: 'Search' }) await expect(searchInput).toHaveAttribute('placeholder', 'Search by keyword or phrase.') }) @@ -138,12 +138,12 @@ test.describe('Search tests.', () => { // Handle "responsive design". If "Search form" isn't visible, // have to click main menu button first. - let searchForm = page.getByLabel('Search Form'); - await searchForm.waitFor(); + const searchForm = page.getByLabel('Search Form') + await searchForm.waitFor() if (!(await searchForm.isVisible())) { - await page.getByLabel('Main Menu').click(); + await page.getByLabel('Main Menu').click() } - await searchForm.click(); + await searchForm.click() } async function checkResult(page, item) { diff --git a/playwright/e2e/atk_sitemap/atk_sitemap.spec.js b/playwright/e2e/atk_sitemap/atk_sitemap.spec.js index ac6f1db34852ff93788fb53c865ab3734fbf036d..4aee722fab2c476e5796e6fd7df99c5f56b68413 100644 --- a/playwright/e2e/atk_sitemap/atk_sitemap.spec.js +++ b/playwright/e2e/atk_sitemap/atk_sitemap.spec.js @@ -11,11 +11,12 @@ import { XMLParser } from 'fast-xml-parser' import axios from 'axios' import https from 'https' -import * as atkUtilities from '../support/atk_utilities' // eslint-disable-line no-unused-vars -import * as atkCommands from '../support/atk_commands' // Set up Playwright. -const { test, expect } = require('@playwright/test') +import { expect, test } from '@playwright/test' + +import * as atkUtilities from '../support/atk_utilities' // eslint-disable-line no-unused-vars +import * as atkCommands from '../support/atk_commands' import playwrightConfig from '../../playwright.config' diff --git a/playwright/support/atk_commands.js b/playwright/support/atk_commands.js index 7786ba0a89bb4ee44f826268563d93ce4c1d4055..ee1e9e4e46704aa8cab6ed6cacf9202da0815e1c 100644 --- a/playwright/support/atk_commands.js +++ b/playwright/support/atk_commands.js @@ -15,6 +15,7 @@ import playwrightConfig from '../../playwright.config' // Fetch the Automated Testing Kit config, which is in the project root. import atkConfig from '../../playwright.atk.config' +import { getProperty, readYAML } from './atk_utilities' const baseUrl = playwrightConfig.use.baseURL @@ -63,12 +64,12 @@ function createUserWithUserObject(user, roles = [], args = [], options = []) { // Attempt to add the roles. // Role(s) may come from the user object or the function arguments. if (user.hasOwnProperty('userRoles')) { - user.userRoles.forEach(function (role) { + user.userRoles.forEach((role) => { roles.push(role) }) } - roles.forEach(function (role) { + roles.forEach((role) => { cmd = `user:role:add '${role}' '${user.userName}'` execDrush(cmd) @@ -413,8 +414,8 @@ async function inputTextIntoCKEditor(page, text, instanceNumber = 0) { // Attempt to get the CKEditor instance. const editorInstance = targetEditorElement.ckeditorInstance - || Object.values(CKEDITOR.instances)[editorIndex] - || Object.values(ClassicEditor.instances)[editorIndex] + || Object.values(CKEDITOR.instances)[editorIndex] + || Object.values(ClassicEditor.instances)[editorIndex] if (editorInstance) { // Set the data in the editor. @@ -518,6 +519,40 @@ function setDrupalConfiguration(objectName, key, value) { execDrush(cmd) } +// Check global prerequisites. +// (Once per test run.) +const prerequisites = readYAML('atk_prerequisites.yml') +const { prerequisitesOk } = globalThis +if (prerequisitesOk === undefined) { + globalThis.prerequisitesOk = false + for (const prerequisite of prerequisites) { + if ('command' in prerequisite) { + const output = execDrush(prerequisite.command) + if (prerequisite.json) { + const outputJson = JSON.parse(output) + // Each property of prerequisite.json is a condition. + for (const [key, condition] of Object.entries(prerequisite.json)) { + const value = getProperty(outputJson, key) + if (typeof condition !== 'object') { + throw new Error('Condition must be {"eq":...} or ...') + } + for (const [conditionType, conditionValue] of Object.entries(condition)) { + switch (conditionType) { + case 'eq': + expect(value).toEqual(conditionValue) + break + // ... + default: + throw new Error(`Condition ${conditionType} is not implemented`) + } + } + } + } + } + } + globalThis.prerequisitesOk = true +} + export { createUserWithUserObject, deleteCurrentNodeViaUi, diff --git a/playwright/support/atk_utilities.js b/playwright/support/atk_utilities.js index afe2bd28a8afdea39a7b2466afa9f67b5b715427..a426c8d8bc88c8d44f7d0ad7286c4b4121b2f6fe 100644 --- a/playwright/support/atk_utilities.js +++ b/playwright/support/atk_utilities.js @@ -45,8 +45,35 @@ function readYAML(filename) { return YAML.parse(file) } +/** + * Get multi-level property from an object. + * E.g. if object is {"foo":{"bar":"buzz"}} and key is "foo.bar", + * "buzz" will be returned. + * If key at some level does not exist, null is returned. + * + * @param object {*} Initial object. + * @param key {string} Property path. + * @return {*} + */ +function getProperty(object, key) { + let result + result = object + for (const p of key.split('.')) { + if (result === undefined) { + return null + } + result = result[p] + } + + if (result === undefined) { + return null + } + return result +} + export { createRandomString, createRandomUser, - readYAML + readYAML, + getProperty, }