From fa8c99a69493c19d7a683c10a3fa2ee8700792e9 Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Wed, 15 Jan 2025 16:57:55 +0800 Subject: [PATCH 01/76] initial commit for e2e --- .github/workflows/playwright.yml | 27 + .gitignore | 6 + e2e/config/global-setup.ts | 30 + e2e/example.spec_.ts | 18 + e2e/playwright.config.ts | 106 ++ e2e/readme.md | 39 + e2e/setup/auth.js | 24 + e2e/test-utils/index.ts | 1 + e2e/test-utils/test.ts | 53 + e2e/test.spec.ts | 34 + package-lock.json | 3062 ++++++++++++++++++++++++++++-- package.json | 8 +- src/dynamic-breakpoints.php | 2 +- 13 files changed, 3274 insertions(+), 136 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 e2e/config/global-setup.ts create mode 100644 e2e/example.spec_.ts create mode 100644 e2e/playwright.config.ts create mode 100644 e2e/readme.md create mode 100644 e2e/setup/auth.js create mode 100644 e2e/test-utils/index.ts create mode 100644 e2e/test-utils/test.ts create mode 100644 e2e/test.spec.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000000..3eb13143c3 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main, master ] + pull_request: + branches: [ main, master ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 8d1cdd1e21..9232e46933 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,14 @@ dist/** !dist/*.json **/*__premium_only* coverage +wp-auth.json +.env composer.phar composer.lock /vendor /wordpress +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/e2e/config/global-setup.ts b/e2e/config/global-setup.ts new file mode 100644 index 0000000000..488b5e5f2c --- /dev/null +++ b/e2e/config/global-setup.ts @@ -0,0 +1,30 @@ +// Gutenberg global setup: https://github.com/WordPress/gutenberg/blob/b4304f8bf6bd9b890b4108adcc326cd586a3ab4e/test/e2e/config/global-setup.ts + +import { request } from '@playwright/test' + +import { RequestUtils } from '@wordpress/e2e-test-utils-playwright' + +// To interact with the WP Guest Bar plugin's settings page, we must be authenticated. +// Before any tests are run, we sign in, save the cookies set by WordPress, and then discard the session. +// Later, when we need to act as a logged-in user, we make those cookies available. +// https://playwright.dev/docs/test-global-setup-teardown#configure-globalsetup-and-globalteardown +async function globalSetup() { + const requestContext = await request.newContext( { + baseURL: process.env.WP_BASE_URL, + } ) + const requestUtils = new RequestUtils( requestContext, { + storageStatePath: process.env.WP_AUTH_STORAGE, + user: { + username: process.env.WP_USERNAME, + password: process.env.WP_PASSWORD, + }, + } ) + + // Alternatively, we could take a more traditional route, + // filling in the input fields for the username and password and submitting the form. + // https://playwright.dev/docs/test-global-setup-teardown#example + await requestUtils.setupRest() + await requestContext.dispose() +} + +export default globalSetup diff --git a/e2e/example.spec_.ts b/e2e/example.spec_.ts new file mode 100644 index 0000000000..a9f8f673e0 --- /dev/null +++ b/e2e/example.spec_.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright' + +test( 'has title', async ( { page } ) => { + await page.goto( 'https://playwright.dev/' ) + + // Expect a title "to contain" a substring. + await expect( page ).toHaveTitle( /Playwright/ ) +} ) + +test( 'get started link', async ( { page } ) => { + await page.goto( 'https://playwright.dev/' ) + + // Click the get started link. + await page.getByRole( 'link', { name: 'Get started' } ).click() + + // Expects page to have a heading with the name of Installation. + await expect( page.getByRole( 'heading', { name: 'Installation' } ) ).toBeVisible() +} ) diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts new file mode 100644 index 0000000000..526fe6e517 --- /dev/null +++ b/e2e/playwright.config.ts @@ -0,0 +1,106 @@ +import { defineConfig, devices } from '@playwright/test' +import { fileURLToPath } from 'url' + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +import dotenv from 'dotenv' +import path from 'path' +dotenv.config( { path: path.resolve( __dirname, '../.env' ) } ) + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig( { + testDir: '.', + // This is run before any tests. Check the file for more information. + // globalSetup: 'e2e-global-setup.ts', + globalSetup: fileURLToPath( + new URL( './config/global-setup.ts', 'file:' + __filename ).href + ), + /* Run tests in files in parallel */ + // fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + // forbidOnly: !! process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + // Locally, we could take advantage of parallelism due to multicore systems. + // However, in the CI, we typically can use only one worker at a time. + // It's more straightforward to align how we run tests in both systems. + // https://playwright.dev/docs/test-parallel + // workers: process.env.CI ? 1 : undefined, + workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + // reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + // trace: 'on-first-retry', + + // It's simpler to use relative paths when referencing our application's URLs. + // https://playwright.dev/docs/test-webserver#adding-a-baseurl + baseURL: process.env.WP_BASE_URL, + + // We save as much information as possible to make debugging easier. + // https://playwright.dev/docs/api/class-testoptions#test-options-screenshot + // https://playwright.dev/docs/api/class-testoptions#test-options-trace + // https://playwright.dev/docs/api/class-testoptions#test-options-video + screenshot: 'only-on-failure', + trace: 'retain-on-failure', + video: 'retain-on-failure', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'auth', + // testDir, + testMatch: 'setup/auth.js', + }, + { + name: 'chromium', + use: { ...devices[ 'Desktop Chrome' ] }, + }, + + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +} ) diff --git a/e2e/readme.md b/e2e/readme.md new file mode 100644 index 0000000000..8d0770bbaf --- /dev/null +++ b/e2e/readme.md @@ -0,0 +1,39 @@ +# E2E Testing + +Stackable's end-to-end testing aims to test the high-level functions of the +plugin in order to quickly assess whether everything is in working condition. + +Our goal is to have this run in every Github pull request, and for RC builds. + +At the minimum, e2e testing should test the following: +- Plugin's admin pages are intact +- Plugin's licensing functionality is working (for Premium codebase only) +- Blocks are present in the Block Editor and working +- Blocks are present in the Site Editor +- Global settings and functionality is working +- Perform the above tests in all supported lower WordPress versions + +Ideally, we should also handle these: +- Blocks / content from old plugin versions to this new one do not show errors + in editor +- Blocks / content from old plugin versions to this new one look identical in + the frontend + +# Usage + +Run this command to run e2e: + +```bash +npm run test:debug +``` + +or without the UI: + +```bash +npm run test +``` + +# Dev Notes + +- Our main basis: https://github.com/meszarosrob/wordpress-e2e-playwright-intro-2023 +- Gutenberg e2e Github workflow: https://github.com/WordPress/gutenberg/blob/trunk/.github/workflows/end2end-test.yml diff --git a/e2e/setup/auth.js b/e2e/setup/auth.js new file mode 100644 index 0000000000..31e3d9bc58 --- /dev/null +++ b/e2e/setup/auth.js @@ -0,0 +1,24 @@ +import { test as setup, expect } from '@wordpress/e2e-test-utils-playwright' +// import '../support/env.js' + +const authFile = 'src/playwright/.auth/state.json' + +setup( 'authenticate', async ( { page } ) => { + // Setup the handler. + // await page.addLocatorHandler( page.getByText( 'Administration email verification' ), async () => { + // await page.getByRole( 'button', { name: 'The email is correct' } ).click() + // } ) + + await page.goto( '/wp-login.php' ) + await page.getByLabel( 'Username or Email Address' ).fill( process.env.WP_USERNAME ) + await page.getByLabel( 'Password', { exact: true } ).fill( process.env.WP_PASSWORD ) + await page.getByRole( 'button', { name: 'Log In' } ).click() + + // Handle if the page suddenly asks for email verification. + if ( await page.locator( 'Administration email verification' ).count() ) { + await page.getByRole( 'button', { name: 'The email is correct' } ).click() + } + + await page.waitForURL( '**/wp-admin/' ) + await page.context().storageState( { path: authFile } ) +} ) diff --git a/e2e/test-utils/index.ts b/e2e/test-utils/index.ts new file mode 100644 index 0000000000..c17497c23a --- /dev/null +++ b/e2e/test-utils/index.ts @@ -0,0 +1 @@ +export { test, expect } from './test' diff --git a/e2e/test-utils/test.ts b/e2e/test-utils/test.ts new file mode 100644 index 0000000000..d9efb44fdd --- /dev/null +++ b/e2e/test-utils/test.ts @@ -0,0 +1,53 @@ +import { test as base, expect } from '@playwright/test' + +// We only require what's essential from the WordPress E2E test utils package. +import { + Admin, + Editor, + PageUtils, + RequestUtils, +} from '@wordpress/e2e-test-utils-playwright' + +// We could also import utils from other packages. +// import { StoreApiUtils } from '@woocommerce/e2e-utils'; + +// We are extending the functionalities of Playwright by adding and bootstrapping the custom utils. +// https://playwright.dev/docs/test-fixtures#creating-a-fixture +// +// We have a minimal setup compared to more involved ones. +// https://github.com/WordPress/gutenberg/blob/trunk/packages/e2e-test-utils-playwright/src/test.ts +// https://github.com/woocommerce/woocommerce-blocks/blob/trunk/tests/e2e/playwright-utils/test.ts +const test = base.extend<{ + admin: Admin; + editor: Editor; + pageUtils: PageUtils; + requestUtils: RequestUtils; +}>( { + async admin( { + page, pageUtils, editor, + }, use ) { + await use( new Admin( { + page, pageUtils, editor, + } ) ) + }, + async editor( { page }, use ) { + await use( new Editor( { page } ) ) + }, + async pageUtils( { page }, use ) { + await use( new PageUtils( { page } ) ) + }, + async requestUtils( {}, use ) { + // We want to make all REST API calls as authenticated users. + const requestUtils = await RequestUtils.setup( { + baseURL: process.env.WP_BASE_URL, + user: { + username: process.env.WP_USERNAME, + password: process.env.WP_PASSWORD, + }, + } ) + + await use( requestUtils ) + }, +} ) + +export { test, expect } diff --git a/e2e/test.spec.ts b/e2e/test.spec.ts new file mode 100644 index 0000000000..8aa9e7302b --- /dev/null +++ b/e2e/test.spec.ts @@ -0,0 +1,34 @@ +// We defined this import path in the tsconfig.json. +import { test, expect } from '@wordpress/e2e-test-utils-playwright' +// import { test, expect } from './test-utils' + +test.describe( 'New editor state', () => { + test( 'content should load, making the post dirty', async ( { + page, + admin, + editor, + } ) => { + await admin.visitAdminPage( 'post-new.php', 'gutenberg-demo' ) + + // await admin.visitAdminPage( 'plugins.php' ) + // await admin.visitAdminPage( '/' ) + + await expect( page ).toHaveTitle( /Stackable/ ) + + // await editor.setPreferences( 'core/edit-site', { + // welcomeGuide: false, + // fullscreenMode: false, + // } ); + + // const isDirty = await page.evaluate( () => { + // return window.wp.data.select( 'core/editor' ).isEditedPostDirty(); + // } ); + // expect( isDirty ).toBe( true ); + + // await expect( + // page + // .getByRole( 'region', { name: 'Editor top bar' } ) + // .getByRole( 'button', { name: 'Save draft' } ) + // ).toBeEnabled(); + } ) +} ) diff --git a/package-lock.json b/package-lock.json index 2f2ca8d163..803c6e7676 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "stackable", - "version": "3.8.0", + "version": "3.13.11", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "stackable", - "version": "3.8.0", + "version": "3.13.11", "license": "GPL-3.0", "dependencies": { "@wordpress/dom-ready": "^3.2.3", @@ -32,15 +32,18 @@ "devDependencies": { "@babel/core": "^7.12.17", "@babel/polyfill": "^7.2.5", + "@playwright/test": "^1.49.1", "@svgr/webpack": "^5.5.0", "@testing-library/dom": "^6.16.0", "@testing-library/jest-dom": "^5.11.10", "@testing-library/react": "^11.2.7", + "@types/node": "^22.10.2", "@wordpress/babel-preset-default": "^6.3.4", "@wordpress/block-editor": "^8.0.5", "@wordpress/blocks": "^11.1.4", "@wordpress/browserslist-config": "^4.1.0", "@wordpress/components": "^19.0.5", + "@wordpress/e2e-test-utils-playwright": "^1.14.0", "@wordpress/eslint-plugin": "^8.0.2", "@wordpress/hooks": "^3.2.2", "@wordpress/i18n": "^4.2.4", @@ -57,6 +60,7 @@ "css-mqpacker": "^7.0.0", "cssnano": "^6.0.1", "delete-empty": "^3.0.0", + "dotenv": "^16.4.7", "eslint-plugin-compat": "^3.11.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jest": "^22.21.0", @@ -2213,6 +2217,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.1.tgz", + "integrity": "sha512-Ip9uV+/MpLXWRk03U/GzeJMuPeOXpJBSB5V1tjA6kJhvqssye5J5LoYLc7Z5IAHb7nR62sRoguzrFiVCP/hnzw==", + "dev": true, + "dependencies": { + "@formatjs/fast-memoize": "2.2.5", + "@formatjs/intl-localematcher": "0.5.9", + "decimal.js": "10", + "tslib": "2" + } + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.5.tgz", + "integrity": "sha512-6PoewUMrrcqxSoBXAOJDiW1m+AmkrAj0RiXnOMD59GRaswjXhm3MDhgepXPBgonc09oSirAJTsAggzAGQf6A6g==", + "dev": true, + "dependencies": { + "tslib": "2" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.9.7", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.7.tgz", + "integrity": "sha512-cuEHyRM5VqLQobANOjtjlgU7+qmk9Q3fDQuBiRRJ3+Wp3ZoZhpUPtUfuimZXsir6SaI2TaAJ+SLo9vLnV5QcbA==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.1", + "@formatjs/icu-skeleton-parser": "1.8.11", + "tslib": "2" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.11.tgz", + "integrity": "sha512-8LlHHE/yL/zVJZHAX3pbKaCjZKmBIO6aJY1mkVh4RMSEu/2WRZ4Ysvv3kKXJ9M8RJLBHdnk1/dUQFdod1Dt7Dw==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.1", + "tslib": "2" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.9.tgz", + "integrity": "sha512-8zkGu/sv5euxbjfZ/xmklqLyDGQSxsLqg8XOq88JW3cmJtzhCP8EtSJXlaKZnVO4beEaoiT9wj4eIoCQ9smwxA==", + "dev": true, + "dependencies": { + "tslib": "2" + } + }, "node_modules/@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -3450,6 +3505,30 @@ "node": ">= 8" } }, + "node_modules/@paulirish/trace_engine": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@paulirish/trace_engine/-/trace_engine-0.0.39.tgz", + "integrity": "sha512-2Y/ejHX5DDi5bjfWY/0c/BLVSfQ61Jw1Hy60Hnh0hfEO632D3FVctkzT4Q/lVAdvIPR0bUaok9JDTr1pu/OziA==", + "dev": true, + "dependencies": { + "third-party-web": "latest" + } + }, + "node_modules/@playwright/test": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", + "dev": true, + "dependencies": { + "playwright": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -3466,6 +3545,219 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@puppeteer/browsers": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", + "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@puppeteer/browsers/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@puppeteer/browsers/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@puppeteer/browsers/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/tar-fs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0" + } + }, + "node_modules/@puppeteer/browsers/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@puppeteer/browsers/node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/@react-spring/animated": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.3.0.tgz", @@ -3555,6 +3847,85 @@ } } }, + "node_modules/@sentry-internal/tracing": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.2.tgz", + "integrity": "sha512-eo2F8cP6X+vr54Mp6vu+NoQEDz0M5O24Tz8jPY0T1CpiWdwCmHb7Sln+oLXeQ3/LlWdVQihBfKDBZfBdUfsBTg==", + "dev": true, + "dependencies": { + "@sentry/core": "7.120.2", + "@sentry/types": "7.120.2", + "@sentry/utils": "7.120.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/core": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.2.tgz", + "integrity": "sha512-eurLBFQJC7WWWYoEna25Z9I/GJjqAmH339tv52XP8sqXV7B5hRcHDcfrsT/UGHpU316M24p3lWhj0eimtCZ0SQ==", + "dev": true, + "dependencies": { + "@sentry/types": "7.120.2", + "@sentry/utils": "7.120.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/integrations": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.2.tgz", + "integrity": "sha512-bMvL2fD3TGLM5YAUoQ2Qz6bYeVU8f7YRFNSjKNxK4EbvFgAU9j1FD6EKg0V0RNOJYnJjGIZYMmcWTXBbVTJL6w==", + "dev": true, + "dependencies": { + "@sentry/core": "7.120.2", + "@sentry/types": "7.120.2", + "@sentry/utils": "7.120.2", + "localforage": "^1.8.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/node": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.120.2.tgz", + "integrity": "sha512-ZnW9gpIGaoU+vYZyVZca9dObfmWYiXEWIMUM/JXaFb8AhP1OXvYweNiU0Pe/gNrz4oGAogU8scJc70ar7Vj0ww==", + "dev": true, + "dependencies": { + "@sentry-internal/tracing": "7.120.2", + "@sentry/core": "7.120.2", + "@sentry/integrations": "7.120.2", + "@sentry/types": "7.120.2", + "@sentry/utils": "7.120.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/types": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.2.tgz", + "integrity": "sha512-FWVoiblHQJ892GaOqdXx/5/n5XDLF28z81vJ0lCY49PMh8waz8LJ0b9RSmt9tasSDl0OQ7eUlPl1xu1jTrv1NA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@sentry/utils": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.2.tgz", + "integrity": "sha512-jgnQlw11mRfQrQRAXbq4zEd+tbYwHel5eqeS/oU6EImXRjmHNtS79nB8MHvJeQu1FMCpFs1Ymrrs5FICwS6VeQ==", + "dev": true, + "dependencies": { + "@sentry/types": "7.120.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@sheerun/mutationobserver-shim": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", @@ -4196,6 +4567,12 @@ "node": ">= 6" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -4560,10 +4937,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.9.tgz", - "integrity": "sha512-MKmdASMf3LtPzwLyRrFjtFFZ48cMf8jmX5VRYrDQiJa8Ybu5VAmkqBWqKU8fdCwD8ysw4mQ9nrEHvzg6gunR7A==", - "dev": true + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -5580,6 +5960,53 @@ "node": ">=12" } }, + "node_modules/@wordpress/e2e-test-utils-playwright": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-1.14.0.tgz", + "integrity": "sha512-G9r3ZysgzAmUbR4bjGAEEP6P2RCIAG8uMU7yyzxOAHegINSbF3shEZKvVNBeKxNwHKAVa9koh/niGN3U4Kr6Rw==", + "dev": true, + "dependencies": { + "change-case": "^4.1.2", + "form-data": "^4.0.0", + "get-port": "^5.1.1", + "lighthouse": "^12.2.2", + "mime": "^3.0.0", + "web-vitals": "^4.2.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "@playwright/test": ">=1" + } + }, + "node_modules/@wordpress/e2e-test-utils-playwright/node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@wordpress/e2e-test-utils-playwright/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@wordpress/element": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-4.0.4.tgz", @@ -7622,6 +8049,18 @@ "@mdn/browser-compat-data": "^3.3.14" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -7793,9 +8232,9 @@ } }, "node_modules/axe-core": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz", - "integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", "dev": true, "engines": { "node": ">=4" @@ -7816,6 +8255,12 @@ "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", "dev": true }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "dev": true + }, "node_modules/babel-core": { "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", @@ -8225,6 +8670,52 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bare-events": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "dev": true, + "optional": true + }, + "node_modules/bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "dev": true, + "optional": true, + "dependencies": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "node_modules/bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "dev": true, + "optional": true + }, + "node_modules/bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "dev": true, + "optional": true, + "dependencies": { + "bare-os": "^2.1.0" + } + }, + "node_modules/bare-stream": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.1.tgz", + "integrity": "sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==", + "dev": true, + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + } + }, "node_modules/base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", @@ -8313,6 +8804,15 @@ } ] }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -9070,6 +9570,16 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/camelcase": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", @@ -9140,6 +9650,17 @@ } ] }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "node_modules/capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -9181,6 +9702,26 @@ "node": ">=4" } }, + "node_modules/change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -9479,6 +10020,36 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "node_modules/chrome-launcher": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.2.tgz", + "integrity": "sha512-YclTJey34KUm5jB1aEJCq807bSievi7Nb/TU4Gu504fUYi3jw3KCIaH6L7nFWQhdEgH3V+wCh+kKD1P5cXnfxw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chrome-launcher/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -9488,6 +10059,19 @@ "node": ">=6.0" } }, + "node_modules/chromium-bidi": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", + "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==", + "dev": true, + "dependencies": { + "mitt": "3.0.1", + "zod": "3.23.8" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, "node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -10176,12 +10760,40 @@ "proto-list": "~1.2.1" } }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/consolidated-events": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/consolidated-events/-/consolidated-events-2.0.2.tgz", "integrity": "sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ==", "dev": true }, + "node_modules/constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, "node_modules/content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -10340,6 +10952,21 @@ "node": "*" } }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/csp_evaluator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/csp_evaluator/-/csp_evaluator-1.1.1.tgz", + "integrity": "sha512-N3ASg0C4kNPUaNxt1XAvzHIVuzdtr8KLgfk1O8WDyimp1GisPAHESupArO2ieHk9QWbrJ/WkQODyh21Ps/xhxw==", + "dev": true + }, "node_modules/css": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -10799,6 +11426,15 @@ "integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==", "dev": true }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -10827,12 +11463,12 @@ } }, "node_modules/debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -10843,6 +11479,12 @@ } } }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -11187,6 +11829,15 @@ "node": ">=10" } }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -11250,6 +11901,20 @@ "node": ">=0.10.0" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/del": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", @@ -11740,6 +12405,49 @@ "domelementtype": "1" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop/node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/download": { "version": "6.2.5", "resolved": "https://registry.npmjs.org/download/-/download-6.2.5.tgz", @@ -12299,15 +13007,14 @@ } }, "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" + "esutils": "^2.0.2" }, "bin": { "escodegen": "bin/escodegen.js", @@ -12329,45 +13036,6 @@ "node": ">=4.0" } }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/escodegen/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -12378,18 +13046,6 @@ "node": ">=0.10.0" } }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint": { "version": "7.32.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", @@ -13853,6 +14509,12 @@ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -14881,6 +15543,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-proxy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", @@ -14933,6 +15607,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", + "dev": true, + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -16407,6 +17095,16 @@ "node": ">=0.10.0" } }, + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "dev": true, + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/hey-listen": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", @@ -16562,6 +17260,15 @@ "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", "dev": true }, + "node_modules/http-link-header": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.1.3.tgz", + "integrity": "sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/http-parser-js": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", @@ -16895,6 +17602,12 @@ "randombytes": "^2.1.0" } }, + "node_modules/image-ssim": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/image-ssim/-/image-ssim-0.2.0.tgz", + "integrity": "sha512-W7+sO6/yhxy83L0G7xR8YAc5Z5QFtYEXXRV6EaE8tuYBZJnA3gVgp3q7X7muhLZVodeb9UfvjSbwt9VJwjIYAg==", + "dev": true + }, "node_modules/imagemin": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz", @@ -17069,6 +17782,12 @@ "node": ">=8" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "node_modules/import-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", @@ -17269,6 +17988,18 @@ "node": ">= 0.10" } }, + "node_modules/intl-messageformat": { + "version": "10.7.10", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.10.tgz", + "integrity": "sha512-hp7iejCBiJdW3zmOe18FdlJu8U/JsADSDiBPQhfdSeI8B9POtvPRvPh3nMlvhYayGMKLv6maldhR7y3Pf1vkpw==", + "dev": true, + "dependencies": { + "@formatjs/ecma402-abstract": "2.3.1", + "@formatjs/fast-memoize": "2.2.5", + "@formatjs/icu-messageformat-parser": "2.9.7", + "tslib": "2" + } + }, "node_modules/into-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", @@ -17299,6 +18030,19 @@ "node": ">=0.10.0" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/irregular-plurals": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.3.0.tgz", @@ -17547,7 +18291,6 @@ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, - "optional": true, "bin": { "is-docker": "cli.js" }, @@ -17952,7 +18695,6 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, - "optional": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -21910,6 +22652,21 @@ "@sideway/pinpoint": "^2.0.0" } }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "dev": true + }, + "node_modules/js-library-detector": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/js-library-detector/-/js-library-detector-6.7.0.tgz", + "integrity": "sha512-c80Qupofp43y4cJ7+8TTDN/AsDwLi5oOm/plBrWI+iQt485vKXCco+yVmOwEgdo9VOdsYTuV0UlTeetVPTriXA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -21928,6 +22685,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, "node_modules/jsdoc-type-pratt-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.2.0.tgz", @@ -22330,6 +23093,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/liftoff": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", @@ -22361,6 +23133,284 @@ "node": ">= 0.10" } }, + "node_modules/lighthouse": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/lighthouse/-/lighthouse-12.3.0.tgz", + "integrity": "sha512-OaLE8DasnwQkn2CBo2lKtD+IQv42mNP3T+Vaw29I++rAh0Zpgc6SM15usdIYyzhRMR5EWFxze5Fyb+HENJSh2A==", + "dev": true, + "dependencies": { + "@paulirish/trace_engine": "0.0.39", + "@sentry/node": "^7.0.0", + "axe-core": "^4.10.2", + "chrome-launcher": "^1.1.2", + "configstore": "^5.0.1", + "csp_evaluator": "1.1.1", + "devtools-protocol": "0.0.1312386", + "enquirer": "^2.3.6", + "http-link-header": "^1.1.1", + "intl-messageformat": "^10.5.3", + "jpeg-js": "^0.4.4", + "js-library-detector": "^6.7.0", + "lighthouse-logger": "^2.0.1", + "lighthouse-stack-packs": "1.12.2", + "lodash-es": "^4.17.21", + "lookup-closest-locale": "6.2.0", + "metaviewport-parser": "0.3.0", + "open": "^8.4.0", + "parse-cache-control": "1.0.1", + "puppeteer-core": "^23.10.4", + "robots-parser": "^3.0.1", + "semver": "^5.3.0", + "speedline-core": "^1.4.3", + "third-party-web": "^0.26.1", + "tldts-icann": "^6.1.16", + "ws": "^7.0.0", + "yargs": "^17.3.1", + "yargs-parser": "^21.0.0" + }, + "bin": { + "chrome-debug": "core/scripts/manual-chrome-launcher.js", + "lighthouse": "cli/index.js", + "smokehouse": "cli/test/smokehouse/frontends/smokehouse-bin.js" + }, + "engines": { + "node": ">=18.16" + } + }, + "node_modules/lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", + "dev": true, + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-logger/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/lighthouse-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/lighthouse-stack-packs": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/lighthouse-stack-packs/-/lighthouse-stack-packs-1.12.2.tgz", + "integrity": "sha512-Ug8feS/A+92TMTCK6yHYLwaFMuelK/hAKRMdldYkMNwv+d9PtWxjXEg6rwKtsUXTADajhdrhXyuNCJ5/sfmPFw==", + "dev": true + }, + "node_modules/lighthouse/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lighthouse/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/lighthouse/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/lighthouse/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/lighthouse/node_modules/devtools-protocol": { + "version": "0.0.1312386", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", + "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", + "dev": true + }, + "node_modules/lighthouse/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/lighthouse/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lighthouse/node_modules/puppeteer-core": { + "version": "23.11.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.11.1.tgz", + "integrity": "sha512-3HZ2/7hdDKZvZQ7dhhITOUg4/wOrDRjyK2ZBllRB0ZCOi9u0cwq1ACHDjBB+nX+7+kltHjQvBRdeY7+W0T+7Gg==", + "dev": true, + "dependencies": { + "@puppeteer/browsers": "2.6.1", + "chromium-bidi": "0.11.0", + "debug": "^4.4.0", + "devtools-protocol": "0.0.1367902", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/devtools-protocol": { + "version": "0.0.1367902", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", + "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", + "dev": true + }, + "node_modules/lighthouse/node_modules/puppeteer-core/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/lighthouse/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/lighthouse/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lighthouse/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lighthouse/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lighthouse/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lighthouse/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/lighthouse/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -22914,6 +23964,15 @@ "node": ">=8.9.0" } }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dev": true, + "dependencies": { + "lie": "3.1.1" + } + }, "node_modules/locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -22932,6 +23991,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, "node_modules/lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -23183,6 +24248,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/lookup-closest-locale": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/lookup-closest-locale/-/lookup-closest-locale-6.2.0.tgz", + "integrity": "sha512-/c2kL+Vnp1jnV6K6RpDTHK3dgg0Tu2VVp+elEiJpjfS1UyY7AjOYHohRug6wT0OpoX2qFgNORndE9RqesfVxWQ==", + "dev": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -23194,6 +24265,15 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -23429,6 +24509,12 @@ "integrity": "sha512-vRTPqSU4JK8vVXmjICHSBhwXUvbfh/VJo+j7hvxqe15tLJyomv3FLgFdFgb8kpj0Fe8SsJa/TZUAXv7/sN+N7A==", "dev": true }, + "node_modules/marky": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", + "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", + "dev": true + }, "node_modules/matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -23902,6 +24988,12 @@ "node": ">= 8" } }, + "node_modules/metaviewport-parser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/metaviewport-parser/-/metaviewport-parser-0.3.0.tgz", + "integrity": "sha512-EoYJ8xfjQ6kpe9VbVHvZTZHiOl4HL1Z18CrZ+qahvLXT7ZO4YTC2JMyt5FaUp9JJp6J4Ybb/z7IsCXZt86/QkQ==", + "dev": true + }, "node_modules/micromark": { "version": "2.11.4", "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", @@ -24108,6 +25200,12 @@ "node": ">=0.10.0" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -24327,6 +25425,15 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -24339,6 +25446,16 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -25081,6 +26198,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -25350,6 +26484,83 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz", + "integrity": "sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==", + "dev": true, + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -25362,6 +26573,12 @@ "node": ">=6" } }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, "node_modules/parse-entities": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", @@ -25445,6 +26662,16 @@ "parse5": "^6.0.1" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", @@ -25454,6 +26681,16 @@ "node": ">=0.10.0" } }, + "node_modules/path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", @@ -25680,6 +26917,36 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "dev": true, + "dependencies": { + "playwright-core": "1.49.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/please-upgrade-node": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", @@ -27133,6 +28400,69 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -27397,6 +28727,12 @@ } ] }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, "node_modules/quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -28599,6 +29935,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robots-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.1.tgz", + "integrity": "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/rst-selector-parser": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", @@ -29078,6 +30423,17 @@ "semver": "bin/semver" } }, + "node_modules/sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -29309,6 +30665,26 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -29469,6 +30845,43 @@ "urix": "^0.1.0" } }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -29652,6 +31065,20 @@ "specificity": "bin/specificity" } }, + "node_modules/speedline-core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/speedline-core/-/speedline-core-1.4.3.tgz", + "integrity": "sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog==", + "dev": true, + "dependencies": { + "@types/node": "*", + "image-ssim": "^0.2.0", + "jpeg-js": "^0.4.1" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -29708,9 +31135,9 @@ } }, "node_modules/sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true }, "node_modules/stable": { @@ -29787,6 +31214,20 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "node_modules/streamx": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", + "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, "node_modules/strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -31084,6 +32525,15 @@ "node": ">=8" } }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -31096,6 +32546,12 @@ "integrity": "sha1-ZUhjk+4fK7A5pgy7oFsLaL2VAdI=", "dev": true }, + "node_modules/third-party-web": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/third-party-web/-/third-party-web-0.26.2.tgz", + "integrity": "sha512-taJ0Us0lKoYBqcbccMuDElSUPOxmBfwlHe1OkHQ3KFf+RwovvBHdXhbFk9XJVQE2vHzxbTwvwg5GFsT9hbDokQ==", + "dev": true + }, "node_modules/throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", @@ -31208,6 +32664,21 @@ "ms": "^2.1.1" } }, + "node_modules/tldts-core": { + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", + "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", + "dev": true + }, + "node_modules/tldts-icann": { + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts-icann/-/tldts-icann-6.1.70.tgz", + "integrity": "sha512-sGnxNnxb/03iSROBEBiXGX49DMEktxWVUoTeHWekJOOrFfNRWfyAcOWphuRDau2jZrshvMhQPf3azYHyxV04/w==", + "dev": true, + "dependencies": { + "tldts-core": "^6.1.70" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -31497,6 +32968,12 @@ "node": ">=8" } }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "dev": true + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -31602,6 +33079,12 @@ "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=", "dev": true }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -31717,6 +33200,18 @@ "through2-filter": "^3.0.0" } }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/unist-util-find-all-after": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", @@ -31856,6 +33351,24 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -32327,6 +33840,12 @@ "resolved": "https://registry.npmjs.org/waypoints/-/waypoints-4.0.1.tgz", "integrity": "sha1-CZeaBXOBCylifLpDZqKEoGLsacg=" }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "dev": true + }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -32963,6 +34482,15 @@ } } }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -33093,6 +34621,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", @@ -34585,6 +36122,57 @@ } } }, + "@formatjs/ecma402-abstract": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.1.tgz", + "integrity": "sha512-Ip9uV+/MpLXWRk03U/GzeJMuPeOXpJBSB5V1tjA6kJhvqssye5J5LoYLc7Z5IAHb7nR62sRoguzrFiVCP/hnzw==", + "dev": true, + "requires": { + "@formatjs/fast-memoize": "2.2.5", + "@formatjs/intl-localematcher": "0.5.9", + "decimal.js": "10", + "tslib": "2" + } + }, + "@formatjs/fast-memoize": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.5.tgz", + "integrity": "sha512-6PoewUMrrcqxSoBXAOJDiW1m+AmkrAj0RiXnOMD59GRaswjXhm3MDhgepXPBgonc09oSirAJTsAggzAGQf6A6g==", + "dev": true, + "requires": { + "tslib": "2" + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.9.7", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.7.tgz", + "integrity": "sha512-cuEHyRM5VqLQobANOjtjlgU7+qmk9Q3fDQuBiRRJ3+Wp3ZoZhpUPtUfuimZXsir6SaI2TaAJ+SLo9vLnV5QcbA==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "2.3.1", + "@formatjs/icu-skeleton-parser": "1.8.11", + "tslib": "2" + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.11.tgz", + "integrity": "sha512-8LlHHE/yL/zVJZHAX3pbKaCjZKmBIO6aJY1mkVh4RMSEu/2WRZ4Ysvv3kKXJ9M8RJLBHdnk1/dUQFdod1Dt7Dw==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "2.3.1", + "tslib": "2" + } + }, + "@formatjs/intl-localematcher": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.9.tgz", + "integrity": "sha512-8zkGu/sv5euxbjfZ/xmklqLyDGQSxsLqg8XOq88JW3cmJtzhCP8EtSJXlaKZnVO4beEaoiT9wj4eIoCQ9smwxA==", + "dev": true, + "requires": { + "tslib": "2" + } + }, "@hapi/hoek": { "version": "9.2.1", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.2.1.tgz", @@ -35561,6 +37149,24 @@ "fastq": "^1.6.0" } }, + "@paulirish/trace_engine": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@paulirish/trace_engine/-/trace_engine-0.0.39.tgz", + "integrity": "sha512-2Y/ejHX5DDi5bjfWY/0c/BLVSfQ61Jw1Hy60Hnh0hfEO632D3FVctkzT4Q/lVAdvIPR0bUaok9JDTr1pu/OziA==", + "dev": true, + "requires": { + "third-party-web": "latest" + } + }, + "@playwright/test": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", + "dev": true, + "requires": { + "playwright": "1.49.1" + } + }, "@polka/url": { "version": "1.0.0-next.21", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", @@ -35573,6 +37179,168 @@ "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==", "dev": true }, + "@puppeteer/browsers": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", + "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", + "dev": true, + "requires": { + "debug": "^4.4.0", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "tar-fs": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", + "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "dev": true, + "requires": { + "bare-fs": "^2.1.1", + "bare-path": "^2.1.0", + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + } + }, + "tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "requires": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, "@react-spring/animated": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.3.0.tgz", @@ -35637,6 +37405,67 @@ "any-observable": "^0.3.0" } }, + "@sentry-internal/tracing": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.120.2.tgz", + "integrity": "sha512-eo2F8cP6X+vr54Mp6vu+NoQEDz0M5O24Tz8jPY0T1CpiWdwCmHb7Sln+oLXeQ3/LlWdVQihBfKDBZfBdUfsBTg==", + "dev": true, + "requires": { + "@sentry/core": "7.120.2", + "@sentry/types": "7.120.2", + "@sentry/utils": "7.120.2" + } + }, + "@sentry/core": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.120.2.tgz", + "integrity": "sha512-eurLBFQJC7WWWYoEna25Z9I/GJjqAmH339tv52XP8sqXV7B5hRcHDcfrsT/UGHpU316M24p3lWhj0eimtCZ0SQ==", + "dev": true, + "requires": { + "@sentry/types": "7.120.2", + "@sentry/utils": "7.120.2" + } + }, + "@sentry/integrations": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-7.120.2.tgz", + "integrity": "sha512-bMvL2fD3TGLM5YAUoQ2Qz6bYeVU8f7YRFNSjKNxK4EbvFgAU9j1FD6EKg0V0RNOJYnJjGIZYMmcWTXBbVTJL6w==", + "dev": true, + "requires": { + "@sentry/core": "7.120.2", + "@sentry/types": "7.120.2", + "@sentry/utils": "7.120.2", + "localforage": "^1.8.1" + } + }, + "@sentry/node": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.120.2.tgz", + "integrity": "sha512-ZnW9gpIGaoU+vYZyVZca9dObfmWYiXEWIMUM/JXaFb8AhP1OXvYweNiU0Pe/gNrz4oGAogU8scJc70ar7Vj0ww==", + "dev": true, + "requires": { + "@sentry-internal/tracing": "7.120.2", + "@sentry/core": "7.120.2", + "@sentry/integrations": "7.120.2", + "@sentry/types": "7.120.2", + "@sentry/utils": "7.120.2" + } + }, + "@sentry/types": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.120.2.tgz", + "integrity": "sha512-FWVoiblHQJ892GaOqdXx/5/n5XDLF28z81vJ0lCY49PMh8waz8LJ0b9RSmt9tasSDl0OQ7eUlPl1xu1jTrv1NA==", + "dev": true + }, + "@sentry/utils": { + "version": "7.120.2", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.120.2.tgz", + "integrity": "sha512-jgnQlw11mRfQrQRAXbq4zEd+tbYwHel5eqeS/oU6EImXRjmHNtS79nB8MHvJeQu1FMCpFs1Ymrrs5FICwS6VeQ==", + "dev": true, + "requires": { + "@sentry/types": "7.120.2" + } + }, "@sheerun/mutationobserver-shim": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.3.tgz", @@ -36099,6 +37928,12 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, + "@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true + }, "@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -36428,10 +38263,13 @@ "dev": true }, "@types/node": { - "version": "16.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.9.tgz", - "integrity": "sha512-MKmdASMf3LtPzwLyRrFjtFFZ48cMf8jmX5VRYrDQiJa8Ybu5VAmkqBWqKU8fdCwD8ysw4mQ9nrEHvzg6gunR7A==", - "dev": true + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "dev": true, + "requires": { + "undici-types": "~6.20.0" + } }, "@types/normalize-package-data": { "version": "2.4.1", @@ -37273,6 +39111,39 @@ "@babel/runtime": "^7.16.0" } }, + "@wordpress/e2e-test-utils-playwright": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils-playwright/-/e2e-test-utils-playwright-1.14.0.tgz", + "integrity": "sha512-G9r3ZysgzAmUbR4bjGAEEP6P2RCIAG8uMU7yyzxOAHegINSbF3shEZKvVNBeKxNwHKAVa9koh/niGN3U4Kr6Rw==", + "dev": true, + "requires": { + "change-case": "^4.1.2", + "form-data": "^4.0.0", + "get-port": "^5.1.1", + "lighthouse": "^12.2.2", + "mime": "^3.0.0", + "web-vitals": "^4.2.1" + }, + "dependencies": { + "form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true + } + } + }, "@wordpress/element": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-4.0.4.tgz", @@ -38730,6 +40601,15 @@ "@mdn/browser-compat-data": "^3.3.14" } }, + "ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "requires": { + "tslib": "^2.0.1" + } + }, "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -38865,9 +40745,9 @@ } }, "axe-core": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz", - "integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.2.tgz", + "integrity": "sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==", "dev": true }, "axios": { @@ -38885,6 +40765,12 @@ "integrity": "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==", "dev": true }, + "b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "dev": true + }, "babel-core": { "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", @@ -39218,6 +41104,52 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "bare-events": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", + "dev": true, + "optional": true + }, + "bare-fs": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", + "dev": true, + "optional": true, + "requires": { + "bare-events": "^2.0.0", + "bare-path": "^2.0.0", + "bare-stream": "^2.0.0" + } + }, + "bare-os": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", + "dev": true, + "optional": true + }, + "bare-path": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", + "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "dev": true, + "optional": true, + "requires": { + "bare-os": "^2.1.0" + } + }, + "bare-stream": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.1.tgz", + "integrity": "sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==", + "dev": true, + "optional": true, + "requires": { + "streamx": "^2.21.0" + } + }, "base": { "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", @@ -39279,6 +41211,12 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true }, + "basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -39895,6 +41833,16 @@ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "camelcase": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", @@ -39938,6 +41886,17 @@ "integrity": "sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==", "dev": true }, + "capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -39970,6 +41929,26 @@ "supports-color": "^5.3.0" } }, + "change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "dev": true, + "requires": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -40185,12 +42164,42 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, + "chrome-launcher": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-1.1.2.tgz", + "integrity": "sha512-YclTJey34KUm5jB1aEJCq807bSievi7Nb/TU4Gu504fUYi3jw3KCIaH6L7nFWQhdEgH3V+wCh+kKD1P5cXnfxw==", + "dev": true, + "requires": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^2.0.1" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + } + } + }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, + "chromium-bidi": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", + "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==", + "dev": true, + "requires": { + "mitt": "3.0.1", + "zod": "3.23.8" + } + }, "ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -40791,12 +42800,37 @@ "proto-list": "~1.2.1" } }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, "consolidated-events": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/consolidated-events/-/consolidated-events-2.0.2.tgz", "integrity": "sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ==", "dev": true }, + "constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, "content-disposition": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", @@ -40925,6 +42959,18 @@ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, + "csp_evaluator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/csp_evaluator/-/csp_evaluator-1.1.1.tgz", + "integrity": "sha512-N3ASg0C4kNPUaNxt1XAvzHIVuzdtr8KLgfk1O8WDyimp1GisPAHESupArO2ieHk9QWbrJ/WkQODyh21Ps/xhxw==", + "dev": true + }, "css": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", @@ -41305,6 +43351,12 @@ "integrity": "sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw==", "dev": true }, + "data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true + }, "data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -41323,12 +43375,20 @@ "dev": true }, "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "requires": { - "ms": "2.1.2" + "ms": "^2.1.3" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } } }, "decamelize": { @@ -41613,6 +43673,12 @@ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -41663,6 +43729,17 @@ } } }, + "degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "requires": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + } + }, "del": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", @@ -42043,6 +44120,39 @@ "domelementtype": "1" } }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + }, + "dependencies": { + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + } + } + }, + "dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true + }, "download": { "version": "6.2.5", "resolved": "https://registry.npmjs.org/download/-/download-6.2.5.tgz", @@ -42520,15 +44630,14 @@ "dev": true }, "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "requires": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2", - "optionator": "^0.8.1", "source-map": "~0.6.1" }, "dependencies": { @@ -42538,51 +44647,12 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } } } }, @@ -43687,6 +45757,12 @@ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, + "fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true + }, "fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -44508,6 +46584,12 @@ "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, + "get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true + }, "get-proxy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", @@ -44542,6 +46624,17 @@ "get-intrinsic": "^1.1.1" } }, + "get-uri": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", + "dev": true, + "requires": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -45729,6 +47822,16 @@ } } }, + "header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "dev": true, + "requires": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, "hey-listen": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", @@ -45855,6 +47958,12 @@ "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", "dev": true }, + "http-link-header": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/http-link-header/-/http-link-header-1.1.3.tgz", + "integrity": "sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==", + "dev": true + }, "http-parser-js": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.3.tgz", @@ -46086,6 +48195,12 @@ } } }, + "image-ssim": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/image-ssim/-/image-ssim-0.2.0.tgz", + "integrity": "sha512-W7+sO6/yhxy83L0G7xR8YAc5Z5QFtYEXXRV6EaE8tuYBZJnA3gVgp3q7X7muhLZVodeb9UfvjSbwt9VJwjIYAg==", + "dev": true + }, "imagemin": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/imagemin/-/imagemin-7.0.1.tgz", @@ -46216,6 +48331,12 @@ } } }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, "import-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", @@ -46369,6 +48490,18 @@ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, + "intl-messageformat": { + "version": "10.7.10", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.10.tgz", + "integrity": "sha512-hp7iejCBiJdW3zmOe18FdlJu8U/JsADSDiBPQhfdSeI8B9POtvPRvPh3nMlvhYayGMKLv6maldhR7y3Pf1vkpw==", + "dev": true, + "requires": { + "@formatjs/ecma402-abstract": "2.3.1", + "@formatjs/fast-memoize": "2.2.5", + "@formatjs/icu-messageformat-parser": "2.9.7", + "tslib": "2" + } + }, "into-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", @@ -46393,6 +48526,16 @@ "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, + "ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "requires": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + } + }, "irregular-plurals": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-3.3.0.tgz", @@ -46577,8 +48720,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "optional": true + "dev": true }, "is-extendable": { "version": "0.1.1", @@ -46856,7 +48998,6 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, - "optional": true, "requires": { "is-docker": "^2.0.0" } @@ -49924,6 +52065,18 @@ "@sideway/pinpoint": "^2.0.0" } }, + "jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", + "dev": true + }, + "js-library-detector": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/js-library-detector/-/js-library-detector-6.7.0.tgz", + "integrity": "sha512-c80Qupofp43y4cJ7+8TTDN/AsDwLi5oOm/plBrWI+iQt485vKXCco+yVmOwEgdo9VOdsYTuV0UlTeetVPTriXA==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -49939,6 +52092,12 @@ "esprima": "^4.0.0" } }, + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, "jsdoc-type-pratt-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.2.0.tgz", @@ -50271,6 +52430,15 @@ "type-check": "~0.4.0" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, "liftoff": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", @@ -50298,6 +52466,223 @@ } } }, + "lighthouse": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/lighthouse/-/lighthouse-12.3.0.tgz", + "integrity": "sha512-OaLE8DasnwQkn2CBo2lKtD+IQv42mNP3T+Vaw29I++rAh0Zpgc6SM15usdIYyzhRMR5EWFxze5Fyb+HENJSh2A==", + "dev": true, + "requires": { + "@paulirish/trace_engine": "0.0.39", + "@sentry/node": "^7.0.0", + "axe-core": "^4.10.2", + "chrome-launcher": "^1.1.2", + "configstore": "^5.0.1", + "csp_evaluator": "1.1.1", + "devtools-protocol": "0.0.1312386", + "enquirer": "^2.3.6", + "http-link-header": "^1.1.1", + "intl-messageformat": "^10.5.3", + "jpeg-js": "^0.4.4", + "js-library-detector": "^6.7.0", + "lighthouse-logger": "^2.0.1", + "lighthouse-stack-packs": "1.12.2", + "lodash-es": "^4.17.21", + "lookup-closest-locale": "6.2.0", + "metaviewport-parser": "0.3.0", + "open": "^8.4.0", + "parse-cache-control": "1.0.1", + "puppeteer-core": "^23.10.4", + "robots-parser": "^3.0.1", + "semver": "^5.3.0", + "speedline-core": "^1.4.3", + "third-party-web": "^0.26.1", + "tldts-icann": "^6.1.16", + "ws": "^7.0.0", + "yargs": "^17.3.1", + "yargs-parser": "^21.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "devtools-protocol": { + "version": "0.0.1312386", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", + "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "puppeteer-core": { + "version": "23.11.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.11.1.tgz", + "integrity": "sha512-3HZ2/7hdDKZvZQ7dhhITOUg4/wOrDRjyK2ZBllRB0ZCOi9u0cwq1ACHDjBB+nX+7+kltHjQvBRdeY7+W0T+7Gg==", + "dev": true, + "requires": { + "@puppeteer/browsers": "2.6.1", + "chromium-bidi": "0.11.0", + "debug": "^4.4.0", + "devtools-protocol": "0.0.1367902", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.0" + }, + "dependencies": { + "devtools-protocol": { + "version": "0.0.1367902", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", + "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", + "dev": true + }, + "ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "requires": {} + } + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } + } + }, + "lighthouse-logger": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-2.0.1.tgz", + "integrity": "sha512-ioBrW3s2i97noEmnXxmUq7cjIcVRjT5HBpAYy8zE11CxU9HqlWHHeRxfeN1tn8F7OEMVPIC9x1f8t3Z7US9ehQ==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "marky": "^1.2.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "lighthouse-stack-packs": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/lighthouse-stack-packs/-/lighthouse-stack-packs-1.12.2.tgz", + "integrity": "sha512-Ug8feS/A+92TMTCK6yHYLwaFMuelK/hAKRMdldYkMNwv+d9PtWxjXEg6rwKtsUXTADajhdrhXyuNCJ5/sfmPFw==", + "dev": true + }, "lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -50729,6 +53114,15 @@ "json5": "^2.1.2" } }, + "localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dev": true, + "requires": { + "lie": "3.1.1" + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -50744,6 +53138,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -50950,6 +53350,12 @@ "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", "dev": true }, + "lookup-closest-locale": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/lookup-closest-locale/-/lookup-closest-locale-6.2.0.tgz", + "integrity": "sha512-/c2kL+Vnp1jnV6K6RpDTHK3dgg0Tu2VVp+elEiJpjfS1UyY7AjOYHohRug6wT0OpoX2qFgNORndE9RqesfVxWQ==", + "dev": true + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -50958,6 +53364,15 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -51140,6 +53555,12 @@ "integrity": "sha512-vRTPqSU4JK8vVXmjICHSBhwXUvbfh/VJo+j7hvxqe15tLJyomv3FLgFdFgb8kpj0Fe8SsJa/TZUAXv7/sN+N7A==", "dev": true }, + "marky": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", + "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==", + "dev": true + }, "matchdep": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", @@ -51518,6 +53939,12 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, + "metaviewport-parser": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/metaviewport-parser/-/metaviewport-parser-0.3.0.tgz", + "integrity": "sha512-EoYJ8xfjQ6kpe9VbVHvZTZHiOl4HL1Z18CrZ+qahvLXT7ZO4YTC2JMyt5FaUp9JJp6J4Ybb/z7IsCXZt86/QkQ==", + "dev": true + }, "micromark": { "version": "2.11.4", "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", @@ -51661,6 +54088,12 @@ } } }, + "mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -51833,6 +54266,12 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -51845,6 +54284,16 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -52412,6 +54861,17 @@ "mimic-fn": "^2.1.0" } }, + "open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, "opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -52607,6 +55067,70 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "pac-proxy-agent": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz", + "integrity": "sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==", + "dev": true, + "requires": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "dependencies": { + "agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true + }, + "http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "requires": { + "agent-base": "^7.1.2", + "debug": "4" + } + } + } + }, + "pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "requires": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + } + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -52616,6 +55140,12 @@ "callsites": "^3.0.0" } }, + "parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, "parse-entities": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", @@ -52680,12 +55210,32 @@ "parse5": "^6.0.1" } }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "path-dirname": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", @@ -52857,6 +55407,22 @@ } } }, + "playwright": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.49.1" + } + }, + "playwright-core": { + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "dev": true + }, "please-upgrade-node": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", @@ -53879,6 +56445,56 @@ "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", "dev": true }, + "proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, + "requires": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "dependencies": { + "agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true + }, + "http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "requires": { + "agent-base": "^7.1.2", + "debug": "4" + } + }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + } + } + }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -54067,6 +56683,12 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, "quick-lru": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", @@ -55032,6 +57654,12 @@ "glob": "^7.1.3" } }, + "robots-parser": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robots-parser/-/robots-parser-3.0.1.tgz", + "integrity": "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ==", + "dev": true + }, "rst-selector-parser": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz", @@ -55397,6 +58025,17 @@ } } }, + "sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -55583,6 +58222,22 @@ } } }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true + }, + "snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -55718,6 +58373,35 @@ } } }, + "socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "requires": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "requires": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "dependencies": { + "agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true + } + } + }, "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -55869,6 +58553,17 @@ "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", "dev": true }, + "speedline-core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/speedline-core/-/speedline-core-1.4.3.tgz", + "integrity": "sha512-DI7/OuAUD+GMpR6dmu8lliO2Wg5zfeh+/xsdyJZCzd8o5JgFUjCeLsBDuZjIQJdwXS3J0L/uZYrELKYqx+PXog==", + "dev": true, + "requires": { + "@types/node": "*", + "image-ssim": "^0.2.0", + "jpeg-js": "^0.4.1" + } + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -55924,9 +58619,9 @@ } }, "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true }, "stable": { @@ -55991,6 +58686,18 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "streamx": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", + "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", + "dev": true, + "requires": { + "bare-events": "^2.2.0", + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + } + }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -56956,6 +59663,15 @@ "minimatch": "^3.0.4" } }, + "text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "requires": { + "b4a": "^1.6.4" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -56968,6 +59684,12 @@ "integrity": "sha1-ZUhjk+4fK7A5pgy7oFsLaL2VAdI=", "dev": true }, + "third-party-web": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/third-party-web/-/third-party-web-0.26.2.tgz", + "integrity": "sha512-taJ0Us0lKoYBqcbccMuDElSUPOxmBfwlHe1OkHQ3KFf+RwovvBHdXhbFk9XJVQE2vHzxbTwvwg5GFsT9hbDokQ==", + "dev": true + }, "throat": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", @@ -57078,6 +59800,21 @@ } } }, + "tldts-core": { + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", + "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", + "dev": true + }, + "tldts-icann": { + "version": "6.1.70", + "resolved": "https://registry.npmjs.org/tldts-icann/-/tldts-icann-6.1.70.tgz", + "integrity": "sha512-sGnxNnxb/03iSROBEBiXGX49DMEktxWVUoTeHWekJOOrFfNRWfyAcOWphuRDau2jZrshvMhQPf3azYHyxV04/w==", + "dev": true, + "requires": { + "tldts-core": "^6.1.70" + } + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -57309,6 +60046,12 @@ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true }, + "typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "dev": true + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -57397,6 +60140,12 @@ "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", "dev": true }, + "undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -57475,6 +60224,15 @@ "through2-filter": "^3.0.0" } }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, "unist-util-find-all-after": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", @@ -57567,6 +60325,24 @@ "picocolors": "^1.0.0" } }, + "upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -57944,6 +60720,12 @@ "resolved": "https://registry.npmjs.org/waypoints/-/waypoints-4.0.1.tgz", "integrity": "sha1-CZeaBXOBCylifLpDZqKEoGLsacg=" }, + "web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "dev": true + }, "webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", @@ -58392,6 +61174,12 @@ "dev": true, "requires": {} }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", @@ -58506,6 +61294,12 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true }, + "zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true + }, "zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", diff --git a/package.json b/package.json index c4b9a9653e..d0585731fe 100644 --- a/package.json +++ b/package.json @@ -37,15 +37,18 @@ "devDependencies": { "@babel/core": "^7.12.17", "@babel/polyfill": "^7.2.5", + "@playwright/test": "^1.49.1", "@svgr/webpack": "^5.5.0", "@testing-library/dom": "^6.16.0", "@testing-library/jest-dom": "^5.11.10", "@testing-library/react": "^11.2.7", + "@types/node": "^22.10.2", "@wordpress/babel-preset-default": "^6.3.4", "@wordpress/block-editor": "^8.0.5", "@wordpress/blocks": "^11.1.4", "@wordpress/browserslist-config": "^4.1.0", "@wordpress/components": "^19.0.5", + "@wordpress/e2e-test-utils-playwright": "^1.14.0", "@wordpress/eslint-plugin": "^8.0.2", "@wordpress/hooks": "^3.2.2", "@wordpress/i18n": "^4.2.4", @@ -62,6 +65,7 @@ "css-mqpacker": "^7.0.0", "cssnano": "^6.0.1", "delete-empty": "^3.0.0", + "dotenv": "^16.4.7", "eslint-plugin-compat": "^3.11.1", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jest": "^22.21.0", @@ -124,7 +128,9 @@ "prebuild": "node ./tools/ensure-version-number && npm run build:translations-js", "build": "npm run build:translations && npm run clean && npm run update-build-type && concurrently --raw \"npm:build:js\" \"npm:build:css\" \"npm:build:sub-builds\" \"npm:build:translations-php\" && npm run package", "build:no-translate": "npm run clean && npm run update-build-type && concurrently --raw \"npm:build:js\" \"npm:build:css\" \"npm:build:sub-builds\" && npm run package", - "test": "jest --no-cache --watchAll", + "test": "npx playwright test --config e2e/playwright.config.ts", + "test:debug": "npx playwright test --config e2e/playwright.config.ts --ui", + "test-old": "jest --no-cache --watchAll", "test:coverage": "jest --no-cache --coverage", "test:fast": "npm run clean:build && jest", "test:scripts": "wp-scripts test-unit-js", diff --git a/src/dynamic-breakpoints.php b/src/dynamic-breakpoints.php index c6113a738a..7ddcd1c6c9 100644 --- a/src/dynamic-breakpoints.php +++ b/src/dynamic-breakpoints.php @@ -15,7 +15,7 @@ function stackable_get_responsive_css() { // NOTE: THE VALUE BELOW IS AUTOMATICALLY GENERATED BY THE BUILD PROCESS. return <<*>.stk-row{flex-wrap:var(--stk-feature-flex-wrap,nowrap)}.stk-row{flex-wrap:nowrap}.stk--hide-desktop,.stk--hide-desktop.stk-block{display:none!important}}@media only screen and (min-width:768px){:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(2)){flex:1 1 calc(50% - var(--stk-column-gap, 0px)*1/2)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):last-child){flex:1 1 calc(50% - var(--stk-column-gap, 0px)*1/2)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(3)){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(2)){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):last-child){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(4)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(3)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(2)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):last-child){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(5)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(4)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(3)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):nth-last-child(2)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(5):last-child){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(6)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(5)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(4)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):nth-last-child(3)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(5):nth-last-child(2)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(6):last-child){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical{grid-template-columns:auto 1fr auto}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical>.stk-block-tab-labels:first-child{grid-column:1/2}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical>.stk-block-tab-content:last-child{grid-column:2/4}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical>.stk-block-tab-content:first-child{grid-column:1/3}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical>.stk-block-tab-labels:last-child{grid-column:3/4}.stk-block .stk-block.aligncenter,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){margin-left:auto;margin-right:auto;max-width:var(--stk-block-default-width,var(--stk-block-width-default-detected,900px));width:100%}.stk-block .stk-block.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide{margin-left:auto;margin-right:auto;max-width:var(--stk-block-wide-width,var(--stk-block-width-wide-detected,80vw));width:100%}.stk-row.stk-columns-2>.stk-column{flex:1 1 50%;max-width:50%}.stk-row.stk-columns-3>.stk-column{flex:1 1 33.3333333333%;max-width:33.3333333333%}.stk-row.stk-columns-4>.stk-column{flex:1 1 25%;max-width:25%}.stk-row.stk-columns-5>.stk-column{flex:1 1 20%;max-width:20%}.stk-row.stk-columns-6>.stk-column{flex:1 1 16.6666666667%;max-width:16.6666666667%}.stk-row.stk-columns-7>.stk-column{flex:1 1 14.2857142857%;max-width:14.2857142857%}.stk-row.stk-columns-8>.stk-column{flex:1 1 12.5%;max-width:12.5%}.stk-row.stk-columns-9>.stk-column{flex:1 1 11.1111111111%;max-width:11.1111111111%}.stk-row.stk-columns-10>.stk-column{flex:1 1 10%;max-width:10%}}@media only screen and (min-width:768px) and (max-width:1023px){.stk-button-group:is(.stk--collapse-on-tablet)>.block-editor-inner-blocks>.block-editor-block-list__layout>[data-block]{margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}:where(.has-text-align-left-tablet) .stk-block-divider{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}:where(.has-text-align-right-tablet) .stk-block-divider{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}:where(.has-text-align-center-tablet) .stk-block-divider{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk--hide-tablet,.stk--hide-tablet.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}}@media only screen and (max-width:1023px){.stk-block-button{min-width:-moz-fit-content;min-width:fit-content}.stk-block.stk-block-divider.has-text-align-center-tablet{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-right-tablet{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-left-tablet{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}.stk-block-timeline.stk-block-timeline__ios-polyfill{--fixed-bg:linear-gradient(to bottom,var(--line-accent-bg-color,#000) 0,var(--line-accent-bg-color-2,#000) var(--line-accent-bg-location,50%))}.has-text-align-center-tablet{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-tablet{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-tablet{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-tablet{--stk-alignment-text-align:justify}.has-text-align-space-between-tablet{--stk-alignment-justify-content:space-between}.has-text-align-space-around-tablet{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-tablet{--stk-alignment-justify-content:space-evenly}}@media only screen and (max-width:767px){.stk-button-group:is(.stk--collapse-on-mobile)>.block-editor-inner-blocks>.block-editor-block-list__layout>[data-block],.stk-button-group:is(.stk--collapse-on-tablet)>.block-editor-inner-blocks>.block-editor-block-list__layout>[data-block]{margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}.stk-block-carousel.stk--hide-mobile-arrows>.stk-block-carousel__content-wrapper>*>.stk-block-carousel__buttons,.stk-block-carousel.stk--hide-mobile-dots>.stk-block-carousel__content-wrapper>.stk-block-carousel__dots{display:none}:where(.has-text-align-left-mobile) .stk-block-divider{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}:where(.has-text-align-right-mobile) .stk-block-divider{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}:where(.has-text-align-center-mobile) .stk-block-divider{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-center-mobile{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-right-mobile{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-left-mobile{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}.stk-block.stk-block-feature:is(.is-style-default,.is-style-horizontal)>.stk-container>.stk-inner-blocks.stk-block-content{flex-direction:column-reverse}.stk-block-posts{--stk-columns:1}.stk-block-tabs>.stk-inner-blocks{grid-template-columns:1fr}.stk-block-tabs>.stk-inner-blocks>.stk-block-tab-labels:last-child{grid-row:1/2}.stk-block-tab-labels:not(.stk-block-tab-labels--wrap-mobile){overflow-x:auto!important;width:100%}.stk-block-tab-labels:not(.stk-block-tab-labels--wrap-mobile)::-webkit-scrollbar{height:0!important}.stk-block-tab-labels:not(.stk-block-tab-labels--wrap-mobile) .stk-block-tab-labels__wrapper{flex-wrap:nowrap;min-width:-webkit-fill-available;min-width:fill-available;width:max-content}.stk-block.stk-block-timeline{--content-line:0!important;padding-top:0}.stk-block-timeline>.stk-inner-blocks{align-items:flex-start;grid-template-columns:var(--line-dot-size,16px) 1fr;grid-template-rows:auto 1fr;padding-left:16px}.stk-block-timeline>.stk-inner-blocks:after{inset-inline-start:calc(var(--line-dot-size, 16px)/2 - var(--line-bg-width, 3px)/2 + 16px)}.stk-block-timeline .stk-block-timeline__middle{inset-block-start:8px;inset-inline-start:16px;position:absolute}.stk-block-timeline .stk-block-timeline__content{grid-column:2/3;grid-row:2/3;text-align:start}.stk-block-timeline .stk-block-timeline__date{grid-column:2/3;grid-row:1/2;text-align:start}.stk-block-timeline>.stk-inner-blocks:after{bottom:calc(100% - var(--line-dot-size, 16px)/2 - .5em);top:calc(var(--line-dot-size, 16px)/2 + .5em)}.stk-block-timeline+.stk-block-timeline>.stk-inner-blocks:after{top:-16px}:root{--stk-block-margin-bottom:16px;--stk-container-padding:24px 24px;--stk-container-padding-large:32px 24px;--stk-container-padding-small:8px 24px;--stk-column-margin:8px;--stk-block-background-padding:16px 16px}.stk-block .stk-block:is(.aligncenter,.alignwide),.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){width:100%}.stk-column{flex:1 1 100%;max-width:100%}.stk--hide-mobile,.stk--hide-mobile.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-mobile) .stk-block:is(.stk-block-button,.stk-block-icon-button),.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}.has-text-align-center-mobile{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-mobile{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-mobile{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-mobile{--stk-alignment-text-align:justify}.has-text-align-space-between-mobile{--stk-alignment-justify-content:space-between}.has-text-align-space-around-mobile{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-mobile{--stk-alignment-justify-content:space-evenly}.entry-content .stk-block.stk-has-top-separator{padding-top:23vw}.entry-content .stk-block.stk-has-bottom-separator{padding-bottom:23vw}.entry-content .stk-block .stk-separator__wrapper{height:23vw}}#end-resizable-editor-section{display:none} +#start-resizable-editor-section{display:none}@media only screen and (min-width:1024px){:where(.has-text-align-left) .stk-block-divider{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}:where(.has-text-align-right) .stk-block-divider{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}:where(.has-text-align-center) .stk-block-divider{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-center{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-right{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-left{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}.stk-block-feature>*>.stk-row{flex-wrap:var(--stk-feature-flex-wrap,nowrap)}.stk-row{flex-wrap:nowrap}.stk--hide-desktop,.stk--hide-desktop.stk-block{display:none!important}}@media only screen and (min-width:768px){:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(2)){flex:1 1 calc(50% - var(--stk-column-gap, 0px)*1/2)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):last-child){flex:1 1 calc(50% - var(--stk-column-gap, 0px)*1/2)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(3)){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(2)){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):last-child){flex:1 1 calc(33.33333% - var(--stk-column-gap, 0px)*2/3)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(4)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(3)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(2)){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):last-child){flex:1 1 calc(25% - var(--stk-column-gap, 0px)*3/4)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(5)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(4)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(3)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):nth-last-child(2)){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(5):last-child){flex:1 1 calc(20% - var(--stk-column-gap, 0px)*4/5)!important}:where(body:not(.wp-admin) .stk-block-column:first-child:nth-last-child(6)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(2):nth-last-child(5)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(3):nth-last-child(4)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(4):nth-last-child(3)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(5):nth-last-child(2)){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}:where(body:not(.wp-admin) .stk-block-column:nth-child(6):last-child){flex:1 1 calc(16.66667% - var(--stk-column-gap, 0px)*5/6)!important}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical{grid-template-columns:auto 1fr auto}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical>.stk-block-tab-labels:first-child{grid-column:1/2}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical>.stk-block-tab-content:last-child{grid-column:2/4}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical>.stk-block-tab-content:first-child{grid-column:1/3}.stk-block-tabs>.stk-inner-blocks.stk-block-tabs--vertical>.stk-block-tab-labels:last-child{grid-column:3/4}.stk-block .stk-block.aligncenter,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){margin-left:auto;margin-right:auto;max-width:var(--stk-block-default-width,var(--stk-block-width-default-detected,900px));width:100%}.stk-block .stk-block.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide{margin-left:auto;margin-right:auto;max-width:var(--stk-block-wide-width,var(--stk-block-width-wide-detected,80vw));width:100%}.stk-row.stk-columns-2>.stk-column{flex:1 1 50%;max-width:50%}.stk-row.stk-columns-3>.stk-column{flex:1 1 33.3333333333%;max-width:33.3333333333%}.stk-row.stk-columns-4>.stk-column{flex:1 1 25%;max-width:25%}.stk-row.stk-columns-5>.stk-column{flex:1 1 20%;max-width:20%}.stk-row.stk-columns-6>.stk-column{flex:1 1 16.6666666667%;max-width:16.6666666667%}.stk-row.stk-columns-7>.stk-column{flex:1 1 14.2857142857%;max-width:14.2857142857%}.stk-row.stk-columns-8>.stk-column{flex:1 1 12.5%;max-width:12.5%}.stk-row.stk-columns-9>.stk-column{flex:1 1 11.1111111111%;max-width:11.1111111111%}.stk-row.stk-columns-10>.stk-column{flex:1 1 10%;max-width:10%}}@media only screen and (min-width:768px) and (max-width:1023px){.stk-button-group:is(.stk--collapse-on-tablet)>.block-editor-inner-blocks>.block-editor-block-list__layout>[data-block]{margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}:where(.has-text-align-left-tablet) .stk-block-divider{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}:where(.has-text-align-right-tablet) .stk-block-divider{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}:where(.has-text-align-center-tablet) .stk-block-divider{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk--hide-tablet,.stk--hide-tablet.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}}@media only screen and (max-width:1023px){.stk-block-button{min-width:-moz-fit-content;min-width:fit-content}.stk-block.stk-block-divider.has-text-align-center-tablet{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-right-tablet{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-left-tablet{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}.stk-block-timeline.stk-block-timeline__ios-polyfill{--fixed-bg:linear-gradient(to bottom,var(--line-accent-bg-color,#000) 0,var(--line-accent-bg-color-2,#000) var(--line-accent-bg-location,50%))}.has-text-align-center-tablet{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-tablet{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-tablet{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-tablet{--stk-alignment-text-align:justify}.has-text-align-space-between-tablet{--stk-alignment-justify-content:space-between}.has-text-align-space-around-tablet{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-tablet{--stk-alignment-justify-content:space-evenly}}@media only screen and (max-width:767px){.stk-button-group:is(.stk--collapse-on-mobile)>.block-editor-inner-blocks>.block-editor-block-list__layout>[data-block],.stk-button-group:is(.stk--collapse-on-tablet)>.block-editor-inner-blocks>.block-editor-block-list__layout>[data-block]{margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}.stk-block-carousel.stk--hide-mobile-arrows>.stk-block-carousel__content-wrapper>*>.stk-block-carousel__buttons,.stk-block-carousel.stk--hide-mobile-dots>.stk-block-carousel__content-wrapper>.stk-block-carousel__dots{display:none}:where(.has-text-align-left-mobile) .stk-block-divider{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}:where(.has-text-align-right-mobile) .stk-block-divider{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}:where(.has-text-align-center-mobile) .stk-block-divider{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-center-mobile{--stk-dots-margin-right:auto;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-right-mobile{--stk-dots-margin-right:0;--stk-dots-margin-left:auto}.stk-block.stk-block-divider.has-text-align-left-mobile{--stk-dots-margin-left:0;--stk-dots-margin-right:auto}.stk-block.stk-block-feature:is(.is-style-default,.is-style-horizontal)>.stk-container>.stk-inner-blocks.stk-block-content{flex-direction:column-reverse}.stk-block-posts{--stk-columns:1}.stk-block-tab-labels:not(.stk-block-tab-labels--wrap-mobile){overflow-x:auto!important;width:100%}.stk-block-tab-labels:not(.stk-block-tab-labels--wrap-mobile)::-webkit-scrollbar{height:0!important}.stk-block-tab-labels:not(.stk-block-tab-labels--wrap-mobile) .stk-block-tab-labels__wrapper{flex-wrap:nowrap;min-width:-webkit-fill-available;min-width:fill-available;width:max-content}.stk-block-tabs>.stk-inner-blocks{grid-template-columns:1fr}.stk-block-tabs>.stk-inner-blocks>.stk-block-tab-labels:last-child{grid-row:1/2}.stk-block.stk-block-timeline{--content-line:0!important;padding-top:0}.stk-block-timeline>.stk-inner-blocks{align-items:flex-start;grid-template-columns:var(--line-dot-size,16px) 1fr;grid-template-rows:auto 1fr;padding-left:16px}.stk-block-timeline>.stk-inner-blocks:after{inset-inline-start:calc(var(--line-dot-size, 16px)/2 - var(--line-bg-width, 3px)/2 + 16px)}.stk-block-timeline .stk-block-timeline__middle{inset-block-start:8px;inset-inline-start:16px;position:absolute}.stk-block-timeline .stk-block-timeline__content{grid-column:2/3;grid-row:2/3;text-align:start}.stk-block-timeline .stk-block-timeline__date{grid-column:2/3;grid-row:1/2;text-align:start}.stk-block-timeline>.stk-inner-blocks:after{bottom:calc(100% - var(--line-dot-size, 16px)/2 - .5em);top:calc(var(--line-dot-size, 16px)/2 + .5em)}.stk-block-timeline+.stk-block-timeline>.stk-inner-blocks:after{top:-16px}:root{--stk-block-margin-bottom:16px;--stk-container-padding:24px 24px;--stk-container-padding-large:32px 24px;--stk-container-padding-small:8px 24px;--stk-column-margin:8px;--stk-block-background-padding:16px 16px}.stk-block .stk-block:is(.aligncenter,.alignwide),.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align.alignwide,.stk-block:is(.aligncenter,.alignwide,.alignfull)>.stk-content-align:not(.alignwide):not(.alignfull){width:100%}.stk-column{flex:1 1 100%;max-width:100%}.stk--hide-mobile,.stk--hide-mobile.stk-block{display:none!important}.stk-button-group:is(.stk--collapse-on-mobile) .stk-block:is(.stk-block-button,.stk-block-icon-button),.stk-button-group:is(.stk--collapse-on-tablet) .stk-block:is(.stk-block-button,.stk-block-icon-button){margin-inline-end:var(--stk-alignment-margin-right);margin-inline-start:var(--stk-alignment-margin-left)}.has-text-align-center-mobile{--stk-alignment-padding-left:0;--stk-alignment-justify-content:center;--stk-alignment-text-align:center;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-left-mobile{--stk-alignment-justify-content:flex-start;--stk-alignment-text-align:start;--stk-alignment-margin-left:0;--stk-alignment-margin-right:auto;text-align:var(--stk-alignment-text-align,start)}.has-text-align-right-mobile{--stk-alignment-justify-content:flex-end;--stk-alignment-text-align:end;--stk-alignment-margin-left:auto;--stk-alignment-margin-right:0;text-align:var(--stk-alignment-text-align,start)}.has-text-align-justify-mobile{--stk-alignment-text-align:justify}.has-text-align-space-between-mobile{--stk-alignment-justify-content:space-between}.has-text-align-space-around-mobile{--stk-alignment-justify-content:space-around}.has-text-align-space-evenly-mobile{--stk-alignment-justify-content:space-evenly}.entry-content .stk-block.stk-has-top-separator{padding-top:23vw}.entry-content .stk-block.stk-has-bottom-separator{padding-bottom:23vw}.entry-content .stk-block .stk-separator__wrapper{height:23vw}}#end-resizable-editor-section{display:none} STK_RESPONSIVE_CSS; } } From 7fe5a1122ff7ba00a622b5da42c8b914e014695d Mon Sep 17 00:00:00 2001 From: "bfintal@gmail.com" <> Date: Wed, 15 Jan 2025 20:44:40 +0800 Subject: [PATCH 02/76] updated readme --- e2e/readme.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/e2e/readme.md b/e2e/readme.md index 8d0770bbaf..e45e4608c2 100644 --- a/e2e/readme.md +++ b/e2e/readme.md @@ -19,8 +19,21 @@ Ideally, we should also handle these: - Blocks / content from old plugin versions to this new one look identical in the frontend +Github workflow should also test: +- Different WP versions that we support +- Different PHP versions that we support + # Usage +Create an `.env` file in the root directory of the plugin with the contents: + +``` +WP_BASE_URL=http://local.local/ +WP_AUTH_STORAGE=wp-auth.json +WP_USERNAME=admin +WP_PASSWORD=password +``` + Run this command to run e2e: ```bash From a438a0520cd766eadc2d04992936fcb1f80bed7c Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 27 Jan 2025 11:45:49 +0800 Subject: [PATCH 03/76] activate only stackable for all tests --- e2e/config/global-setup.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/e2e/config/global-setup.ts b/e2e/config/global-setup.ts index 488b5e5f2c..a838b1e142 100644 --- a/e2e/config/global-setup.ts +++ b/e2e/config/global-setup.ts @@ -24,6 +24,17 @@ async function globalSetup() { // filling in the input fields for the username and password and submitting the form. // https://playwright.dev/docs/test-global-setup-teardown#example await requestUtils.setupRest() + + // Deactivate all plugins except Stackable + const plugins = await requestUtils.getPluginsMap() + + // Use for loop because forEach cannot handle async operations + for ( const slug of Object.keys( plugins ) ) { + await requestUtils.deactivatePlugin( slug ) + } + + await requestUtils.activatePlugin( 'stackable-gutenberg-blocks' ) + await requestContext.dispose() } From aa58c5a898024f5ffbde545a1f1264aaeb6bcabf Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 27 Jan 2025 11:46:17 +0800 Subject: [PATCH 04/76] add tsconfig --- tsconfig.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tsconfig.json diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..3c4e7c97d6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "NodeNext", + "baseUrl": ".", + "paths": { + "@wordpress/*": ["./node_modules/@wordpress/*"], + "~stackable/*": ["./src/*"] + }, + "types": ["node"] + }, +} From d543e12ceb4982323d060d77d0e75d81e9e62a4a Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 27 Jan 2025 11:47:20 +0800 Subject: [PATCH 05/76] add tests for settings page and editor page --- e2e/playwright.config.ts | 7 +++ e2e/tests/admin.spec.ts | 55 +++++++++++++++++++++ e2e/tests/block-editor.spec.ts | 88 ++++++++++++++++++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 e2e/tests/admin.spec.ts create mode 100644 e2e/tests/block-editor.spec.ts diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 526fe6e517..29e1ad9da9 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -61,6 +61,13 @@ export default defineConfig( { // testDir, testMatch: 'setup/auth.js', }, + { + name: 'tests', + use: { + storageState: process.env.WP_AUTH_STORAGE, + }, + testMatch: 'tests/*.ts', + }, { name: 'chromium', use: { ...devices[ 'Desktop Chrome' ] }, diff --git a/e2e/tests/admin.spec.ts b/e2e/tests/admin.spec.ts new file mode 100644 index 0000000000..1db56e866f --- /dev/null +++ b/e2e/tests/admin.spec.ts @@ -0,0 +1,55 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright' + +test( 'Activating Stackable should redirect to the Getting Started Page', async ( { + page, + admin, +} ) => { + await admin.visitAdminPage( 'plugins.php' ) + + // Deactivate Stackable + const deactivate = page.locator( '#deactivate-stackable-ultimate-gutenberg-blocks' ) + await expect( deactivate ).toBeVisible() + await deactivate.click() + + // Activate Stackable + const activate = page.locator( '#activate-stackable-ultimate-gutenberg-blocks' ) + await expect( activate ).toBeVisible() + await activate.click() + + await expect( page ).toHaveURL( /stackable-getting-started/ ) +} ) + +test( 'Stackable settings should be saved', async ( { + page, + admin, + baseURL, +} ) => { + // Start waiting for Stackable Settings JSON Response before visiting the page + let settings = page.waitForResponse( response => + response.url() === `${ baseURL }wp-json/wp/v2/settings/` && response.request().method() === 'POST' + ) + await admin.visitAdminPage( 'options-general.php?page=stackable' ) + // Make sure all Stackable settings are loaded + let response = await settings + await response.finished() + + // Retrieves the value of the first option, toggles it and check if the value changed + const option = page.locator( '.ugb-admin-toggle-setting__button' ).first() + const oldVal = await option.evaluate( node => node.getAttribute( 'aria-checked' ) ) + + await option.click() + const newVal = await option.evaluate( node => node.getAttribute( 'aria-checked' ) ) + + expect( newVal ).not.toBe( oldVal ) + + // Check if the value is correct after reloading + settings = page.waitForResponse( response => + response.url() === `${ baseURL }wp-json/wp/v2/settings/` && response.request().method() === 'POST' + ) + await page.reload() + response = await settings + await response.finished() + + const _option = page.locator( '.ugb-admin-toggle-setting__button' ).first() + expect( await _option.evaluate( node => node.getAttribute( 'aria-checked' ) ) ).toBe( newVal ) +} ) diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts new file mode 100644 index 0000000000..6b86d6a284 --- /dev/null +++ b/e2e/tests/block-editor.spec.ts @@ -0,0 +1,88 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright' + +test( 'Stackable blocks can be added in the editor', async ( { + page, + admin, + editor, +} ) => { + await admin.createNewPost() + await page.getByLabel( 'Toggle block inserter' ).click() + + await page.locator( '.editor-block-list-item-stackable-text' ).click() + + const blocks = await editor.getBlocks() + + expect( blocks[ 0 ].name ).toContain( 'stackable/text' ) +} ) + +test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { + page, + admin, + editor, +} ) => { + await admin.createNewPost() + + await editor.insertBlock( { + name: 'stackable/text', + } ) + + await editor.selectBlocks( page.locator( 'iframe[name="editor-canvas"]' ).contentFrame().getByLabel( 'Block: Text' ) ) + await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() +} ) + +test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { + page, + admin, + editor, +} ) => { + await admin.createNewPost() + + await editor.insertBlock( { + name: 'stackable/text', + } ) + + const editorCanvas = page.locator( 'iframe[name="editor-canvas"]' ).contentFrame() + await editorCanvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ).fill( 'test' ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) + await editorCanvas.locator( 'body' ).click() + + await expect( editorCanvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) + await expect( editorCanvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + + await editor.saveDraft() + + const blocks = await editor.getBlocks() + const attributes = blocks[ 0 ].attributes + + expect( attributes.textColor1 ).toBe( '#ff0000' ) + expect( attributes.text ).toBe( 'test' ) +} ) + +test( 'The Stackable block added in the editor should be visible in the frontend', async ( { + admin, + editor, +} ) => { + await admin.createNewPost() + + await editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + textColor1: '#ff0000', + }, + } ) + + const blocks = await editor.getBlocks() + const uniqueId = blocks[ 0 ].attributes.uniqueId + + await editor.saveDraft() + + const preview = await editor.openPreviewPage() + + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) + await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) +} ) From 497f8f3df9c2e78854b776debcd4439363644d19 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 31 Jan 2025 18:51:49 +0800 Subject: [PATCH 06/76] add tests for site editor, global settings --- e2e/playwright.config.ts | 19 ++--- e2e/test-utils/deletePost.ts | 13 +++ e2e/test-utils/index.ts | 1 + e2e/tests/global-settings.spec.ts | 129 ++++++++++++++++++++++++++++++ e2e/tests/site-editor.spec.ts | 110 +++++++++++++++++++++++++ 5 files changed, 263 insertions(+), 9 deletions(-) create mode 100644 e2e/test-utils/deletePost.ts create mode 100644 e2e/tests/global-settings.spec.ts create mode 100644 e2e/tests/site-editor.spec.ts diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 29e1ad9da9..16a4a0702d 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -56,22 +56,23 @@ export default defineConfig( { /* Configure projects for major browsers */ projects: [ - { - name: 'auth', - // testDir, - testMatch: 'setup/auth.js', - }, + // { + // name: 'auth', + // // testDir, + // testMatch: 'setup/auth.js', + // }, { name: 'tests', use: { storageState: process.env.WP_AUTH_STORAGE, + ...devices[ 'Desktop Chrome' ], }, testMatch: 'tests/*.ts', }, - { - name: 'chromium', - use: { ...devices[ 'Desktop Chrome' ] }, - }, + // { + // name: 'chromium', + // use: { ...devices[ 'Desktop Chrome' ] }, + // }, // { // name: 'firefox', diff --git a/e2e/test-utils/deletePost.ts b/e2e/test-utils/deletePost.ts new file mode 100644 index 0000000000..9750f535d0 --- /dev/null +++ b/e2e/test-utils/deletePost.ts @@ -0,0 +1,13 @@ +import { RequestUtils } from '@wordpress/e2e-test-utils-playwright/build-types' + +const deletePost = async ( requestUtils : RequestUtils, pid: string, postType: string = 'posts' ) => { + await requestUtils.rest( { + method: 'DELETE', + path: `/wp/v2/${ postType }/${ pid }`, + params: { + force: true, + }, + } ) +} + +export { deletePost } diff --git a/e2e/test-utils/index.ts b/e2e/test-utils/index.ts index c17497c23a..54b150fe23 100644 --- a/e2e/test-utils/index.ts +++ b/e2e/test-utils/index.ts @@ -1 +1,2 @@ export { test, expect } from './test' +export { deletePost } from './deletePost' diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts new file mode 100644 index 0000000000..d143bf151c --- /dev/null +++ b/e2e/tests/global-settings.spec.ts @@ -0,0 +1,129 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright' + +import { deletePost } from 'e2e/test-utils' +import { createColor, getRgb } from '~stackable/plugins/global-settings/colors/util' + +test.describe( 'Global Settigs', () => { + let pid = null + + // Create Posts for testing + test.beforeEach( async ( { editor, admin } ) => { + await admin.createNewPost( { title: 'Global Settings Test' } ) + await editor.saveDraft() + const postQuery = new URL( editor.page.url() ).search + pid = new URLSearchParams( postQuery ).get( 'post' ) + } ) + + // Delete created post + test.afterEach( async ( { requestUtils } ) => { + await deletePost( requestUtils, pid ) + } ) + + test( 'When a color is added in the Global Colors, it should be present in the color picker', async ( { + page, + editor, + } ) => { + await page.getByLabel( 'Stackable Settings' ).click() + + // Add a new Global Color + const panel = page.locator( '.ugb-global-settings-color-picker ' ).filter( { hasText: 'Global Colors' } ) + await panel.locator( 'button.ugb-global-settings-color-picker__add-button' ).click() + + const globalColors = panel.locator( '.ugb-global-settings-color-picker__color-indicators > div' ) + const count = ( await globalColors.evaluate( node => Array.from( node.childNodes ) ) ).length + + const newColor = globalColors.getByRole( 'button', { name: `Custom Color ${ count } ` } ) + await expect( newColor ).toBeVisible() + await newColor.click() + const hexValue = await page.getByLabel( 'Hex color' ).inputValue() + + // Insert a Stackable Text Block and check if the added Global Colors is in the color picker + await page.getByLabel( 'Settings', { exact: true } ).click() + editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + }, + } ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + + await expect( page.getByRole( 'heading', { name: 'Global Colors' } ) ).toBeVisible() + await expect( page.getByLabel( `Color: Custom Color ${ count }` ) ).toBeVisible() + await page.getByLabel( `Color: Custom Color ${ count }` ).click() + await expect( page.getByLabel( 'Hex color' ) ).toHaveValue( hexValue ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + + // Delete added Global Color + await page.getByLabel( 'Stackable Settings' ).click() + await panel.locator( `.ugb-global-settings-color-picker__color-indicators > div > div:nth-child(${ count }) > button.stk-global-settings-color-picker__delete-button` ).click() + } ) + + test( 'Global Typography Styles should be applied when adding a heading', async ( { + page, + editor, + } ) => { + await page.getByLabel( 'Stackable Settings' ).click() + await page.getByRole( 'button', { name: 'Global Typography' } ).click() + + // Set Global Typography Styles of Heading 2 to have a font-size of 32 + await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() + await page.locator( '.stk-popover .components-base-control:nth-of-type(2)', { hasText: /Size/ } ).getByRole( 'textbox' ).fill( '32' ) + await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() + await expect( page.getByRole( 'heading', { name: 'Heading 2' } ) ).toHaveCSS( 'font-size', '32px' ) + await page.getByLabel( 'Settings', { exact: true } ).click() + + // Check if the added Stackable Heading Block has a font-size of 32 + editor.insertBlock( { + name: 'stackable/heading', + attributes: { + text: 'test', + }, + } ) + + await expect( editor.canvas.locator( '[data-type="stackable/heading"] > .stk-block-heading > h2[role="textbox"]' ) ).toHaveCSS( 'font-size', '32px' ) + + // Reset Global Typography Styles + await page.getByLabel( 'Stackable Settings' ).click() + await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).getByRole( 'button', { name: 'Reset' } ).click() + } ) + + test( 'When a default block is created, adding the block should have the default block\'s attributes', async ( { + page, + editor, + } ) => { + // Generate a color + const color = createColor() + + await page.getByLabel( 'Stackable Settings' ).click() + await page.getByRole( 'button', { name: 'Block Defaults' } ).click() + + // Open the Default Text Block Editor + const defaultBlockPagePromise = page.waitForEvent( 'popup' ) + await page.locator( 'div:nth-child(37) > .components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() + const defaultBlockPage = await defaultBlockPagePromise + + // Set a color for the default Text Block + await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await defaultBlockPage.getByLabel( 'Hex color' ).fill( color ) + await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await defaultBlockPage.getByRole( 'button', { name: 'Save', exact: true } ).click() + await defaultBlockPage.close() + + // Insert a Stackable Text Block, and check if the color is the same as the one set in the default block + await page.reload() + await editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + }, + } ) + + await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', `rgb(${ getRgb( color ) })` ) + + // Reset Default Block + await page.getByLabel( 'Stackable Settings' ).click() + await page.getByRole( 'button', { name: 'Block Defaults' } ).click() + await page.locator( 'div' ).filter( { hasText: /^Text$/ } ).first().getByLabel( 'Reset' ).click() + } ) +} ) + diff --git a/e2e/tests/site-editor.spec.ts b/e2e/tests/site-editor.spec.ts new file mode 100644 index 0000000000..2f4e19212f --- /dev/null +++ b/e2e/tests/site-editor.spec.ts @@ -0,0 +1,110 @@ +import { test, expect } from '@wordpress/e2e-test-utils-playwright' +import { deletePost } from 'e2e/test-utils' + +test.describe( 'Site Editor', () => { + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.activateTheme( 'twentytwentyfour' ) + } ) + + let pid = null + let postContentBlock = null + + test.beforeEach( async ( { + page, admin, editor, + } ) => { + await admin.createNewPost( { title: 'Site Editor Test', postType: 'page' } ) + await editor.saveDraft() + const postQuery = new URL( editor.page.url() ).search + pid = new URLSearchParams( postQuery ).get( 'post' ) + + await admin.visitSiteEditor( { + canvas: 'edit', postType: 'page', postId: pid, showWelcomeGuide: false, + } ) + + if ( await page.getByRole( 'heading', { name: 'Choose a pattern' } ).isVisible() ) { + await page.getByLabel( 'Close', { exact: true } ).click() + } + + postContentBlock = ( await editor.getBlocks( { full: true } ) ) + .filter( block => block.attributes?.tagName === 'main' )[ 0 ].innerBlocks + .filter( block => block.name === 'core/post-content' )[ 0 ] + } ) + + test.afterEach( async ( { requestUtils } ) => { + await deletePost( requestUtils, pid, 'pages' ) + } ) + + test( 'Stackable blocks can be added in the site editor', async ( { + page, + editor, + } ) => { + await page.getByLabel( 'Toggle block inserter' ).click() + await page.locator( '.editor-block-list-item-stackable-text' ).click() + + const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + + expect( blocks.find( block => block.name === 'stackable/text' ) ).toBeTruthy() + } ) + + test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + }, { clientId: postContentBlock.clientId } ) + + await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) + await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() + } ) + + test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + }, { clientId: postContentBlock.clientId } ) + await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) + await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) + await editor.canvas.locator( 'body' ).click() + + await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) + await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + + await editor.saveDraft() + + const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + const textBlock = blocks.find( block => block.name === 'stackable/text' ) + expect( textBlock.attributes.text ).toBe( 'test' ) + expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) + } ) + + test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + textColor1: '#ff0000', + }, + }, { clientId: postContentBlock.clientId } ) + + const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId + + await editor.saveDraft() + + const preview = await editor.openPreviewPage() + + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) + await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + } ) +} ) + From 3864ca475052a4e882676294ce331bf908e129ee Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 31 Jan 2025 18:52:22 +0800 Subject: [PATCH 07/76] minor change to tests for admin and block editor --- e2e/tests/admin.spec.ts | 4 +- e2e/tests/block-editor.spec.ts | 139 ++++++++++++++++++--------------- 2 files changed, 76 insertions(+), 67 deletions(-) diff --git a/e2e/tests/admin.spec.ts b/e2e/tests/admin.spec.ts index 1db56e866f..9424bb9bf2 100644 --- a/e2e/tests/admin.spec.ts +++ b/e2e/tests/admin.spec.ts @@ -7,12 +7,12 @@ test( 'Activating Stackable should redirect to the Getting Started Page', async await admin.visitAdminPage( 'plugins.php' ) // Deactivate Stackable - const deactivate = page.locator( '#deactivate-stackable-ultimate-gutenberg-blocks' ) + const deactivate = page.getByLabel( 'Deactivate Stackable -' ) await expect( deactivate ).toBeVisible() await deactivate.click() // Activate Stackable - const activate = page.locator( '#activate-stackable-ultimate-gutenberg-blocks' ) + const activate = page.getByLabel( 'Activate Stackable -' ) await expect( activate ).toBeVisible() await activate.click() diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts index 6b86d6a284..bd0b7665bc 100644 --- a/e2e/tests/block-editor.spec.ts +++ b/e2e/tests/block-editor.spec.ts @@ -1,88 +1,97 @@ import { test, expect } from '@wordpress/e2e-test-utils-playwright' +import { deletePost } from 'e2e/test-utils' -test( 'Stackable blocks can be added in the editor', async ( { - page, - admin, - editor, -} ) => { - await admin.createNewPost() - await page.getByLabel( 'Toggle block inserter' ).click() +test.describe( 'Block Editor', () => { + let pid = null - await page.locator( '.editor-block-list-item-stackable-text' ).click() + // Create Posts for testing + test.beforeEach( async ( { editor, admin } ) => { + await admin.createNewPost( { title: 'Block Editor Test' } ) + await editor.saveDraft() + const postQuery = new URL( editor.page.url() ).search + pid = new URLSearchParams( postQuery ).get( 'post' ) + } ) - const blocks = await editor.getBlocks() + // Delete created post + test.afterEach( async ( { requestUtils } ) => { + await deletePost( requestUtils, pid ) + } ) - expect( blocks[ 0 ].name ).toContain( 'stackable/text' ) -} ) + test( 'Stackable blocks can be added in the editor', async ( { + page, -test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { - page, - admin, - editor, -} ) => { - await admin.createNewPost() + editor, + } ) => { + await page.getByLabel( 'Toggle block inserter' ).click() - await editor.insertBlock( { - name: 'stackable/text', - } ) + await page.locator( '.editor-block-list-item-stackable-text' ).click() - await editor.selectBlocks( page.locator( 'iframe[name="editor-canvas"]' ).contentFrame().getByLabel( 'Block: Text' ) ) - await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() - await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() - await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() -} ) + const blocks = await editor.getBlocks() -test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { - page, - admin, - editor, -} ) => { - await admin.createNewPost() + expect( blocks[ 0 ].name ).toContain( 'stackable/text' ) + } ) - await editor.insertBlock( { - name: 'stackable/text', + test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + } ) + + await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) + await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() } ) - const editorCanvas = page.locator( 'iframe[name="editor-canvas"]' ).contentFrame() - await editorCanvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ).fill( 'test' ) - await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) - await editorCanvas.locator( 'body' ).click() + test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + } ) - await expect( editorCanvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) - await expect( editorCanvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + // Add content and color to Stackable Text Block + await editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ).fill( 'test' ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) + await editor.canvas.locator( 'body' ).click() - await editor.saveDraft() + await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) + await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - const blocks = await editor.getBlocks() - const attributes = blocks[ 0 ].attributes + await editor.saveDraft() - expect( attributes.textColor1 ).toBe( '#ff0000' ) - expect( attributes.text ).toBe( 'test' ) -} ) + const blocks = await editor.getBlocks() + const attributes = blocks[ 0 ].attributes -test( 'The Stackable block added in the editor should be visible in the frontend', async ( { - admin, - editor, -} ) => { - await admin.createNewPost() - - await editor.insertBlock( { - name: 'stackable/text', - attributes: { - text: 'test', - textColor1: '#ff0000', - }, + expect( attributes.textColor1 ).toBe( '#ff0000' ) + expect( attributes.text ).toBe( 'test' ) } ) - const blocks = await editor.getBlocks() - const uniqueId = blocks[ 0 ].attributes.uniqueId + test( 'The Stackable block added in the editor should be visible in the frontend', async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + textColor1: '#ff0000', + }, + } ) + + const blocks = await editor.getBlocks() + const uniqueId = blocks[ 0 ].attributes.uniqueId - await editor.saveDraft() + await editor.saveDraft() - const preview = await editor.openPreviewPage() + const preview = await editor.openPreviewPage() - await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() - await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) - await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) + await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + } ) } ) + From c2f4a6d40ac1ce93dd2974ba15ba5732ad3afcfc Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 13:03:15 +0800 Subject: [PATCH 08/76] use custom test utils --- e2e/config/global-setup.ts | 8 ++--- e2e/test-utils/deletePost.ts | 13 ------- e2e/test-utils/index.ts | 3 +- e2e/test-utils/requestUtils.ts | 52 ++++++++++++++++++++++++++++ e2e/test-utils/stackable.ts | 56 +++++++++++++++++++++++++++++++ e2e/test-utils/test.ts | 40 +++++++++------------- e2e/tests/admin.spec.ts | 44 +++++++++++++----------- e2e/tests/block-editor.spec.ts | 5 ++- e2e/tests/global-settings.spec.ts | 6 ++-- e2e/tests/site-editor.spec.ts | 11 +++--- 10 files changed, 162 insertions(+), 76 deletions(-) delete mode 100644 e2e/test-utils/deletePost.ts create mode 100644 e2e/test-utils/requestUtils.ts create mode 100644 e2e/test-utils/stackable.ts diff --git a/e2e/config/global-setup.ts b/e2e/config/global-setup.ts index a838b1e142..ad6222463e 100644 --- a/e2e/config/global-setup.ts +++ b/e2e/config/global-setup.ts @@ -2,7 +2,7 @@ import { request } from '@playwright/test' -import { RequestUtils } from '@wordpress/e2e-test-utils-playwright' +import { ExtendedRequestUtils } from 'e2e/test-utils' // To interact with the WP Guest Bar plugin's settings page, we must be authenticated. // Before any tests are run, we sign in, save the cookies set by WordPress, and then discard the session. @@ -12,7 +12,7 @@ async function globalSetup() { const requestContext = await request.newContext( { baseURL: process.env.WP_BASE_URL, } ) - const requestUtils = new RequestUtils( requestContext, { + const requestUtils = new ExtendedRequestUtils( requestContext, { storageStatePath: process.env.WP_AUTH_STORAGE, user: { username: process.env.WP_USERNAME, @@ -26,14 +26,14 @@ async function globalSetup() { await requestUtils.setupRest() // Deactivate all plugins except Stackable - const plugins = await requestUtils.getPluginsMap() + const plugins = await requestUtils.getActivePlugins() // Use for loop because forEach cannot handle async operations for ( const slug of Object.keys( plugins ) ) { await requestUtils.deactivatePlugin( slug ) } - await requestUtils.activatePlugin( 'stackable-gutenberg-blocks' ) + await requestUtils.activatePlugin( process.env.STACKABLE_SLUG ) await requestContext.dispose() } diff --git a/e2e/test-utils/deletePost.ts b/e2e/test-utils/deletePost.ts deleted file mode 100644 index 9750f535d0..0000000000 --- a/e2e/test-utils/deletePost.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { RequestUtils } from '@wordpress/e2e-test-utils-playwright/build-types' - -const deletePost = async ( requestUtils : RequestUtils, pid: string, postType: string = 'posts' ) => { - await requestUtils.rest( { - method: 'DELETE', - path: `/wp/v2/${ postType }/${ pid }`, - params: { - force: true, - }, - } ) -} - -export { deletePost } diff --git a/e2e/test-utils/index.ts b/e2e/test-utils/index.ts index 54b150fe23..dc6e3c0f72 100644 --- a/e2e/test-utils/index.ts +++ b/e2e/test-utils/index.ts @@ -1,2 +1,3 @@ export { test, expect } from './test' -export { deletePost } from './deletePost' +export { ExtendedRequestUtils } from './requestUtils' +export { StackableFixture } from './stackable' diff --git a/e2e/test-utils/requestUtils.ts b/e2e/test-utils/requestUtils.ts new file mode 100644 index 0000000000..5ecfa47c79 --- /dev/null +++ b/e2e/test-utils/requestUtils.ts @@ -0,0 +1,52 @@ +import { RequestUtils as BaseRequestUtils } from '@wordpress/e2e-test-utils-playwright' + +class ExtendedRequestUtils extends BaseRequestUtils { + getActivePlugins = async function() { + const plugins : { [key: string]: any }[] = await this.rest( { + path: '/wp/v2/plugins', + } ) + + const activePlugins = plugins.filter( plugin => plugin.status === 'active' ).reduce( ( pluginsMap, plugin ) => { + pluginsMap[ plugin.plugin ] = plugin.plugin + return pluginsMap + }, {} ) + + return activePlugins + } + + deactivatePlugin = async function( slug: string ) { + try { + await this.rest( { + method: 'PUT', + path: `/wp/v2/plugins/${ slug }`, + data: { status: 'inactive' }, + } ) + } catch ( error ) { + throw new Error( `Cannot deactivate ${ slug }: ${ error.message }` ) + } + } + + activatePlugin = async function( slug: string ) { + try { + await this.rest( { + method: 'PUT', + path: `/wp/v2/plugins/${ slug }`, + data: { status: 'active' }, + } ) + } catch ( error ) { + throw new Error( `Cannot activate ${ slug }: ${ error.message }` ) + } + } + + deletePost = async function( pid: string, postType: string = 'posts' ) { + await this.rest( { + method: 'DELETE', + path: `/wp/v2/${ postType }/${ pid }`, + params: { + force: true, + }, + } ) + } +} + +export { ExtendedRequestUtils } diff --git a/e2e/test-utils/stackable.ts b/e2e/test-utils/stackable.ts new file mode 100644 index 0000000000..fc7415b555 --- /dev/null +++ b/e2e/test-utils/stackable.ts @@ -0,0 +1,56 @@ +import { Page, Request } from '@playwright/test' +import { test } from './test' + +export class StackableFixture { + page: Page; + + constructor( page: Page ) { + this.page = page + } + + async waitForSettings(): Promise { + return new Promise( ( resolve, reject ) => { + let responseCount = 0 + + const cleanup = () => { + this.page.off( 'requestfinished', finishedCallback ) + this.page.off( 'requestfinished', failedCallback ) + } + + const finishedCallback = async ( request: Request ) => { + if ( request.url().includes( 'wp-json/wp/v2/settings' ) && request.method() === 'GET' ) { + try { + let settings = null + await test.step( 'Wait for Stackable settings to load', async () => { + const response = await request.response() + settings = await response.body() + } ) + + if ( settings ) { + responseCount++ + } else { + throw Error( 'Failed to get Stackable settings' ) + } + + if ( responseCount === 4 ) { + cleanup() + resolve() + } + } catch ( error ) { + cleanup() + reject( `Error: ${ error.message }` ) + } + } + } + const failedCallback = async ( request: Request ) => { + if ( request.url().includes( 'wp-json/wp/v2/settings' ) && request.method() === 'GET' ) { + cleanup() + throw Error( 'Failed to get Stackable settings' ) + } + } + + this.page.on( 'requestfinished', finishedCallback ) + this.page.on( 'requestfailed', failedCallback ) + } ) + } +} diff --git a/e2e/test-utils/test.ts b/e2e/test-utils/test.ts index d9efb44fdd..f163445c65 100644 --- a/e2e/test-utils/test.ts +++ b/e2e/test-utils/test.ts @@ -1,13 +1,12 @@ -import { test as base, expect } from '@playwright/test' - // We only require what's essential from the WordPress E2E test utils package. import { - Admin, - Editor, - PageUtils, - RequestUtils, + test as base, + expect, } from '@wordpress/e2e-test-utils-playwright' +import { ExtendedRequestUtils } from './requestUtils' +import { StackableFixture } from './stackable' + // We could also import utils from other packages. // import { StoreApiUtils } from '@woocommerce/e2e-utils'; @@ -18,27 +17,14 @@ import { // https://github.com/WordPress/gutenberg/blob/trunk/packages/e2e-test-utils-playwright/src/test.ts // https://github.com/woocommerce/woocommerce-blocks/blob/trunk/tests/e2e/playwright-utils/test.ts const test = base.extend<{ - admin: Admin; - editor: Editor; - pageUtils: PageUtils; - requestUtils: RequestUtils; + requestUtils: ExtendedRequestUtils; + stackable: StackableFixture; }>( { - async admin( { - page, pageUtils, editor, - }, use ) { - await use( new Admin( { - page, pageUtils, editor, - } ) ) - }, - async editor( { page }, use ) { - await use( new Editor( { page } ) ) - }, - async pageUtils( { page }, use ) { - await use( new PageUtils( { page } ) ) - }, - async requestUtils( {}, use ) { + requestUtils: async ( {}, use ) => { + let requestUtils = null + // We want to make all REST API calls as authenticated users. - const requestUtils = await RequestUtils.setup( { + requestUtils = await ExtendedRequestUtils.setup( { baseURL: process.env.WP_BASE_URL, user: { username: process.env.WP_USERNAME, @@ -48,6 +34,10 @@ const test = base.extend<{ await use( requestUtils ) }, + + stackable: async ( { page }, use ) => { + await use( new StackableFixture( page ) ) + }, } ) export { test, expect } diff --git a/e2e/tests/admin.spec.ts b/e2e/tests/admin.spec.ts index 9424bb9bf2..50fcf3b22c 100644 --- a/e2e/tests/admin.spec.ts +++ b/e2e/tests/admin.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@wordpress/e2e-test-utils-playwright' +import { test, expect } from 'e2e/test-utils' test( 'Activating Stackable should redirect to the Getting Started Page', async ( { page, @@ -6,13 +6,14 @@ test( 'Activating Stackable should redirect to the Getting Started Page', async } ) => { await admin.visitAdminPage( 'plugins.php' ) + const plugin = page.locator( `[data-plugin="${ process.env.STACKABLE_SLUG }.php"]` ) // Deactivate Stackable - const deactivate = page.getByLabel( 'Deactivate Stackable -' ) + const deactivate = plugin.getByLabel( 'Deactivate Stackable -' ) await expect( deactivate ).toBeVisible() await deactivate.click() // Activate Stackable - const activate = page.getByLabel( 'Activate Stackable -' ) + const activate = plugin.getByLabel( 'Activate Stackable -' ) await expect( activate ).toBeVisible() await activate.click() @@ -22,34 +23,37 @@ test( 'Activating Stackable should redirect to the Getting Started Page', async test( 'Stackable settings should be saved', async ( { page, admin, - baseURL, + stackable, } ) => { // Start waiting for Stackable Settings JSON Response before visiting the page - let settings = page.waitForResponse( response => - response.url() === `${ baseURL }wp-json/wp/v2/settings/` && response.request().method() === 'POST' - ) + let settings = null + settings = stackable.waitForSettings() + await admin.visitAdminPage( 'options-general.php?page=stackable' ) // Make sure all Stackable settings are loaded - let response = await settings - await response.finished() + await settings // Retrieves the value of the first option, toggles it and check if the value changed - const option = page.locator( '.ugb-admin-toggle-setting__button' ).first() - const oldVal = await option.evaluate( node => node.getAttribute( 'aria-checked' ) ) + const option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) + const val = await option.getAttribute( 'aria-checked' ) await option.click() - const newVal = await option.evaluate( node => node.getAttribute( 'aria-checked' ) ) + const newVal = await option.getAttribute( 'aria-checked' ) - expect( newVal ).not.toBe( oldVal ) + expect( newVal ).not.toBe( val ) + await page.getByRole( 'button', { name: 'Save Changes' } ).click() // Check if the value is correct after reloading - settings = page.waitForResponse( response => - response.url() === `${ baseURL }wp-json/wp/v2/settings/` && response.request().method() === 'POST' - ) + settings = stackable.waitForSettings() await page.reload() - response = await settings - await response.finished() + await settings + + const _option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) + + await expect( _option ).toHaveAttribute( 'aria-checked', newVal ) - const _option = page.locator( '.ugb-admin-toggle-setting__button' ).first() - expect( await _option.evaluate( node => node.getAttribute( 'aria-checked' ) ) ).toBe( newVal ) + // Revert back the settings to the original value + await _option.click() + await page.getByRole( 'button', { name: 'Save Changes' } ).click() + await expect( _option ).toHaveAttribute( 'aria-checked', val ) } ) diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts index bd0b7665bc..724f4139e6 100644 --- a/e2e/tests/block-editor.spec.ts +++ b/e2e/tests/block-editor.spec.ts @@ -1,5 +1,4 @@ -import { test, expect } from '@wordpress/e2e-test-utils-playwright' -import { deletePost } from 'e2e/test-utils' +import { test, expect } from 'e2e/test-utils' test.describe( 'Block Editor', () => { let pid = null @@ -14,7 +13,7 @@ test.describe( 'Block Editor', () => { // Delete created post test.afterEach( async ( { requestUtils } ) => { - await deletePost( requestUtils, pid ) + await requestUtils.deletePost( pid ) } ) test( 'Stackable blocks can be added in the editor', async ( { diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts index d143bf151c..4ee9451c05 100644 --- a/e2e/tests/global-settings.spec.ts +++ b/e2e/tests/global-settings.spec.ts @@ -1,6 +1,4 @@ -import { test, expect } from '@wordpress/e2e-test-utils-playwright' - -import { deletePost } from 'e2e/test-utils' +import { test, expect } from 'e2e/test-utils' import { createColor, getRgb } from '~stackable/plugins/global-settings/colors/util' test.describe( 'Global Settigs', () => { @@ -16,7 +14,7 @@ test.describe( 'Global Settigs', () => { // Delete created post test.afterEach( async ( { requestUtils } ) => { - await deletePost( requestUtils, pid ) + await requestUtils.deletePost( pid ) } ) test( 'When a color is added in the Global Colors, it should be present in the color picker', async ( { diff --git a/e2e/tests/site-editor.spec.ts b/e2e/tests/site-editor.spec.ts index 2f4e19212f..95131778a0 100644 --- a/e2e/tests/site-editor.spec.ts +++ b/e2e/tests/site-editor.spec.ts @@ -1,10 +1,9 @@ -import { test, expect } from '@wordpress/e2e-test-utils-playwright' -import { deletePost } from 'e2e/test-utils' +import { test, expect } from 'e2e/test-utils' test.describe( 'Site Editor', () => { - test.beforeAll( async ( { requestUtils } ) => { - await requestUtils.activateTheme( 'twentytwentyfour' ) - } ) + // test.beforeAll( async ( { requestUtils } ) => { + // await requestUtils.activateTheme( 'twentytwentyfour' ) + // } ) let pid = null let postContentBlock = null @@ -31,7 +30,7 @@ test.describe( 'Site Editor', () => { } ) test.afterEach( async ( { requestUtils } ) => { - await deletePost( requestUtils, pid, 'pages' ) + await requestUtils.deletePost( pid, 'pages' ) } ) test( 'Stackable blocks can be added in the site editor', async ( { From 19a9e06166702197d84778fcb1f4f7d080cc08cf Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 13:10:46 +0800 Subject: [PATCH 09/76] run tests --- .github/workflows/plugin-build.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/plugin-build.yml b/.github/workflows/plugin-build.yml index 8aeb6e3f5d..fc2efa3e85 100644 --- a/.github/workflows/plugin-build.yml +++ b/.github/workflows/plugin-build.yml @@ -27,6 +27,19 @@ jobs: node ./tools/append-build-version.js $VERSION_SUFFIX cp -r build/stackable stackable-$VERSION_SUFFIX + - name: Install wp-env + run: | + npm install -g @wordpress/env + + - name: Start wp-env + run: wp-env start + + - name: Run playwright tests + run: npm run test + + - name: Stop wp-env + run: wp-env stop + # Keep this for now. Our plan is to simplify all tests into this one # workflow, this is needed so we can reuse the build folder across sub jobs. # - name: Create plugin zip file artifact uses: actions/upload-artifact@v3 From d63fe723ab52fa33aa385308929bb012807f10dd Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 14:11:28 +0800 Subject: [PATCH 10/76] update playwright workflows --- .github/workflows/playwright.yml | 21 ++++++++++++++------- .github/workflows/plugin-build.yml | 13 ------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 3eb13143c3..cd973a604e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -1,9 +1,9 @@ name: Playwright Tests on: push: - branches: [ main, master ] + branches: [ master, develop ] pull_request: - branches: [ main, master ] + branches: [ master, develop ] jobs: test: timeout-minutes: 60 @@ -14,11 +14,18 @@ jobs: with: node-version: lts/* - name: Install dependencies - run: npm ci - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - name: Run Playwright tests - run: npx playwright test + run: | + npm ci + npm run build:no-translate + - name: Install wp-env + run: | + npm install -g @wordpress/env + - name: Start wp-env + run: wp-env start + - name: Run playwright tests + run: npm run test + - name: Stop wp-env + run: wp-env stop - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} with: diff --git a/.github/workflows/plugin-build.yml b/.github/workflows/plugin-build.yml index fc2efa3e85..8aeb6e3f5d 100644 --- a/.github/workflows/plugin-build.yml +++ b/.github/workflows/plugin-build.yml @@ -27,19 +27,6 @@ jobs: node ./tools/append-build-version.js $VERSION_SUFFIX cp -r build/stackable stackable-$VERSION_SUFFIX - - name: Install wp-env - run: | - npm install -g @wordpress/env - - - name: Start wp-env - run: wp-env start - - - name: Run playwright tests - run: npm run test - - - name: Stop wp-env - run: wp-env stop - # Keep this for now. Our plan is to simplify all tests into this one # workflow, this is needed so we can reuse the build folder across sub jobs. # - name: Create plugin zip file artifact uses: actions/upload-artifact@v3 From 0623f04397612c091decc633744ca2e607f4e3ce Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 14:48:09 +0800 Subject: [PATCH 11/76] add .wp-env.json --- .github/workflows/playwright.yml | 10 ++++------ .wp-env.json | 4 ++++ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 .wp-env.json diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index cd973a604e..53697f36c8 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -15,17 +15,15 @@ jobs: node-version: lts/* - name: Install dependencies run: | - npm ci + npm ci --legacy-peer-deps npm run build:no-translate - - name: Install wp-env - run: | - npm install -g @wordpress/env + - name: Install wp-env + run: | + npm install -g @wordpress/env - name: Start wp-env run: wp-env start - name: Run playwright tests run: npm run test - - name: Stop wp-env - run: wp-env stop - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} with: diff --git a/.wp-env.json b/.wp-env.json new file mode 100644 index 0000000000..d67cd96421 --- /dev/null +++ b/.wp-env.json @@ -0,0 +1,4 @@ +{ + "core": "WordPress/WordPress", + "plugins": [ "." ] +} From 70c5f33347f3a8f640f27d7d589649a1a1215a8b Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 15:11:48 +0800 Subject: [PATCH 12/76] add env --- .github/workflows/playwright.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 53697f36c8..31643b223f 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -13,16 +13,28 @@ jobs: - uses: actions/setup-node@v4 with: node-version: lts/* - - name: Install dependencies + - name: Set the version suffix for the output + run: echo VERSION_SUFFIX=${GITHUB_REF_NAME//\//-} >> $GITHUB_ENV + - name: Build Stackable Free Plugin run: | npm ci --legacy-peer-deps npm run build:no-translate + - name: Append PR in build version & create PR build folder + run: | + node ./tools/append-build-version.js $VERSION_SUFFIX + cp -r build/stackable stackable-$VERSION_SUFFIX - name: Install wp-env run: | npm install -g @wordpress/env - name: Start wp-env run: wp-env start - name: Run playwright tests + env: + WP_BASE_URL: http://localhost:8888 + WP_AUTH_STORAGE: wp-auth.json + WP_USERNAME: admin + WP_PASSWORD: password + STACKABLE_SLUG: stackable-$VERSION_SUFFIX run: npm run test - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} From fa6a5d4409cec5fbcfbaf0ef536bc569a44bfb9e Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 15:21:24 +0800 Subject: [PATCH 13/76] fix env slug --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 31643b223f..579e24de35 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -34,7 +34,7 @@ jobs: WP_AUTH_STORAGE: wp-auth.json WP_USERNAME: admin WP_PASSWORD: password - STACKABLE_SLUG: stackable-$VERSION_SUFFIX + STACKABLE_SLUG: stackable-${{ env.VERSION_SUFFIX }} run: npm run test - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} From 20da04091bbf18616b49ce65ffaf45a7b21c50c1 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 15:33:08 +0800 Subject: [PATCH 14/76] log plugins installed --- e2e/test-utils/requestUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e/test-utils/requestUtils.ts b/e2e/test-utils/requestUtils.ts index 5ecfa47c79..bf8c3b60fa 100644 --- a/e2e/test-utils/requestUtils.ts +++ b/e2e/test-utils/requestUtils.ts @@ -5,7 +5,8 @@ class ExtendedRequestUtils extends BaseRequestUtils { const plugins : { [key: string]: any }[] = await this.rest( { path: '/wp/v2/plugins', } ) - + // eslint-disable-next-line no-console + console.info( 'plugins installed:', plugins.map( plugin => plugin.plugin ) ) const activePlugins = plugins.filter( plugin => plugin.status === 'active' ).reduce( ( pluginsMap, plugin ) => { pluginsMap[ plugin.plugin ] = plugin.plugin return pluginsMap From 8ae8947eb3fbfb7cefae88133c9396e18c529682 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 15:41:32 +0800 Subject: [PATCH 15/76] update steps, slug --- .github/workflows/playwright.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 579e24de35..660c2217ab 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -13,16 +13,10 @@ jobs: - uses: actions/setup-node@v4 with: node-version: lts/* - - name: Set the version suffix for the output - run: echo VERSION_SUFFIX=${GITHUB_REF_NAME//\//-} >> $GITHUB_ENV - name: Build Stackable Free Plugin run: | npm ci --legacy-peer-deps npm run build:no-translate - - name: Append PR in build version & create PR build folder - run: | - node ./tools/append-build-version.js $VERSION_SUFFIX - cp -r build/stackable stackable-$VERSION_SUFFIX - name: Install wp-env run: | npm install -g @wordpress/env @@ -34,7 +28,7 @@ jobs: WP_AUTH_STORAGE: wp-auth.json WP_USERNAME: admin WP_PASSWORD: password - STACKABLE_SLUG: stackable-${{ env.VERSION_SUFFIX }} + STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} From e32eb62a27e4843af7104d76b0f5d1f696e37083 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 15:46:49 +0800 Subject: [PATCH 16/76] install playwright browsers --- .github/workflows/playwright.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 660c2217ab..8468c4a2fc 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -17,6 +17,8 @@ jobs: run: | npm ci --legacy-peer-deps npm run build:no-translate + - name: Install Playwright Browsers + run: npx playwright install --with-deps - name: Install wp-env run: | npm install -g @wordpress/env From bd3377486d37c57a5faec317d7d2cc8fff9e46f1 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 17:07:46 +0800 Subject: [PATCH 17/76] comment out site editor tests, add reporter --- .wp-env.json | 7 +- e2e/playwright.config.ts | 5 +- e2e/tests/site-editor.spec.ts | 216 +++++++++++++++++----------------- 3 files changed, 117 insertions(+), 111 deletions(-) diff --git a/.wp-env.json b/.wp-env.json index d67cd96421..6b24e5bc31 100644 --- a/.wp-env.json +++ b/.wp-env.json @@ -1,4 +1,7 @@ { - "core": "WordPress/WordPress", - "plugins": [ "." ] + "core": null, + "plugins": [ "." ], + "config": { + "SCRIPT_DEBUG": false + } } diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 16a4a0702d..f5d032f4a7 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -32,7 +32,10 @@ export default defineConfig( { // workers: process.env.CI ? 1 : undefined, workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - // reporter: 'html', + reporter: [ + [ 'list' ], + [ 'html', { outputFolder: '../playwright-report' } ], + ], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ diff --git a/e2e/tests/site-editor.spec.ts b/e2e/tests/site-editor.spec.ts index 95131778a0..244ef4aa6d 100644 --- a/e2e/tests/site-editor.spec.ts +++ b/e2e/tests/site-editor.spec.ts @@ -1,109 +1,109 @@ -import { test, expect } from 'e2e/test-utils' - -test.describe( 'Site Editor', () => { - // test.beforeAll( async ( { requestUtils } ) => { - // await requestUtils.activateTheme( 'twentytwentyfour' ) - // } ) - - let pid = null - let postContentBlock = null - - test.beforeEach( async ( { - page, admin, editor, - } ) => { - await admin.createNewPost( { title: 'Site Editor Test', postType: 'page' } ) - await editor.saveDraft() - const postQuery = new URL( editor.page.url() ).search - pid = new URLSearchParams( postQuery ).get( 'post' ) - - await admin.visitSiteEditor( { - canvas: 'edit', postType: 'page', postId: pid, showWelcomeGuide: false, - } ) - - if ( await page.getByRole( 'heading', { name: 'Choose a pattern' } ).isVisible() ) { - await page.getByLabel( 'Close', { exact: true } ).click() - } - - postContentBlock = ( await editor.getBlocks( { full: true } ) ) - .filter( block => block.attributes?.tagName === 'main' )[ 0 ].innerBlocks - .filter( block => block.name === 'core/post-content' )[ 0 ] - } ) - - test.afterEach( async ( { requestUtils } ) => { - await requestUtils.deletePost( pid, 'pages' ) - } ) - - test( 'Stackable blocks can be added in the site editor', async ( { - page, - editor, - } ) => { - await page.getByLabel( 'Toggle block inserter' ).click() - await page.locator( '.editor-block-list-item-stackable-text' ).click() - - const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - - expect( blocks.find( block => block.name === 'stackable/text' ) ).toBeTruthy() - } ) - - test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { - page, - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - }, { clientId: postContentBlock.clientId } ) - - await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) - await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() - await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() - await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() - } ) - - test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { - page, - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - }, { clientId: postContentBlock.clientId } ) - await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) - await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) - await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) - await editor.canvas.locator( 'body' ).click() - - await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) - await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - - await editor.saveDraft() - - const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - const textBlock = blocks.find( block => block.name === 'stackable/text' ) - expect( textBlock.attributes.text ).toBe( 'test' ) - expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) - } ) - - test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - attributes: { - text: 'test', - textColor1: '#ff0000', - }, - }, { clientId: postContentBlock.clientId } ) - - const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId - - await editor.saveDraft() - - const preview = await editor.openPreviewPage() - - await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() - await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) - await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - } ) -} ) +// import { test, expect } from 'e2e/test-utils' + +// test.describe( 'Site Editor', () => { +// // test.beforeAll( async ( { requestUtils } ) => { +// // await requestUtils.activateTheme( 'twentytwentyfour' ) +// // } ) + +// let pid = null +// let postContentBlock = null + +// test.beforeEach( async ( { +// page, admin, editor, +// } ) => { +// await admin.createNewPost( { title: 'Site Editor Test', postType: 'page' } ) +// await editor.saveDraft() +// const postQuery = new URL( editor.page.url() ).search +// pid = new URLSearchParams( postQuery ).get( 'post' ) + +// await admin.visitSiteEditor( { +// canvas: 'edit', postType: 'page', postId: pid, showWelcomeGuide: false, +// } ) + +// if ( await page.getByRole( 'heading', { name: 'Choose a pattern' } ).isVisible() ) { +// await page.getByLabel( 'Close', { exact: true } ).click() +// } + +// postContentBlock = ( await editor.getBlocks( { full: true } ) ) +// .filter( block => block.attributes?.tagName === 'main' )[ 0 ].innerBlocks +// .filter( block => block.name === 'core/post-content' )[ 0 ] +// } ) + +// test.afterEach( async ( { requestUtils } ) => { +// await requestUtils.deletePost( pid, 'pages' ) +// } ) + +// test( 'Stackable blocks can be added in the site editor', async ( { +// page, +// editor, +// } ) => { +// await page.getByLabel( 'Toggle block inserter' ).click() +// await page.locator( '.editor-block-list-item-stackable-text' ).click() + +// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + +// expect( blocks.find( block => block.name === 'stackable/text' ) ).toBeTruthy() +// } ) + +// test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { +// page, +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// }, { clientId: postContentBlock.clientId } ) + +// await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) +// await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() +// await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() +// await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() +// } ) + +// test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { +// page, +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// }, { clientId: postContentBlock.clientId } ) +// await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) +// await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) +// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() +// await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) +// await editor.canvas.locator( 'body' ).click() + +// await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) +// await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + +// await editor.saveDraft() + +// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) +// const textBlock = blocks.find( block => block.name === 'stackable/text' ) +// expect( textBlock.attributes.text ).toBe( 'test' ) +// expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) +// } ) + +// test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// attributes: { +// text: 'test', +// textColor1: '#ff0000', +// }, +// }, { clientId: postContentBlock.clientId } ) + +// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) +// const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId + +// await editor.saveDraft() + +// const preview = await editor.openPreviewPage() + +// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() +// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) +// await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) +// } ) +// } ) From 2a8fda54d651eca690fba547ec2630747e108a19 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 17:32:38 +0800 Subject: [PATCH 18/76] add try catch for newly installed plugin --- e2e/tests/admin.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/e2e/tests/admin.spec.ts b/e2e/tests/admin.spec.ts index 50fcf3b22c..6193e0ea76 100644 --- a/e2e/tests/admin.spec.ts +++ b/e2e/tests/admin.spec.ts @@ -17,7 +17,14 @@ test( 'Activating Stackable should redirect to the Getting Started Page', async await expect( activate ).toBeVisible() await activate.click() - await expect( page ).toHaveURL( /stackable-getting-started/ ) + try { + await expect( page ).toHaveURL( /stackable-getting-started/ ) + } catch { + await expect( page ).toHaveURL( /page=stackable/ ) + await expect( page.getByRole( 'link', { name: 'Activate Free Version' } ) ).toBeVisible() + await page.getByRole( 'link', { name: 'Activate Free Version' } ).click() + await expect( page ).toHaveURL( /stackable-getting-started/ ) + } } ) test( 'Stackable settings should be saved', async ( { From b3606ba1decf2a9d335ccd212dc1bacffe37be86 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 17:53:31 +0800 Subject: [PATCH 19/76] update admin test --- e2e/playwright.config.ts | 7 +++---- e2e/tests/admin.spec.ts | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index f5d032f4a7..b232a3ce6e 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -32,10 +32,9 @@ export default defineConfig( { // workers: process.env.CI ? 1 : undefined, workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: [ - [ 'list' ], - [ 'html', { outputFolder: '../playwright-report' } ], - ], + reporter: process.env.CI + ? [ [ 'list' ], [ 'github', { useDetails: true, showError: true } ], [ 'html', { outputFolder: '../playwright-report' } ] ] + : [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report' } ] ], /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ diff --git a/e2e/tests/admin.spec.ts b/e2e/tests/admin.spec.ts index 6193e0ea76..a7f4c79dc6 100644 --- a/e2e/tests/admin.spec.ts +++ b/e2e/tests/admin.spec.ts @@ -23,6 +23,7 @@ test( 'Activating Stackable should redirect to the Getting Started Page', async await expect( page ).toHaveURL( /page=stackable/ ) await expect( page.getByRole( 'link', { name: 'Activate Free Version' } ) ).toBeVisible() await page.getByRole( 'link', { name: 'Activate Free Version' } ).click() + await page.getByRole( 'link', { name: 'Skip', exact: true } ).click() await expect( page ).toHaveURL( /stackable-getting-started/ ) } } ) From addf024b0f0daead512641b2e085cc3292203622 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 18:09:25 +0800 Subject: [PATCH 20/76] update stackable fixture --- e2e/playwright.config.ts | 1 + e2e/test-utils/stackable.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index b232a3ce6e..80fe51ea6d 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -35,6 +35,7 @@ export default defineConfig( { reporter: process.env.CI ? [ [ 'list' ], [ 'github', { useDetails: true, showError: true } ], [ 'html', { outputFolder: '../playwright-report' } ] ] : [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report' } ] ], + reportSlowTests: null, /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ diff --git a/e2e/test-utils/stackable.ts b/e2e/test-utils/stackable.ts index fc7415b555..63e41fefdd 100644 --- a/e2e/test-utils/stackable.ts +++ b/e2e/test-utils/stackable.ts @@ -18,7 +18,7 @@ export class StackableFixture { } const finishedCallback = async ( request: Request ) => { - if ( request.url().includes( 'wp-json/wp/v2/settings' ) && request.method() === 'GET' ) { + if ( request.url().includes( 'wp/v2/settings' ) && request.method() === 'GET' ) { try { let settings = null await test.step( 'Wait for Stackable settings to load', async () => { @@ -43,7 +43,7 @@ export class StackableFixture { } } const failedCallback = async ( request: Request ) => { - if ( request.url().includes( 'wp-json/wp/v2/settings' ) && request.method() === 'GET' ) { + if ( request.url().includes( 'wp/v2/settings' ) && request.method() === 'GET' ) { cleanup() throw Error( 'Failed to get Stackable settings' ) } From 60f789080413dafb34775c53dbd998f1d9b23eee Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 18:41:56 +0800 Subject: [PATCH 21/76] comment on PR --- .github/workflows/playwright.yml | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 8468c4a2fc..30388f2276 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -13,6 +13,8 @@ jobs: - uses: actions/setup-node@v4 with: node-version: lts/* + - name: Set the version suffix for the output + run: echo VERSION_SUFFIX=${GITHUB_REF_NAME//\//-} >> $GITHUB_ENV - name: Build Stackable Free Plugin run: | npm ci --legacy-peer-deps @@ -32,9 +34,15 @@ jobs: WP_PASSWORD: password STACKABLE_SLUG: Stackable/plugin run: npm run test - - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} + - name: Zip Playwright Report + uses: montudor/action-zip@v1 with: - name: playwright-report - path: playwright-report/ - retention-days: 30 + args: zip -qq -r playwright-report-${{ env.VERSION_SUFFIX }}.zip playwright-report + - name: Upload Playwright report artifact + if: ${{ github.event_name == 'pull_request' }} + uses: gavv/pull-request-artifacts@v1.0.0 + with: + commit: ${{ github.event.pull_request.head.sha }} + repo-token: ${{ secrets.GITHUB_TOKEN }} + artifacts: playwright-report-${{ env.VERSION_SUFFIX }}.zip + artifacts-branch: artifacts From 4b895ae451c056bb4cbdee23f83886db1308e195 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 20:08:22 +0800 Subject: [PATCH 22/76] fix PR comment --- .github/workflows/playwright.yml | 33 ++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 30388f2276..15c489ff16 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -34,15 +34,28 @@ jobs: WP_PASSWORD: password STACKABLE_SLUG: Stackable/plugin run: npm run test - - name: Zip Playwright Report - uses: montudor/action-zip@v1 + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} with: - args: zip -qq -r playwright-report-${{ env.VERSION_SUFFIX }}.zip playwright-report - - name: Upload Playwright report artifact - if: ${{ github.event_name == 'pull_request' }} - uses: gavv/pull-request-artifacts@v1.0.0 + name: playwright-report-${{ env.VERSION_SUFFIX }}.zip + path: playwright-report/ + retention-days: 30 + - name: Find Comment + uses: peter-evans/find-comment@v3 + id: fc with: - commit: ${{ github.event.pull_request.head.sha }} - repo-token: ${{ secrets.GITHUB_TOKEN }} - artifacts: playwright-report-${{ env.VERSION_SUFFIX }}.zip - artifacts-branch: artifacts + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: Playwright Report Artifact + - name: Create or update comment + uses: peter-evans/create-or-update-comment@v4 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body: | + ` ## Playwright Report Artifact + | file | commit | + | ---- | ------ | + | [playwright-report-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts) | ${{ github.sha }} | + ` + edit-mode: replace From 4d6ef186325deaf1da81d9826e1a12db4a2a86b9 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 20:21:29 +0800 Subject: [PATCH 23/76] add id for upload artifact, update comment body --- .github/workflows/playwright.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 15c489ff16..8c2c9a20cd 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -36,9 +36,11 @@ jobs: run: npm run test - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} + id: artifact-upload-step with: name: playwright-report-${{ env.VERSION_SUFFIX }}.zip path: playwright-report/ + overwrite: true retention-days: 30 - name: Find Comment uses: peter-evans/find-comment@v3 @@ -53,9 +55,8 @@ jobs: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | - ` ## Playwright Report Artifact + ## Playwright Report Artifact | file | commit | | ---- | ------ | - | [playwright-report-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts) | ${{ github.sha }} | - ` + | [playwright-report-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}) | ${{ github.sha }} | edit-mode: replace From 3b1eaf75811266a210b1b890beb4c00b6112ffd8 Mon Sep 17 00:00:00 2001 From: mxkae Date: Wed, 5 Feb 2025 20:35:08 +0800 Subject: [PATCH 24/76] update yml file --- .github/workflows/playwright.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 8c2c9a20cd..1647d0e250 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -20,7 +20,7 @@ jobs: npm ci --legacy-peer-deps npm run build:no-translate - name: Install Playwright Browsers - run: npx playwright install --with-deps + run: npx playwright install chromium firefox webkit --with-deps - name: Install wp-env run: | npm install -g @wordpress/env @@ -58,5 +58,5 @@ jobs: ## Playwright Report Artifact | file | commit | | ---- | ------ | - | [playwright-report-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}) | ${{ github.sha }} | + | [playwright-report-${{ env.VERSION_SUFFIX }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}) | ${{ github.sha }} | edit-mode: replace From 382039002748f05c0118f27ee2f8cd7cd6793f2c Mon Sep 17 00:00:00 2001 From: mxkae Date: Thu, 6 Feb 2025 13:01:23 +0800 Subject: [PATCH 25/76] include site editor tests --- .github/workflows/playwright.yml | 4 +- e2e/tests/site-editor.spec.ts | 212 +++++++++++++++---------------- 2 files changed, 106 insertions(+), 110 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 1647d0e250..37e1f9bbcd 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -38,7 +38,7 @@ jobs: if: ${{ !cancelled() }} id: artifact-upload-step with: - name: playwright-report-${{ env.VERSION_SUFFIX }}.zip + name: playwright-report-${{ env.VERSION_SUFFIX }} path: playwright-report/ overwrite: true retention-days: 30 @@ -58,5 +58,5 @@ jobs: ## Playwright Report Artifact | file | commit | | ---- | ------ | - | [playwright-report-${{ env.VERSION_SUFFIX }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}) | ${{ github.sha }} | + | [playwright-report-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}) | ${{ github.sha }} | edit-mode: replace diff --git a/e2e/tests/site-editor.spec.ts b/e2e/tests/site-editor.spec.ts index 244ef4aa6d..eb82c0352f 100644 --- a/e2e/tests/site-editor.spec.ts +++ b/e2e/tests/site-editor.spec.ts @@ -1,109 +1,105 @@ -// import { test, expect } from 'e2e/test-utils' - -// test.describe( 'Site Editor', () => { -// // test.beforeAll( async ( { requestUtils } ) => { -// // await requestUtils.activateTheme( 'twentytwentyfour' ) -// // } ) - -// let pid = null -// let postContentBlock = null - -// test.beforeEach( async ( { -// page, admin, editor, -// } ) => { -// await admin.createNewPost( { title: 'Site Editor Test', postType: 'page' } ) -// await editor.saveDraft() -// const postQuery = new URL( editor.page.url() ).search -// pid = new URLSearchParams( postQuery ).get( 'post' ) - -// await admin.visitSiteEditor( { -// canvas: 'edit', postType: 'page', postId: pid, showWelcomeGuide: false, -// } ) - -// if ( await page.getByRole( 'heading', { name: 'Choose a pattern' } ).isVisible() ) { -// await page.getByLabel( 'Close', { exact: true } ).click() -// } - -// postContentBlock = ( await editor.getBlocks( { full: true } ) ) -// .filter( block => block.attributes?.tagName === 'main' )[ 0 ].innerBlocks -// .filter( block => block.name === 'core/post-content' )[ 0 ] -// } ) - -// test.afterEach( async ( { requestUtils } ) => { -// await requestUtils.deletePost( pid, 'pages' ) -// } ) - -// test( 'Stackable blocks can be added in the site editor', async ( { -// page, -// editor, -// } ) => { -// await page.getByLabel( 'Toggle block inserter' ).click() -// await page.locator( '.editor-block-list-item-stackable-text' ).click() - -// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - -// expect( blocks.find( block => block.name === 'stackable/text' ) ).toBeTruthy() -// } ) - -// test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { -// page, -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// }, { clientId: postContentBlock.clientId } ) - -// await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) -// await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() -// await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() -// await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() -// } ) - -// test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { -// page, -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// }, { clientId: postContentBlock.clientId } ) -// await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) -// await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) -// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() -// await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) -// await editor.canvas.locator( 'body' ).click() - -// await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) -// await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - -// await editor.saveDraft() - -// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) -// const textBlock = blocks.find( block => block.name === 'stackable/text' ) -// expect( textBlock.attributes.text ).toBe( 'test' ) -// expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) -// } ) - -// test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// attributes: { -// text: 'test', -// textColor1: '#ff0000', -// }, -// }, { clientId: postContentBlock.clientId } ) - -// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) -// const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId - -// await editor.saveDraft() - -// const preview = await editor.openPreviewPage() - -// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() -// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) -// await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) -// } ) -// } ) +import { test, expect } from 'e2e/test-utils' + +test.describe( 'Site Editor', () => { + let pid = null + let postContentBlock = null + + test.beforeEach( async ( { + page, admin, editor, + } ) => { + await admin.createNewPost( { title: 'Site Editor Test', postType: 'page' } ) + await editor.saveDraft() + const postQuery = new URL( editor.page.url() ).search + pid = new URLSearchParams( postQuery ).get( 'post' ) + + await admin.visitSiteEditor( { + canvas: 'edit', postType: 'page', postId: pid, showWelcomeGuide: false, + } ) + + if ( await page.getByRole( 'heading', { name: 'Choose a pattern' } ).isVisible() ) { + await page.getByLabel( 'Close', { exact: true } ).click() + } + + postContentBlock = ( await editor.getBlocks( { full: true } ) ) + .filter( block => block.attributes?.tagName === 'main' )[ 0 ].innerBlocks + .filter( block => block.name === 'core/post-content' )[ 0 ] + } ) + + test.afterEach( async ( { requestUtils } ) => { + await requestUtils.deletePost( pid, 'pages' ) + } ) + + test( 'Stackable blocks can be added in the site editor', async ( { + page, + editor, + } ) => { + await page.getByLabel( 'Toggle block inserter' ).click() + await page.locator( '.editor-block-list-item-stackable-text' ).click() + + const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + + expect( blocks.find( block => block.name === 'stackable/text' ) ).toBeTruthy() + } ) + + test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + }, { clientId: postContentBlock.clientId } ) + + await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) + await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() + } ) + + test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + }, { clientId: postContentBlock.clientId } ) + await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) + await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) + await editor.canvas.locator( 'body' ).click() + + await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) + await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + + await editor.saveDraft() + + const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + const textBlock = blocks.find( block => block.name === 'stackable/text' ) + expect( textBlock.attributes.text ).toBe( 'test' ) + expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) + } ) + + test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + textColor1: '#ff0000', + }, + }, { clientId: postContentBlock.clientId } ) + + const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId + + await editor.saveDraft() + + const preview = await editor.openPreviewPage() + + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) + await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + } ) +} ) From b1043f83a83abebca78c92b2f0cc59186a20b5c2 Mon Sep 17 00:00:00 2001 From: mxkae Date: Thu, 6 Feb 2025 13:17:05 +0800 Subject: [PATCH 26/76] minor update in yml file --- .github/workflows/playwright.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 37e1f9bbcd..5bab370c12 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -35,12 +35,10 @@ jobs: STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} id: artifact-upload-step with: name: playwright-report-${{ env.VERSION_SUFFIX }} path: playwright-report/ - overwrite: true retention-days: 30 - name: Find Comment uses: peter-evans/find-comment@v3 From 57c36086faada583d2780d2860588450abdc2a8e Mon Sep 17 00:00:00 2001 From: mxkae Date: Thu, 6 Feb 2025 13:26:51 +0800 Subject: [PATCH 27/76] always update comment --- .github/workflows/playwright.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 5bab370c12..ba7937e27d 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -35,13 +35,16 @@ jobs: STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} id: artifact-upload-step with: name: playwright-report-${{ env.VERSION_SUFFIX }} path: playwright-report/ + overwrite: true retention-days: 30 - name: Find Comment uses: peter-evans/find-comment@v3 + if: ${{ !cancelled() }} id: fc with: issue-number: ${{ github.event.pull_request.number }} @@ -49,6 +52,7 @@ jobs: body-includes: Playwright Report Artifact - name: Create or update comment uses: peter-evans/create-or-update-comment@v4 + if: ${{ !cancelled() }} with: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} From a519bde30b5ca6045bc34685ba44ac7510efd862 Mon Sep 17 00:00:00 2001 From: mxkae Date: Thu, 6 Feb 2025 22:48:08 +0800 Subject: [PATCH 28/76] upload traces --- .github/scripts/unzip_traces.sh | 59 ++++++++++++++++++++++++++++++ .github/workflows/playwright.yml | 4 +++ e2e/config/reporter.ts | 61 ++++++++++++++++++++++++++++++++ e2e/playwright.config.ts | 5 ++- 4 files changed, 126 insertions(+), 3 deletions(-) create mode 100755 .github/scripts/unzip_traces.sh create mode 100644 e2e/config/reporter.ts diff --git a/.github/scripts/unzip_traces.sh b/.github/scripts/unzip_traces.sh new file mode 100755 index 0000000000..544d8185f6 --- /dev/null +++ b/.github/scripts/unzip_traces.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# Path to the JSON file +json_file="./playwright-traces/test-traces.json" + +# Function to check jq version +check_jq_version() { + jq_version=$(jq --version | cut -d- -f2) + major_version=$(echo "$jq_version" | cut -d. -f1) + minor_version=$(echo "$jq_version" | cut -d. -f2) + + if [ "$major_version" -eq 1 ] && [ "$minor_version" -ge 6 ]; then + echo "jq version: $jq_version" + return 0 # jq 1.6 or higher + else + return 1 # Unsupported jq version + fi +} + +# Check if jq is installed +if ! command -v jq &> /dev/null; then + echo "jq is not installed. Please install jq first." + exit 1 +fi + +# Check jq version +if ! check_jq_version; then + echo "Unsupported jq version. Please use jq 1.6 or higher." + exit 1 +fi + +# Read the JSON file +cat "$json_file" | jq . + +jq -r '.[] | "\(.id) \(.trace)"' "$json_file" | while read id trace_file; do + if [ -f "$trace_file" ]; then + # Create the directory for the trace file + output_dir="./traces/$id" + mkdir -p "$output_dir" + + # Unzip the trace file into the directory + unzip -o "$trace_file" -d "$output_dir" > /dev/null 2>&1 && echo "Unzipped $trace_file to $output_dir/" + else + echo "Trace file not found: $trace_file" + fi +done + +for dir in ./traces/*; do + if [ -d "$dir" ]; then + artifact_name=$(basename "$dir") + echo "Uploading artifact: $artifact_name" + # Upload each trace/{id} as an artifact using actions/upload-artifact + - name: Upload $artifact_name + uses: actions/upload-artifact@v3 + with: + name: "$artifact_name" + path: "$dir" + fi +done diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index ba7937e27d..fd6e6eccc0 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -34,6 +34,10 @@ jobs: WP_PASSWORD: password STACKABLE_SLUG: Stackable/plugin run: npm run test + - name: Run script to unzip and upload traces + run: | + chmod +x ./.github/scripts/unzip_traces.sh + ./.github/scripts/unzip_traces.sh - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts new file mode 100644 index 0000000000..46e5eebf68 --- /dev/null +++ b/e2e/config/reporter.ts @@ -0,0 +1,61 @@ +/* eslint-disable no-console */ +import type { + Reporter, TestCase, TestResult, +} from '@playwright/test/reporter' + +import fs from 'fs' +import path from 'path' + +class MyReporter implements Reporter { + outputFolder: string; + testResults: Array<{title: string, id: string, trace: any}>; + + constructor() { + this.outputFolder = 'playwright-traces' + this.cleanupFolder() + this.testResults = [] + } + cleanupFolder() { + const folderPath = path.resolve( this.outputFolder ) + + if ( fs.existsSync( folderPath ) ) { + // Read the directory and delete files + const files = fs.readdirSync( folderPath ) + for ( const file of files ) { + const filePath = path.join( folderPath, file ) + if ( fs.lstatSync( filePath ).isFile() ) { + fs.unlinkSync( filePath ) // Remove the file + } + } + console.log( `All files removed from: ${ folderPath }` ) + } else { + // If folder doesn't exist, create it + fs.mkdirSync( folderPath, { recursive: true } ) + } + } + + onTestEnd( test: TestCase, result: TestResult ) { + if ( result.attachments.length !== 0 ) { + console.log( 'title:', test.title ) + console.log( 'attachments', result.attachments ) + this.testResults.push( { + id: test.id, + title: test.title, + trace: result.attachments.find( attachment => attachment.name === 'trace' ).path, + } ) + } + } + + async onEnd() { + // Ensure the output folder exists + const folderPath = path.resolve( this.outputFolder ) + if ( ! fs.existsSync( folderPath ) ) { + fs.mkdirSync( folderPath, { recursive: true } ) + } + + // Write the collected results to a JSON file + const reportPath = path.join( folderPath, 'test-traces.json' ) + fs.writeFileSync( reportPath, JSON.stringify( this.testResults, null, 2 ) ) + } +} +export default MyReporter diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 80fe51ea6d..11d208c65c 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -13,7 +13,6 @@ dotenv.config( { path: path.resolve( __dirname, '../.env' ) } ) * See https://playwright.dev/docs/test-configuration. */ export default defineConfig( { - testDir: '.', // This is run before any tests. Check the file for more information. // globalSetup: 'e2e-global-setup.ts', globalSetup: fileURLToPath( @@ -33,7 +32,7 @@ export default defineConfig( { workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: process.env.CI - ? [ [ 'list' ], [ 'github', { useDetails: true, showError: true } ], [ 'html', { outputFolder: '../playwright-report' } ] ] + ? [ [ 'list' ], [ 'github', { useDetails: true, showError: true } ], [ 'html', { outputFolder: '../playwright-report' } ], [ './config/reporter.ts' ] ] : [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report' } ] ], reportSlowTests: null, /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ @@ -70,7 +69,7 @@ export default defineConfig( { storageState: process.env.WP_AUTH_STORAGE, ...devices[ 'Desktop Chrome' ], }, - testMatch: 'tests/*.ts', + testDir: './tests', }, // { // name: 'chromium', From abf48b9b3f9c1b30c51efa67dfd1c313ced6d1d6 Mon Sep 17 00:00:00 2001 From: mxkae Date: Thu, 6 Feb 2025 22:56:21 +0800 Subject: [PATCH 29/76] test script --- .github/workflows/playwright.yml | 1 + e2e/playwright.config.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index fd6e6eccc0..8c30305d5c 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -35,6 +35,7 @@ jobs: STACKABLE_SLUG: Stackable/plugin run: npm run test - name: Run script to unzip and upload traces + if: ${{ !cancelled() }} run: | chmod +x ./.github/scripts/unzip_traces.sh ./.github/scripts/unzip_traces.sh diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 11d208c65c..b16224b2bb 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -23,7 +23,8 @@ export default defineConfig( { /* Fail the build on CI if you accidentally left test.only in the source code. */ // forbidOnly: !! process.env.CI, /* Retry on CI only */ - retries: process.env.CI ? 2 : 0, + // retries: process.env.CI ? 2 : 0, + retries: 0, // Locally, we could take advantage of parallelism due to multicore systems. // However, in the CI, we typically can use only one worker at a time. // It's more straightforward to align how we run tests in both systems. From a3cb37d7a315a59a04e02d580fb9f4508a46bae5 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 02:21:41 +0800 Subject: [PATCH 30/76] update comment content --- .github/scripts/unzip_traces.sh | 59 -------------------------------- .github/workflows/playwright.yml | 9 ++--- e2e/config/reporter.ts | 53 +++++++++++++++++++++------- e2e/playwright.config.ts | 7 ++-- 4 files changed, 49 insertions(+), 79 deletions(-) delete mode 100755 .github/scripts/unzip_traces.sh diff --git a/.github/scripts/unzip_traces.sh b/.github/scripts/unzip_traces.sh deleted file mode 100755 index 544d8185f6..0000000000 --- a/.github/scripts/unzip_traces.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash - -# Path to the JSON file -json_file="./playwright-traces/test-traces.json" - -# Function to check jq version -check_jq_version() { - jq_version=$(jq --version | cut -d- -f2) - major_version=$(echo "$jq_version" | cut -d. -f1) - minor_version=$(echo "$jq_version" | cut -d. -f2) - - if [ "$major_version" -eq 1 ] && [ "$minor_version" -ge 6 ]; then - echo "jq version: $jq_version" - return 0 # jq 1.6 or higher - else - return 1 # Unsupported jq version - fi -} - -# Check if jq is installed -if ! command -v jq &> /dev/null; then - echo "jq is not installed. Please install jq first." - exit 1 -fi - -# Check jq version -if ! check_jq_version; then - echo "Unsupported jq version. Please use jq 1.6 or higher." - exit 1 -fi - -# Read the JSON file -cat "$json_file" | jq . - -jq -r '.[] | "\(.id) \(.trace)"' "$json_file" | while read id trace_file; do - if [ -f "$trace_file" ]; then - # Create the directory for the trace file - output_dir="./traces/$id" - mkdir -p "$output_dir" - - # Unzip the trace file into the directory - unzip -o "$trace_file" -d "$output_dir" > /dev/null 2>&1 && echo "Unzipped $trace_file to $output_dir/" - else - echo "Trace file not found: $trace_file" - fi -done - -for dir in ./traces/*; do - if [ -d "$dir" ]; then - artifact_name=$(basename "$dir") - echo "Uploading artifact: $artifact_name" - # Upload each trace/{id} as an artifact using actions/upload-artifact - - name: Upload $artifact_name - uses: actions/upload-artifact@v3 - with: - name: "$artifact_name" - path: "$dir" - fi -done diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 8c30305d5c..8e46dcd505 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -34,11 +34,11 @@ jobs: WP_PASSWORD: password STACKABLE_SLUG: Stackable/plugin run: npm run test - - name: Run script to unzip and upload traces - if: ${{ !cancelled() }} + - name: Get failed tests + id: get-failed-tests run: | - chmod +x ./.github/scripts/unzip_traces.sh - ./.github/scripts/unzip_traces.sh + CONTENT=$(cat ./playwright-errors/errors.md) + echo "::set-output name=content::$CONTENT" - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step @@ -62,6 +62,7 @@ jobs: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | + ${{ steps.get-failed-tests.outputs.content }} ## Playwright Report Artifact | file | commit | | ---- | ------ | diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 46e5eebf68..65cfb14cb3 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -8,13 +8,18 @@ import path from 'path' class MyReporter implements Reporter { outputFolder: string; - testResults: Array<{title: string, id: string, trace: any}>; + testResults: Array; constructor() { - this.outputFolder = 'playwright-traces' + this.outputFolder = 'playwright-errors' this.cleanupFolder() this.testResults = [] } + + removeColorCodes( input : string ) { + return input.replace( /\[[0-9;]*m/g, '' ) + } + cleanupFolder() { const folderPath = path.resolve( this.outputFolder ) @@ -35,14 +40,31 @@ class MyReporter implements Reporter { } onTestEnd( test: TestCase, result: TestResult ) { - if ( result.attachments.length !== 0 ) { - console.log( 'title:', test.title ) - console.log( 'attachments', result.attachments ) - this.testResults.push( { - id: test.id, - title: test.title, - trace: result.attachments.find( attachment => attachment.name === 'trace' ).path, - } ) + if ( result.status !== test.expectedStatus ) { + let testResult = `### ${ test.title } +` + if ( result.errors.length >= 1 ) { + testResult += `\`\`\` +` + result.errors.forEach( error => { + if ( error.message ) { + testResult += `${ this.removeColorCodes( error.message ) } + +` + } + + if ( error.snippet ) { + testResult += `${ this.removeColorCodes( error.snippet ) } + +` + } + } ) + testResult += `\`\`\` + +` + } + + this.testResults.push( testResult ) } } @@ -54,8 +76,15 @@ class MyReporter implements Reporter { } // Write the collected results to a JSON file - const reportPath = path.join( folderPath, 'test-traces.json' ) - fs.writeFileSync( reportPath, JSON.stringify( this.testResults, null, 2 ) ) + const reportPath = path.join( folderPath, 'errors.md' ) + let reportContent = '' + if ( this.testResults.length ) { + reportContent += `## Failed Tests + +${ this.testResults.join( '' ) }` + } + + fs.writeFileSync( reportPath, reportContent ) } } export default MyReporter diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index b16224b2bb..55034b722b 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -23,8 +23,7 @@ export default defineConfig( { /* Fail the build on CI if you accidentally left test.only in the source code. */ // forbidOnly: !! process.env.CI, /* Retry on CI only */ - // retries: process.env.CI ? 2 : 0, - retries: 0, + retries: process.env.CI ? 1 : 0, // Locally, we could take advantage of parallelism due to multicore systems. // However, in the CI, we typically can use only one worker at a time. // It's more straightforward to align how we run tests in both systems. @@ -33,8 +32,8 @@ export default defineConfig( { workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: process.env.CI - ? [ [ 'list' ], [ 'github', { useDetails: true, showError: true } ], [ 'html', { outputFolder: '../playwright-report' } ], [ './config/reporter.ts' ] ] - : [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report' } ] ], + ? [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report', open: 'never' } ], [ './config/reporter.ts' ] ] + : [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report', open: 'never' } ] ], reportSlowTests: null, /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { From dedf6f2b59ce3d359a9cdd88ccacdcac0d014b3a Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 02:30:02 +0800 Subject: [PATCH 31/76] get failed tests --- .github/workflows/playwright.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 8e46dcd505..851250e1af 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -36,6 +36,7 @@ jobs: run: npm run test - name: Get failed tests id: get-failed-tests + if: ${{ !cancelled() }} run: | CONTENT=$(cat ./playwright-errors/errors.md) echo "::set-output name=content::$CONTENT" From 3ceb2e84bfdb46aee62c5018179fff9fa5e25ddb Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 02:40:17 +0800 Subject: [PATCH 32/76] use $GITHUB_OUTPUT --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 851250e1af..df8b3156dd 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -39,7 +39,7 @@ jobs: if: ${{ !cancelled() }} run: | CONTENT=$(cat ./playwright-errors/errors.md) - echo "::set-output name=content::$CONTENT" + echo "content=$CONTENT" >> $GITHUB_OUTPUT - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step From 8b43484e7e82de11968b2ef5f7cdf81611df2005 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 02:57:37 +0800 Subject: [PATCH 33/76] update to multiline --- .github/workflows/playwright.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index df8b3156dd..7438954b3e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -39,7 +39,11 @@ jobs: if: ${{ !cancelled() }} run: | CONTENT=$(cat ./playwright-errors/errors.md) - echo "content=$CONTENT" >> $GITHUB_OUTPUT + { + echo 'JSON_RESPONSE<> $GITHUB_OUTPUT - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step From 253332e00cc537cb4b4e8bb50438d9e52b77dae2 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 03:04:08 +0800 Subject: [PATCH 34/76] update output name --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 7438954b3e..8a052270ed 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -40,7 +40,7 @@ jobs: run: | CONTENT=$(cat ./playwright-errors/errors.md) { - echo 'JSON_RESPONSE<> $GITHUB_OUTPUT From e4f092474b12e4a2fc255baf99a4e0997b11f742 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 03:26:15 +0800 Subject: [PATCH 35/76] fix failed tests content --- .github/workflows/playwright.yml | 8 +------- e2e/config/reporter.ts | 6 ++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 8a052270ed..a5bae10add 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -37,13 +37,7 @@ jobs: - name: Get failed tests id: get-failed-tests if: ${{ !cancelled() }} - run: | - CONTENT=$(cat ./playwright-errors/errors.md) - { - echo 'content<> $GITHUB_OUTPUT + run: echo "content='$(printf "%s\n" "$(cat ./playwright-errors/errors.md)")'" >> $GITHUB_OUTPUT - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 65cfb14cb3..c26fce8af7 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -20,6 +20,10 @@ class MyReporter implements Reporter { return input.replace( /\[[0-9;]*m/g, '' ) } + escapeQuotes( input: string ) { + return input.replace( /([\'\"])/g, '\\$1' ) + } + cleanupFolder() { const folderPath = path.resolve( this.outputFolder ) @@ -82,6 +86,8 @@ class MyReporter implements Reporter { reportContent += `## Failed Tests ${ this.testResults.join( '' ) }` + + reportContent = this.escapeQuotes( reportContent ) } fs.writeFileSync( reportPath, reportContent ) From 66d099575ea452c9196438a409c1f334ce60327d Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 03:38:59 +0800 Subject: [PATCH 36/76] escape special characters --- .github/workflows/playwright.yml | 6 +----- e2e/config/reporter.ts | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index a5bae10add..c88879d61d 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -34,10 +34,6 @@ jobs: WP_PASSWORD: password STACKABLE_SLUG: Stackable/plugin run: npm run test - - name: Get failed tests - id: get-failed-tests - if: ${{ !cancelled() }} - run: echo "content='$(printf "%s\n" "$(cat ./playwright-errors/errors.md)")'" >> $GITHUB_OUTPUT - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step @@ -61,7 +57,7 @@ jobs: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | - ${{ steps.get-failed-tests.outputs.content }} + $(cat ./playwright-errors/errors.md) ## Playwright Report Artifact | file | commit | | ---- | ------ | diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index c26fce8af7..945949c5ef 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -20,8 +20,8 @@ class MyReporter implements Reporter { return input.replace( /\[[0-9;]*m/g, '' ) } - escapeQuotes( input: string ) { - return input.replace( /([\'\"])/g, '\\$1' ) + escapeSpecialCharacters( input: string ) { + return input.replace( /([\'\"\&\{\}])/g, '\\$1' ) } cleanupFolder() { @@ -87,7 +87,7 @@ class MyReporter implements Reporter { ${ this.testResults.join( '' ) }` - reportContent = this.escapeQuotes( reportContent ) + reportContent = this.escapeSpecialCharacters( reportContent ) } fs.writeFileSync( reportPath, reportContent ) From 790f603fabe2e044f8954ac200ac025ca0267404 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 03:52:48 +0800 Subject: [PATCH 37/76] use markdown to output --- .github/workflows/playwright.yml | 6 +- e2e/playwright.config.ts | 3 +- e2e/tests/admin.spec.ts | 108 ++++++------- e2e/tests/block-editor.spec.ts | 190 +++++++++++----------- e2e/tests/global-settings.spec.ts | 252 +++++++++++++++--------------- 5 files changed, 282 insertions(+), 277 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index c88879d61d..9e8e00b075 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -34,6 +34,10 @@ jobs: WP_PASSWORD: password STACKABLE_SLUG: Stackable/plugin run: npm run test + - uses: markpatterson27/markdown-to-output@v1 + id: mto + with: + filepath: ./playwright-errors/errors.md - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step @@ -57,7 +61,7 @@ jobs: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | - $(cat ./playwright-errors/errors.md) + ${{ steps.mto.outputs.body }} ## Playwright Report Artifact | file | commit | | ---- | ------ | diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 55034b722b..bc63ffb697 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -23,7 +23,8 @@ export default defineConfig( { /* Fail the build on CI if you accidentally left test.only in the source code. */ // forbidOnly: !! process.env.CI, /* Retry on CI only */ - retries: process.env.CI ? 1 : 0, + // retries: process.env.CI ? 1 : 0, + retries: 0, // Locally, we could take advantage of parallelism due to multicore systems. // However, in the CI, we typically can use only one worker at a time. // It's more straightforward to align how we run tests in both systems. diff --git a/e2e/tests/admin.spec.ts b/e2e/tests/admin.spec.ts index a7f4c79dc6..ef17db8f81 100644 --- a/e2e/tests/admin.spec.ts +++ b/e2e/tests/admin.spec.ts @@ -1,67 +1,67 @@ -import { test, expect } from 'e2e/test-utils' +// import { test, expect } from 'e2e/test-utils' -test( 'Activating Stackable should redirect to the Getting Started Page', async ( { - page, - admin, -} ) => { - await admin.visitAdminPage( 'plugins.php' ) +// test( 'Activating Stackable should redirect to the Getting Started Page', async ( { +// page, +// admin, +// } ) => { +// await admin.visitAdminPage( 'plugins.php' ) - const plugin = page.locator( `[data-plugin="${ process.env.STACKABLE_SLUG }.php"]` ) - // Deactivate Stackable - const deactivate = plugin.getByLabel( 'Deactivate Stackable -' ) - await expect( deactivate ).toBeVisible() - await deactivate.click() +// const plugin = page.locator( `[data-plugin="${ process.env.STACKABLE_SLUG }.php"]` ) +// // Deactivate Stackable +// const deactivate = plugin.getByLabel( 'Deactivate Stackable -' ) +// await expect( deactivate ).toBeVisible() +// await deactivate.click() - // Activate Stackable - const activate = plugin.getByLabel( 'Activate Stackable -' ) - await expect( activate ).toBeVisible() - await activate.click() +// // Activate Stackable +// const activate = plugin.getByLabel( 'Activate Stackable -' ) +// await expect( activate ).toBeVisible() +// await activate.click() - try { - await expect( page ).toHaveURL( /stackable-getting-started/ ) - } catch { - await expect( page ).toHaveURL( /page=stackable/ ) - await expect( page.getByRole( 'link', { name: 'Activate Free Version' } ) ).toBeVisible() - await page.getByRole( 'link', { name: 'Activate Free Version' } ).click() - await page.getByRole( 'link', { name: 'Skip', exact: true } ).click() - await expect( page ).toHaveURL( /stackable-getting-started/ ) - } -} ) +// try { +// await expect( page ).toHaveURL( /stackable-getting-started/ ) +// } catch { +// await expect( page ).toHaveURL( /page=stackable/ ) +// await expect( page.getByRole( 'link', { name: 'Activate Free Version' } ) ).toBeVisible() +// await page.getByRole( 'link', { name: 'Activate Free Version' } ).click() +// await page.getByRole( 'link', { name: 'Skip', exact: true } ).click() +// await expect( page ).toHaveURL( /stackable-getting-started/ ) +// } +// } ) -test( 'Stackable settings should be saved', async ( { - page, - admin, - stackable, -} ) => { - // Start waiting for Stackable Settings JSON Response before visiting the page - let settings = null - settings = stackable.waitForSettings() +// test( 'Stackable settings should be saved', async ( { +// page, +// admin, +// stackable, +// } ) => { +// // Start waiting for Stackable Settings JSON Response before visiting the page +// let settings = null +// settings = stackable.waitForSettings() - await admin.visitAdminPage( 'options-general.php?page=stackable' ) - // Make sure all Stackable settings are loaded - await settings +// await admin.visitAdminPage( 'options-general.php?page=stackable' ) +// // Make sure all Stackable settings are loaded +// await settings - // Retrieves the value of the first option, toggles it and check if the value changed - const option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) - const val = await option.getAttribute( 'aria-checked' ) +// // Retrieves the value of the first option, toggles it and check if the value changed +// const option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) +// const val = await option.getAttribute( 'aria-checked' ) - await option.click() - const newVal = await option.getAttribute( 'aria-checked' ) +// await option.click() +// const newVal = await option.getAttribute( 'aria-checked' ) - expect( newVal ).not.toBe( val ) - await page.getByRole( 'button', { name: 'Save Changes' } ).click() +// expect( newVal ).not.toBe( val ) +// await page.getByRole( 'button', { name: 'Save Changes' } ).click() - // Check if the value is correct after reloading - settings = stackable.waitForSettings() - await page.reload() - await settings +// // Check if the value is correct after reloading +// settings = stackable.waitForSettings() +// await page.reload() +// await settings - const _option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) +// const _option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) - await expect( _option ).toHaveAttribute( 'aria-checked', newVal ) +// await expect( _option ).toHaveAttribute( 'aria-checked', newVal ) - // Revert back the settings to the original value - await _option.click() - await page.getByRole( 'button', { name: 'Save Changes' } ).click() - await expect( _option ).toHaveAttribute( 'aria-checked', val ) -} ) +// // Revert back the settings to the original value +// await _option.click() +// await page.getByRole( 'button', { name: 'Save Changes' } ).click() +// await expect( _option ).toHaveAttribute( 'aria-checked', val ) +// } ) diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts index 724f4139e6..1d86be4570 100644 --- a/e2e/tests/block-editor.spec.ts +++ b/e2e/tests/block-editor.spec.ts @@ -1,96 +1,96 @@ -import { test, expect } from 'e2e/test-utils' - -test.describe( 'Block Editor', () => { - let pid = null - - // Create Posts for testing - test.beforeEach( async ( { editor, admin } ) => { - await admin.createNewPost( { title: 'Block Editor Test' } ) - await editor.saveDraft() - const postQuery = new URL( editor.page.url() ).search - pid = new URLSearchParams( postQuery ).get( 'post' ) - } ) - - // Delete created post - test.afterEach( async ( { requestUtils } ) => { - await requestUtils.deletePost( pid ) - } ) - - test( 'Stackable blocks can be added in the editor', async ( { - page, - - editor, - } ) => { - await page.getByLabel( 'Toggle block inserter' ).click() - - await page.locator( '.editor-block-list-item-stackable-text' ).click() - - const blocks = await editor.getBlocks() - - expect( blocks[ 0 ].name ).toContain( 'stackable/text' ) - } ) - - test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { - page, - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - } ) - - await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) - await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() - await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() - await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() - } ) - - test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { - page, - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - } ) - - // Add content and color to Stackable Text Block - await editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ).fill( 'test' ) - await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) - await editor.canvas.locator( 'body' ).click() - - await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) - await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - - await editor.saveDraft() - - const blocks = await editor.getBlocks() - const attributes = blocks[ 0 ].attributes - - expect( attributes.textColor1 ).toBe( '#ff0000' ) - expect( attributes.text ).toBe( 'test' ) - } ) - - test( 'The Stackable block added in the editor should be visible in the frontend', async ( { - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - attributes: { - text: 'test', - textColor1: '#ff0000', - }, - } ) - - const blocks = await editor.getBlocks() - const uniqueId = blocks[ 0 ].attributes.uniqueId - - await editor.saveDraft() - - const preview = await editor.openPreviewPage() - - await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() - await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) - await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - } ) -} ) +// import { test, expect } from 'e2e/test-utils' + +// test.describe( 'Block Editor', () => { +// let pid = null + +// // Create Posts for testing +// test.beforeEach( async ( { editor, admin } ) => { +// await admin.createNewPost( { title: 'Block Editor Test' } ) +// await editor.saveDraft() +// const postQuery = new URL( editor.page.url() ).search +// pid = new URLSearchParams( postQuery ).get( 'post' ) +// } ) + +// // Delete created post +// test.afterEach( async ( { requestUtils } ) => { +// await requestUtils.deletePost( pid ) +// } ) + +// test( 'Stackable blocks can be added in the editor', async ( { +// page, + +// editor, +// } ) => { +// await page.getByLabel( 'Toggle block inserter' ).click() + +// await page.locator( '.editor-block-list-item-stackable-text' ).click() + +// const blocks = await editor.getBlocks() + +// expect( blocks[ 0 ].name ).toContain( 'stackable/text' ) +// } ) + +// test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { +// page, +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// } ) + +// await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) +// await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() +// await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() +// await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() +// } ) + +// test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { +// page, +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// } ) + +// // Add content and color to Stackable Text Block +// await editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ).fill( 'test' ) +// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() +// await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) +// await editor.canvas.locator( 'body' ).click() + +// await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) +// await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + +// await editor.saveDraft() + +// const blocks = await editor.getBlocks() +// const attributes = blocks[ 0 ].attributes + +// expect( attributes.textColor1 ).toBe( '#ff0000' ) +// expect( attributes.text ).toBe( 'test' ) +// } ) + +// test( 'The Stackable block added in the editor should be visible in the frontend', async ( { +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// attributes: { +// text: 'test', +// textColor1: '#ff0000', +// }, +// } ) + +// const blocks = await editor.getBlocks() +// const uniqueId = blocks[ 0 ].attributes.uniqueId + +// await editor.saveDraft() + +// const preview = await editor.openPreviewPage() + +// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() +// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) +// await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) +// } ) +// } ) diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts index 4ee9451c05..05fc4865cf 100644 --- a/e2e/tests/global-settings.spec.ts +++ b/e2e/tests/global-settings.spec.ts @@ -1,127 +1,127 @@ -import { test, expect } from 'e2e/test-utils' -import { createColor, getRgb } from '~stackable/plugins/global-settings/colors/util' - -test.describe( 'Global Settigs', () => { - let pid = null - - // Create Posts for testing - test.beforeEach( async ( { editor, admin } ) => { - await admin.createNewPost( { title: 'Global Settings Test' } ) - await editor.saveDraft() - const postQuery = new URL( editor.page.url() ).search - pid = new URLSearchParams( postQuery ).get( 'post' ) - } ) - - // Delete created post - test.afterEach( async ( { requestUtils } ) => { - await requestUtils.deletePost( pid ) - } ) - - test( 'When a color is added in the Global Colors, it should be present in the color picker', async ( { - page, - editor, - } ) => { - await page.getByLabel( 'Stackable Settings' ).click() - - // Add a new Global Color - const panel = page.locator( '.ugb-global-settings-color-picker ' ).filter( { hasText: 'Global Colors' } ) - await panel.locator( 'button.ugb-global-settings-color-picker__add-button' ).click() - - const globalColors = panel.locator( '.ugb-global-settings-color-picker__color-indicators > div' ) - const count = ( await globalColors.evaluate( node => Array.from( node.childNodes ) ) ).length - - const newColor = globalColors.getByRole( 'button', { name: `Custom Color ${ count } ` } ) - await expect( newColor ).toBeVisible() - await newColor.click() - const hexValue = await page.getByLabel( 'Hex color' ).inputValue() - - // Insert a Stackable Text Block and check if the added Global Colors is in the color picker - await page.getByLabel( 'Settings', { exact: true } ).click() - editor.insertBlock( { - name: 'stackable/text', - attributes: { - text: 'test', - }, - } ) - await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - - await expect( page.getByRole( 'heading', { name: 'Global Colors' } ) ).toBeVisible() - await expect( page.getByLabel( `Color: Custom Color ${ count }` ) ).toBeVisible() - await page.getByLabel( `Color: Custom Color ${ count }` ).click() - await expect( page.getByLabel( 'Hex color' ) ).toHaveValue( hexValue ) - await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - - // Delete added Global Color - await page.getByLabel( 'Stackable Settings' ).click() - await panel.locator( `.ugb-global-settings-color-picker__color-indicators > div > div:nth-child(${ count }) > button.stk-global-settings-color-picker__delete-button` ).click() - } ) - - test( 'Global Typography Styles should be applied when adding a heading', async ( { - page, - editor, - } ) => { - await page.getByLabel( 'Stackable Settings' ).click() - await page.getByRole( 'button', { name: 'Global Typography' } ).click() - - // Set Global Typography Styles of Heading 2 to have a font-size of 32 - await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() - await page.locator( '.stk-popover .components-base-control:nth-of-type(2)', { hasText: /Size/ } ).getByRole( 'textbox' ).fill( '32' ) - await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() - await expect( page.getByRole( 'heading', { name: 'Heading 2' } ) ).toHaveCSS( 'font-size', '32px' ) - await page.getByLabel( 'Settings', { exact: true } ).click() - - // Check if the added Stackable Heading Block has a font-size of 32 - editor.insertBlock( { - name: 'stackable/heading', - attributes: { - text: 'test', - }, - } ) - - await expect( editor.canvas.locator( '[data-type="stackable/heading"] > .stk-block-heading > h2[role="textbox"]' ) ).toHaveCSS( 'font-size', '32px' ) - - // Reset Global Typography Styles - await page.getByLabel( 'Stackable Settings' ).click() - await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).getByRole( 'button', { name: 'Reset' } ).click() - } ) - - test( 'When a default block is created, adding the block should have the default block\'s attributes', async ( { - page, - editor, - } ) => { - // Generate a color - const color = createColor() - - await page.getByLabel( 'Stackable Settings' ).click() - await page.getByRole( 'button', { name: 'Block Defaults' } ).click() - - // Open the Default Text Block Editor - const defaultBlockPagePromise = page.waitForEvent( 'popup' ) - await page.locator( 'div:nth-child(37) > .components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() - const defaultBlockPage = await defaultBlockPagePromise - - // Set a color for the default Text Block - await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - await defaultBlockPage.getByLabel( 'Hex color' ).fill( color ) - await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - await defaultBlockPage.getByRole( 'button', { name: 'Save', exact: true } ).click() - await defaultBlockPage.close() - - // Insert a Stackable Text Block, and check if the color is the same as the one set in the default block - await page.reload() - await editor.insertBlock( { - name: 'stackable/text', - attributes: { - text: 'test', - }, - } ) - - await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', `rgb(${ getRgb( color ) })` ) - - // Reset Default Block - await page.getByLabel( 'Stackable Settings' ).click() - await page.getByRole( 'button', { name: 'Block Defaults' } ).click() - await page.locator( 'div' ).filter( { hasText: /^Text$/ } ).first().getByLabel( 'Reset' ).click() - } ) -} ) +// import { test, expect } from 'e2e/test-utils' +// import { createColor, getRgb } from '~stackable/plugins/global-settings/colors/util' + +// test.describe( 'Global Settigs', () => { +// let pid = null + +// // Create Posts for testing +// test.beforeEach( async ( { editor, admin } ) => { +// await admin.createNewPost( { title: 'Global Settings Test' } ) +// await editor.saveDraft() +// const postQuery = new URL( editor.page.url() ).search +// pid = new URLSearchParams( postQuery ).get( 'post' ) +// } ) + +// // Delete created post +// test.afterEach( async ( { requestUtils } ) => { +// await requestUtils.deletePost( pid ) +// } ) + +// test( 'When a color is added in the Global Colors, it should be present in the color picker', async ( { +// page, +// editor, +// } ) => { +// await page.getByLabel( 'Stackable Settings' ).click() + +// // Add a new Global Color +// const panel = page.locator( '.ugb-global-settings-color-picker ' ).filter( { hasText: 'Global Colors' } ) +// await panel.locator( 'button.ugb-global-settings-color-picker__add-button' ).click() + +// const globalColors = panel.locator( '.ugb-global-settings-color-picker__color-indicators > div' ) +// const count = ( await globalColors.evaluate( node => Array.from( node.childNodes ) ) ).length + +// const newColor = globalColors.getByRole( 'button', { name: `Custom Color ${ count } ` } ) +// await expect( newColor ).toBeVisible() +// await newColor.click() +// const hexValue = await page.getByLabel( 'Hex color' ).inputValue() + +// // Insert a Stackable Text Block and check if the added Global Colors is in the color picker +// await page.getByLabel( 'Settings', { exact: true } ).click() +// editor.insertBlock( { +// name: 'stackable/text', +// attributes: { +// text: 'test', +// }, +// } ) +// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + +// await expect( page.getByRole( 'heading', { name: 'Global Colors' } ) ).toBeVisible() +// await expect( page.getByLabel( `Color: Custom Color ${ count }` ) ).toBeVisible() +// await page.getByLabel( `Color: Custom Color ${ count }` ).click() +// await expect( page.getByLabel( 'Hex color' ) ).toHaveValue( hexValue ) +// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + +// // Delete added Global Color +// await page.getByLabel( 'Stackable Settings' ).click() +// await panel.locator( `.ugb-global-settings-color-picker__color-indicators > div > div:nth-child(${ count }) > button.stk-global-settings-color-picker__delete-button` ).click() +// } ) + +// test( 'Global Typography Styles should be applied when adding a heading', async ( { +// page, +// editor, +// } ) => { +// await page.getByLabel( 'Stackable Settings' ).click() +// await page.getByRole( 'button', { name: 'Global Typography' } ).click() + +// // Set Global Typography Styles of Heading 2 to have a font-size of 32 +// await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() +// await page.locator( '.stk-popover .components-base-control:nth-of-type(2)', { hasText: /Size/ } ).getByRole( 'textbox' ).fill( '32' ) +// await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() +// await expect( page.getByRole( 'heading', { name: 'Heading 2' } ) ).toHaveCSS( 'font-size', '32px' ) +// await page.getByLabel( 'Settings', { exact: true } ).click() + +// // Check if the added Stackable Heading Block has a font-size of 32 +// editor.insertBlock( { +// name: 'stackable/heading', +// attributes: { +// text: 'test', +// }, +// } ) + +// await expect( editor.canvas.locator( '[data-type="stackable/heading"] > .stk-block-heading > h2[role="textbox"]' ) ).toHaveCSS( 'font-size', '32px' ) + +// // Reset Global Typography Styles +// await page.getByLabel( 'Stackable Settings' ).click() +// await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).getByRole( 'button', { name: 'Reset' } ).click() +// } ) + +// test( 'When a default block is created, adding the block should have the default block\'s attributes', async ( { +// page, +// editor, +// } ) => { +// // Generate a color +// const color = createColor() + +// await page.getByLabel( 'Stackable Settings' ).click() +// await page.getByRole( 'button', { name: 'Block Defaults' } ).click() + +// // Open the Default Text Block Editor +// const defaultBlockPagePromise = page.waitForEvent( 'popup' ) +// await page.locator( 'div:nth-child(37) > .components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() +// const defaultBlockPage = await defaultBlockPagePromise + +// // Set a color for the default Text Block +// await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() +// await defaultBlockPage.getByLabel( 'Hex color' ).fill( color ) +// await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() +// await defaultBlockPage.getByRole( 'button', { name: 'Save', exact: true } ).click() +// await defaultBlockPage.close() + +// // Insert a Stackable Text Block, and check if the color is the same as the one set in the default block +// await page.reload() +// await editor.insertBlock( { +// name: 'stackable/text', +// attributes: { +// text: 'test', +// }, +// } ) + +// await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', `rgb(${ getRgb( color ) })` ) + +// // Reset Default Block +// await page.getByLabel( 'Stackable Settings' ).click() +// await page.getByRole( 'button', { name: 'Block Defaults' } ).click() +// await page.locator( 'div' ).filter( { hasText: /^Text$/ } ).first().getByLabel( 'Reset' ).click() +// } ) +// } ) From b6706e2ea3810c22afc818d7793368d799cdc780 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 03:59:08 +0800 Subject: [PATCH 38/76] run mto even on fail --- .github/workflows/playwright.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 9e8e00b075..0268aefcd2 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -36,6 +36,7 @@ jobs: run: npm run test - uses: markpatterson27/markdown-to-output@v1 id: mto + if: ${{ !cancelled() }} with: filepath: ./playwright-errors/errors.md - uses: actions/upload-artifact@v4 From 926fdb02cf2922e4f9c85e07cb15c9a2f1c5ae41 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 04:15:34 +0800 Subject: [PATCH 39/76] strip Ansi Escapes --- e2e/config/reporter.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 945949c5ef..050d9b3f4f 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -6,6 +6,8 @@ import type { import fs from 'fs' import path from 'path' +const ansiRegex = new RegExp( '([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))', 'g' ) + class MyReporter implements Reporter { outputFolder: string; testResults: Array; @@ -16,12 +18,8 @@ class MyReporter implements Reporter { this.testResults = [] } - removeColorCodes( input : string ) { - return input.replace( /\[[0-9;]*m/g, '' ) - } - - escapeSpecialCharacters( input: string ) { - return input.replace( /([\'\"\&\{\}])/g, '\\$1' ) + stripAnsiEscapes( str: string ): string { + return str.replace( ansiRegex, '' ) } cleanupFolder() { @@ -52,13 +50,13 @@ class MyReporter implements Reporter { ` result.errors.forEach( error => { if ( error.message ) { - testResult += `${ this.removeColorCodes( error.message ) } + testResult += `${ error.message } ` } if ( error.snippet ) { - testResult += `${ this.removeColorCodes( error.snippet ) } + testResult += `${ error.snippet } ` } @@ -87,7 +85,7 @@ class MyReporter implements Reporter { ${ this.testResults.join( '' ) }` - reportContent = this.escapeSpecialCharacters( reportContent ) + reportContent = this.stripAnsiEscapes( reportContent ) } fs.writeFileSync( reportPath, reportContent ) From 643dfeed06ecce556fc3c59a5bc07eb98b218274 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 11:35:34 +0800 Subject: [PATCH 40/76] try multiple upload --- .github/workflows/playwright.yml | 21 +++++--- e2e/tests/site-editor.spec.ts | 92 ++++++++++++++++---------------- 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 0268aefcd2..03cf016dc9 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -28,17 +28,26 @@ jobs: run: wp-env start - name: Run playwright tests env: - WP_BASE_URL: http://localhost:8888 - WP_AUTH_STORAGE: wp-auth.json - WP_USERNAME: admin - WP_PASSWORD: password - STACKABLE_SLUG: Stackable/plugin + WP_BASE_URL: http://localhost:8888 + WP_AUTH_STORAGE: wp-auth.json + WP_USERNAME: admin + WP_PASSWORD: password + STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: markpatterson27/markdown-to-output@v1 id: mto if: ${{ !cancelled() }} with: - filepath: ./playwright-errors/errors.md + filepath: ./playwright-errors/errors.md + - uses: initdc/upload-artifact@baac12d9f85834b2f8baa26a08ff97c27cc82cd9 + id: upload-traces + if: ${{ !cancelled() }} + with: + artifact-per-file: true + artifact-name-rule: ${dir}-${name}${ext} + path: | + test-results/*/trace.zip + retention-days: 1 - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step diff --git a/e2e/tests/site-editor.spec.ts b/e2e/tests/site-editor.spec.ts index eb82c0352f..970fec3dce 100644 --- a/e2e/tests/site-editor.spec.ts +++ b/e2e/tests/site-editor.spec.ts @@ -55,51 +55,51 @@ test.describe( 'Site Editor', () => { await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() } ) - test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { - page, - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - }, { clientId: postContentBlock.clientId } ) - await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) - await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) - await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) - await editor.canvas.locator( 'body' ).click() - - await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) - await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - - await editor.saveDraft() - - const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - const textBlock = blocks.find( block => block.name === 'stackable/text' ) - expect( textBlock.attributes.text ).toBe( 'test' ) - expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) - } ) - - test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - attributes: { - text: 'test', - textColor1: '#ff0000', - }, - }, { clientId: postContentBlock.clientId } ) - - const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId - - await editor.saveDraft() - - const preview = await editor.openPreviewPage() - - await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() - await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) - await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - } ) + // test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { + // page, + // editor, + // } ) => { + // await editor.insertBlock( { + // name: 'stackable/text', + // }, { clientId: postContentBlock.clientId } ) + // await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) + // await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) + // await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + // await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) + // await editor.canvas.locator( 'body' ).click() + + // await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) + // await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + + // await editor.saveDraft() + + // const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + // const textBlock = blocks.find( block => block.name === 'stackable/text' ) + // expect( textBlock.attributes.text ).toBe( 'test' ) + // expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) + // } ) + + // test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { + // editor, + // } ) => { + // await editor.insertBlock( { + // name: 'stackable/text', + // attributes: { + // text: 'test', + // textColor1: '#ff0000', + // }, + // }, { clientId: postContentBlock.clientId } ) + + // const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + // const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId + + // await editor.saveDraft() + + // const preview = await editor.openPreviewPage() + + // await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() + // await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) + // await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + // } ) } ) From b15d33a9fbe5063c15151efce09f1fb1af768d71 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 11:52:42 +0800 Subject: [PATCH 41/76] test multiple upload --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 03cf016dc9..008fe4e896 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -44,7 +44,7 @@ jobs: if: ${{ !cancelled() }} with: artifact-per-file: true - artifact-name-rule: ${dir}-${name}${ext} + artifact-name-rule: ${name}${ext} path: | test-results/*/trace.zip retention-days: 1 From 74eda4b9296e6772140b0ce83d5cb5f9d6bde3c8 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 12:25:31 +0800 Subject: [PATCH 42/76] use test ids and trace file path --- .github/workflows/playwright.yml | 16 +++++++++++----- e2e/config/reporter.ts | 16 ++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 008fe4e896..c86aea3785 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -39,14 +39,20 @@ jobs: if: ${{ !cancelled() }} with: filepath: ./playwright-errors/errors.md - - uses: initdc/upload-artifact@baac12d9f85834b2f8baa26a08ff97c27cc82cd9 + - name: Get trace files + id: get-trace-files + if: ${{ !cancelled() }} + run: | + IDS=$(cat ./playwright-errors/testIds.json) + TRACES=$(cat ./playwright-errors/traceFiles.json) + echo "testIds=$IDS" >> $GITHUB_OUTPUT + echo "traceFiles=$TRACES" >> $GITHUB_OUTPUT + - uses: BToersche/upload-artifact@5442a4e8a3867a1c14be1ee3a04c4f47b261d632 id: upload-traces if: ${{ !cancelled() }} with: - artifact-per-file: true - artifact-name-rule: ${name}${ext} - path: | - test-results/*/trace.zip + name: '${{ steps.get-trace-files.outputs.testIds }}' + path: '${{ steps.get-trace-files.outputs.traceFiles }}' retention-days: 1 - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 050d9b3f4f..6f07833005 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -11,11 +11,15 @@ const ansiRegex = new RegExp( '([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d] class MyReporter implements Reporter { outputFolder: string; testResults: Array; + testIds: Array; + traceFiles: Array; constructor() { this.outputFolder = 'playwright-errors' this.cleanupFolder() this.testResults = [] + this.testIds = [] + this.traceFiles = [] } stripAnsiEscapes( str: string ): string { @@ -67,6 +71,8 @@ class MyReporter implements Reporter { } this.testResults.push( testResult ) + this.testIds.push( test.id ) + this.traceFiles.push( result.attachments.find( attachment => attachment.name === 'trace' ).path ) } } @@ -80,15 +86,25 @@ class MyReporter implements Reporter { // Write the collected results to a JSON file const reportPath = path.join( folderPath, 'errors.md' ) let reportContent = '' + + const testIdsPath = path.join( folderPath, 'testIds.json' ) + let testIdsContent = '' + + const traceFilesPath = path.join( folderPath, 'traceFiles.json' ) + let traceFilesContent = '' if ( this.testResults.length ) { reportContent += `## Failed Tests ${ this.testResults.join( '' ) }` reportContent = this.stripAnsiEscapes( reportContent ) + testIdsContent = JSON.stringify( this.testIds, null, 2 ) + traceFilesContent = JSON.stringify( this.traceFiles, null, 2 ) } fs.writeFileSync( reportPath, reportContent ) + fs.writeFileSync( testIdsPath, testIdsContent ) + fs.writeFileSync( traceFilesPath, traceFilesContent ) } } export default MyReporter From 7cad56016c4ee4b3a269a8db7932842e7ae8e8f8 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 12:33:47 +0800 Subject: [PATCH 43/76] update reporter --- e2e/config/reporter.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 6f07833005..cfc2708852 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -87,10 +87,10 @@ class MyReporter implements Reporter { const reportPath = path.join( folderPath, 'errors.md' ) let reportContent = '' - const testIdsPath = path.join( folderPath, 'testIds.json' ) + const testIdsPath = path.join( folderPath, 'testIds.txt' ) let testIdsContent = '' - const traceFilesPath = path.join( folderPath, 'traceFiles.json' ) + const traceFilesPath = path.join( folderPath, 'traceFiles.txt' ) let traceFilesContent = '' if ( this.testResults.length ) { reportContent += `## Failed Tests @@ -98,8 +98,8 @@ class MyReporter implements Reporter { ${ this.testResults.join( '' ) }` reportContent = this.stripAnsiEscapes( reportContent ) - testIdsContent = JSON.stringify( this.testIds, null, 2 ) - traceFilesContent = JSON.stringify( this.traceFiles, null, 2 ) + testIdsContent = JSON.stringify( this.testIds ) + traceFilesContent = JSON.stringify( this.traceFiles ) } fs.writeFileSync( reportPath, reportContent ) From 1d94db4965fa8e2b18918b5a048068549594eda1 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 13:11:10 +0800 Subject: [PATCH 44/76] test matrix --- .github/workflows/playwright.yml | 62 +++++++++++++++++++++++--------- .wp-env.json | 7 ---- 2 files changed, 45 insertions(+), 24 deletions(-) delete mode 100644 .wp-env.json diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index c86aea3785..6a3f5b5980 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -8,6 +8,27 @@ jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest + strategy: + matrix: + include: + # - php_version: 5.6 + # wp_version: null + + - php_version: 7.3 + wp_version: null + + # - php_version: 8.2 + # wp_version: 6.4.5 + + # - php_version: 8.2 + # wp_version: 6.5.5 + + - php_version: 8.2 + wp_version: 6.6.2 + + - php_version: 8.2 + wp_version: null + steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -15,6 +36,28 @@ jobs: node-version: lts/* - name: Set the version suffix for the output run: echo VERSION_SUFFIX=${GITHUB_REF_NAME//\//-} >> $GITHUB_ENV + - name: Set WP Version and create .wp-env.json + run: | + WP_VERSION="${{ matrix.wp_version }}" + core="Wordpress/Wordpress#${{ matrix.wp_version }}" + + if [[ -z "$WP_VERSION" || "$WP_VERSION" == "null" ]]; then + WP_VERSION="Latest" + core=null + fi + + echo "WP_VERSION=$WP_VERSION" >> $GITHUB_ENV + + echo '{ + "core": $core, + "phpVersion": "${{ matrix.php_version }}" + "plugins": [ "." ], + "config": { + "SCRIPT_DEBUG": false + } + }' > .wp-env.json + + cat .wp-env.json - name: Build Stackable Free Plugin run: | npm ci --legacy-peer-deps @@ -39,21 +82,6 @@ jobs: if: ${{ !cancelled() }} with: filepath: ./playwright-errors/errors.md - - name: Get trace files - id: get-trace-files - if: ${{ !cancelled() }} - run: | - IDS=$(cat ./playwright-errors/testIds.json) - TRACES=$(cat ./playwright-errors/traceFiles.json) - echo "testIds=$IDS" >> $GITHUB_OUTPUT - echo "traceFiles=$TRACES" >> $GITHUB_OUTPUT - - uses: BToersche/upload-artifact@5442a4e8a3867a1c14be1ee3a04c4f47b261d632 - id: upload-traces - if: ${{ !cancelled() }} - with: - name: '${{ steps.get-trace-files.outputs.testIds }}' - path: '${{ steps.get-trace-files.outputs.traceFiles }}' - retention-days: 1 - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step @@ -69,7 +97,7 @@ jobs: with: issue-number: ${{ github.event.pull_request.number }} comment-author: 'github-actions[bot]' - body-includes: Playwright Report Artifact + body-includes: Playwright Report Artifact for PHP ${{ matrix.php_version }} and WP ${{ env.WP_VERSION }} - name: Create or update comment uses: peter-evans/create-or-update-comment@v4 if: ${{ !cancelled() }} @@ -78,7 +106,7 @@ jobs: issue-number: ${{ github.event.pull_request.number }} body: | ${{ steps.mto.outputs.body }} - ## Playwright Report Artifact + ## Playwright Report Artifact for PHP ${{ matrix.php_version }} and WP ${{ env.WP_VERSION }} | file | commit | | ---- | ------ | | [playwright-report-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}) | ${{ github.sha }} | diff --git a/.wp-env.json b/.wp-env.json deleted file mode 100644 index 6b24e5bc31..0000000000 --- a/.wp-env.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "core": null, - "plugins": [ "." ], - "config": { - "SCRIPT_DEBUG": false - } -} From 03a2c7e0123b752f9be19b8b989694e7f514a33a Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 13:22:54 +0800 Subject: [PATCH 45/76] update .wp-env.json and name --- .github/workflows/playwright.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 6a3f5b5980..4609e7f194 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -29,6 +29,7 @@ jobs: - php_version: 8.2 wp_version: null + name: Playwright tests on PHP ${{ matrix.php_version }} and WP ${{ matrix.wp_version || 'latest' }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -39,22 +40,22 @@ jobs: - name: Set WP Version and create .wp-env.json run: | WP_VERSION="${{ matrix.wp_version }}" - core="Wordpress/Wordpress#${{ matrix.wp_version }}" + core='"Wordpress/Wordpress#'${{ matrix.wp_version }}'"' if [[ -z "$WP_VERSION" || "$WP_VERSION" == "null" ]]; then - WP_VERSION="Latest" + WP_VERSION="latest" core=null fi echo "WP_VERSION=$WP_VERSION" >> $GITHUB_ENV echo '{ - "core": $core, - "phpVersion": "${{ matrix.php_version }}" - "plugins": [ "." ], - "config": { - "SCRIPT_DEBUG": false - } + "core": '$core', + "phpVersion": "7.5", + "plugins": [ "." ], + "config": { + "SCRIPT_DEBUG": false + } }' > .wp-env.json cat .wp-env.json From 93e5821711a27d1c54ff812b0486ea077bfb69cf Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 13:26:26 +0800 Subject: [PATCH 46/76] update php version, remove unit tests --- .github/workflows/js-unit-test.yml | 45 ----------------- .github/workflows/php-5.6.39.yml | 79 ----------------------------- .github/workflows/php-7.3.5.yml | 80 ------------------------------ .github/workflows/php-8.0.0.yml | 75 ---------------------------- .github/workflows/playwright.yml | 4 +- 5 files changed, 2 insertions(+), 281 deletions(-) delete mode 100644 .github/workflows/js-unit-test.yml delete mode 100644 .github/workflows/php-5.6.39.yml delete mode 100644 .github/workflows/php-7.3.5.yml delete mode 100644 .github/workflows/php-8.0.0.yml diff --git a/.github/workflows/js-unit-test.yml b/.github/workflows/js-unit-test.yml deleted file mode 100644 index 3d3082a52f..0000000000 --- a/.github/workflows/js-unit-test.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Unit Tests / JS -on: - push: - branches: [ master, develop ] - pull_request: - branches: [ master, develop ] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [14.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - uses: actions/checkout@v2 - - name: Install Composer Dependencies - run: | - composer install --prefer-dist --no-progress --ignore-platform-reqs - - - name: Cache node modules - uses: actions/cache@v2 - env: - cache-name: cache-node-modules - with: - path: ~/.npm - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - - name: Install NPM Dependencies - run: | - npm ci - - - name: Run Test Suite - run: | - npm test -- --watchAll=false - diff --git a/.github/workflows/php-5.6.39.yml b/.github/workflows/php-5.6.39.yml deleted file mode 100644 index 3a3b46ad13..0000000000 --- a/.github/workflows/php-5.6.39.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Unit Tests / PHP (v5.6.39) - -on: - push: - branches: [ master, develop ] - pull_request: - branches: [ master, develop ] - -jobs: - build: - - # Use Ubuntu 20.04. - runs-on: ubuntu-20.04 - strategy: - matrix: - wp-versions: ['6.3.5'] - - steps: - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 # Checkout the Stackable Premium repo. - with: - repository: 'bfintal/Stackable-Premium' - ref: 'v3' - path: 'pro__premium_only' - token: '${{ secrets.ACCESS_KEY }}' - - # Change the PHP version to v5.6.39 - - name: Setup PHP v5.6.39 - uses: shivammathur/setup-php@v2 - with: - php-version: '5.6.39' - - # Check the current PHP version installed - - name: Check PHP version - run: php -v - - - name: Install dependencies - run: | - composer require phpunit/phpunit:5.6.8 --dev - composer require wp-phpunit/wp-phpunit:${{ matrix.wp-versions }} --dev - composer require roots/wordpress:${{ matrix.wp-versions }} --dev - composer install --prefer-dist --no-progress - - - name: Setup Node - uses: actions/setup-node@v1 - with: - node-version: 14.x - - - name: Build Stackable Free Plugin - run: | - npm ci - npm run build --if-present - - - name: Setup MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql database: 'stackable' # Optional, default value is "test". The specified database which will be create - mysql root password: 'root' # Required if "mysql user" is empty, default is empty. The root superuser password - mysql version: '5.6' - mysql user: 'root' # Required if "mysql root password" is empty, default is empty. The superuser for the specified database. Can use secrets, too - mysql password: 'root' # Required if "mysql user" exists. The password for the "mysql user" - - # Wait for MySQL server to initialize. - - name: Wait for MySQL - run: | - while ! mysqladmin ping --host=127.0.0.1 --password=root --silent; do - sleep 1 - done - - # Run all tests. - - name: Run test suite - env: - MYSQL_GITHUB_ACTION: true - MYSQL_DATABASE: stackable - MYSQL_USER: root - MYSQL_PASSWORD: root - MYSQL_ROOT_PASSWORD: root - MYSQL_PORT: 3306 - run: composer run-script test diff --git a/.github/workflows/php-7.3.5.yml b/.github/workflows/php-7.3.5.yml deleted file mode 100644 index 641c292cce..0000000000 --- a/.github/workflows/php-7.3.5.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Unit Tests / PHP (v7.3.5) - -on: - push: - branches: [ master, develop ] - pull_request: - branches: [ master, develop ] - -jobs: - build: - - # Use Ubuntu 18.04. - runs-on: ubuntu-18.04 - strategy: - matrix: - wp-versions: ['6.3.5'] - - steps: - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 # Checkout the Stackable Premium repo. - with: - repository: 'bfintal/Stackable-Premium' - ref: 'v3' - path: 'pro__premium_only' - token: '${{ secrets.ACCESS_KEY }}' - - # Change the PHP version to v7.3.5 - - name: Setup PHP v7.3.5 - uses: shivammathur/setup-php@v2 - with: - php-version: '7.3.5' - - # Check the current PHP version installed - - name: Check PHP version - run: php -v - - # Install all dependencies from composer.json - - name: Install dependencies - run: | - composer require phpunit/phpunit:5.6.8 --dev - composer require wp-phpunit/wp-phpunit:${{ matrix.wp-versions }} --dev - composer require roots/wordpress:${{ matrix.wp-versions }} --dev - composer install --prefer-dist --no-progress - - - name: Setup Node - uses: actions/setup-node@v1 - with: - node-version: 14.x - - - name: Build Stackable Free Plugin - run: | - npm ci - npm run build --if-present - - - name: Setup MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql database: 'stackable' # Optional, default value is "test". The specified database which will be create - mysql root password: 'root' # Required if "mysql user" is empty, default is empty. The root superuser password - mysql version: '5.6' - mysql user: 'root' # Required if "mysql root password" is empty, default is empty. The superuser for the specified database. Can use secrets, too - mysql password: 'root' # Required if "mysql user" exists. The password for the "mysql user" - - # Wait for MySQL server to initialize. - - name: Wait for MySQL - run: | - while ! mysqladmin ping --host=127.0.0.1 --password=root --silent; do - sleep 1 - done - - # Run all tests. - - name: Run test suite - env: - MYSQL_GITHUB_ACTION: true - MYSQL_DATABASE: stackable - MYSQL_USER: root - MYSQL_PASSWORD: root - MYSQL_ROOT_PASSWORD: root - MYSQL_PORT: 3306 - run: composer run-script test diff --git a/.github/workflows/php-8.0.0.yml b/.github/workflows/php-8.0.0.yml deleted file mode 100644 index 9f01dc60a2..0000000000 --- a/.github/workflows/php-8.0.0.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: Unit Tests / PHP (v8.0.0) - -on: - push: - branches: [ master, develop ] - pull_request: - branches: [ master, develop ] - -jobs: - build: - - runs-on: ubuntu-20.04 - strategy: - matrix: - wp-versions: ['6.3.5'] - - steps: - - uses: actions/checkout@v2 - - uses: actions/checkout@v2 # Checkout the Stackable Premium repo. - with: - repository: 'bfintal/Stackable-Premium' - ref: 'v3' - path: 'pro__premium_only' - token: '${{ secrets.ACCESS_KEY }}' - - # Change the PHP version to v8.0.0 - - name: Setup PHP v8.0.0 - uses: shivammathur/setup-php@v2 - with: - php-version: '8.0.0' - - - name: Check PHP version - run: php -v - - - name: Install dependencies - run: | - composer require phpunit/phpunit:7.5.20 --dev --ignore-platform-reqs - composer require wp-phpunit/wp-phpunit:${{ matrix.wp-versions }} --dev --ignore-platform-reqs - composer require roots/wordpress:${{ matrix.wp-versions }} --dev --ignore-platform-reqs - composer install --prefer-dist --no-progress --ignore-platform-reqs - - - name: Setup Node - uses: actions/setup-node@v1 - with: - node-version: 14.x - - - name: Build Stackable Free Plugin - run: | - npm ci - npm run build --if-present - - - name: Setup MySQL - uses: mirromutth/mysql-action@v1.1 - with: - mysql database: 'stackable' # Optional, default value is "test". The specified database which will be create - mysql root password: 'root' # Required if "mysql user" is empty, default is empty. The root superuser password - mysql version: '5.6' - mysql user: 'root' # Required if "mysql root password" is empty, default is empty. The superuser for the specified database. Can use secrets, too - mysql password: 'root' # Required if "mysql user" exists. The password for the "mysql user" - - - name: Wait for MySQL - run: | - while ! mysqladmin ping --host=127.0.0.1 --password=root --silent; do - sleep 1 - done - - - name: Run test suite - env: - MYSQL_GITHUB_ACTION: true - MYSQL_DATABASE: stackable - MYSQL_USER: root - MYSQL_PASSWORD: root - MYSQL_ROOT_PASSWORD: root - MYSQL_PORT: 3306 - run: composer run-script test diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 4609e7f194..19b1a9326f 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -29,7 +29,7 @@ jobs: - php_version: 8.2 wp_version: null - name: Playwright tests on PHP ${{ matrix.php_version }} and WP ${{ matrix.wp_version || 'latest' }} + name: PHP ${{ matrix.php_version }} and WP ${{ matrix.wp_version || 'latest' }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 @@ -51,7 +51,7 @@ jobs: echo '{ "core": '$core', - "phpVersion": "7.5", + "phpVersion": "${{ matrix.php_version }}", "plugins": [ "." ], "config": { "SCRIPT_DEBUG": false From e40aa820af5219d087debe8467b6388193ad37cb Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 13:34:57 +0800 Subject: [PATCH 47/76] continue matrix even if one job fails --- .github/workflows/playwright.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 19b1a9326f..502eedd397 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -9,6 +9,7 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest strategy: + fail-fast: false # Ensures the matrix doesn't stop if one job fails matrix: include: # - php_version: 5.6 @@ -106,9 +107,9 @@ jobs: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body: | - ${{ steps.mto.outputs.body }} ## Playwright Report Artifact for PHP ${{ matrix.php_version }} and WP ${{ env.WP_VERSION }} | file | commit | | ---- | ------ | | [playwright-report-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}) | ${{ github.sha }} | + ${{ steps.mto.outputs.body }} edit-mode: replace From 7bf5aa1a780b7df3ce6aacea0fd030d3175825a0 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 13:47:34 +0800 Subject: [PATCH 48/76] use different artifact name based on matrix --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 502eedd397..35aad24e58 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -88,7 +88,7 @@ jobs: if: ${{ !cancelled() }} id: artifact-upload-step with: - name: playwright-report-${{ env.VERSION_SUFFIX }} + name: playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }} path: playwright-report/ overwrite: true retention-days: 30 From e5f53b44ffb6d820daebc7ca64549c97aa8500a9 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 14:18:19 +0800 Subject: [PATCH 49/76] try all test env --- .github/workflows/playwright.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 35aad24e58..ce5f76bcc7 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -12,17 +12,17 @@ jobs: fail-fast: false # Ensures the matrix doesn't stop if one job fails matrix: include: - # - php_version: 5.6 - # wp_version: null + - php_version: 5.6 + wp_version: null - php_version: 7.3 wp_version: null - # - php_version: 8.2 - # wp_version: 6.4.5 + - php_version: 8.2 + wp_version: 6.4.5 - # - php_version: 8.2 - # wp_version: 6.5.5 + - php_version: 8.2 + wp_version: 6.5.5 - php_version: 8.2 wp_version: 6.6.2 @@ -65,7 +65,7 @@ jobs: npm ci --legacy-peer-deps npm run build:no-translate - name: Install Playwright Browsers - run: npx playwright install chromium firefox webkit --with-deps + run: npx playwright install chromium --with-deps - name: Install wp-env run: | npm install -g @wordpress/env @@ -73,7 +73,7 @@ jobs: run: wp-env start - name: Run playwright tests env: - WP_BASE_URL: http://localhost:8888 + WP_BASE_URL: http://localhost:8889 WP_AUTH_STORAGE: wp-auth.json WP_USERNAME: admin WP_PASSWORD: password From a2c9632f3fa9627cc8ac30476d3be18fb8b24ad6 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 14:40:38 +0800 Subject: [PATCH 50/76] remove comments, update matrix --- .github/workflows/playwright.yml | 37 +++-------- e2e/config/reporter.ts | 110 ------------------------------- e2e/playwright.config.ts | 4 +- 3 files changed, 10 insertions(+), 141 deletions(-) delete mode 100644 e2e/config/reporter.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index ce5f76bcc7..52cec2959a 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -12,8 +12,8 @@ jobs: fail-fast: false # Ensures the matrix doesn't stop if one job fails matrix: include: - - php_version: 5.6 - wp_version: null + - php_version: 7.0.0 + wp_version: 6.4 - php_version: 7.3 wp_version: null @@ -71,6 +71,13 @@ jobs: npm install -g @wordpress/env - name: Start wp-env run: wp-env start + - name: Get PHP and WordPress versions + run: | + CONTAINER_NAME=$(docker ps --filter "ancestor=wordpress" --format "{{.Names}}") + echo "PHP Version:" + docker exec $CONTAINER_NAME php -v + echo "WordPress Version:" + docker exec $CONTAINER_NAME wp core version - name: Run playwright tests env: WP_BASE_URL: http://localhost:8889 @@ -79,11 +86,6 @@ jobs: WP_PASSWORD: password STACKABLE_SLUG: Stackable/plugin run: npm run test - - uses: markpatterson27/markdown-to-output@v1 - id: mto - if: ${{ !cancelled() }} - with: - filepath: ./playwright-errors/errors.md - uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} id: artifact-upload-step @@ -92,24 +94,3 @@ jobs: path: playwright-report/ overwrite: true retention-days: 30 - - name: Find Comment - uses: peter-evans/find-comment@v3 - if: ${{ !cancelled() }} - id: fc - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: Playwright Report Artifact for PHP ${{ matrix.php_version }} and WP ${{ env.WP_VERSION }} - - name: Create or update comment - uses: peter-evans/create-or-update-comment@v4 - if: ${{ !cancelled() }} - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - ## Playwright Report Artifact for PHP ${{ matrix.php_version }} and WP ${{ env.WP_VERSION }} - | file | commit | - | ---- | ------ | - | [playwright-report-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }}) | ${{ github.sha }} | - ${{ steps.mto.outputs.body }} - edit-mode: replace diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts deleted file mode 100644 index cfc2708852..0000000000 --- a/e2e/config/reporter.ts +++ /dev/null @@ -1,110 +0,0 @@ -/* eslint-disable no-console */ -import type { - Reporter, TestCase, TestResult, -} from '@playwright/test/reporter' - -import fs from 'fs' -import path from 'path' - -const ansiRegex = new RegExp( '([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))', 'g' ) - -class MyReporter implements Reporter { - outputFolder: string; - testResults: Array; - testIds: Array; - traceFiles: Array; - - constructor() { - this.outputFolder = 'playwright-errors' - this.cleanupFolder() - this.testResults = [] - this.testIds = [] - this.traceFiles = [] - } - - stripAnsiEscapes( str: string ): string { - return str.replace( ansiRegex, '' ) - } - - cleanupFolder() { - const folderPath = path.resolve( this.outputFolder ) - - if ( fs.existsSync( folderPath ) ) { - // Read the directory and delete files - const files = fs.readdirSync( folderPath ) - for ( const file of files ) { - const filePath = path.join( folderPath, file ) - if ( fs.lstatSync( filePath ).isFile() ) { - fs.unlinkSync( filePath ) // Remove the file - } - } - console.log( `All files removed from: ${ folderPath }` ) - } else { - // If folder doesn't exist, create it - fs.mkdirSync( folderPath, { recursive: true } ) - } - } - - onTestEnd( test: TestCase, result: TestResult ) { - if ( result.status !== test.expectedStatus ) { - let testResult = `### ${ test.title } -` - if ( result.errors.length >= 1 ) { - testResult += `\`\`\` -` - result.errors.forEach( error => { - if ( error.message ) { - testResult += `${ error.message } - -` - } - - if ( error.snippet ) { - testResult += `${ error.snippet } - -` - } - } ) - testResult += `\`\`\` - -` - } - - this.testResults.push( testResult ) - this.testIds.push( test.id ) - this.traceFiles.push( result.attachments.find( attachment => attachment.name === 'trace' ).path ) - } - } - - async onEnd() { - // Ensure the output folder exists - const folderPath = path.resolve( this.outputFolder ) - if ( ! fs.existsSync( folderPath ) ) { - fs.mkdirSync( folderPath, { recursive: true } ) - } - - // Write the collected results to a JSON file - const reportPath = path.join( folderPath, 'errors.md' ) - let reportContent = '' - - const testIdsPath = path.join( folderPath, 'testIds.txt' ) - let testIdsContent = '' - - const traceFilesPath = path.join( folderPath, 'traceFiles.txt' ) - let traceFilesContent = '' - if ( this.testResults.length ) { - reportContent += `## Failed Tests - -${ this.testResults.join( '' ) }` - - reportContent = this.stripAnsiEscapes( reportContent ) - testIdsContent = JSON.stringify( this.testIds ) - traceFilesContent = JSON.stringify( this.traceFiles ) - } - - fs.writeFileSync( reportPath, reportContent ) - fs.writeFileSync( testIdsPath, testIdsContent ) - fs.writeFileSync( traceFilesPath, traceFilesContent ) - } -} -export default MyReporter diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index bc63ffb697..bb3654e19d 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -32,9 +32,7 @@ export default defineConfig( { // workers: process.env.CI ? 1 : undefined, workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: process.env.CI - ? [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report', open: 'never' } ], [ './config/reporter.ts' ] ] - : [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report', open: 'never' } ] ], + reporter: [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report', open: 'never' } ] ], reportSlowTests: null, /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { From f310faac6026e65833e8931d49ef1d3f46fcfe96 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 14:52:59 +0800 Subject: [PATCH 51/76] update php version --- .github/workflows/playwright.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 52cec2959a..1091599bb9 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false # Ensures the matrix doesn't stop if one job fails matrix: include: - - php_version: 7.0.0 + - php_version: 7.0 wp_version: 6.4 - php_version: 7.3 @@ -73,12 +73,10 @@ jobs: run: wp-env start - name: Get PHP and WordPress versions run: | - CONTAINER_NAME=$(docker ps --filter "ancestor=wordpress" --format "{{.Names}}") - echo "PHP Version:" - docker exec $CONTAINER_NAME php -v - echo "WordPress Version:" - docker exec $CONTAINER_NAME wp core version + docker ps + wp-env logs - name: Run playwright tests + if: ${{ !cancelled() }} env: WP_BASE_URL: http://localhost:8889 WP_AUTH_STORAGE: wp-auth.json From aba59a7d859049053f377453eb40a620b9ba7709 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 16:16:21 +0800 Subject: [PATCH 52/76] update conditions, log wp and php version --- .github/workflows/playwright.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 1091599bb9..9f18d2a8af 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -71,12 +71,16 @@ jobs: npm install -g @wordpress/env - name: Start wp-env run: wp-env start - - name: Get PHP and WordPress versions + id: start-wp-env + - name: Verify PHP and WP versions running run: | - docker ps - wp-env logs + CONTAINER_ID=$(docker ps | grep 'tests-wordpress' | awk '{ print $1 }') + echo "Container ID: $CONTAINER_ID" + docker exec $CONTAINER_ID php -v + wp-env run cli grep wp_version wp-includes/version.php - name: Run playwright tests - if: ${{ !cancelled() }} + if: ${{ steps.start-wp-env.outcome != 'failure' }} + id: run-playwright-tests env: WP_BASE_URL: http://localhost:8889 WP_AUTH_STORAGE: wp-auth.json @@ -85,7 +89,7 @@ jobs: STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} + if: ${{ steps.run-playwright-tests.outcome == 'failure' }} id: artifact-upload-step with: name: playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }} From 1feb5d5e1721113bf7671a248a4a635210be2c5d Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 16:27:11 +0800 Subject: [PATCH 53/76] add quotes --- .github/workflows/playwright.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 9f18d2a8af..c722cd20e2 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -12,22 +12,22 @@ jobs: fail-fast: false # Ensures the matrix doesn't stop if one job fails matrix: include: - - php_version: 7.0 + - php_version: '7.0' wp_version: 6.4 - - php_version: 7.3 + - php_version: '7.3' wp_version: null - - php_version: 8.2 + - php_version: '8.2' wp_version: 6.4.5 - - php_version: 8.2 + - php_version: '8.2' wp_version: 6.5.5 - - php_version: 8.2 + - php_version: '8.2' wp_version: 6.6.2 - - php_version: 8.2 + - php_version: '8.2' wp_version: null name: PHP ${{ matrix.php_version }} and WP ${{ matrix.wp_version || 'latest' }} From a64cb2404ed499e3ceaf3471875477ea28801aef Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 16:56:15 +0800 Subject: [PATCH 54/76] update php version --- .github/workflows/playwright.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index c722cd20e2..6c252b4ef3 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false # Ensures the matrix doesn't stop if one job fails matrix: include: - - php_version: '7.0' + - php_version: '7.3' wp_version: 6.4 - php_version: '7.3' @@ -72,12 +72,6 @@ jobs: - name: Start wp-env run: wp-env start id: start-wp-env - - name: Verify PHP and WP versions running - run: | - CONTAINER_ID=$(docker ps | grep 'tests-wordpress' | awk '{ print $1 }') - echo "Container ID: $CONTAINER_ID" - docker exec $CONTAINER_ID php -v - wp-env run cli grep wp_version wp-includes/version.php - name: Run playwright tests if: ${{ steps.start-wp-env.outcome != 'failure' }} id: run-playwright-tests @@ -89,7 +83,7 @@ jobs: STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: actions/upload-artifact@v4 - if: ${{ steps.run-playwright-tests.outcome == 'failure' }} + if: ${{ !cancelled() }} id: artifact-upload-step with: name: playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }} From f596c50034c97497dc2c2821d2766d658f3e3b88 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 17:07:33 +0800 Subject: [PATCH 55/76] run other tests --- .github/workflows/playwright.yml | 4 +- e2e/tests/admin.spec.ts | 108 ++++++------- e2e/tests/block-editor.spec.ts | 190 +++++++++++----------- e2e/tests/global-settings.spec.ts | 252 +++++++++++++++--------------- e2e/tests/site-editor.spec.ts | 4 +- 5 files changed, 278 insertions(+), 280 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 6c252b4ef3..1ab800433f 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -71,9 +71,7 @@ jobs: npm install -g @wordpress/env - name: Start wp-env run: wp-env start - id: start-wp-env - name: Run playwright tests - if: ${{ steps.start-wp-env.outcome != 'failure' }} id: run-playwright-tests env: WP_BASE_URL: http://localhost:8889 @@ -83,7 +81,7 @@ jobs: STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} + if: ${{ !failure() && !cancelled() }} id: artifact-upload-step with: name: playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }} diff --git a/e2e/tests/admin.spec.ts b/e2e/tests/admin.spec.ts index ef17db8f81..a7f4c79dc6 100644 --- a/e2e/tests/admin.spec.ts +++ b/e2e/tests/admin.spec.ts @@ -1,67 +1,67 @@ -// import { test, expect } from 'e2e/test-utils' +import { test, expect } from 'e2e/test-utils' -// test( 'Activating Stackable should redirect to the Getting Started Page', async ( { -// page, -// admin, -// } ) => { -// await admin.visitAdminPage( 'plugins.php' ) +test( 'Activating Stackable should redirect to the Getting Started Page', async ( { + page, + admin, +} ) => { + await admin.visitAdminPage( 'plugins.php' ) -// const plugin = page.locator( `[data-plugin="${ process.env.STACKABLE_SLUG }.php"]` ) -// // Deactivate Stackable -// const deactivate = plugin.getByLabel( 'Deactivate Stackable -' ) -// await expect( deactivate ).toBeVisible() -// await deactivate.click() + const plugin = page.locator( `[data-plugin="${ process.env.STACKABLE_SLUG }.php"]` ) + // Deactivate Stackable + const deactivate = plugin.getByLabel( 'Deactivate Stackable -' ) + await expect( deactivate ).toBeVisible() + await deactivate.click() -// // Activate Stackable -// const activate = plugin.getByLabel( 'Activate Stackable -' ) -// await expect( activate ).toBeVisible() -// await activate.click() + // Activate Stackable + const activate = plugin.getByLabel( 'Activate Stackable -' ) + await expect( activate ).toBeVisible() + await activate.click() -// try { -// await expect( page ).toHaveURL( /stackable-getting-started/ ) -// } catch { -// await expect( page ).toHaveURL( /page=stackable/ ) -// await expect( page.getByRole( 'link', { name: 'Activate Free Version' } ) ).toBeVisible() -// await page.getByRole( 'link', { name: 'Activate Free Version' } ).click() -// await page.getByRole( 'link', { name: 'Skip', exact: true } ).click() -// await expect( page ).toHaveURL( /stackable-getting-started/ ) -// } -// } ) + try { + await expect( page ).toHaveURL( /stackable-getting-started/ ) + } catch { + await expect( page ).toHaveURL( /page=stackable/ ) + await expect( page.getByRole( 'link', { name: 'Activate Free Version' } ) ).toBeVisible() + await page.getByRole( 'link', { name: 'Activate Free Version' } ).click() + await page.getByRole( 'link', { name: 'Skip', exact: true } ).click() + await expect( page ).toHaveURL( /stackable-getting-started/ ) + } +} ) -// test( 'Stackable settings should be saved', async ( { -// page, -// admin, -// stackable, -// } ) => { -// // Start waiting for Stackable Settings JSON Response before visiting the page -// let settings = null -// settings = stackable.waitForSettings() +test( 'Stackable settings should be saved', async ( { + page, + admin, + stackable, +} ) => { + // Start waiting for Stackable Settings JSON Response before visiting the page + let settings = null + settings = stackable.waitForSettings() -// await admin.visitAdminPage( 'options-general.php?page=stackable' ) -// // Make sure all Stackable settings are loaded -// await settings + await admin.visitAdminPage( 'options-general.php?page=stackable' ) + // Make sure all Stackable settings are loaded + await settings -// // Retrieves the value of the first option, toggles it and check if the value changed -// const option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) -// const val = await option.getAttribute( 'aria-checked' ) + // Retrieves the value of the first option, toggles it and check if the value changed + const option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) + const val = await option.getAttribute( 'aria-checked' ) -// await option.click() -// const newVal = await option.getAttribute( 'aria-checked' ) + await option.click() + const newVal = await option.getAttribute( 'aria-checked' ) -// expect( newVal ).not.toBe( val ) -// await page.getByRole( 'button', { name: 'Save Changes' } ).click() + expect( newVal ).not.toBe( val ) + await page.getByRole( 'button', { name: 'Save Changes' } ).click() -// // Check if the value is correct after reloading -// settings = stackable.waitForSettings() -// await page.reload() -// await settings + // Check if the value is correct after reloading + settings = stackable.waitForSettings() + await page.reload() + await settings -// const _option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) + const _option = page.locator( '.ugb-admin-toggle-setting' ).first().getByRole( 'switch' ) -// await expect( _option ).toHaveAttribute( 'aria-checked', newVal ) + await expect( _option ).toHaveAttribute( 'aria-checked', newVal ) -// // Revert back the settings to the original value -// await _option.click() -// await page.getByRole( 'button', { name: 'Save Changes' } ).click() -// await expect( _option ).toHaveAttribute( 'aria-checked', val ) -// } ) + // Revert back the settings to the original value + await _option.click() + await page.getByRole( 'button', { name: 'Save Changes' } ).click() + await expect( _option ).toHaveAttribute( 'aria-checked', val ) +} ) diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts index 1d86be4570..724f4139e6 100644 --- a/e2e/tests/block-editor.spec.ts +++ b/e2e/tests/block-editor.spec.ts @@ -1,96 +1,96 @@ -// import { test, expect } from 'e2e/test-utils' - -// test.describe( 'Block Editor', () => { -// let pid = null - -// // Create Posts for testing -// test.beforeEach( async ( { editor, admin } ) => { -// await admin.createNewPost( { title: 'Block Editor Test' } ) -// await editor.saveDraft() -// const postQuery = new URL( editor.page.url() ).search -// pid = new URLSearchParams( postQuery ).get( 'post' ) -// } ) - -// // Delete created post -// test.afterEach( async ( { requestUtils } ) => { -// await requestUtils.deletePost( pid ) -// } ) - -// test( 'Stackable blocks can be added in the editor', async ( { -// page, - -// editor, -// } ) => { -// await page.getByLabel( 'Toggle block inserter' ).click() - -// await page.locator( '.editor-block-list-item-stackable-text' ).click() - -// const blocks = await editor.getBlocks() - -// expect( blocks[ 0 ].name ).toContain( 'stackable/text' ) -// } ) - -// test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { -// page, -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// } ) - -// await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) -// await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() -// await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() -// await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() -// } ) - -// test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { -// page, -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// } ) - -// // Add content and color to Stackable Text Block -// await editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ).fill( 'test' ) -// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() -// await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) -// await editor.canvas.locator( 'body' ).click() - -// await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) -// await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - -// await editor.saveDraft() - -// const blocks = await editor.getBlocks() -// const attributes = blocks[ 0 ].attributes - -// expect( attributes.textColor1 ).toBe( '#ff0000' ) -// expect( attributes.text ).toBe( 'test' ) -// } ) - -// test( 'The Stackable block added in the editor should be visible in the frontend', async ( { -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// attributes: { -// text: 'test', -// textColor1: '#ff0000', -// }, -// } ) - -// const blocks = await editor.getBlocks() -// const uniqueId = blocks[ 0 ].attributes.uniqueId - -// await editor.saveDraft() - -// const preview = await editor.openPreviewPage() - -// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() -// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) -// await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) -// } ) -// } ) +import { test, expect } from 'e2e/test-utils' + +test.describe( 'Block Editor', () => { + let pid = null + + // Create Posts for testing + test.beforeEach( async ( { editor, admin } ) => { + await admin.createNewPost( { title: 'Block Editor Test' } ) + await editor.saveDraft() + const postQuery = new URL( editor.page.url() ).search + pid = new URLSearchParams( postQuery ).get( 'post' ) + } ) + + // Delete created post + test.afterEach( async ( { requestUtils } ) => { + await requestUtils.deletePost( pid ) + } ) + + test( 'Stackable blocks can be added in the editor', async ( { + page, + + editor, + } ) => { + await page.getByLabel( 'Toggle block inserter' ).click() + + await page.locator( '.editor-block-list-item-stackable-text' ).click() + + const blocks = await editor.getBlocks() + + expect( blocks[ 0 ].name ).toContain( 'stackable/text' ) + } ) + + test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + } ) + + await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) + await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() + } ) + + test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { + page, + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + } ) + + // Add content and color to Stackable Text Block + await editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ).fill( 'test' ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) + await editor.canvas.locator( 'body' ).click() + + await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) + await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + + await editor.saveDraft() + + const blocks = await editor.getBlocks() + const attributes = blocks[ 0 ].attributes + + expect( attributes.textColor1 ).toBe( '#ff0000' ) + expect( attributes.text ).toBe( 'test' ) + } ) + + test( 'The Stackable block added in the editor should be visible in the frontend', async ( { + editor, + } ) => { + await editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + textColor1: '#ff0000', + }, + } ) + + const blocks = await editor.getBlocks() + const uniqueId = blocks[ 0 ].attributes.uniqueId + + await editor.saveDraft() + + const preview = await editor.openPreviewPage() + + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() + await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) + await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + } ) +} ) diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts index 05fc4865cf..4ee9451c05 100644 --- a/e2e/tests/global-settings.spec.ts +++ b/e2e/tests/global-settings.spec.ts @@ -1,127 +1,127 @@ -// import { test, expect } from 'e2e/test-utils' -// import { createColor, getRgb } from '~stackable/plugins/global-settings/colors/util' - -// test.describe( 'Global Settigs', () => { -// let pid = null - -// // Create Posts for testing -// test.beforeEach( async ( { editor, admin } ) => { -// await admin.createNewPost( { title: 'Global Settings Test' } ) -// await editor.saveDraft() -// const postQuery = new URL( editor.page.url() ).search -// pid = new URLSearchParams( postQuery ).get( 'post' ) -// } ) - -// // Delete created post -// test.afterEach( async ( { requestUtils } ) => { -// await requestUtils.deletePost( pid ) -// } ) - -// test( 'When a color is added in the Global Colors, it should be present in the color picker', async ( { -// page, -// editor, -// } ) => { -// await page.getByLabel( 'Stackable Settings' ).click() - -// // Add a new Global Color -// const panel = page.locator( '.ugb-global-settings-color-picker ' ).filter( { hasText: 'Global Colors' } ) -// await panel.locator( 'button.ugb-global-settings-color-picker__add-button' ).click() - -// const globalColors = panel.locator( '.ugb-global-settings-color-picker__color-indicators > div' ) -// const count = ( await globalColors.evaluate( node => Array.from( node.childNodes ) ) ).length - -// const newColor = globalColors.getByRole( 'button', { name: `Custom Color ${ count } ` } ) -// await expect( newColor ).toBeVisible() -// await newColor.click() -// const hexValue = await page.getByLabel( 'Hex color' ).inputValue() - -// // Insert a Stackable Text Block and check if the added Global Colors is in the color picker -// await page.getByLabel( 'Settings', { exact: true } ).click() -// editor.insertBlock( { -// name: 'stackable/text', -// attributes: { -// text: 'test', -// }, -// } ) -// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - -// await expect( page.getByRole( 'heading', { name: 'Global Colors' } ) ).toBeVisible() -// await expect( page.getByLabel( `Color: Custom Color ${ count }` ) ).toBeVisible() -// await page.getByLabel( `Color: Custom Color ${ count }` ).click() -// await expect( page.getByLabel( 'Hex color' ) ).toHaveValue( hexValue ) -// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - -// // Delete added Global Color -// await page.getByLabel( 'Stackable Settings' ).click() -// await panel.locator( `.ugb-global-settings-color-picker__color-indicators > div > div:nth-child(${ count }) > button.stk-global-settings-color-picker__delete-button` ).click() -// } ) - -// test( 'Global Typography Styles should be applied when adding a heading', async ( { -// page, -// editor, -// } ) => { -// await page.getByLabel( 'Stackable Settings' ).click() -// await page.getByRole( 'button', { name: 'Global Typography' } ).click() - -// // Set Global Typography Styles of Heading 2 to have a font-size of 32 -// await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() -// await page.locator( '.stk-popover .components-base-control:nth-of-type(2)', { hasText: /Size/ } ).getByRole( 'textbox' ).fill( '32' ) -// await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() -// await expect( page.getByRole( 'heading', { name: 'Heading 2' } ) ).toHaveCSS( 'font-size', '32px' ) -// await page.getByLabel( 'Settings', { exact: true } ).click() - -// // Check if the added Stackable Heading Block has a font-size of 32 -// editor.insertBlock( { -// name: 'stackable/heading', -// attributes: { -// text: 'test', -// }, -// } ) - -// await expect( editor.canvas.locator( '[data-type="stackable/heading"] > .stk-block-heading > h2[role="textbox"]' ) ).toHaveCSS( 'font-size', '32px' ) - -// // Reset Global Typography Styles -// await page.getByLabel( 'Stackable Settings' ).click() -// await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).getByRole( 'button', { name: 'Reset' } ).click() -// } ) - -// test( 'When a default block is created, adding the block should have the default block\'s attributes', async ( { -// page, -// editor, -// } ) => { -// // Generate a color -// const color = createColor() - -// await page.getByLabel( 'Stackable Settings' ).click() -// await page.getByRole( 'button', { name: 'Block Defaults' } ).click() - -// // Open the Default Text Block Editor -// const defaultBlockPagePromise = page.waitForEvent( 'popup' ) -// await page.locator( 'div:nth-child(37) > .components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() -// const defaultBlockPage = await defaultBlockPagePromise - -// // Set a color for the default Text Block -// await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() -// await defaultBlockPage.getByLabel( 'Hex color' ).fill( color ) -// await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() -// await defaultBlockPage.getByRole( 'button', { name: 'Save', exact: true } ).click() -// await defaultBlockPage.close() - -// // Insert a Stackable Text Block, and check if the color is the same as the one set in the default block -// await page.reload() -// await editor.insertBlock( { -// name: 'stackable/text', -// attributes: { -// text: 'test', -// }, -// } ) - -// await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', `rgb(${ getRgb( color ) })` ) - -// // Reset Default Block -// await page.getByLabel( 'Stackable Settings' ).click() -// await page.getByRole( 'button', { name: 'Block Defaults' } ).click() -// await page.locator( 'div' ).filter( { hasText: /^Text$/ } ).first().getByLabel( 'Reset' ).click() -// } ) -// } ) +import { test, expect } from 'e2e/test-utils' +import { createColor, getRgb } from '~stackable/plugins/global-settings/colors/util' + +test.describe( 'Global Settigs', () => { + let pid = null + + // Create Posts for testing + test.beforeEach( async ( { editor, admin } ) => { + await admin.createNewPost( { title: 'Global Settings Test' } ) + await editor.saveDraft() + const postQuery = new URL( editor.page.url() ).search + pid = new URLSearchParams( postQuery ).get( 'post' ) + } ) + + // Delete created post + test.afterEach( async ( { requestUtils } ) => { + await requestUtils.deletePost( pid ) + } ) + + test( 'When a color is added in the Global Colors, it should be present in the color picker', async ( { + page, + editor, + } ) => { + await page.getByLabel( 'Stackable Settings' ).click() + + // Add a new Global Color + const panel = page.locator( '.ugb-global-settings-color-picker ' ).filter( { hasText: 'Global Colors' } ) + await panel.locator( 'button.ugb-global-settings-color-picker__add-button' ).click() + + const globalColors = panel.locator( '.ugb-global-settings-color-picker__color-indicators > div' ) + const count = ( await globalColors.evaluate( node => Array.from( node.childNodes ) ) ).length + + const newColor = globalColors.getByRole( 'button', { name: `Custom Color ${ count } ` } ) + await expect( newColor ).toBeVisible() + await newColor.click() + const hexValue = await page.getByLabel( 'Hex color' ).inputValue() + + // Insert a Stackable Text Block and check if the added Global Colors is in the color picker + await page.getByLabel( 'Settings', { exact: true } ).click() + editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + }, + } ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + + await expect( page.getByRole( 'heading', { name: 'Global Colors' } ) ).toBeVisible() + await expect( page.getByLabel( `Color: Custom Color ${ count }` ) ).toBeVisible() + await page.getByLabel( `Color: Custom Color ${ count }` ).click() + await expect( page.getByLabel( 'Hex color' ) ).toHaveValue( hexValue ) + await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + + // Delete added Global Color + await page.getByLabel( 'Stackable Settings' ).click() + await panel.locator( `.ugb-global-settings-color-picker__color-indicators > div > div:nth-child(${ count }) > button.stk-global-settings-color-picker__delete-button` ).click() + } ) + + test( 'Global Typography Styles should be applied when adding a heading', async ( { + page, + editor, + } ) => { + await page.getByLabel( 'Stackable Settings' ).click() + await page.getByRole( 'button', { name: 'Global Typography' } ).click() + + // Set Global Typography Styles of Heading 2 to have a font-size of 32 + await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() + await page.locator( '.stk-popover .components-base-control:nth-of-type(2)', { hasText: /Size/ } ).getByRole( 'textbox' ).fill( '32' ) + await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() + await expect( page.getByRole( 'heading', { name: 'Heading 2' } ) ).toHaveCSS( 'font-size', '32px' ) + await page.getByLabel( 'Settings', { exact: true } ).click() + + // Check if the added Stackable Heading Block has a font-size of 32 + editor.insertBlock( { + name: 'stackable/heading', + attributes: { + text: 'test', + }, + } ) + + await expect( editor.canvas.locator( '[data-type="stackable/heading"] > .stk-block-heading > h2[role="textbox"]' ) ).toHaveCSS( 'font-size', '32px' ) + + // Reset Global Typography Styles + await page.getByLabel( 'Stackable Settings' ).click() + await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).getByRole( 'button', { name: 'Reset' } ).click() + } ) + + test( 'When a default block is created, adding the block should have the default block\'s attributes', async ( { + page, + editor, + } ) => { + // Generate a color + const color = createColor() + + await page.getByLabel( 'Stackable Settings' ).click() + await page.getByRole( 'button', { name: 'Block Defaults' } ).click() + + // Open the Default Text Block Editor + const defaultBlockPagePromise = page.waitForEvent( 'popup' ) + await page.locator( 'div:nth-child(37) > .components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() + const defaultBlockPage = await defaultBlockPagePromise + + // Set a color for the default Text Block + await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await defaultBlockPage.getByLabel( 'Hex color' ).fill( color ) + await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + await defaultBlockPage.getByRole( 'button', { name: 'Save', exact: true } ).click() + await defaultBlockPage.close() + + // Insert a Stackable Text Block, and check if the color is the same as the one set in the default block + await page.reload() + await editor.insertBlock( { + name: 'stackable/text', + attributes: { + text: 'test', + }, + } ) + + await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', `rgb(${ getRgb( color ) })` ) + + // Reset Default Block + await page.getByLabel( 'Stackable Settings' ).click() + await page.getByRole( 'button', { name: 'Block Defaults' } ).click() + await page.locator( 'div' ).filter( { hasText: /^Text$/ } ).first().getByLabel( 'Reset' ).click() + } ) +} ) diff --git a/e2e/tests/site-editor.spec.ts b/e2e/tests/site-editor.spec.ts index 970fec3dce..b92ad41986 100644 --- a/e2e/tests/site-editor.spec.ts +++ b/e2e/tests/site-editor.spec.ts @@ -33,7 +33,7 @@ test.describe( 'Site Editor', () => { page, editor, } ) => { - await page.getByLabel( 'Toggle block inserter' ).click() + await page.getByLabel( 'Toggle block inserterx' ).click() await page.locator( '.editor-block-list-item-stackable-text' ).click() const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) @@ -50,7 +50,7 @@ test.describe( 'Site Editor', () => { }, { clientId: postContentBlock.clientId } ) await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) - await expect( page.getByLabel( 'Layout Tab' ) ).toBeVisible() + await expect( page.getByLabel( 'Layout Tabx' ) ).toBeVisible() await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() } ) From 06ec688d582e44f29254f28ec0d5dfc9c2cee2a0 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 22:30:23 +0800 Subject: [PATCH 56/76] test custom reporter --- .github/workflows/playwright.yml | 12 ++- e2e/config/reporter.ts | 136 +++++++++++++++++++++++++++++++ e2e/playwright.config.ts | 4 +- e2e/test-utils/format.ts | 29 +++++++ e2e/test-utils/index.ts | 1 + e2e/test-utils/requestUtils.ts | 2 +- 6 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 e2e/config/reporter.ts create mode 100644 e2e/test-utils/format.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 1ab800433f..be1826a5ab 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -81,10 +81,20 @@ jobs: STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: actions/upload-artifact@v4 - if: ${{ !failure() && !cancelled() }} + if: ${{ steps.run-playwright-tests.outcome == 'failure' }} id: artifact-upload-step with: name: playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }} path: playwright-report/ overwrite: true retention-days: 30 + - uses: markpatterson27/markdown-to-output@v1 + id: mto + if: ${{ steps.run-playwright-tests.outcome == 'failure' }} + with: + filepath: ./playwright-stk/errors.md + - name: Add test results to summary + if: ${{ steps.run-playwright-tests.outcome == 'failure' }} + run: | + echo "### Results for PHP ${{ matrix.php_version }} and WP ${{ matrix.wp_version || 'latest' }}" >> $GITHUB_STEP_SUMMARY + echo "${{ steps.mto.outputs.body }}" >> $GITHUB_STEP_SUMMARY diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts new file mode 100644 index 0000000000..7be44ecd3b --- /dev/null +++ b/e2e/config/reporter.ts @@ -0,0 +1,136 @@ +/* eslint-disable no-console */ +import type { + FullConfig, TestResult, + Reporter, Suite, TestCase, +} from '@playwright/test/reporter' + +import { ansiRegex, ms } from 'e2e/test-utils' + +import fs from 'fs' +import path from 'path' + +class StkReporter implements Reporter { + outputFolder: string; + failedTestErrors: Array; + totalTestCount: number; + suite: Suite; + + constructor() { + this.outputFolder = 'playwright-stk' + this.cleanupFolder() + + this.totalTestCount = 0 + this.failedTestErrors = [] + } + + stripAnsiEscapes( str: string ): string { + return str.replace( ansiRegex, '' ) + } + + cleanupFolder() { + const folderPath = path.resolve( this.outputFolder ) + + if ( fs.existsSync( folderPath ) ) { + // Read the directory and delete files + const files = fs.readdirSync( folderPath ) + for ( const file of files ) { + const filePath = path.join( folderPath, file ) + if ( fs.lstatSync( filePath ).isFile() ) { + fs.unlinkSync( filePath ) // Remove the file + } + } + console.log( `All files removed from: ${ folderPath }` ) + } else { + // If folder doesn't exist, create it + fs.mkdirSync( folderPath, { recursive: true } ) + } + } + + onBegin( config: FullConfig, suite: Suite ): void { + this.totalTestCount = suite.allTests().length + this.suite = suite + console.log( `Running ${ this.totalTestCount } tests:` ) + } + + onTestEnd( test: TestCase, result: TestResult ) { + if ( test.outcome() === 'expected' ) { + process.stdout.write( '·' ) + } else { + const titlePath = test.titlePath().filter( t => t !== '' ).join( ' › ' ) + + const out = test.results.length <= test.retries ? '×' : ( result.status === 'timedOut' ? 'T' : 'F' ) + process.stdout.write( out ) + + let testResult = `${ titlePath } ${ test.results.length > 1 ? `(retry #${ test.results.length - 1 }) ` : '' }[${ ms( result.duration ) }]` + '\n\n' + if ( result.errors.length >= 1 ) { + result.errors.forEach( error => { + if ( error.message ) { + testResult += `${ error.message }` + '\n\n' + } + + if ( error.snippet ) { + testResult += `${ error.snippet }` + `\n\n` + } + } ) + } + + this.failedTestErrors.push( testResult ) + } + } + + getSummary() { + let skipped = 0 + let expected = 0 + let unexpected = 0 + let flaky = 0 + + const unexpectedTestTitles : Array = [] + + this.suite.allTests().forEach( test => { + switch ( test.outcome() ) { + case 'skipped': skipped++; break + case 'expected': expected++; break + case 'unexpected': + unexpected++ + unexpectedTestTitles.push( '- ' + test.titlePath().filter( t => t !== '' ).join( ' › ' ) ) + break + case 'flaky': flaky++; break + } + } ) + + if ( unexpected ) { + console.log( '\nSummary:' ) + console.log( `${ expected } passed` ) + console.log( `${ flaky } flaky` ) + console.log( `${ skipped } skipped` ) + console.log( `${ unexpected } failed` ) + console.log( `---\n\nFailed Tests:` ) + console.log( this.failedTestErrors.join( '' ) ) + + const md = ` +| PASSED | FLAKY | SKIPPED | FAILED | +| ------ | ----- | ------- | ------ | +| ${ expected } | ${ flaky } | ${ skipped } | ${ unexpected } | + +Failed Tests: +${ unexpectedTestTitles.join( '\n' ) } +` + + const folderPath = path.resolve( this.outputFolder ) + if ( ! fs.existsSync( folderPath ) ) { + fs.mkdirSync( folderPath, { recursive: true } ) + } + + // Write the collected results to a JSON file + const reportPath = path.join( folderPath, 'errors.md' ) + fs.writeFileSync( reportPath, md ) + } + } + + async onEnd() { + process.stdout.write( '\n' ) + this.getSummary() + } +} + +export default StkReporter diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index bb3654e19d..e9a39ae663 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -24,7 +24,7 @@ export default defineConfig( { // forbidOnly: !! process.env.CI, /* Retry on CI only */ // retries: process.env.CI ? 1 : 0, - retries: 0, + retries: 1, // Locally, we could take advantage of parallelism due to multicore systems. // However, in the CI, we typically can use only one worker at a time. // It's more straightforward to align how we run tests in both systems. @@ -32,7 +32,7 @@ export default defineConfig( { // workers: process.env.CI ? 1 : undefined, workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: [ [ 'list' ], [ 'html', { outputFolder: '../playwright-report', open: 'never' } ] ], + reporter: [ [ './config/reporter.ts' ], [ 'html', { outputFolder: '../playwright-report', open: 'never' } ] ], reportSlowTests: null, /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { diff --git a/e2e/test-utils/format.ts b/e2e/test-utils/format.ts new file mode 100644 index 0000000000..0db5c17164 --- /dev/null +++ b/e2e/test-utils/format.ts @@ -0,0 +1,29 @@ +// Reference: https://github.com/microsoft/playwright/issues/16084 +export const ansiRegex = new RegExp( '([\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~])))', 'g' ) + +// Reference: /playwright-core/lib/utilsBundle.js +export const ms = ( ms : number ) => { + if ( ! isFinite( ms ) ) { + return '-' + } + if ( ms === 0 ) { + return '0ms' + } + if ( ms < 1000 ) { + return ms.toFixed( 0 ) + 'ms' + } + const seconds = ms / 1000 + if ( seconds < 60 ) { + return seconds.toFixed( 1 ) + 's' + } + const minutes = seconds / 60 + if ( minutes < 60 ) { + return minutes.toFixed( 1 ) + 'm' + } + const hours = minutes / 60 + if ( hours < 24 ) { + return hours.toFixed( 1 ) + 'h' + } + const days = hours / 24 + return days.toFixed( 1 ) + 'd' +} diff --git a/e2e/test-utils/index.ts b/e2e/test-utils/index.ts index dc6e3c0f72..5595bda796 100644 --- a/e2e/test-utils/index.ts +++ b/e2e/test-utils/index.ts @@ -1,3 +1,4 @@ export { test, expect } from './test' export { ExtendedRequestUtils } from './requestUtils' export { StackableFixture } from './stackable' +export * from './format' diff --git a/e2e/test-utils/requestUtils.ts b/e2e/test-utils/requestUtils.ts index bf8c3b60fa..dfc23645c2 100644 --- a/e2e/test-utils/requestUtils.ts +++ b/e2e/test-utils/requestUtils.ts @@ -6,7 +6,7 @@ class ExtendedRequestUtils extends BaseRequestUtils { path: '/wp/v2/plugins', } ) // eslint-disable-next-line no-console - console.info( 'plugins installed:', plugins.map( plugin => plugin.plugin ) ) + console.info( 'plugins installed:', plugins.map( plugin => plugin.plugin ), '\n' ) const activePlugins = plugins.filter( plugin => plugin.status === 'active' ).reduce( ( pluginsMap, plugin ) => { pluginsMap[ plugin.plugin ] = plugin.plugin return pluginsMap From 5b44cefa9a3bda0c367b2c278779c0b544317221 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 22:44:13 +0800 Subject: [PATCH 57/76] update condition --- .github/workflows/playwright.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index be1826a5ab..3dc3fa7d07 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -81,7 +81,7 @@ jobs: STACKABLE_SLUG: Stackable/plugin run: npm run test - uses: actions/upload-artifact@v4 - if: ${{ steps.run-playwright-tests.outcome == 'failure' }} + if: ${{ !cancelled() && steps.run-playwright-tests.outcome == 'failure' }} id: artifact-upload-step with: name: playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }} @@ -90,11 +90,11 @@ jobs: retention-days: 30 - uses: markpatterson27/markdown-to-output@v1 id: mto - if: ${{ steps.run-playwright-tests.outcome == 'failure' }} + if: ${{ !cancelled() && steps.run-playwright-tests.outcome == 'failure' }} with: filepath: ./playwright-stk/errors.md - name: Add test results to summary - if: ${{ steps.run-playwright-tests.outcome == 'failure' }} + if: ${{ !cancelled() && steps.run-playwright-tests.outcome == 'failure' }} run: | echo "### Results for PHP ${{ matrix.php_version }} and WP ${{ matrix.wp_version || 'latest' }}" >> $GITHUB_STEP_SUMMARY echo "${{ steps.mto.outputs.body }}" >> $GITHUB_STEP_SUMMARY From a42c89efad36f9eedc1596239622672ba02207ca Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 23:10:16 +0800 Subject: [PATCH 58/76] add report to summary --- .github/workflows/playwright.yml | 2 +- e2e/config/reporter.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 3dc3fa7d07..1f7ab8e2c0 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -96,5 +96,5 @@ jobs: - name: Add test results to summary if: ${{ !cancelled() && steps.run-playwright-tests.outcome == 'failure' }} run: | - echo "### Results for PHP ${{ matrix.php_version }} and WP ${{ matrix.wp_version || 'latest' }}" >> $GITHUB_STEP_SUMMARY echo "${{ steps.mto.outputs.body }}" >> $GITHUB_STEP_SUMMARY + echo "Report: [playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }}.zip](${{ steps.artifact-upload-step.outcome.artifact-url}})" diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 7be44ecd3b..73fb321258 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -114,6 +114,7 @@ class StkReporter implements Reporter { Failed Tests: ${ unexpectedTestTitles.join( '\n' ) } + ` const folderPath = path.resolve( this.outputFolder ) From bf2c16a258a5f6ccbe36f231267a7243a7a27d40 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 23:19:26 +0800 Subject: [PATCH 59/76] comment site editor tests temporarily --- .github/workflows/playwright.yml | 2 +- e2e/tests/site-editor.spec.ts | 208 +++++++++++++++---------------- 2 files changed, 105 insertions(+), 105 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 1f7ab8e2c0..dc2c6a6e82 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -97,4 +97,4 @@ jobs: if: ${{ !cancelled() && steps.run-playwright-tests.outcome == 'failure' }} run: | echo "${{ steps.mto.outputs.body }}" >> $GITHUB_STEP_SUMMARY - echo "Report: [playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }}.zip](${{ steps.artifact-upload-step.outcome.artifact-url}})" + echo "Report: [playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }}.zip](${{ steps.artifact-upload-step.outcome.artifact-url}})" >> $GITHUB_STEP_SUMMARY diff --git a/e2e/tests/site-editor.spec.ts b/e2e/tests/site-editor.spec.ts index b92ad41986..c89aa8ccae 100644 --- a/e2e/tests/site-editor.spec.ts +++ b/e2e/tests/site-editor.spec.ts @@ -1,105 +1,105 @@ -import { test, expect } from 'e2e/test-utils' - -test.describe( 'Site Editor', () => { - let pid = null - let postContentBlock = null - - test.beforeEach( async ( { - page, admin, editor, - } ) => { - await admin.createNewPost( { title: 'Site Editor Test', postType: 'page' } ) - await editor.saveDraft() - const postQuery = new URL( editor.page.url() ).search - pid = new URLSearchParams( postQuery ).get( 'post' ) - - await admin.visitSiteEditor( { - canvas: 'edit', postType: 'page', postId: pid, showWelcomeGuide: false, - } ) - - if ( await page.getByRole( 'heading', { name: 'Choose a pattern' } ).isVisible() ) { - await page.getByLabel( 'Close', { exact: true } ).click() - } - - postContentBlock = ( await editor.getBlocks( { full: true } ) ) - .filter( block => block.attributes?.tagName === 'main' )[ 0 ].innerBlocks - .filter( block => block.name === 'core/post-content' )[ 0 ] - } ) - - test.afterEach( async ( { requestUtils } ) => { - await requestUtils.deletePost( pid, 'pages' ) - } ) - - test( 'Stackable blocks can be added in the site editor', async ( { - page, - editor, - } ) => { - await page.getByLabel( 'Toggle block inserterx' ).click() - await page.locator( '.editor-block-list-item-stackable-text' ).click() - - const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - - expect( blocks.find( block => block.name === 'stackable/text' ) ).toBeTruthy() - } ) - - test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { - page, - editor, - } ) => { - await editor.insertBlock( { - name: 'stackable/text', - }, { clientId: postContentBlock.clientId } ) - - await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) - await expect( page.getByLabel( 'Layout Tabx' ) ).toBeVisible() - await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() - await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() - } ) - - // test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { - // page, - // editor, - // } ) => { - // await editor.insertBlock( { - // name: 'stackable/text', - // }, { clientId: postContentBlock.clientId } ) - // await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) - // await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) - // await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - // await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) - // await editor.canvas.locator( 'body' ).click() - - // await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) - // await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - - // await editor.saveDraft() - - // const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - // const textBlock = blocks.find( block => block.name === 'stackable/text' ) - // expect( textBlock.attributes.text ).toBe( 'test' ) - // expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) - // } ) - - // test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { - // editor, - // } ) => { - // await editor.insertBlock( { - // name: 'stackable/text', - // attributes: { - // text: 'test', - // textColor1: '#ff0000', - // }, - // }, { clientId: postContentBlock.clientId } ) - - // const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - // const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId - - // await editor.saveDraft() - - // const preview = await editor.openPreviewPage() - - // await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() - // await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) - // await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - // } ) -} ) +// import { test, expect } from 'e2e/test-utils' + +// test.describe( 'Site Editor', () => { +// let pid = null +// let postContentBlock = null + +// test.beforeEach( async ( { +// page, admin, editor, +// } ) => { +// await admin.createNewPost( { title: 'Site Editor Test', postType: 'page' } ) +// await editor.saveDraft() +// const postQuery = new URL( editor.page.url() ).search +// pid = new URLSearchParams( postQuery ).get( 'post' ) + +// await admin.visitSiteEditor( { +// canvas: 'edit', postType: 'page', postId: pid, showWelcomeGuide: false, +// } ) + +// if ( await page.getByRole( 'heading', { name: 'Choose a pattern' } ).isVisible() ) { +// await page.getByLabel( 'Close', { exact: true } ).click() +// } + +// postContentBlock = ( await editor.getBlocks( { full: true } ) ) +// .filter( block => block.attributes?.tagName === 'main' )[ 0 ].innerBlocks +// .filter( block => block.name === 'core/post-content' )[ 0 ] +// } ) + +// test.afterEach( async ( { requestUtils } ) => { +// await requestUtils.deletePost( pid, 'pages' ) +// } ) + +// test( 'Stackable blocks can be added in the site editor', async ( { +// page, +// editor, +// } ) => { +// await page.getByLabel( 'Toggle block inserterx' ).click() +// await page.locator( '.editor-block-list-item-stackable-text' ).click() + +// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) + +// expect( blocks.find( block => block.name === 'stackable/text' ) ).toBeTruthy() +// } ) + +// test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { +// page, +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// }, { clientId: postContentBlock.clientId } ) + +// await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) +// await expect( page.getByLabel( 'Layout Tabx' ) ).toBeVisible() +// await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() +// await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() +// } ) + +// test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { +// page, +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// }, { clientId: postContentBlock.clientId } ) +// await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) +// await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) +// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() +// await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) +// await editor.canvas.locator( 'body' ).click() + +// await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) +// await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) + +// await editor.saveDraft() + +// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) +// const textBlock = blocks.find( block => block.name === 'stackable/text' ) +// expect( textBlock.attributes.text ).toBe( 'test' ) +// expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) +// } ) + +// test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { +// editor, +// } ) => { +// await editor.insertBlock( { +// name: 'stackable/text', +// attributes: { +// text: 'test', +// textColor1: '#ff0000', +// }, +// }, { clientId: postContentBlock.clientId } ) + +// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) +// const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId + +// await editor.saveDraft() + +// const preview = await editor.openPreviewPage() + +// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() +// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) +// await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) +// } ) +// } ) From 252ebb1d8341baa1bc005d9af0dc48eece671f54 Mon Sep 17 00:00:00 2001 From: mxkae Date: Fri, 7 Feb 2025 23:33:00 +0800 Subject: [PATCH 60/76] update report url --- .github/workflows/playwright.yml | 2 +- e2e/config/reporter.ts | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index dc2c6a6e82..83a989876e 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -97,4 +97,4 @@ jobs: if: ${{ !cancelled() && steps.run-playwright-tests.outcome == 'failure' }} run: | echo "${{ steps.mto.outputs.body }}" >> $GITHUB_STEP_SUMMARY - echo "Report: [playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }}.zip](${{ steps.artifact-upload-step.outcome.artifact-url}})" >> $GITHUB_STEP_SUMMARY + echo "Report: [playwright-report-php_${{ matrix.php_version }}-wp_${{ env.WP_VERSION }}-${{ env.VERSION_SUFFIX }}.zip](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/${{ steps.artifact-upload-step.outputs.artifact-id }})" >> $GITHUB_STEP_SUMMARY diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 73fb321258..7fdeb93dfd 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -4,7 +4,7 @@ import type { Reporter, Suite, TestCase, } from '@playwright/test/reporter' -import { ansiRegex, ms } from 'e2e/test-utils' +import { ms } from 'e2e/test-utils' import fs from 'fs' import path from 'path' @@ -23,10 +23,6 @@ class StkReporter implements Reporter { this.failedTestErrors = [] } - stripAnsiEscapes( str: string ): string { - return str.replace( ansiRegex, '' ) - } - cleanupFolder() { const folderPath = path.resolve( this.outputFolder ) From 3585bdd31549b2eaaa25618beb0b22ef7e8724e3 Mon Sep 17 00:00:00 2001 From: mxkae Date: Sat, 8 Feb 2025 00:34:20 +0800 Subject: [PATCH 61/76] update wp version --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 83a989876e..496faea928 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -13,7 +13,7 @@ jobs: matrix: include: - php_version: '7.3' - wp_version: 6.4 + wp_version: 6.4.5 - php_version: '7.3' wp_version: null From 0a09b087cf8b244bc23ac8faff517e41135d7ebe Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 14:15:16 +0800 Subject: [PATCH 62/76] update block editor test --- e2e/tests/block-editor.spec.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts index 724f4139e6..d3fd1366d2 100644 --- a/e2e/tests/block-editor.spec.ts +++ b/e2e/tests/block-editor.spec.ts @@ -25,9 +25,7 @@ test.describe( 'Block Editor', () => { await page.locator( '.editor-block-list-item-stackable-text' ).click() - const blocks = await editor.getBlocks() - - expect( blocks[ 0 ].name ).toContain( 'stackable/text' ) + await expect( editor.canvas.getByLabel( 'Block: Text' ) ).toBeVisible() } ) test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { @@ -63,11 +61,13 @@ test.describe( 'Block Editor', () => { await editor.saveDraft() - const blocks = await editor.getBlocks() - const attributes = blocks[ 0 ].attributes + await expect( async () => { + const blocks = await editor.getBlocks() + const attributes = blocks.find( block => block.name === 'stackable/text' ).attributes - expect( attributes.textColor1 ).toBe( '#ff0000' ) - expect( attributes.text ).toBe( 'test' ) + expect( attributes.textColor1 ).toBe( '#ff0000' ) + expect( attributes.text ).toBe( 'test' ) + } ).toPass( { intervals: [ 1_000, 2_000, 5_000 ] } ) } ) test( 'The Stackable block added in the editor should be visible in the frontend', async ( { @@ -82,7 +82,7 @@ test.describe( 'Block Editor', () => { } ) const blocks = await editor.getBlocks() - const uniqueId = blocks[ 0 ].attributes.uniqueId + const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId await editor.saveDraft() From d86d9be8c0a6661a59e8d29c25b9fd4205cd84cc Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 14:47:47 +0800 Subject: [PATCH 63/76] use custom way to get block attributes --- e2e/tests/block-editor.spec.ts | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts index d3fd1366d2..b40c8fef29 100644 --- a/e2e/tests/block-editor.spec.ts +++ b/e2e/tests/block-editor.spec.ts @@ -61,17 +61,22 @@ test.describe( 'Block Editor', () => { await editor.saveDraft() - await expect( async () => { - const blocks = await editor.getBlocks() - const attributes = blocks.find( block => block.name === 'stackable/text' ).attributes + await page.waitForFunction( + () => window?.wp?.blocks && window?.wp?.data + ) - expect( attributes.textColor1 ).toBe( '#ff0000' ) - expect( attributes.text ).toBe( 'test' ) - } ).toPass( { intervals: [ 1_000, 2_000, 5_000 ] } ) + const clientId = await editor.canvas.getByLabel( 'Block: Text' ).getAttribute( 'data-block' ) + + const attributes = await page.evaluate( async ( [ _clientId ] ) => { + return await window.wp.data.select( 'core/block-editor' ).getBlockAttributes( _clientId ) + }, [ clientId ] ) + + expect( attributes ).toHaveAttribute( 'textColor1', '#ff0000' ) + expect( attributes ).toHaveAttribute( 'text', 'test' ) } ) test( 'The Stackable block added in the editor should be visible in the frontend', async ( { - editor, + page, editor, } ) => { await editor.insertBlock( { name: 'stackable/text', @@ -81,8 +86,17 @@ test.describe( 'Block Editor', () => { }, } ) - const blocks = await editor.getBlocks() - const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId + await page.waitForFunction( + () => window?.wp?.blocks && window?.wp?.data + ) + + const clientId = await editor.canvas.getByLabel( 'Block: Text' ).getAttribute( 'data-block' ) + + const attributes = await page.evaluate( async ( [ _clientId ] ) => { + return await window.wp.data.select( 'core/block-editor' ).getBlockAttributes( _clientId ) + }, [ clientId ] ) + + const uniqueId = attributes.uniqueId await editor.saveDraft() From 6159ea70a9409ca4977d51f4b90f0edb1bb0edcd Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 18:10:04 +0800 Subject: [PATCH 64/76] fix missing global settings for older wp versions --- src/plugins/global-settings/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/global-settings/index.js b/src/plugins/global-settings/index.js index bb57dc12c6..33ff27e03e 100644 --- a/src/plugins/global-settings/index.js +++ b/src/plugins/global-settings/index.js @@ -35,7 +35,9 @@ addAction( 'stackable.global-settings.toggle-sidebar', 'toggle', () => { } ) const GlobalSettings = () => { - const PluginSidebar = window.wp.editor.PluginSidebar + // For older WP versions (<6.6), wp.editor.PluginSidebar is undefined, + // use wp.editSite.PluginSidebar and wp.editPost.PluginSidebar as fallback + const PluginSidebar = window.wp.editor.PluginSidebar || window.wp.editSite?.PluginSidebar || window.wp.editPost?.PluginSidebar return ( <> From 4ef30cec80a7af494a3c15d3b6a5657073079e9b Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 18:12:19 +0800 Subject: [PATCH 65/76] add comments, update tests --- e2e/config/reporter.ts | 8 +++ e2e/playwright.config.ts | 3 +- e2e/test-utils/editor.ts | 30 +++++++++ e2e/test-utils/stackable.ts | 2 + e2e/test-utils/test.ts | 6 ++ e2e/tests/admin.spec.ts | 3 +- e2e/tests/block-editor.spec.ts | 47 ++++++------- e2e/tests/existing-blocks.spec.ts | 16 +++++ e2e/tests/global-settings.spec.ts | 15 ++++- e2e/tests/site-editor.spec.ts | 105 ------------------------------ 10 files changed, 102 insertions(+), 133 deletions(-) create mode 100644 e2e/test-utils/editor.ts create mode 100644 e2e/tests/existing-blocks.spec.ts delete mode 100644 e2e/tests/site-editor.spec.ts diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 7fdeb93dfd..61c616fd43 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -49,6 +49,12 @@ class StkReporter implements Reporter { } onTestEnd( test: TestCase, result: TestResult ) { + // Console Reporter similar to Playwright's Dot Reporter: + // '·' for passed test + // '×' for attempt for retry + // 'T' for timed out + // 'F' for failed test + if ( test.outcome() === 'expected' ) { process.stdout.write( '·' ) } else { @@ -57,6 +63,7 @@ class StkReporter implements Reporter { const out = test.results.length <= test.retries ? '×' : ( result.status === 'timedOut' ? 'T' : 'F' ) process.stdout.write( out ) + // Compile error messages and snippets let testResult = `${ titlePath } ${ test.results.length > 1 ? `(retry #${ test.results.length - 1 }) ` : '' }[${ ms( result.duration ) }]` + '\n\n' if ( result.errors.length >= 1 ) { result.errors.forEach( error => { @@ -103,6 +110,7 @@ class StkReporter implements Reporter { console.log( `---\n\nFailed Tests:` ) console.log( this.failedTestErrors.join( '' ) ) + // Generate md file for GitHub Job Summary Report const md = ` | PASSED | FLAKY | SKIPPED | FAILED | | ------ | ----- | ------- | ------ | diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index e9a39ae663..df1aa68a59 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -23,8 +23,7 @@ export default defineConfig( { /* Fail the build on CI if you accidentally left test.only in the source code. */ // forbidOnly: !! process.env.CI, /* Retry on CI only */ - // retries: process.env.CI ? 1 : 0, - retries: 1, + retries: process.env.CI ? 1 : 0, // Locally, we could take advantage of parallelism due to multicore systems. // However, in the CI, we typically can use only one worker at a time. // It's more straightforward to align how we run tests in both systems. diff --git a/e2e/test-utils/editor.ts b/e2e/test-utils/editor.ts new file mode 100644 index 0000000000..a378df73bc --- /dev/null +++ b/e2e/test-utils/editor.ts @@ -0,0 +1,30 @@ +import { Editor as BaseEditor } from '@wordpress/e2e-test-utils-playwright' + +class ExtendedEditor extends BaseEditor { + blockErrors: Array = [] + + getBlockAttributes = async function( clientId : String ) { + await this.page.waitForFunction( + () => window?.wp?.blocks && window?.wp?.data + ) + + const attributes = await this.page.evaluate( async ( [ _clientId ] ) => { + return await window.wp.data.select( 'core/block-editor' ).getBlockAttributes( _clientId ) + }, [ clientId ] ) + + return attributes + } + + getBlockErrors() :Array { + this.blockErrors = [] + this.page.on( 'console', msg => { + if ( msg.type() === 'error' && msg.text().startsWith( 'Block validation' ) ) { + this.blockErrors.push( msg.text() ) + } + } ) + + return this.blockErrors + } +} + +export { ExtendedEditor } diff --git a/e2e/test-utils/stackable.ts b/e2e/test-utils/stackable.ts index 63e41fefdd..9344fd3eb3 100644 --- a/e2e/test-utils/stackable.ts +++ b/e2e/test-utils/stackable.ts @@ -8,6 +8,8 @@ export class StackableFixture { this.page = page } + // When loading the Stackable settings in the Admin, there are 4 requests to wp/v2/settings + // This function waits for all 4 requests to finish to ensure Stackable settings are loaded and reflected on the page async waitForSettings(): Promise { return new Promise( ( resolve, reject ) => { let responseCount = 0 diff --git a/e2e/test-utils/test.ts b/e2e/test-utils/test.ts index f163445c65..0c1b9c0cb2 100644 --- a/e2e/test-utils/test.ts +++ b/e2e/test-utils/test.ts @@ -6,6 +6,7 @@ import { import { ExtendedRequestUtils } from './requestUtils' import { StackableFixture } from './stackable' +import { ExtendedEditor } from './editor' // We could also import utils from other packages. // import { StoreApiUtils } from '@woocommerce/e2e-utils'; @@ -19,6 +20,7 @@ import { StackableFixture } from './stackable' const test = base.extend<{ requestUtils: ExtendedRequestUtils; stackable: StackableFixture; + editor: ExtendedEditor; }>( { requestUtils: async ( {}, use ) => { let requestUtils = null @@ -38,6 +40,10 @@ const test = base.extend<{ stackable: async ( { page }, use ) => { await use( new StackableFixture( page ) ) }, + + editor: async ( { page }, use ) => { + await use( new ExtendedEditor( { page } ) ) + }, } ) export { test, expect } diff --git a/e2e/tests/admin.spec.ts b/e2e/tests/admin.spec.ts index a7f4c79dc6..507dccc60b 100644 --- a/e2e/tests/admin.spec.ts +++ b/e2e/tests/admin.spec.ts @@ -34,8 +34,7 @@ test( 'Stackable settings should be saved', async ( { stackable, } ) => { // Start waiting for Stackable Settings JSON Response before visiting the page - let settings = null - settings = stackable.waitForSettings() + let settings = stackable.waitForSettings() await admin.visitAdminPage( 'options-general.php?page=stackable' ) // Make sure all Stackable settings are loaded diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts index b40c8fef29..685308ca80 100644 --- a/e2e/tests/block-editor.spec.ts +++ b/e2e/tests/block-editor.spec.ts @@ -1,3 +1,4 @@ +import { Page } from '@playwright/test' import { test, expect } from 'e2e/test-utils' test.describe( 'Block Editor', () => { @@ -21,8 +22,9 @@ test.describe( 'Block Editor', () => { editor, } ) => { + // Insert Stackable Text Block through block inserter + // Also checks if Stackable Block is in the list of blocks in the Editor await page.getByLabel( 'Toggle block inserter' ).click() - await page.locator( '.editor-block-list-item-stackable-text' ).click() await expect( editor.canvas.getByLabel( 'Block: Text' ) ).toBeVisible() @@ -54,25 +56,22 @@ test.describe( 'Block Editor', () => { await editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ).fill( 'test' ) await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) + + // Click on the body to close the Color Picker popup await editor.canvas.locator( 'body' ).click() + // Verify if Text block contains correct content and color await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) await editor.saveDraft() - await page.waitForFunction( - () => window?.wp?.blocks && window?.wp?.data - ) - + // Verify block attributes const clientId = await editor.canvas.getByLabel( 'Block: Text' ).getAttribute( 'data-block' ) + const attributes = await editor.getBlockAttributes( clientId ) - const attributes = await page.evaluate( async ( [ _clientId ] ) => { - return await window.wp.data.select( 'core/block-editor' ).getBlockAttributes( _clientId ) - }, [ clientId ] ) - - expect( attributes ).toHaveAttribute( 'textColor1', '#ff0000' ) - expect( attributes ).toHaveAttribute( 'text', 'test' ) + expect( attributes ).toHaveProperty( 'textColor1', '#ff0000' ) + expect( attributes ).toHaveProperty( 'text', 'test' ) } ) test( 'The Stackable block added in the editor should be visible in the frontend', async ( { @@ -86,25 +85,27 @@ test.describe( 'Block Editor', () => { }, } ) - await page.waitForFunction( - () => window?.wp?.blocks && window?.wp?.data - ) - const clientId = await editor.canvas.getByLabel( 'Block: Text' ).getAttribute( 'data-block' ) - - const attributes = await page.evaluate( async ( [ _clientId ] ) => { - return await window.wp.data.select( 'core/block-editor' ).getBlockAttributes( _clientId ) - }, [ clientId ] ) - - const uniqueId = attributes.uniqueId + const uniqueId = ( await editor.getBlockAttributes( clientId ) ).uniqueId await editor.saveDraft() - const preview = await editor.openPreviewPage() + let preview : Page + + // openPreviewPage() from WordPress may fail as it relies on a button with a attribute name of 'view' + // https://github.com/WordPress/gutenberg/blob/trunk/packages/e2e-test-utils-playwright/src/editor/preview.ts + // Older versions of WordPress uses 'Preview' as the label for the Preview Button + if ( await page.getByLabel( 'View', { exact: true } ).isVisible() ) { + preview = await editor.openPreviewPage() + } else { + await page.getByLabel( 'Preview' ).click() + const previewPromise = page.waitForEvent( 'popup' ) + await page.getByRole( 'menuitem', { name: 'Preview in new tab' } ).click() + preview = await previewPromise + } await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) } ) } ) - diff --git a/e2e/tests/existing-blocks.spec.ts b/e2e/tests/existing-blocks.spec.ts new file mode 100644 index 0000000000..2fb67bd07d --- /dev/null +++ b/e2e/tests/existing-blocks.spec.ts @@ -0,0 +1,16 @@ +/* eslint-disable jest/no-disabled-tests */ +import { test, expect } from 'e2e/test-utils' + +test.skip( process.env.WP_TEST_POSTID === undefined, 'For existing test page only' ) + +test( 'Existing Stackable blocks should have no errors', async ( { + admin, + editor, +} ) => { + // Start listening on console errors for block validation + const blockErrors = editor.getBlockErrors() + + await admin.editPost( process.env.WP_TEST_POSTID ) + + expect( blockErrors ).toHaveLength( 0 ) +} ) diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts index 4ee9451c05..8fc09c4193 100644 --- a/e2e/tests/global-settings.spec.ts +++ b/e2e/tests/global-settings.spec.ts @@ -30,12 +30,14 @@ test.describe( 'Global Settigs', () => { const globalColors = panel.locator( '.ugb-global-settings-color-picker__color-indicators > div' ) const count = ( await globalColors.evaluate( node => Array.from( node.childNodes ) ) ).length + // Verify if a new color is added to the list const newColor = globalColors.getByRole( 'button', { name: `Custom Color ${ count } ` } ) await expect( newColor ).toBeVisible() + + // Get the value of the new global color await newColor.click() const hexValue = await page.getByLabel( 'Hex color' ).inputValue() - // Insert a Stackable Text Block and check if the added Global Colors is in the color picker await page.getByLabel( 'Settings', { exact: true } ).click() editor.insertBlock( { name: 'stackable/text', @@ -43,12 +45,19 @@ test.describe( 'Global Settigs', () => { text: 'test', }, } ) + + // Open the color picker await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() + // Verify the newly added global color is in the color picker await expect( page.getByRole( 'heading', { name: 'Global Colors' } ) ).toBeVisible() await expect( page.getByLabel( `Color: Custom Color ${ count }` ) ).toBeVisible() + + // Verify the color value await page.getByLabel( `Color: Custom Color ${ count }` ).click() await expect( page.getByLabel( 'Hex color' ) ).toHaveValue( hexValue ) + + // Click on the color picker button to close the popup await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() // Delete added Global Color @@ -67,7 +76,11 @@ test.describe( 'Global Settigs', () => { await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() await page.locator( '.stk-popover .components-base-control:nth-of-type(2)', { hasText: /Size/ } ).getByRole( 'textbox' ).fill( '32' ) await page.locator( '.ugb-global-settings-typography-control' ).nth( 1 ).locator( '.components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() + + // Verify if the Heading 2 in Global Typography Styles has correct font size await expect( page.getByRole( 'heading', { name: 'Heading 2' } ) ).toHaveCSS( 'font-size', '32px' ) + + // Open Block Settings await page.getByLabel( 'Settings', { exact: true } ).click() // Check if the added Stackable Heading Block has a font-size of 32 diff --git a/e2e/tests/site-editor.spec.ts b/e2e/tests/site-editor.spec.ts deleted file mode 100644 index c89aa8ccae..0000000000 --- a/e2e/tests/site-editor.spec.ts +++ /dev/null @@ -1,105 +0,0 @@ -// import { test, expect } from 'e2e/test-utils' - -// test.describe( 'Site Editor', () => { -// let pid = null -// let postContentBlock = null - -// test.beforeEach( async ( { -// page, admin, editor, -// } ) => { -// await admin.createNewPost( { title: 'Site Editor Test', postType: 'page' } ) -// await editor.saveDraft() -// const postQuery = new URL( editor.page.url() ).search -// pid = new URLSearchParams( postQuery ).get( 'post' ) - -// await admin.visitSiteEditor( { -// canvas: 'edit', postType: 'page', postId: pid, showWelcomeGuide: false, -// } ) - -// if ( await page.getByRole( 'heading', { name: 'Choose a pattern' } ).isVisible() ) { -// await page.getByLabel( 'Close', { exact: true } ).click() -// } - -// postContentBlock = ( await editor.getBlocks( { full: true } ) ) -// .filter( block => block.attributes?.tagName === 'main' )[ 0 ].innerBlocks -// .filter( block => block.name === 'core/post-content' )[ 0 ] -// } ) - -// test.afterEach( async ( { requestUtils } ) => { -// await requestUtils.deletePost( pid, 'pages' ) -// } ) - -// test( 'Stackable blocks can be added in the site editor', async ( { -// page, -// editor, -// } ) => { -// await page.getByLabel( 'Toggle block inserterx' ).click() -// await page.locator( '.editor-block-list-item-stackable-text' ).click() - -// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) - -// expect( blocks.find( block => block.name === 'stackable/text' ) ).toBeTruthy() -// } ) - -// test( 'Stackable Inspector Controls should show up upon clicking a Stackable block', async ( { -// page, -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// }, { clientId: postContentBlock.clientId } ) - -// await editor.selectBlocks( editor.canvas.getByLabel( 'Block: Text' ) ) -// await expect( page.getByLabel( 'Layout Tabx' ) ).toBeVisible() -// await expect( page.getByLabel( 'Style Tab' ) ).toBeVisible() -// await expect( page.getByLabel( 'Advanced Tab' ) ).toBeVisible() -// } ) - -// test( 'A Stackable block\'s attributes should update when settings are changed in the Inspector Controls.', async ( { -// page, -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// }, { clientId: postContentBlock.clientId } ) -// await editor.canvas.getByLabel( 'Type / to choose a block' ).fill( 'test' ) -// await expect( page.locator( '#inspector-textarea-control-0' ) ).toContainText( 'test' ) -// await page.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() -// await page.getByLabel( 'Hex color' ).fill( 'ff0000' ) -// await editor.canvas.locator( 'body' ).click() - -// await expect( editor.canvas.locator( '[data-type="stackable/text"]' ) ).toContainText( 'test' ) -// await expect( editor.canvas.locator( '[data-type="stackable/text"] > .stk-block-text > p[role="textbox"]' ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) - -// await editor.saveDraft() - -// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) -// const textBlock = blocks.find( block => block.name === 'stackable/text' ) -// expect( textBlock.attributes.text ).toBe( 'test' ) -// expect( textBlock.attributes.textColor1 ).toBe( '#ff0000' ) -// } ) - -// test( 'The Stackable block added in the site editor should be visible in the frontend', async ( { -// editor, -// } ) => { -// await editor.insertBlock( { -// name: 'stackable/text', -// attributes: { -// text: 'test', -// textColor1: '#ff0000', -// }, -// }, { clientId: postContentBlock.clientId } ) - -// const blocks = await editor.getBlocks( { clientId: postContentBlock.clientId } ) -// const uniqueId = blocks.find( block => block.name === 'stackable/text' ).attributes.uniqueId - -// await editor.saveDraft() - -// const preview = await editor.openPreviewPage() - -// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toBeVisible() -// await expect( preview.locator( `[data-block-id="${ uniqueId }"]` ) ).toContainText( 'test' ) -// await expect( preview.locator( `[data-block-id="${ uniqueId }"] p` ) ).toHaveCSS( 'color', 'rgb(255, 0, 0)' ) -// } ) -// } ) - From 4bd89418ac8025ecd9f7bfbd2c015f768ebc2ced Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 18:38:03 +0800 Subject: [PATCH 66/76] update tests --- e2e/tests/block-editor.spec.ts | 10 +++++++--- e2e/tests/global-settings.spec.ts | 9 ++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/e2e/tests/block-editor.spec.ts b/e2e/tests/block-editor.spec.ts index 685308ca80..d38d022125 100644 --- a/e2e/tests/block-editor.spec.ts +++ b/e2e/tests/block-editor.spec.ts @@ -68,10 +68,14 @@ test.describe( 'Block Editor', () => { // Verify block attributes const clientId = await editor.canvas.getByLabel( 'Block: Text' ).getAttribute( 'data-block' ) - const attributes = await editor.getBlockAttributes( clientId ) - expect( attributes ).toHaveProperty( 'textColor1', '#ff0000' ) - expect( attributes ).toHaveProperty( 'text', 'test' ) + // Block attributes may not update immediately + await expect( async () => { + const attributes = await editor.getBlockAttributes( clientId ) + + expect( attributes ).toHaveProperty( 'textColor1', '#ff0000' ) + expect( attributes ).toHaveProperty( 'text', 'test' ) + } ).toPass( { intervals: [ 1_000, 2_000, 5_000 ] } ) } ) test( 'The Stackable block added in the editor should be visible in the frontend', async ( { diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts index 8fc09c4193..5679de0ca1 100644 --- a/e2e/tests/global-settings.spec.ts +++ b/e2e/tests/global-settings.spec.ts @@ -117,7 +117,14 @@ test.describe( 'Global Settigs', () => { await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() await defaultBlockPage.getByLabel( 'Hex color' ).fill( color ) await defaultBlockPage.locator( '.stk-color-palette-control .stk-control-content > .components-dropdown > .components-button' ).first().click() - await defaultBlockPage.getByRole( 'button', { name: 'Save', exact: true } ).click() + + // In older WP versions, the button text is 'Update' instead of 'Save' + if ( await defaultBlockPage.getByRole( 'button', { name: 'Save', exact: true } ).isVisible() ) { + await defaultBlockPage.getByRole( 'button', { name: 'Save', exact: true } ).click() + } else { + await defaultBlockPage.getByRole( 'button', { name: 'Update' } ).click() + } + await defaultBlockPage.close() // Insert a Stackable Text Block, and check if the color is the same as the one set in the default block From 6d032e4bfb660bef56e8894fb4a6316ef6ec1587 Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 18:43:41 +0800 Subject: [PATCH 67/76] test with existing post --- .github/workflows/playwright.yml | 5 + e2e/config/post-content.txt | 859 +++++++++++++++++++++++++++++++ 2 files changed, 864 insertions(+) create mode 100644 e2e/config/post-content.txt diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 496faea928..402cc0e3b6 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -69,6 +69,10 @@ jobs: - name: Install wp-env run: | npm install -g @wordpress/env + - name: Create post with existing blocks + run: | + POST_ID=$(wp post create ./e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) + echo "WP_TEST_POSTID=$POST_ID" >> $GITHUB_ENV - name: Start wp-env run: wp-env start - name: Run playwright tests @@ -79,6 +83,7 @@ jobs: WP_USERNAME: admin WP_PASSWORD: password STACKABLE_SLUG: Stackable/plugin + WP_TEST_POSTID: ${{ env.WP_TEST_POSTID }} run: npm run test - uses: actions/upload-artifact@v4 if: ${{ !cancelled() && steps.run-playwright-tests.outcome == 'failure' }} diff --git a/e2e/config/post-content.txt b/e2e/config/post-content.txt new file mode 100644 index 0000000000..0ebf457d93 --- /dev/null +++ b/e2e/config/post-content.txt @@ -0,0 +1,859 @@ + +
+
+

Powerful but Lightweight Custom Blocks

+ + + +

A WordPress plugin that gives you custom WordPress blocks, designs, templates, advanced features like dynamic content, conditional display and many many more

+ + + +
+
GET PREMIUM
+ + + +
TRY DEMO
+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
+

Stackable works right in the WordPress Block and Site Editors

+
+ + + +
+

Effortlessly build and modify templates and template parts

+
+ + + +
+

Personalize your site’s headers and footers using Stackable blocks

+
+
+ + + +
+
+

All Blocks

+ + + +

ESSENTIAL BLOCKS

+ + + +
+
+
+
Button
+ + + +
Icon Button
+
+
+ + + +
+
+
Columns/Container
+ + + +
Icon List
+
+
+ + + +
+
+
Heading
+ + + +
Image
+
+
+ + + +
+
+
Icon
+ + + +
Text
+
+
+
+ + + +

SPECIAL BLOCKS

+ + + +
+
+
+
Accordion
+ + + +
Notification
+ + + +
Progress Bar
+ + + +
Carousel
+ + + +
Separator
+ + + +
Horizontal Scroller
+ + + +
Divider
+
+
+ + + +
+
+
Social Buttons
+ + + +
Expand
+ + + +
Number Box
+ + + +
Progress Circle
+ + + +
Tabs
+ + + +
Countdown
+ + + +
Pagination
+
+
+ + + +
+
+
Card
+ + + +
Icon Label
+ + + +
Blog Post
+ + + +
Subtitle
+ + + +
Table of Contents
+ + + +
Load More
+
+
+ + + +
+
+
Count Up
+ + + +
Image Box
+ + + +
Price
+ + + +
Video Popup
+ + + +
Map
+ + + +
Spacer
+
+
+
+ + + +

SECTION BLOCKS

+ + + +
+
+
+
Block Quote
+ + + +
Hero
+ + + +
Testimonial
+
+
+ + + +
+
+
Call to Action
+ + + +
Icon Box
+ + + +
Timeline
+
+
+ + + +
+
+
Feature
+ + + +
Pricing Box
+
+
+ + + +
+
+
Feature Grid
+ + + +
Team Member
+
+
+
+
+
+ + + +
+
+

Why Use Stackable Blocks

+ + + +
+ + + +
+
+

BLOCK LAYOUTS

+ + + +

Get the Layout. Own the Look.

+
+ + + +
+

Skip the guesswork and jumpstart your design with pre-designed layouts for quick and easy page building.

+
+
+ + + +
+ + + +
+ + + +
+
+

COPY PASTE STYLES

+ + + +

Duplicate Designs without Starting from Scratch

+
+
+ + + +
+
+
    +
  • Easily replicate styles across blocks to save time
  • + + + +
  • Apply styles to multiple blocks at once
  • + + + +
  • Transfer styles across posts or pages for design consistency
  • +
+
+ + + +
+
+
+
+ + + +
+ + + +
+
+

BLOCK CSS CUSTOMIZER

+ + + +

Customize. Control. Conquer.

+
+
+ + + +
+
+
    +
  • Customize blocks with precision using Custom CSS
  • + + + +
  • Easily apply unique styles to any element.
  • + + + +
  • Enhance design flexibility with full control over styling.
  • +
+
+ + + +
+
+
+
+ + + +
+ + + +
+
+

BUILT-IN SEPARATORS

+ + + +

Create Seamless Transitions and Define Spaces

+
+
+ + + +
+
+
    +
  • Choose from 13 unique and versatile separator styles–top and bottom
  • + + + +
  • Fully customizable––from color, size, to shadows
  • + + + +
  • Add depth and creativity with Layered Separators
  • +
+
+ + + +
+
+
+
+
+
+ + + +
+

Discover a Whole New Experience of Web Design

+ + + +

No matter who you are, blogger, entrepreneur, designer, developer, we guarantee you’ll want Stackable.

+ + + +
+
GET PREMIUM
+ + + +
TRY DEMO
+
+
+ From 210503ab967fdaf35e2a8ca927377b2a36fc68ef Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 18:51:03 +0800 Subject: [PATCH 68/76] update command --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 402cc0e3b6..7c32bdb1ca 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -71,7 +71,7 @@ jobs: npm install -g @wordpress/env - name: Create post with existing blocks run: | - POST_ID=$(wp post create ./e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) + POST_ID=$(wp-env run cli wp post create ./e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) echo "WP_TEST_POSTID=$POST_ID" >> $GITHUB_ENV - name: Start wp-env run: wp-env start From fd3788530075bccaa9d0624c9707110c1ee7d634 Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 18:58:21 +0800 Subject: [PATCH 69/76] use tests-cli --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 7c32bdb1ca..ff39001b43 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -71,7 +71,7 @@ jobs: npm install -g @wordpress/env - name: Create post with existing blocks run: | - POST_ID=$(wp-env run cli wp post create ./e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) + POST_ID=$(wp-env run tests-cli wp post create ./e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) echo "WP_TEST_POSTID=$POST_ID" >> $GITHUB_ENV - name: Start wp-env run: wp-env start From 3dcc46ae3c1c9ae068e642c674dcf96228944f7d Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 19:06:28 +0800 Subject: [PATCH 70/76] run wp-env first --- .github/workflows/playwright.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index ff39001b43..f766c903ce 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -69,12 +69,12 @@ jobs: - name: Install wp-env run: | npm install -g @wordpress/env + - name: Start wp-env + run: wp-env start - name: Create post with existing blocks run: | POST_ID=$(wp-env run tests-cli wp post create ./e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) echo "WP_TEST_POSTID=$POST_ID" >> $GITHUB_ENV - - name: Start wp-env - run: wp-env start - name: Run playwright tests id: run-playwright-tests env: From 38bc72d469a8ec27c74fc69a7a415c3c979710d7 Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 19:12:54 +0800 Subject: [PATCH 71/76] update file path --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f766c903ce..ad577a29c4 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -73,7 +73,7 @@ jobs: run: wp-env start - name: Create post with existing blocks run: | - POST_ID=$(wp-env run tests-cli wp post create ./e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) + POST_ID=$(wp-env run tests-cli wp post create e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) echo "WP_TEST_POSTID=$POST_ID" >> $GITHUB_ENV - name: Run playwright tests id: run-playwright-tests From cdd9c62f4c53b01de442d386b20d5de41d86f54f Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 19:19:26 +0800 Subject: [PATCH 72/76] check post content --- .github/workflows/playwright.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index ad577a29c4..a61be7d5ae 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -73,8 +73,10 @@ jobs: run: wp-env start - name: Create post with existing blocks run: | + cat e2e/config/post-content.txt POST_ID=$(wp-env run tests-cli wp post create e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) echo "WP_TEST_POSTID=$POST_ID" >> $GITHUB_ENV + continue-on-error: true - name: Run playwright tests id: run-playwright-tests env: From b7e24824b9abab8d7dee9a746aa2bc10c6427b32 Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 19:37:43 +0800 Subject: [PATCH 73/76] update file path --- .github/workflows/playwright.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index a61be7d5ae..d8ad968a40 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -73,8 +73,7 @@ jobs: run: wp-env start - name: Create post with existing blocks run: | - cat e2e/config/post-content.txt - POST_ID=$(wp-env run tests-cli wp post create e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) + POST_ID=$(wp-env run tests-cli wp post create plugins/Stackable/e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) echo "WP_TEST_POSTID=$POST_ID" >> $GITHUB_ENV continue-on-error: true - name: Run playwright tests From c90d8f7c2aab14f312c111b7b96b3e40503df4b9 Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 19:45:21 +0800 Subject: [PATCH 74/76] update file path --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index d8ad968a40..e1fff3f8ca 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -73,7 +73,7 @@ jobs: run: wp-env start - name: Create post with existing blocks run: | - POST_ID=$(wp-env run tests-cli wp post create plugins/Stackable/e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) + POST_ID=$(wp-env run tests-cli wp post create wp-content/plugins/Stackable/e2e/config/post-content.txt --post_title="Existing Blocks" --post_status=publish --porcelain) echo "WP_TEST_POSTID=$POST_ID" >> $GITHUB_ENV continue-on-error: true - name: Run playwright tests From a19e21b3922104ee92b1b019db5b65380f5dd5d3 Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 20:02:12 +0800 Subject: [PATCH 75/76] update reporter --- e2e/config/reporter.ts | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index 61c616fd43..bf5f0d2e79 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -49,22 +49,16 @@ class StkReporter implements Reporter { } onTestEnd( test: TestCase, result: TestResult ) { - // Console Reporter similar to Playwright's Dot Reporter: - // '·' for passed test - // '×' for attempt for retry - // 'T' for timed out - // 'F' for failed test - - if ( test.outcome() === 'expected' ) { - process.stdout.write( '·' ) - } else { - const titlePath = test.titlePath().filter( t => t !== '' ).join( ' › ' ) - - const out = test.results.length <= test.retries ? '×' : ( result.status === 'timedOut' ? 'T' : 'F' ) - process.stdout.write( out ) - + // Console Reporter similar to Playwright's List Reporter: + const resultMark = test.ok() ? '✓' : '✘' + const titlePath = test.titlePath().filter( t => t !== '' ).join( ' › ' ) + const retryLabel = test.results.length > 1 ? `(retry #${ test.results.length - 1 }) ` : '' + const duration = `[${ ms( result.duration ) }]` + console.log( `${ resultMark } ${ titlePath } ${ retryLabel }${ duration }` ) + + if ( result.status !== test.expectedStatus ) { // Compile error messages and snippets - let testResult = `${ titlePath } ${ test.results.length > 1 ? `(retry #${ test.results.length - 1 }) ` : '' }[${ ms( result.duration ) }]` + '\n\n' + let testResult = `${ titlePath } ${ retryLabel }${ duration }` + '\n\n' if ( result.errors.length >= 1 ) { result.errors.forEach( error => { if ( error.message ) { From 072eb8bc58136c0be65cebffbd9dec58b0921b8c Mon Sep 17 00:00:00 2001 From: mxkae Date: Mon, 10 Feb 2025 21:45:31 +0800 Subject: [PATCH 76/76] update locator for global settings block defaults --- e2e/config/reporter.ts | 4 ++-- e2e/playwright.config.ts | 2 +- e2e/tests/global-settings.spec.ts | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/e2e/config/reporter.ts b/e2e/config/reporter.ts index bf5f0d2e79..2cd45af9b8 100644 --- a/e2e/config/reporter.ts +++ b/e2e/config/reporter.ts @@ -15,8 +15,8 @@ class StkReporter implements Reporter { totalTestCount: number; suite: Suite; - constructor() { - this.outputFolder = 'playwright-stk' + constructor( options: { outputFolder?: string } = {} ) { + this.outputFolder = options.outputFolder || 'playwright-stk' this.cleanupFolder() this.totalTestCount = 0 diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index df1aa68a59..82ce274bcb 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -62,7 +62,7 @@ export default defineConfig( { // testMatch: 'setup/auth.js', // }, { - name: 'tests', + name: 'Stackable Free', use: { storageState: process.env.WP_AUTH_STORAGE, ...devices[ 'Desktop Chrome' ], diff --git a/e2e/tests/global-settings.spec.ts b/e2e/tests/global-settings.spec.ts index 5679de0ca1..152e02807c 100644 --- a/e2e/tests/global-settings.spec.ts +++ b/e2e/tests/global-settings.spec.ts @@ -110,7 +110,8 @@ test.describe( 'Global Settigs', () => { // Open the Default Text Block Editor const defaultBlockPagePromise = page.waitForEvent( 'popup' ) - await page.locator( 'div:nth-child(37) > .components-base-control__field > .ugb-button-icon-control__wrapper > .components-button' ).click() + const textBlock = page.locator( '.components-panel__body', { hasText: 'Block Defaults' } ).locator( '.stk-block-default-control', { hasText: /^Text$/ } ).first().getByLabel( 'Edit' ) + await textBlock.click() const defaultBlockPage = await defaultBlockPagePromise // Set a color for the default Text Block @@ -141,7 +142,7 @@ test.describe( 'Global Settigs', () => { // Reset Default Block await page.getByLabel( 'Stackable Settings' ).click() await page.getByRole( 'button', { name: 'Block Defaults' } ).click() - await page.locator( 'div' ).filter( { hasText: /^Text$/ } ).first().getByLabel( 'Reset' ).click() + await page.locator( '.components-panel__body', { hasText: 'Block Defaults' } ).locator( '.stk-block-default-control', { hasText: /^Text$/ } ).first().getByLabel( 'Reset' ).click() } ) } )