From 444119ba1669261d949d288737799a5b5483807b Mon Sep 17 00:00:00 2001 From: Ilya Lyaukin <kzerby@gmail.com> Date: Fri, 21 Mar 2025 08:04:18 -0600 Subject: [PATCH 1/2] Add prerequisites check (for cypress) --- cypress/support/atk_utilities.js | 39 ++++++++++++++++++++----- cypress/support/e2e.js | 50 ++++++++++++++++++++++++++++++++ data/atk_prerequisites.yml | 7 +++++ 3 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 cypress/support/e2e.js create mode 100644 data/atk_prerequisites.yml diff --git a/cypress/support/atk_utilities.js b/cypress/support/atk_utilities.js index b56113a..fe97687 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 0000000..3f03cc6 --- /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 0000000..ce7f181 --- /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' + -- GitLab From db1dbe1acac414fa557104fe3a12e8fe2ff0089f Mon Sep 17 00:00:00 2001 From: Ilya Lyaukin <kzerby@gmail.com> Date: Fri, 21 Mar 2025 10:25:19 -0600 Subject: [PATCH 2/2] Prerequisites check (for playwright) --- .../e2e/atk_caching/atk_caching.spec.js | 6 +-- .../e2e/atk_contact_us/atk_contact_us.spec.js | 6 +-- playwright/e2e/atk_entity/atk_media.spec.js | 6 +-- playwright/e2e/atk_entity/atk_node.spec.js | 6 +-- .../e2e/atk_entity/atk_taxonomy.spec.js | 6 +-- playwright/e2e/atk_entity/atk_user.spec.js | 6 +-- playwright/e2e/atk_menu/atk_menu.spec.js | 6 +-- .../e2e/atk_page_error/atk_page_error.spec.js | 6 +-- .../atk_register_login.spec.js | 6 +-- playwright/e2e/atk_search/atk_search.spec.js | 30 ++++++------- .../e2e/atk_sitemap/atk_sitemap.spec.js | 7 +-- playwright/support/atk_commands.js | 43 +++++++++++++++++-- playwright/support/atk_utilities.js | 29 ++++++++++++- 13 files changed, 113 insertions(+), 50 deletions(-) diff --git a/playwright/e2e/atk_caching/atk_caching.spec.js b/playwright/e2e/atk_caching/atk_caching.spec.js index 6bd1cf8..8218696 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 cc33967..29b1f3d 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 23a7f8c..a0bfc18 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 bc849e5..e48bad1 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 3c7b914..c4c950e 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 ac7d40d..459e762 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 3dfba02..dbb5650 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 249a4c0..db84d41 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 ccba0b8..79ca890 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 7033c36..d4033b7 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 ac6f1db..4aee722 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 7786ba0..ee1e9e4 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 afe2bd2..a426c8d 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, } -- GitLab