From d9907213466060b966629d3d8627f959c546692b Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 19:31:39 +0200 Subject: [PATCH 001/135] chore: Add visual regression testing --- .github/workflows/visual-regression.yml | 86 +++++++++++++++ build-tools/tasks/visual.js | 107 +++++++++++++++++++ build-tools/visual/global-setup.js | 4 + build-tools/visual/global-teardown.js | 4 + build-tools/visual/setup.js | 18 ++++ docs/RUNNING_TESTS.md | 59 +++++++++- eslint.config.mjs | 2 +- gulpfile.js | 2 + jest.visual.config.js | 25 +++++ package.json | 1 + test/visual/compare-screenshots.ts | 76 +++++++++++++ test/visual/definitions/alert.ts | 15 +++ test/visual/definitions/button.ts | 15 +++ test/visual/definitions/date-range-picker.ts | 40 +++++++ test/visual/definitions/index.ts | 12 +++ test/visual/definitions/table.ts | 15 +++ test/visual/types.ts | 23 ++++ test/visual/visual.test.ts | 6 ++ 18 files changed, 504 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/visual-regression.yml create mode 100644 build-tools/tasks/visual.js create mode 100644 build-tools/visual/global-setup.js create mode 100644 build-tools/visual/global-teardown.js create mode 100644 build-tools/visual/setup.js create mode 100644 jest.visual.config.js create mode 100644 test/visual/compare-screenshots.ts create mode 100644 test/visual/definitions/alert.ts create mode 100644 test/visual/definitions/button.ts create mode 100644 test/visual/definitions/date-range-picker.ts create mode 100644 test/visual/definitions/index.ts create mode 100644 test/visual/definitions/table.ts create mode 100644 test/visual/types.ts create mode 100644 test/visual/visual.test.ts diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml new file mode 100644 index 0000000000..a211178f4c --- /dev/null +++ b/.github/workflows/visual-regression.yml @@ -0,0 +1,86 @@ +name: Visual Regression Tests + +on: + pull_request: + branches: + - main + +defaults: + run: + shell: bash + +permissions: + id-token: write + contents: read + +jobs: + visual: + name: Visual regression + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + cache: npm + + - name: Install ChromeDriver + run: npm install -g chromedriver + + # ── Build PR (test) pages ────────────────────────────────────────────── + # Install the PR's dependencies and build its pages. + - name: Install PR dependencies + run: npm ci + + - name: Build PR pages + run: npx gulp quick-build + env: + NODE_ENV: production + + - name: Bundle PR pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-default + env: + NODE_ENV: production + + # ── Build baseline (main) pages ──────────────────────────────────────── + # Use a git worktree so the baseline has its own directory and its own + # node_modules. This means a PR that changes package-lock.json will still + # produce a correct baseline: the baseline installs from main's lockfile + # and the PR build installs from the PR's lockfile, so both sides use the + # dependency versions that are correct for their respective source trees. + - name: Create baseline worktree from origin/main + run: git worktree add /tmp/baseline origin/main + + - name: Install baseline dependencies + run: npm ci + working-directory: /tmp/baseline + + - name: Build baseline pages + run: npx gulp quick-build + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Bundle baseline pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline + working-directory: /tmp/baseline + env: + NODE_ENV: production + + # ── Run tests ───────────────────────────────────────────────────────── + - name: Run visual regression tests + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js + env: + TZ: UTC + + - name: Upload diff artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: visual-regression-diffs + path: visual-regression-output/ + retention-days: 14 diff --git a/build-tools/tasks/visual.js b/build-tools/tasks/visual.js new file mode 100644 index 0000000000..0864790afb --- /dev/null +++ b/build-tools/tasks/visual.js @@ -0,0 +1,107 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +const execa = require('execa'); +const path = require('path'); +const fs = require('fs'); +const waitOn = require('wait-on'); +const { task } = require('../utils/gulp-utils.js'); +const { parseArgs } = require('node:util'); + +const BASELINE_WORKTREE = '/tmp/visual-baseline'; +const BASELINE_OUTPUT = path.resolve('pages/lib/static-visual-baseline'); +const TEST_OUTPUT = path.resolve('pages/lib/static-default'); + +// Port assignments: +// 8080 — test build (PR / local changes) +// 8081 — baseline build (main branch) +const TEST_PORT = 8080; +const BASELINE_PORT = 8081; + +/** + * Serves a pre-built static directory using webpack-dev-server in static mode. + */ +function serveStatic(dir, port) { + return execa( + 'node_modules/.bin/webpack', + ['serve', '--config', 'pages/webpack.config.integ.cjs', '--port', String(port), '--static', dir, '--no-hot'], + { env: { ...process.env, NODE_ENV: 'development' } } + ); +} + +/** + * Builds the dev pages from the source tree at `cwd` into `outputPath`. + * Uses the node_modules present in `cwd`. + */ +async function buildPages(cwd, outputPath) { + await execa('npx', ['gulp', 'quick-build'], { + stdio: 'inherit', + cwd, + env: { ...process.env, NODE_ENV: 'production' }, + }); + await execa( + path.join(cwd, 'node_modules/.bin/webpack'), + ['--config', 'pages/webpack.config.integ.cjs', '--output-path', outputPath], + { stdio: 'inherit', cwd, env: { ...process.env, NODE_ENV: 'production' } } + ); +} + +module.exports = task('test:visual', async () => { + const options = { + shard: { type: 'string' }, + // Pass --skip-build to skip the build steps when artifacts are already present. + skipBuild: { type: 'boolean' }, + }; + const { shard, skipBuild } = parseArgs({ options, strict: false }).values; + + const cwd = process.cwd(); + + if (!skipBuild) { + // ── 1. Build the test (PR) pages ──────────────────────────────────────── + console.log('Building test pages (current branch)…'); + await buildPages(cwd, TEST_OUTPUT); + + // ── 2. Build the baseline (main) pages ────────────────────────────────── + // Create a worktree for origin/main so it gets its own node_modules. + // This correctly handles PRs that change package-lock.json: each side + // installs from its own lockfile. + console.log('Setting up baseline worktree from origin/main…'); + if (fs.existsSync(BASELINE_WORKTREE)) { + await execa('git', ['worktree', 'remove', '--force', BASELINE_WORKTREE]); + } + await execa('git', ['worktree', 'add', BASELINE_WORKTREE, 'origin/main']); + + try { + console.log('Installing baseline dependencies…'); + await execa('npm', ['ci'], { stdio: 'inherit', cwd: BASELINE_WORKTREE }); + + console.log('Building baseline pages (origin/main)…'); + await buildPages(BASELINE_WORKTREE, BASELINE_OUTPUT); + } finally { + await execa('git', ['worktree', 'remove', '--force', BASELINE_WORKTREE]); + } + } + + // ── 3. Start both static servers ────────────────────────────────────────── + console.log(`Starting test server on :${TEST_PORT} (${TEST_OUTPUT})…`); + const testServer = serveStatic(TEST_OUTPUT, TEST_PORT); + + console.log(`Starting baseline server on :${BASELINE_PORT} (${BASELINE_OUTPUT})…`); + const baselineServer = serveStatic(BASELINE_OUTPUT, BASELINE_PORT); + + try { + await waitOn({ resources: [`http://localhost:${TEST_PORT}`, `http://localhost:${BASELINE_PORT}`] }); + + // ── 4. Run visual tests ────────────────────────────────────────────────── + const jestArgs = ['-c', 'jest.visual.config.js']; + if (shard) { + jestArgs.push(`--shard=${shard}`); + } + await execa('jest', jestArgs, { + stdio: 'inherit', + env: { ...process.env, NODE_OPTIONS: '--experimental-vm-modules' }, + }); + } finally { + testServer.cancel(); + baselineServer.cancel(); + } +}); diff --git a/build-tools/visual/global-setup.js b/build-tools/visual/global-setup.js new file mode 100644 index 0000000000..075ee6e398 --- /dev/null +++ b/build-tools/visual/global-setup.js @@ -0,0 +1,4 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); +module.exports = () => startWebdriver(); diff --git a/build-tools/visual/global-teardown.js b/build-tools/visual/global-teardown.js new file mode 100644 index 0000000000..57ad21b454 --- /dev/null +++ b/build-tools/visual/global-teardown.js @@ -0,0 +1,4 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); +module.exports = () => shutdownWebdriver(); diff --git a/build-tools/visual/setup.js b/build-tools/visual/setup.js new file mode 100644 index 0000000000..2625a43809 --- /dev/null +++ b/build-tools/visual/setup.js @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* global jest */ +const { configure } = require('@cloudscape-design/browser-test-tools/use-browser'); + +// The PR build (the code under test) is served on port 8080. +// The baseline build (main branch, same node_modules) is served on port 8081. +configure({ + browserName: 'ChromeHeadlessIntegration', + browserCreatorOptions: { + seleniumUrl: 'http://localhost:9515', + }, + webdriverOptions: { + baseUrl: 'http://localhost:8080', + }, +}); + +jest.retryTimes(2, { logErrorsBeforeRetry: true }); diff --git a/docs/RUNNING_TESTS.md b/docs/RUNNING_TESTS.md index 525cdf181d..c5c483e981 100644 --- a/docs/RUNNING_TESTS.md +++ b/docs/RUNNING_TESTS.md @@ -60,11 +60,60 @@ TZ=UTC npx jest -u -c jest.unit.config.js src/ ``` ## Visual Regression Tests -> **Note:** The components repository does not have visual regression tests on GitHub. This section applies to other repositories such as chat-components, code-view, chart-components, and board-components. +Visual regression tests run automatically when opening a pull request in GitHub (see `.github/workflows/visual-regression.yml`). -Visual regression tests for permutation pages run automatically when opening a pull request in GitHub. +They compare permutation pages between the PR build and a baseline build of `main`, both served locally in the same CI job. Each side installs from its own `package-lock.json` via a git worktree, so dependency changes in the PR are handled correctly and unpinned updates in sister repositories affect both sides equally. -To check results: look at the "Visual Regression Tests" action in the PR. The "Test for regressions" step logs which pages failed. For a full report, download the `visual-regression-snapshots-results` artifact from the action summary. +### How it works -If there are unexpected regressions, fix your pull request. -If the changes are expected, call this out in your pull request comments. +1. The PR pages are built and served on port 8080. +2. A git worktree of `origin/main` is created, its dependencies installed, and its pages built and served on port 8081. +3. The single test runner (`test/visual/visual.test.ts`) iterates over all test definitions, captures the `.screenshot-area` element from both servers for each test, and fails if any pixels differ. + +### Running locally + +``` +npm run test:visual +``` + +This handles the full build and comparison in one command. If both outputs are already built, skip the build step: + +``` +NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js +``` + +(Requires both servers to be running — start the PR build with `npm run start:integ` on port 8080 and the baseline build on port 8081, or set `NEW_HOST` / `OLD_HOST` env vars to point at different hosts.) + +### Adding tests for a new component + +Create `test/visual/definitions/.ts`: + +```ts +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'my-component', + tests: [ + { + description: 'permutations', + path: 'my-component/permutations', + }, + ], +}; + +export default suite; +``` + +Then import and add it to `test/visual/definitions/index.ts`: + +```ts +import myComponent from './my-component'; + +export const allSuites: TestSuite[] = [..., myComponent]; +``` + +### Reviewing failures + +If the CI job fails, download the `visual-regression-diffs` artifact from the Actions summary. + +If the diff is expected (intentional visual change), note it in your PR description. diff --git a/eslint.config.mjs b/eslint.config.mjs index 0d9423aa5b..f03eb9ce60 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -225,7 +225,7 @@ export default tsEslint.config( }, }, { - files: ['**/__integ__/**', '**/__motion__/**', '**/__a11y__/**'], + files: ['**/__integ__/**', '**/__motion__/**', '**/__a11y__/**', 'test/visual/**'], rules: { // useBrowser is not a hook 'react-hooks/rules-of-hooks': 'off', diff --git a/gulpfile.js b/gulpfile.js index 6e3f389082..221c065982 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,6 +19,7 @@ const { generateI18nMessages, integ, motion, + visual, copyFiles, themeableSource, bundleVendorFiles, @@ -43,6 +44,7 @@ exports['test:unit'] = unit; exports['test:integ'] = integ; exports['test:a11y'] = a11y; exports['test:motion'] = motion; +exports['test:visual'] = visual; exports.watch = () => { watch( diff --git a/jest.visual.config.js b/jest.visual.config.js new file mode 100644 index 0000000000..7f1d020880 --- /dev/null +++ b/jest.visual.config.js @@ -0,0 +1,25 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +const path = require('path'); +const os = require('os'); + +module.exports = { + verbose: true, + testEnvironment: 'node', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: 'tsconfig.integ.json', + }, + ], + }, + reporters: ['default', 'github-actions'], + testTimeout: 120_000, // 2min — pages can be tall and slow to capture + maxWorkers: os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), + globalSetup: '/build-tools/visual/global-setup.js', + globalTeardown: '/build-tools/visual/global-teardown.js', + setupFilesAfterEnv: [path.join(__dirname, 'build-tools', 'visual', 'setup.js')], + moduleFileExtensions: ['js', 'ts'], + testMatch: ['/test/visual/visual.test.ts'], +}; diff --git a/package.json b/package.json index 0274e72393..f26ce0bbfa 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test:a11y": "gulp test:a11y", "test:integ": "gulp test:integ", "test:motion": "gulp test:motion", + "test:visual": "gulp test:visual", "lint": "npm-run-all --parallel lint:*", "lint:eslint": "eslint .", "lint:stylelint": "stylelint --ignore-path .gitignore '{src,pages}/**/*.{css,scss}'", diff --git a/test/visual/compare-screenshots.ts b/test/visual/compare-screenshots.ts new file mode 100644 index 0000000000..51254363ac --- /dev/null +++ b/test/visual/compare-screenshots.ts @@ -0,0 +1,76 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import pixelmatch from 'pixelmatch'; +import { PNG } from 'pngjs'; + +import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; +import useBrowser from '@cloudscape-design/browser-test-tools/use-browser'; + +import { TestDefinition, TestSuite } from './types'; + +const screenshotAreaSelector = '.screenshot-area'; +const defaultWindowSize = { width: 1600, height: 800 }; + +// NEW_HOST serves the PR's pages, OLD_HOST serves the baseline (main) pages. +const newHost = process.env.NEW_HOST || 'http://localhost:8080'; +const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; + +async function captureScreenshot( + browser: WebdriverIO.Browser, + url: string, + setup?: (page: ScreenshotPageObject) => Promise +): Promise { + await browser.url(url); + const page = new ScreenshotPageObject(browser); + await page.waitForVisible(screenshotAreaSelector); + if (setup) { + await setup(page); + } + const { image } = await page.captureBySelector(screenshotAreaSelector); + return image; +} + +function buildUrl(host: string, path: string, queryParams?: Record): string { + const params = new URLSearchParams(queryParams); + const qs = params.toString(); + return `${host}/#/${path}${qs ? `?${qs}` : ''}`; +} + +function compareImages(newImage: PNG, oldImage: PNG): number { + const { width, height } = newImage; + const diff = new PNG({ width, height }); + return pixelmatch(newImage.data, oldImage.data, diff.data, width, height, { threshold: 0.1 }); +} + +function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinition { + return (item as TestDefinition).path !== undefined; +} + +export function runTestSuites(suites: Array) { + for (const item of suites) { + if (isTestDefinition(item)) { + runSingleTest(item); + } else { + describe(item.description, () => { + runTestSuites(item.tests); + }); + } + } +} + +function runSingleTest(testDef: TestDefinition) { + const windowSize = { ...defaultWindowSize, ...testDef.configuration }; + + test( + testDef.description, + useBrowser(windowSize, async browser => { + const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); + const newScreenshot = await captureScreenshot(browser, newUrl, testDef.setup); + + const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); + const oldScreenshot = await captureScreenshot(browser, oldUrl, testDef.setup); + const diffPixels = compareImages(newScreenshot, oldScreenshot); + expect(diffPixels).toBe(0); + }) + ); +} diff --git a/test/visual/definitions/alert.ts b/test/visual/definitions/alert.ts new file mode 100644 index 0000000000..37f0985f24 --- /dev/null +++ b/test/visual/definitions/alert.ts @@ -0,0 +1,15 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'alert', + tests: [ + { + description: 'permutations', + path: 'alert/permutations', + }, + ], +}; + +export default suite; diff --git a/test/visual/definitions/button.ts b/test/visual/definitions/button.ts new file mode 100644 index 0000000000..cb7d590a59 --- /dev/null +++ b/test/visual/definitions/button.ts @@ -0,0 +1,15 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'button', + tests: [ + { + description: 'permutations', + path: 'button/permutations', + }, + ], +}; + +export default suite; diff --git a/test/visual/definitions/date-range-picker.ts b/test/visual/definitions/date-range-picker.ts new file mode 100644 index 0000000000..6800eebc90 --- /dev/null +++ b/test/visual/definitions/date-range-picker.ts @@ -0,0 +1,40 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'date-range-picker', + tests: [ + { + description: 'with value', + path: 'date-range-picker/with-value', + }, + { + description: 'range calendar', + path: 'date-range-picker/range-calendar', + }, + { + description: 'month calendar permutations', + path: 'date-range-picker/month-calendar-permutations', + }, + { + description: 'year calendar permutations', + path: 'date-range-picker/year-calendar-permutations', + }, + { + description: 'in small viewport', + path: 'date-range-picker/small-viewport', + configuration: { width: 400 }, + }, + { + description: 'with dropdown open', + path: 'date-range-picker/with-value', + setup: async page => { + await page.click('[data-testid="date-range-picker-trigger"]'); + await page.waitForVisible('.awsui-context-content-header'); + }, + }, + ], +}; + +export default suite; diff --git a/test/visual/definitions/index.ts b/test/visual/definitions/index.ts new file mode 100644 index 0000000000..27f5c9e7ca --- /dev/null +++ b/test/visual/definitions/index.ts @@ -0,0 +1,12 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Each component has its own test definition file. +// Import them here manually to form the full test suite. +import { TestSuite } from '../types'; +import alert from './alert'; +import button from './button'; +import dateRangePicker from './date-range-picker'; +import table from './table'; + +export const allSuites: TestSuite[] = [alert, button, dateRangePicker, table]; diff --git a/test/visual/definitions/table.ts b/test/visual/definitions/table.ts new file mode 100644 index 0000000000..6529046d7b --- /dev/null +++ b/test/visual/definitions/table.ts @@ -0,0 +1,15 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'table', + tests: [ + { + description: 'permutations', + path: 'table/permutations', + }, + ], +}; + +export default suite; diff --git a/test/visual/types.ts b/test/visual/types.ts new file mode 100644 index 0000000000..7c4c90809a --- /dev/null +++ b/test/visual/types.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; + +export interface ScreenshotTestConfiguration { + width?: number; + height?: number; +} + +export type TestCallback = (page: ScreenshotPageObject) => Promise; + +export interface TestDefinition { + description: string; + path: string; + queryParams?: Record; + configuration?: ScreenshotTestConfiguration; + setup?: TestCallback; +} + +export interface TestSuite { + description: string; + tests: Array; +} diff --git a/test/visual/visual.test.ts b/test/visual/visual.test.ts new file mode 100644 index 0000000000..06ef0fa8a4 --- /dev/null +++ b/test/visual/visual.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from './compare-screenshots'; +import { allSuites } from './definitions'; + +runTestSuites(allSuites); From bb56f3e7c11f82f7e621345e050c826a8088508a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 19:36:06 +0200 Subject: [PATCH 002/135] Install dependencies with npm i --- .github/workflows/visual-regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index a211178f4c..33e01c79fd 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -34,7 +34,7 @@ jobs: # ── Build PR (test) pages ────────────────────────────────────────────── # Install the PR's dependencies and build its pages. - name: Install PR dependencies - run: npm ci + run: npm i - name: Build PR pages run: npx gulp quick-build @@ -56,7 +56,7 @@ jobs: run: git worktree add /tmp/baseline origin/main - name: Install baseline dependencies - run: npm ci + run: npm i working-directory: /tmp/baseline - name: Build baseline pages From 93bcfdb72f2018caf89e16b2211211a512d800ec Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 20:05:09 +0200 Subject: [PATCH 003/135] Use node 20 --- .github/workflows/visual-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 33e01c79fd..642ab08bac 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -25,7 +25,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 cache: npm - name: Install ChromeDriver From 795064ffba0be870b0529e3dd32ab9b47469db49 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 20:12:53 +0200 Subject: [PATCH 004/135] Add pixelmatch types --- package-lock.json | 11 +++++++++++ package.json | 1 + 2 files changed, 12 insertions(+) diff --git a/package-lock.json b/package-lock.json index 0ba71cff88..0f8b3bab11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,6 +52,7 @@ "@types/jest": "^29.5.13", "@types/lodash": "^4.14.176", "@types/node": "^20.17.14", + "@types/pixelmatch": "^5.2.6", "@types/react": "^16.14.20", "@types/react-dom": "^16.9.14", "@types/react-is": "^18.2.0", @@ -4857,6 +4858,16 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/pixelmatch": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@types/pixelmatch/-/pixelmatch-5.2.6.tgz", + "integrity": "sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/pngjs": { "version": "6.0.5", "dev": true, diff --git a/package.json b/package.json index f26ce0bbfa..5cc3eb9757 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@types/jest": "^29.5.13", "@types/lodash": "^4.14.176", "@types/node": "^20.17.14", + "@types/pixelmatch": "^5.2.6", "@types/react": "^16.14.20", "@types/react-dom": "^16.9.14", "@types/react-is": "^18.2.0", From dfb8590d50087831b9a57763509f342b64a871cf Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 20:44:04 +0200 Subject: [PATCH 005/135] Install Chromedriver in CI --- .github/workflows/visual-regression.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 642ab08bac..90af77becf 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -28,8 +28,10 @@ jobs: node-version: 20 cache: npm - - name: Install ChromeDriver - run: npm install -g chromedriver + - name: Setup Chrome and ChromeDriver + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable # ── Build PR (test) pages ────────────────────────────────────────────── # Install the PR's dependencies and build its pages. From 663864c0f4bf51439fb2b4b509570933b3847570 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 21:01:25 +0200 Subject: [PATCH 006/135] Start servers --- .github/workflows/visual-regression.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 90af77becf..7ee7d42669 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -74,6 +74,19 @@ jobs: NODE_ENV: production # ── Run tests ───────────────────────────────────────────────────────── + - name: Start test server (port 8080) + run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8080 --static pages/lib/static-default --no-hot & + env: + NODE_ENV: development + + - name: Start baseline server (port 8081) + run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8081 --static pages/lib/static-visual-baseline --no-hot & + env: + NODE_ENV: development + + - name: Wait for servers to be ready + run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 + - name: Run visual regression tests run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js env: From 61a61b00fb83ce2515566f9d9cbb523fc85a5ca9 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 21:39:49 +0200 Subject: [PATCH 007/135] Install Puppeteer --- package-lock.json | 71 +++++++++++++++++++++++++++++++++++++++++++++-- package.json | 1 + 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0f8b3bab11..a1ace81398 100644 --- a/package-lock.json +++ b/package-lock.json @@ -97,6 +97,7 @@ "mockdate": "^3.0.5", "npm-run-all": "^4.1.5", "prettier": "^3.6.1", + "puppeteer-core": "^24.43.1", "react": "^16.14.0", "react-dom": "^16.14.0", "react-dom18": "npm:react-dom@^18.3.1", @@ -3594,9 +3595,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz", - "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==", + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz", + "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7284,6 +7285,20 @@ "node": ">=6.0" } }, + "node_modules/chromium-bidi": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", + "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, "node_modules/ci-info": { "version": "3.9.0", "dev": true, @@ -8810,6 +8825,13 @@ "dev": true, "license": "MIT" }, + "node_modules/devtools-protocol": { + "version": "0.0.1608973", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz", + "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/diff-sequences": { "version": "29.6.3", "dev": true, @@ -17782,6 +17804,25 @@ "node": ">=6" } }, + "node_modules/puppeteer-core": { + "version": "24.43.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz", + "integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.13.2", + "chromium-bidi": "14.0.0", + "debug": "^4.4.3", + "devtools-protocol": "0.0.1608973", + "typed-query-selector": "^2.12.2", + "webdriver-bidi-protocol": "0.4.1", + "ws": "^8.20.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "dev": true, @@ -21196,6 +21237,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-query-selector": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", + "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", + "dev": true, + "license": "MIT" + }, "node_modules/typedarray": { "version": "0.0.6", "dev": true, @@ -21715,6 +21763,13 @@ "node": ">=18.20.0" } }, + "node_modules/webdriver-bidi-protocol": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", + "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/webdriver/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -22494,6 +22549,16 @@ "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 5cc3eb9757..6e78ba7d82 100644 --- a/package.json +++ b/package.json @@ -121,6 +121,7 @@ "mockdate": "^3.0.5", "npm-run-all": "^4.1.5", "prettier": "^3.6.1", + "puppeteer-core": "^24.43.1", "react": "^16.14.0", "react-dom": "^16.14.0", "react-dom18": "npm:react-dom@^18.3.1", From 1043a0316a9477c4ddb23eb4a0b7f0e6836f046e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 22:19:28 +0200 Subject: [PATCH 008/135] Capture screenshot area or permutations --- test/visual/compare-screenshots.ts | 51 +++++++++++++++++--- test/visual/definitions/action-card.ts | 31 ++++++++++++ test/visual/definitions/alert.ts | 11 +++++ test/visual/definitions/button.ts | 15 ------ test/visual/definitions/date-range-picker.ts | 40 --------------- test/visual/definitions/index.ts | 7 +-- test/visual/definitions/table.ts | 15 ------ test/visual/types.ts | 5 ++ 8 files changed, 93 insertions(+), 82 deletions(-) create mode 100644 test/visual/definitions/action-card.ts delete mode 100644 test/visual/definitions/button.ts delete mode 100644 test/visual/definitions/date-range-picker.ts delete mode 100644 test/visual/definitions/table.ts diff --git a/test/visual/compare-screenshots.ts b/test/visual/compare-screenshots.ts index 51254363ac..8847e10d21 100644 --- a/test/visual/compare-screenshots.ts +++ b/test/visual/compare-screenshots.ts @@ -3,6 +3,7 @@ import pixelmatch from 'pixelmatch'; import { PNG } from 'pngjs'; +import { parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; import useBrowser from '@cloudscape-design/browser-test-tools/use-browser'; @@ -15,19 +16,52 @@ const defaultWindowSize = { width: 1600, height: 800 }; const newHost = process.env.NEW_HOST || 'http://localhost:8080'; const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; +/** + * Captures the .screenshot-area element on a focused page. + * Uses a standard ScreenshotPageObject (no forced scroll-and-merge). + */ +async function captureScreenshotArea(browser: WebdriverIO.Browser, url: string): Promise { + await browser.url(url); + const page = new ScreenshotPageObject(browser); + await page.waitForVisible(screenshotAreaSelector); + const { image } = await page.captureBySelector(screenshotAreaSelector); + return image; +} + +/** + * Captures the full page as a PNG for permutation pages. + * Uses fullPageScreenshot which handles pages taller than the viewport. + */ +async function capturePermutations(browser: WebdriverIO.Browser, url: string): Promise { + await browser.url(url); + const page = new ScreenshotPageObject(browser); + await page.waitForVisible(screenshotAreaSelector); + const base64 = await page.fullPageScreenshot(); + return parsePng(base64); +} + async function captureScreenshot( browser: WebdriverIO.Browser, url: string, + testDef: TestDefinition, setup?: (page: ScreenshotPageObject) => Promise ): Promise { - await browser.url(url); - const page = new ScreenshotPageObject(browser); - await page.waitForVisible(screenshotAreaSelector); if (setup) { + await browser.url(url); + const page = new ScreenshotPageObject(browser); + await page.waitForVisible(screenshotAreaSelector); await setup(page); + if (testDef.screenshotType === 'permutations') { + const base64 = await page.fullPageScreenshot(); + return parsePng(base64); + } + const { image } = await page.captureBySelector(screenshotAreaSelector); + return image; } - const { image } = await page.captureBySelector(screenshotAreaSelector); - return image; + if (testDef.screenshotType === 'permutations') { + return capturePermutations(browser, url); + } + return captureScreenshotArea(browser, url); } function buildUrl(host: string, path: string, queryParams?: Record): string { @@ -65,12 +99,15 @@ function runSingleTest(testDef: TestDefinition) { testDef.description, useBrowser(windowSize, async browser => { const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); - const newScreenshot = await captureScreenshot(browser, newUrl, testDef.setup); + const newScreenshot = await captureScreenshot(browser, newUrl, testDef, testDef.setup); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - const oldScreenshot = await captureScreenshot(browser, oldUrl, testDef.setup); + const oldScreenshot = await captureScreenshot(browser, oldUrl, testDef, testDef.setup); const diffPixels = compareImages(newScreenshot, oldScreenshot); expect(diffPixels).toBe(0); }) ); } + +// Export the capture functions for use in custom setup callbacks if needed. +export { captureScreenshotArea, capturePermutations }; diff --git a/test/visual/definitions/action-card.ts b/test/visual/definitions/action-card.ts new file mode 100644 index 0000000000..8730ac9504 --- /dev/null +++ b/test/visual/definitions/action-card.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'action-card', + tests: [ + { + description: 'permutations', + path: 'action-card/permutations', + screenshotType: 'permutations', + }, + { + description: 'variant permutations', + path: 'action-card/variant-permutations', + screenshotType: 'permutations', + }, + { + description: 'padding permutations', + path: 'action-card/padding-permutations', + screenshotType: 'permutations', + }, + { + description: 'simple', + path: 'action-card/simple', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/visual/definitions/alert.ts b/test/visual/definitions/alert.ts index 37f0985f24..a272e4dfee 100644 --- a/test/visual/definitions/alert.ts +++ b/test/visual/definitions/alert.ts @@ -8,6 +8,17 @@ const suite: TestSuite = { { description: 'permutations', path: 'alert/permutations', + screenshotType: 'permutations', + }, + { + description: 'simple', + path: 'alert/simple', + screenshotType: 'screenshotArea', + }, + { + description: 'custom types', + path: 'alert/style-custom-types', + screenshotType: 'screenshotArea', }, ], }; diff --git a/test/visual/definitions/button.ts b/test/visual/definitions/button.ts deleted file mode 100644 index cb7d590a59..0000000000 --- a/test/visual/definitions/button.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'button', - tests: [ - { - description: 'permutations', - path: 'button/permutations', - }, - ], -}; - -export default suite; diff --git a/test/visual/definitions/date-range-picker.ts b/test/visual/definitions/date-range-picker.ts deleted file mode 100644 index 6800eebc90..0000000000 --- a/test/visual/definitions/date-range-picker.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'date-range-picker', - tests: [ - { - description: 'with value', - path: 'date-range-picker/with-value', - }, - { - description: 'range calendar', - path: 'date-range-picker/range-calendar', - }, - { - description: 'month calendar permutations', - path: 'date-range-picker/month-calendar-permutations', - }, - { - description: 'year calendar permutations', - path: 'date-range-picker/year-calendar-permutations', - }, - { - description: 'in small viewport', - path: 'date-range-picker/small-viewport', - configuration: { width: 400 }, - }, - { - description: 'with dropdown open', - path: 'date-range-picker/with-value', - setup: async page => { - await page.click('[data-testid="date-range-picker-trigger"]'); - await page.waitForVisible('.awsui-context-content-header'); - }, - }, - ], -}; - -export default suite; diff --git a/test/visual/definitions/index.ts b/test/visual/definitions/index.ts index 27f5c9e7ca..55c1374550 100644 --- a/test/visual/definitions/index.ts +++ b/test/visual/definitions/index.ts @@ -4,9 +4,6 @@ // Each component has its own test definition file. // Import them here manually to form the full test suite. import { TestSuite } from '../types'; -import alert from './alert'; -import button from './button'; -import dateRangePicker from './date-range-picker'; -import table from './table'; +import actionCard from './action-card'; -export const allSuites: TestSuite[] = [alert, button, dateRangePicker, table]; +export const allSuites: TestSuite[] = [actionCard]; diff --git a/test/visual/definitions/table.ts b/test/visual/definitions/table.ts deleted file mode 100644 index 6529046d7b..0000000000 --- a/test/visual/definitions/table.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'table', - tests: [ - { - description: 'permutations', - path: 'table/permutations', - }, - ], -}; - -export default suite; diff --git a/test/visual/types.ts b/test/visual/types.ts index 7c4c90809a..f0fa665863 100644 --- a/test/visual/types.ts +++ b/test/visual/types.ts @@ -9,9 +9,14 @@ export interface ScreenshotTestConfiguration { export type TestCallback = (page: ScreenshotPageObject) => Promise; +// 'screenshotArea' — captures the .screenshot-area element on a focused page. +// 'permutations' — captures the entire page and crops permutations out of it. +export type ScreenshotType = 'screenshotArea' | 'permutations'; + export interface TestDefinition { description: string; path: string; + screenshotType: ScreenshotType; queryParams?: Record; configuration?: ScreenshotTestConfiguration; setup?: TestCallback; From a951e54bde5797cc520248e50148fa771c147dc9 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 11:54:13 +0200 Subject: [PATCH 009/135] Reuse build --- .github/workflows/deploy.yml | 9 +++++ .github/workflows/visual-regression.yml | 45 ++++++++++++------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5593984949..01bb5a45d2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -65,3 +65,12 @@ jobs: with: artifact-name: dev-pages-react${{ matrix.react }} deployment-path: pages/lib/static-default + + visual: + name: Visual regression + needs: quick-build + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} + uses: ./.github/workflows/visual-regression.yml + secrets: inherit + with: + pr-artifact-name: dev-pages-react18 \ No newline at end of file diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 7ee7d42669..f358626055 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -1,9 +1,12 @@ name: Visual Regression Tests on: - pull_request: - branches: - - main + workflow_call: + inputs: + pr-artifact-name: + description: Name of the GitHub Actions artifact containing the PR's built dev pages + required: true + type: string defaults: run: @@ -33,27 +36,21 @@ jobs: with: chrome-version: stable - # ── Build PR (test) pages ────────────────────────────────────────────── - # Install the PR's dependencies and build its pages. - - name: Install PR dependencies + - name: Install dependencies run: npm i - - name: Build PR pages - run: npx gulp quick-build - env: - NODE_ENV: production - - - name: Bundle PR pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-default - env: - NODE_ENV: production - - # ── Build baseline (main) pages ──────────────────────────────────────── - # Use a git worktree so the baseline has its own directory and its own - # node_modules. This means a PR that changes package-lock.json will still - # produce a correct baseline: the baseline installs from main's lockfile - # and the PR build installs from the PR's lockfile, so both sides use the - # dependency versions that are correct for their respective source trees. + # ── PR pages: reuse the artifact built by the build job ─────────────── + - name: Download PR pages artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.pr-artifact-name }} + path: pages/lib/static-default + + # ── Baseline (main) pages: build from origin/main ───────────────────── + # GitHub Actions artifacts are scoped to a workflow run, so there is no + # built-in way to reuse a previous main-branch artifact without the API. + # We build main locally instead — it shares node_modules with the PR + # checkout, so both sides resolve the same dependency versions. - name: Create baseline worktree from origin/main run: git worktree add /tmp/baseline origin/main @@ -73,8 +70,8 @@ jobs: env: NODE_ENV: production - # ── Run tests ───────────────────────────────────────────────────────── - - name: Start test server (port 8080) + # ── Start both servers and run tests ────────────────────────────────── + - name: Start PR server (port 8080) run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8080 --static pages/lib/static-default --no-hot & env: NODE_ENV: development From 13736cf9d8d9fdec2d5969d9570d1ee0938067c4 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 12:16:27 +0200 Subject: [PATCH 010/135] Fix wofklow deps --- .github/workflows/visual-regression.yml | 98 ------------------------- 1 file changed, 98 deletions(-) delete mode 100644 .github/workflows/visual-regression.yml diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml deleted file mode 100644 index f358626055..0000000000 --- a/.github/workflows/visual-regression.yml +++ /dev/null @@ -1,98 +0,0 @@ -name: Visual Regression Tests - -on: - workflow_call: - inputs: - pr-artifact-name: - description: Name of the GitHub Actions artifact containing the PR's built dev pages - required: true - type: string - -defaults: - run: - shell: bash - -permissions: - id-token: write - contents: read - -jobs: - visual: - name: Visual regression - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm - - - name: Setup Chrome and ChromeDriver - uses: browser-actions/setup-chrome@v1 - with: - chrome-version: stable - - - name: Install dependencies - run: npm i - - # ── PR pages: reuse the artifact built by the build job ─────────────── - - name: Download PR pages artifact - uses: actions/download-artifact@v4 - with: - name: ${{ inputs.pr-artifact-name }} - path: pages/lib/static-default - - # ── Baseline (main) pages: build from origin/main ───────────────────── - # GitHub Actions artifacts are scoped to a workflow run, so there is no - # built-in way to reuse a previous main-branch artifact without the API. - # We build main locally instead — it shares node_modules with the PR - # checkout, so both sides resolve the same dependency versions. - - name: Create baseline worktree from origin/main - run: git worktree add /tmp/baseline origin/main - - - name: Install baseline dependencies - run: npm i - working-directory: /tmp/baseline - - - name: Build baseline pages - run: npx gulp quick-build - working-directory: /tmp/baseline - env: - NODE_ENV: production - - - name: Bundle baseline pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline - working-directory: /tmp/baseline - env: - NODE_ENV: production - - # ── Start both servers and run tests ────────────────────────────────── - - name: Start PR server (port 8080) - run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8080 --static pages/lib/static-default --no-hot & - env: - NODE_ENV: development - - - name: Start baseline server (port 8081) - run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8081 --static pages/lib/static-visual-baseline --no-hot & - env: - NODE_ENV: development - - - name: Wait for servers to be ready - run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - - - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js - env: - TZ: UTC - - - name: Upload diff artifacts - if: failure() - uses: actions/upload-artifact@v4 - with: - name: visual-regression-diffs - path: visual-regression-output/ - retention-days: 14 From 0ca63b6d21f58d1137b0f2a245017b5f81cdaabb Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 12:36:12 +0200 Subject: [PATCH 011/135] Again --- .github/workflows/visual-regression.yml | 120 ++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 .github/workflows/visual-regression.yml diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml new file mode 100644 index 0000000000..bbe372b7d6 --- /dev/null +++ b/.github/workflows/visual-regression.yml @@ -0,0 +1,120 @@ +name: Visual Regression Tests + +on: + workflow_run: + workflows: + - Build, lint and test + types: + - completed + +defaults: + run: + shell: bash + +permissions: + id-token: write + contents: read + actions: read + +jobs: + visual: + name: Visual regression + # Only run on PRs from the same repo (not forks), and only when the build succeeded. + if: > + github.event.workflow_run.event == 'pull_request' && + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_repository.full_name == github.repository + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # Check out the PR head commit, not the merge commit that workflow_run uses. + ref: ${{ github.event.workflow_run.head_sha }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Setup Chrome and ChromeDriver + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable + + - name: Install dependencies + run: npm ci + + # ── PR pages: download the artifact produced by the build workflow ───── + - name: Download PR pages artifact + uses: actions/github-script@v7 + with: + script: | + const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: ${{ github.event.workflow_run.id }}, + }); + const artifact = artifacts.data.artifacts.find(a => a.name === 'dev-pages-react18'); + if (!artifact) throw new Error('dev-pages-react18 artifact not found'); + const download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: artifact.id, + archive_format: 'zip', + }); + const fs = require('fs'); + fs.writeFileSync('/tmp/dev-pages.zip', Buffer.from(download.data)); + + - name: Extract PR pages artifact + run: | + mkdir -p pages/lib/static-default + unzip /tmp/dev-pages.zip -d pages/lib/static-default + + # ── Baseline (main) pages: build from origin/main ───────────────────── + - name: Create baseline worktree from origin/main + run: git worktree add /tmp/baseline origin/main + + - name: Install baseline dependencies + run: npm ci + working-directory: /tmp/baseline + + - name: Build baseline pages + run: npx gulp quick-build + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Bundle baseline pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline + working-directory: /tmp/baseline + env: + NODE_ENV: production + + # ── Start both servers and run tests ────────────────────────────────── + - name: Start PR server (port 8080) + run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8080 --static pages/lib/static-default --no-hot & + env: + NODE_ENV: development + + - name: Start baseline server (port 8081) + run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8081 --static pages/lib/static-visual-baseline --no-hot & + env: + NODE_ENV: development + + - name: Wait for servers to be ready + run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 + + - name: Run visual regression tests + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js + env: + TZ: UTC + + - name: Upload diff artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: visual-regression-diffs + path: visual-regression-output/ + retention-days: 14 From 785e6fd6c31a4cd9d1798eb0ecbd8483745c7017 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 12:50:50 +0200 Subject: [PATCH 012/135] Fix npm install command --- .github/workflows/visual-regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index bbe372b7d6..858a35a01e 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -44,7 +44,7 @@ jobs: chrome-version: stable - name: Install dependencies - run: npm ci + run: npm i # ── PR pages: download the artifact produced by the build workflow ───── - name: Download PR pages artifact @@ -77,7 +77,7 @@ jobs: run: git worktree add /tmp/baseline origin/main - name: Install baseline dependencies - run: npm ci + run: npm i working-directory: /tmp/baseline - name: Build baseline pages From 689a1ef91a493b9925f8dd21e0b06390d8ec4a08 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 13:09:44 +0200 Subject: [PATCH 013/135] Wait only for the build --- .github/workflows/build-lint-test.yml | 14 ++++------- .github/workflows/build.yml | 31 +++++++++++++++++++++++++ .github/workflows/visual-regression.yml | 2 +- 3 files changed, 37 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index d9e7487d3d..553dbc4d02 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -1,15 +1,11 @@ name: Build, lint and test on: - pull_request: - branches: - - main - merge_group: - branches: - - main - push: - branches: - - main + workflow_run: + workflows: + - Build + types: + - completed permissions: id-token: write diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..9ddca205f7 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,31 @@ +name: Build + +on: + pull_request: + branches: + - main + merge_group: + branches: + - main + push: + branches: + - main + +permissions: + id-token: write + actions: read + contents: read + security-events: write + +jobs: + build: + name: build${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} + strategy: + matrix: + react: [16, 18] + uses: cloudscape-design/actions/.github/workflows/build-lint-test.yml@main + secrets: inherit + with: + artifact-path: pages/lib/static-default + artifact-name: dev-pages-react${{ matrix.react }} + react-version: ${{ matrix.react }} diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 858a35a01e..224e7bb94a 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -3,7 +3,7 @@ name: Visual Regression Tests on: workflow_run: workflows: - - Build, lint and test + - Build types: - completed From 2cbb9e5e4a77e8180e6e36c3c5579ebb83f3bd01 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:37 +0200 Subject: [PATCH 014/135] Revert "Wait only for the build" This reverts commit 23a62c420b23f83e90190060649e3b6895e84e50. --- .github/workflows/build-lint-test.yml | 14 +++++++---- .github/workflows/build.yml | 31 ------------------------- .github/workflows/visual-regression.yml | 2 +- 3 files changed, 10 insertions(+), 37 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 553dbc4d02..d9e7487d3d 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -1,11 +1,15 @@ name: Build, lint and test on: - workflow_run: - workflows: - - Build - types: - - completed + pull_request: + branches: + - main + merge_group: + branches: + - main + push: + branches: + - main permissions: id-token: write diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 9ddca205f7..0000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Build - -on: - pull_request: - branches: - - main - merge_group: - branches: - - main - push: - branches: - - main - -permissions: - id-token: write - actions: read - contents: read - security-events: write - -jobs: - build: - name: build${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} - strategy: - matrix: - react: [16, 18] - uses: cloudscape-design/actions/.github/workflows/build-lint-test.yml@main - secrets: inherit - with: - artifact-path: pages/lib/static-default - artifact-name: dev-pages-react${{ matrix.react }} - react-version: ${{ matrix.react }} diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 224e7bb94a..858a35a01e 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -3,7 +3,7 @@ name: Visual Regression Tests on: workflow_run: workflows: - - Build + - Build, lint and test types: - completed From 7e7e7581108c70d4c27029d77ea88a11162ae606 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:44 +0200 Subject: [PATCH 015/135] Revert "Fix npm install command" This reverts commit ffb88c1feb2fa9076c3463d4800c921f05fa66d7. --- .github/workflows/visual-regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 858a35a01e..bbe372b7d6 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -44,7 +44,7 @@ jobs: chrome-version: stable - name: Install dependencies - run: npm i + run: npm ci # ── PR pages: download the artifact produced by the build workflow ───── - name: Download PR pages artifact @@ -77,7 +77,7 @@ jobs: run: git worktree add /tmp/baseline origin/main - name: Install baseline dependencies - run: npm i + run: npm ci working-directory: /tmp/baseline - name: Build baseline pages From f65463cbea87019d39542492a2e5a632c86a7b99 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:46 +0200 Subject: [PATCH 016/135] Revert "Again" This reverts commit 263a253c442ce54ea385252ef8291cce91839172. --- .github/workflows/visual-regression.yml | 120 ------------------------ 1 file changed, 120 deletions(-) delete mode 100644 .github/workflows/visual-regression.yml diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml deleted file mode 100644 index bbe372b7d6..0000000000 --- a/.github/workflows/visual-regression.yml +++ /dev/null @@ -1,120 +0,0 @@ -name: Visual Regression Tests - -on: - workflow_run: - workflows: - - Build, lint and test - types: - - completed - -defaults: - run: - shell: bash - -permissions: - id-token: write - contents: read - actions: read - -jobs: - visual: - name: Visual regression - # Only run on PRs from the same repo (not forks), and only when the build succeeded. - if: > - github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.conclusion == 'success' && - github.event.workflow_run.head_repository.full_name == github.repository - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - # Check out the PR head commit, not the merge commit that workflow_run uses. - ref: ${{ github.event.workflow_run.head_sha }} - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm - - - name: Setup Chrome and ChromeDriver - uses: browser-actions/setup-chrome@v1 - with: - chrome-version: stable - - - name: Install dependencies - run: npm ci - - # ── PR pages: download the artifact produced by the build workflow ───── - - name: Download PR pages artifact - uses: actions/github-script@v7 - with: - script: | - const artifacts = await github.rest.actions.listWorkflowRunArtifacts({ - owner: context.repo.owner, - repo: context.repo.repo, - run_id: ${{ github.event.workflow_run.id }}, - }); - const artifact = artifacts.data.artifacts.find(a => a.name === 'dev-pages-react18'); - if (!artifact) throw new Error('dev-pages-react18 artifact not found'); - const download = await github.rest.actions.downloadArtifact({ - owner: context.repo.owner, - repo: context.repo.repo, - artifact_id: artifact.id, - archive_format: 'zip', - }); - const fs = require('fs'); - fs.writeFileSync('/tmp/dev-pages.zip', Buffer.from(download.data)); - - - name: Extract PR pages artifact - run: | - mkdir -p pages/lib/static-default - unzip /tmp/dev-pages.zip -d pages/lib/static-default - - # ── Baseline (main) pages: build from origin/main ───────────────────── - - name: Create baseline worktree from origin/main - run: git worktree add /tmp/baseline origin/main - - - name: Install baseline dependencies - run: npm ci - working-directory: /tmp/baseline - - - name: Build baseline pages - run: npx gulp quick-build - working-directory: /tmp/baseline - env: - NODE_ENV: production - - - name: Bundle baseline pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline - working-directory: /tmp/baseline - env: - NODE_ENV: production - - # ── Start both servers and run tests ────────────────────────────────── - - name: Start PR server (port 8080) - run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8080 --static pages/lib/static-default --no-hot & - env: - NODE_ENV: development - - - name: Start baseline server (port 8081) - run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8081 --static pages/lib/static-visual-baseline --no-hot & - env: - NODE_ENV: development - - - name: Wait for servers to be ready - run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - - - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js - env: - TZ: UTC - - - name: Upload diff artifacts - if: failure() - uses: actions/upload-artifact@v4 - with: - name: visual-regression-diffs - path: visual-regression-output/ - retention-days: 14 From f868757a44c04662b63d5c9406189837c759982a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:52 +0200 Subject: [PATCH 017/135] Revert "Fix wofklow deps" This reverts commit b1eee0b5ba3596aa6e90c6be8e917edf7af898ba. --- .github/workflows/visual-regression.yml | 98 +++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/visual-regression.yml diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml new file mode 100644 index 0000000000..f358626055 --- /dev/null +++ b/.github/workflows/visual-regression.yml @@ -0,0 +1,98 @@ +name: Visual Regression Tests + +on: + workflow_call: + inputs: + pr-artifact-name: + description: Name of the GitHub Actions artifact containing the PR's built dev pages + required: true + type: string + +defaults: + run: + shell: bash + +permissions: + id-token: write + contents: read + +jobs: + visual: + name: Visual regression + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Setup Chrome and ChromeDriver + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable + + - name: Install dependencies + run: npm i + + # ── PR pages: reuse the artifact built by the build job ─────────────── + - name: Download PR pages artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.pr-artifact-name }} + path: pages/lib/static-default + + # ── Baseline (main) pages: build from origin/main ───────────────────── + # GitHub Actions artifacts are scoped to a workflow run, so there is no + # built-in way to reuse a previous main-branch artifact without the API. + # We build main locally instead — it shares node_modules with the PR + # checkout, so both sides resolve the same dependency versions. + - name: Create baseline worktree from origin/main + run: git worktree add /tmp/baseline origin/main + + - name: Install baseline dependencies + run: npm i + working-directory: /tmp/baseline + + - name: Build baseline pages + run: npx gulp quick-build + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Bundle baseline pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline + working-directory: /tmp/baseline + env: + NODE_ENV: production + + # ── Start both servers and run tests ────────────────────────────────── + - name: Start PR server (port 8080) + run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8080 --static pages/lib/static-default --no-hot & + env: + NODE_ENV: development + + - name: Start baseline server (port 8081) + run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8081 --static pages/lib/static-visual-baseline --no-hot & + env: + NODE_ENV: development + + - name: Wait for servers to be ready + run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 + + - name: Run visual regression tests + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js + env: + TZ: UTC + + - name: Upload diff artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: visual-regression-diffs + path: visual-regression-output/ + retention-days: 14 From aa00fb6a852d662a0d96f065433685e5d17c15db Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:53 +0200 Subject: [PATCH 018/135] Revert "Reuse build" This reverts commit 1f71f10a69ef3b99627b3f8bec675dc7a2e4802f. --- .github/workflows/visual-regression.yml | 45 +++++++++++++------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index f358626055..7ee7d42669 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -1,12 +1,9 @@ name: Visual Regression Tests on: - workflow_call: - inputs: - pr-artifact-name: - description: Name of the GitHub Actions artifact containing the PR's built dev pages - required: true - type: string + pull_request: + branches: + - main defaults: run: @@ -36,21 +33,27 @@ jobs: with: chrome-version: stable - - name: Install dependencies + # ── Build PR (test) pages ────────────────────────────────────────────── + # Install the PR's dependencies and build its pages. + - name: Install PR dependencies run: npm i - # ── PR pages: reuse the artifact built by the build job ─────────────── - - name: Download PR pages artifact - uses: actions/download-artifact@v4 - with: - name: ${{ inputs.pr-artifact-name }} - path: pages/lib/static-default - - # ── Baseline (main) pages: build from origin/main ───────────────────── - # GitHub Actions artifacts are scoped to a workflow run, so there is no - # built-in way to reuse a previous main-branch artifact without the API. - # We build main locally instead — it shares node_modules with the PR - # checkout, so both sides resolve the same dependency versions. + - name: Build PR pages + run: npx gulp quick-build + env: + NODE_ENV: production + + - name: Bundle PR pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-default + env: + NODE_ENV: production + + # ── Build baseline (main) pages ──────────────────────────────────────── + # Use a git worktree so the baseline has its own directory and its own + # node_modules. This means a PR that changes package-lock.json will still + # produce a correct baseline: the baseline installs from main's lockfile + # and the PR build installs from the PR's lockfile, so both sides use the + # dependency versions that are correct for their respective source trees. - name: Create baseline worktree from origin/main run: git worktree add /tmp/baseline origin/main @@ -70,8 +73,8 @@ jobs: env: NODE_ENV: production - # ── Start both servers and run tests ────────────────────────────────── - - name: Start PR server (port 8080) + # ── Run tests ───────────────────────────────────────────────────────── + - name: Start test server (port 8080) run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8080 --static pages/lib/static-default --no-hot & env: NODE_ENV: development From 66506b7bd02c517b6ba7ba1aa4ce601bea67ed13 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 15:20:37 +0200 Subject: [PATCH 019/135] Include alert tests --- test/visual/definitions/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/visual/definitions/index.ts b/test/visual/definitions/index.ts index 55c1374550..318ce7c68b 100644 --- a/test/visual/definitions/index.ts +++ b/test/visual/definitions/index.ts @@ -5,5 +5,6 @@ // Import them here manually to form the full test suite. import { TestSuite } from '../types'; import actionCard from './action-card'; +import alert from './alert'; -export const allSuites: TestSuite[] = [actionCard]; +export const allSuites: TestSuite[] = [actionCard, alert]; From 48bddb747add69f080e64c521115d255ce887bb1 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 15:21:01 +0200 Subject: [PATCH 020/135] Add more alert tests --- test/visual/definitions/alert.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/test/visual/definitions/alert.ts b/test/visual/definitions/alert.ts index a272e4dfee..30792c0d2d 100644 --- a/test/visual/definitions/alert.ts +++ b/test/visual/definitions/alert.ts @@ -1,25 +1,36 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 + import { TestSuite } from '../types'; const suite: TestSuite = { description: 'alert', tests: [ - { - description: 'permutations', - path: 'alert/permutations', - screenshotType: 'permutations', - }, { description: 'simple', path: 'alert/simple', screenshotType: 'screenshotArea', }, { - description: 'custom types', + description: 'style custom page', path: 'alert/style-custom-types', screenshotType: 'screenshotArea', }, + ...[600, 1280].map(width => ({ + description: `width ${width}px`, + tests: [ + { + description: 'permutations', + path: 'alert/permutations', + screenshotType: 'permutations' as const, + }, + { + description: 'custom types', + path: 'alert/style-custom-types', + screenshotType: 'screenshotArea' as const, + }, + ], + })), ], }; From 88c91b283a4ad7f1c7fd99f1a9355a357192f174 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 15:46:26 +0200 Subject: [PATCH 021/135] chore: Export visual test definitions --- build-tools/tasks/index.js | 5 +++++ build-tools/tasks/visual-definitions.js | 8 ++++++++ gulpfile.js | 2 ++ test/visual/types.ts | 2 +- tsconfig.visual-definitions.json | 19 +++++++++++++++++++ 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 build-tools/tasks/visual-definitions.js create mode 100644 tsconfig.visual-definitions.json diff --git a/build-tools/tasks/index.js b/build-tools/tasks/index.js index d7db3c0784..413695be14 100644 --- a/build-tools/tasks/index.js +++ b/build-tools/tasks/index.js @@ -21,5 +21,10 @@ module.exports = { themeableSource: require('./themeable-source'), bundleVendorFiles: require('./bundle-vendor-files'), sizeLimit: require('./size-limit'), +<<<<<<< HEAD testDefinitions: require('./test-definitions'), +======= + visual: require('./visual'), + visualDefinitions: require('./visual-definitions'), +>>>>>>> 4213557f5 (chore: Export visual test definitions) }; diff --git a/build-tools/tasks/visual-definitions.js b/build-tools/tasks/visual-definitions.js new file mode 100644 index 0000000000..34dfe16adf --- /dev/null +++ b/build-tools/tasks/visual-definitions.js @@ -0,0 +1,8 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +const execa = require('execa'); +const { task } = require('../utils/gulp-utils'); + +module.exports = task('visual-definitions', () => + execa('tsc', ['-p', 'tsconfig.visual-definitions.json'], { stdio: 'inherit' }) +); diff --git a/gulpfile.js b/gulpfile.js index 221c065982..09e0833868 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -20,6 +20,7 @@ const { integ, motion, visual, + visualDefinitions, copyFiles, themeableSource, bundleVendorFiles, @@ -45,6 +46,7 @@ exports['test:integ'] = integ; exports['test:a11y'] = a11y; exports['test:motion'] = motion; exports['test:visual'] = visual; +exports['build:visual-definitions'] = visualDefinitions; exports.watch = () => { watch( diff --git a/test/visual/types.ts b/test/visual/types.ts index f0fa665863..c4c23b622f 100644 --- a/test/visual/types.ts +++ b/test/visual/types.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; +import type { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; export interface ScreenshotTestConfiguration { width?: number; diff --git a/tsconfig.visual-definitions.json b/tsconfig.visual-definitions.json new file mode 100644 index 0000000000..103f77ee47 --- /dev/null +++ b/tsconfig.visual-definitions.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "lib": ["ES2021"], + "target": "ES2019", + "types": [], + "module": "CommonJS", + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "inlineSources": true, + "rootDir": "test/visual", + "outDir": "lib/visual-test-definitions" + }, + "include": ["test/visual/definitions", "test/visual/types.ts"], + "exclude": [] +} From a2aa01724b8ac23705bdd408f173bc297e259111 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 15:17:49 +0200 Subject: [PATCH 022/135] Use the quick build --- .github/workflows/visual-regression.yml | 31 ++++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 7ee7d42669..35ae55ba9b 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -13,6 +13,11 @@ permissions: id-token: write contents: read +inputs: + pr-artifact-name: + description: 'Name of the artifact containing PR pages (built by quick-build job). If not provided, pages will be built locally.' + required: false + jobs: visual: name: Visual regression @@ -33,20 +38,24 @@ jobs: with: chrome-version: stable - # ── Build PR (test) pages ────────────────────────────────────────────── - # Install the PR's dependencies and build its pages. - - name: Install PR dependencies - run: npm i - - - name: Build PR pages - run: npx gulp quick-build + # ── Build or download PR (test) pages ───────────────────────────────── + # When called from deploy.yml with pr-artifact-name, download the artifact. + # When run standalone (no pr-artifact-name provided), build the pages locally. + - name: Build PR pages locally + if: ${{ !inputs.pr-artifact-name }} + run: | + npm i + npx gulp quick-build + node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-default env: NODE_ENV: production - - name: Bundle PR pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-default - env: - NODE_ENV: production + - name: Download PR pages artifact + if: ${{ inputs.pr-artifact-name }} + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.pr-artifact-name }} + path: pages/lib/static-default # ── Build baseline (main) pages ──────────────────────────────────────── # Use a git worktree so the baseline has its own directory and its own From c66d3b753dd917d5325d011f63ab839214d0d7f9 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 18:16:20 +0200 Subject: [PATCH 023/135] Fix tsconfig --- tsconfig.visual-definitions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.visual-definitions.json b/tsconfig.visual-definitions.json index 103f77ee47..a31ccd02d3 100644 --- a/tsconfig.visual-definitions.json +++ b/tsconfig.visual-definitions.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "lib": ["ES2021"], + "lib": ["ES2021", "DOM"], "target": "ES2019", "types": [], "module": "CommonJS", From 0bd90be2bb9c8cb5106b1fd540fad550be12574a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 18:20:07 +0200 Subject: [PATCH 024/135] Fix workflow --- .github/workflows/visual-regression.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 35ae55ba9b..c6d1bf031a 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -4,6 +4,12 @@ on: pull_request: branches: - main + workflow_call: + inputs: + pr-artifact-name: + description: 'Name of the artifact containing PR pages (built by quick-build job). If not provided, pages will be built locally.' + required: false + type: string defaults: run: @@ -13,11 +19,6 @@ permissions: id-token: write contents: read -inputs: - pr-artifact-name: - description: 'Name of the artifact containing PR pages (built by quick-build job). If not provided, pages will be built locally.' - required: false - jobs: visual: name: Visual regression From e0cbbb91918913248c5d5cc5628d1f3bcec2eba2 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 18:41:58 +0200 Subject: [PATCH 025/135] Fix workflow --- .github/workflows/visual-regression.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index c6d1bf031a..d3b36a4ac9 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -39,13 +39,15 @@ jobs: with: chrome-version: stable + - name: Install dependencies + run: npm i + # ── Build or download PR (test) pages ───────────────────────────────── # When called from deploy.yml with pr-artifact-name, download the artifact. # When run standalone (no pr-artifact-name provided), build the pages locally. - name: Build PR pages locally if: ${{ !inputs.pr-artifact-name }} run: | - npm i npx gulp quick-build node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-default env: From f244200806911d49630eac29564295f854f2eee7 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 19:01:10 +0200 Subject: [PATCH 026/135] Use serve --- .github/workflows/visual-regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index d3b36a4ac9..742cc72f68 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -87,12 +87,12 @@ jobs: # ── Run tests ───────────────────────────────────────────────────────── - name: Start test server (port 8080) - run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8080 --static pages/lib/static-default --no-hot & + run: npx serve --no-clipboard --listen 8080 pages/lib/static-default & env: NODE_ENV: development - name: Start baseline server (port 8081) - run: node_modules/.bin/webpack serve --config pages/webpack.config.integ.cjs --port 8081 --static pages/lib/static-visual-baseline --no-hot & + run: npx serve --no-clipboard --listen 8081 pages/lib/static-visual-baseline & env: NODE_ENV: development From 670ef1a09abca17471653db25e193bb31676e71c Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 19:10:56 +0200 Subject: [PATCH 027/135] Run tests on Safari --- .github/workflows/visual-regression.yml | 27 ++++++++++++++++--------- build-tools/visual/global-setup.js | 20 ++++++++++++++++-- build-tools/visual/global-teardown.js | 12 +++++++++-- build-tools/visual/setup.js | 6 ++++-- 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 742cc72f68..39cea38599 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -21,8 +21,16 @@ permissions: jobs: visual: - name: Visual regression - runs-on: ubuntu-latest + name: Visual regression (${{ matrix.browser }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - browser: chrome + os: ubuntu-latest + - browser: safari + os: macos-latest steps: - uses: actions/checkout@v4 with: @@ -35,16 +43,19 @@ jobs: cache: npm - name: Setup Chrome and ChromeDriver + if: matrix.browser == 'chrome' uses: browser-actions/setup-chrome@v1 with: chrome-version: stable + - name: Enable SafariDriver + if: matrix.browser == 'safari' + run: sudo safaridriver --enable + - name: Install dependencies run: npm i # ── Build or download PR (test) pages ───────────────────────────────── - # When called from deploy.yml with pr-artifact-name, download the artifact. - # When run standalone (no pr-artifact-name provided), build the pages locally. - name: Build PR pages locally if: ${{ !inputs.pr-artifact-name }} run: | @@ -61,11 +72,6 @@ jobs: path: pages/lib/static-default # ── Build baseline (main) pages ──────────────────────────────────────── - # Use a git worktree so the baseline has its own directory and its own - # node_modules. This means a PR that changes package-lock.json will still - # produce a correct baseline: the baseline installs from main's lockfile - # and the PR build installs from the PR's lockfile, so both sides use the - # dependency versions that are correct for their respective source trees. - name: Create baseline worktree from origin/main run: git worktree add /tmp/baseline origin/main @@ -103,11 +109,12 @@ jobs: run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js env: TZ: UTC + BROWSER: ${{ matrix.browser }} - name: Upload diff artifacts if: failure() uses: actions/upload-artifact@v4 with: - name: visual-regression-diffs + name: visual-regression-diffs-${{ matrix.browser }} path: visual-regression-output/ retention-days: 14 diff --git a/build-tools/visual/global-setup.js b/build-tools/visual/global-setup.js index 075ee6e398..f27f7d04cb 100644 --- a/build-tools/visual/global-setup.js +++ b/build-tools/visual/global-setup.js @@ -1,4 +1,20 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); -module.exports = () => startWebdriver(); +const { spawn } = require('child_process'); +const waitOn = require('wait-on'); + +let driverProcess; + +module.exports = async () => { + if (process.env.BROWSER === 'safari') { + driverProcess = spawn('safaridriver', ['--port', '4444']); + driverProcess.on('error', err => { + throw err; + }); + await waitOn({ resources: ['http-get://localhost:4444/status'], timeout: 10000 }); + } else { + const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); + await startWebdriver(); + } + global.__DRIVER_PROCESS__ = driverProcess; +}; diff --git a/build-tools/visual/global-teardown.js b/build-tools/visual/global-teardown.js index 57ad21b454..366c3f7660 100644 --- a/build-tools/visual/global-teardown.js +++ b/build-tools/visual/global-teardown.js @@ -1,4 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); -module.exports = () => shutdownWebdriver(); +module.exports = () => { + if (process.env.BROWSER === 'safari') { + if (global.__DRIVER_PROCESS__) { + global.__DRIVER_PROCESS__.kill(); + } + } else { + const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); + shutdownWebdriver(); + } +}; diff --git a/build-tools/visual/setup.js b/build-tools/visual/setup.js index 2625a43809..79050cab6a 100644 --- a/build-tools/visual/setup.js +++ b/build-tools/visual/setup.js @@ -3,12 +3,14 @@ /* global jest */ const { configure } = require('@cloudscape-design/browser-test-tools/use-browser'); +const isSafari = process.env.BROWSER === 'safari'; + // The PR build (the code under test) is served on port 8080. // The baseline build (main branch, same node_modules) is served on port 8081. configure({ - browserName: 'ChromeHeadlessIntegration', + browserName: isSafari ? 'Safari' : 'ChromeHeadlessIntegration', browserCreatorOptions: { - seleniumUrl: 'http://localhost:9515', + seleniumUrl: isSafari ? 'http://localhost:4444' : 'http://localhost:9515', }, webdriverOptions: { baseUrl: 'http://localhost:8080', From 4456fd320ff59c018c996085a4e8c11c242f24ae Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 19:27:05 +0200 Subject: [PATCH 028/135] Prevent visual regression workflow from running twice --- .github/workflows/visual-regression.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 39cea38599..09685d9bf5 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -1,9 +1,6 @@ name: Visual Regression Tests on: - pull_request: - branches: - - main workflow_call: inputs: pr-artifact-name: From b508370b0371d3b5690453f9a29230a7db0c5664 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 19:45:39 +0200 Subject: [PATCH 029/135] Reuse baseline build across browsers --- .github/workflows/visual-regression.yml | 99 ++++++++++++++++++------- 1 file changed, 73 insertions(+), 26 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 09685d9bf5..2c9e633b05 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -17,17 +17,10 @@ permissions: contents: read jobs: - visual: - name: Visual regression (${{ matrix.browser }}) - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - include: - - browser: chrome - os: ubuntu-latest - - browser: safari - os: macos-latest + # Build the baseline (main branch) pages once and share them across all browser jobs. + build-baseline: + name: Build baseline pages + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: @@ -39,20 +32,12 @@ jobs: node-version: 20 cache: npm - - name: Setup Chrome and ChromeDriver - if: matrix.browser == 'chrome' - uses: browser-actions/setup-chrome@v1 - with: - chrome-version: stable - - - name: Enable SafariDriver - if: matrix.browser == 'safari' - run: sudo safaridriver --enable - - name: Install dependencies run: npm i # ── Build or download PR (test) pages ───────────────────────────────── + # When called from deploy.yml with pr-artifact-name, download the artifact. + # When run standalone (no pr-artifact-name provided), build the pages locally. - name: Build PR pages locally if: ${{ !inputs.pr-artifact-name }} run: | @@ -69,6 +54,11 @@ jobs: path: pages/lib/static-default # ── Build baseline (main) pages ──────────────────────────────────────── + # Use a git worktree so the baseline has its own directory and its own + # node_modules. This means a PR that changes package-lock.json will still + # produce a correct baseline: the baseline installs from main's lockfile + # and the PR build installs from the PR's lockfile, so both sides use the + # dependency versions that are correct for their respective source trees. - name: Create baseline worktree from origin/main run: git worktree add /tmp/baseline origin/main @@ -83,21 +73,78 @@ jobs: NODE_ENV: production - name: Bundle baseline pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-visual-baseline working-directory: /tmp/baseline env: NODE_ENV: production + - name: Upload baseline artifact + uses: actions/upload-artifact@v4 + with: + name: visual-baseline-pages + path: pages/lib/static-visual-baseline + retention-days: 1 + + - name: Upload PR pages artifact + if: ${{ !inputs.pr-artifact-name }} + uses: actions/upload-artifact@v4 + with: + name: visual-pr-pages + path: pages/lib/static-default + retention-days: 1 + + visual: + name: Visual regression (${{ matrix.browser }}) + needs: build-baseline + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - browser: chrome + os: ubuntu-latest + - browser: safari + os: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Setup Chrome and ChromeDriver + if: matrix.browser == 'chrome' + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable + + - name: Enable SafariDriver + if: matrix.browser == 'safari' + run: sudo safaridriver --enable + + - name: Install dependencies + run: npm i + + - name: Download PR pages artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.pr-artifact-name || 'visual-pr-pages' }} + path: pages/lib/static-default + + - name: Download baseline artifact + uses: actions/download-artifact@v4 + with: + name: visual-baseline-pages + path: pages/lib/static-visual-baseline + # ── Run tests ───────────────────────────────────────────────────────── - name: Start test server (port 8080) run: npx serve --no-clipboard --listen 8080 pages/lib/static-default & - env: - NODE_ENV: development - name: Start baseline server (port 8081) run: npx serve --no-clipboard --listen 8081 pages/lib/static-visual-baseline & - env: - NODE_ENV: development - name: Wait for servers to be ready run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 From e540d320d8c590d02b026a3071f8882ee589d405 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 19:46:53 +0200 Subject: [PATCH 030/135] Limit Safari concurrency --- jest.visual.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jest.visual.config.js b/jest.visual.config.js index 7f1d020880..0d3abc58fd 100644 --- a/jest.visual.config.js +++ b/jest.visual.config.js @@ -16,7 +16,9 @@ module.exports = { }, reporters: ['default', 'github-actions'], testTimeout: 120_000, // 2min — pages can be tall and slow to capture - maxWorkers: os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), + // Safari's WebDriver only supports one concurrent session, so tests must run serially. + // Chrome can run multiple workers to speed things up. + maxWorkers: process.env.BROWSER === 'safari' ? 1 : os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), globalSetup: '/build-tools/visual/global-setup.js', globalTeardown: '/build-tools/visual/global-teardown.js', setupFilesAfterEnv: [path.join(__dirname, 'build-tools', 'visual', 'setup.js')], From 5912a0a8d08bb777c60731d713f7763c610f1061 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 20:05:39 +0200 Subject: [PATCH 031/135] Fix workflow --- .github/workflows/deploy.yml | 3 ++- .github/workflows/visual-regression.yml | 34 +++++++++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 01bb5a45d2..8705c33048 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -73,4 +73,5 @@ jobs: uses: ./.github/workflows/visual-regression.yml secrets: inherit with: - pr-artifact-name: dev-pages-react18 \ No newline at end of file + pr-artifact-name: dev-pages-react18 + caller-run-id: ${{ github.run_id }} \ No newline at end of file diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 2c9e633b05..bedbb5435e 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -7,6 +7,10 @@ on: description: 'Name of the artifact containing PR pages (built by quick-build job). If not provided, pages will be built locally.' required: false type: string + caller-run-id: + description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' + required: false + type: string defaults: run: @@ -15,9 +19,11 @@ defaults: permissions: id-token: write contents: read + actions: read jobs: # Build the baseline (main branch) pages once and share them across all browser jobs. + # Also stages the PR pages artifact within this run so all jobs use the same run context. build-baseline: name: Build baseline pages runs-on: ubuntu-latest @@ -35,9 +41,10 @@ jobs: - name: Install dependencies run: npm i - # ── Build or download PR (test) pages ───────────────────────────────── - # When called from deploy.yml with pr-artifact-name, download the artifact. - # When run standalone (no pr-artifact-name provided), build the pages locally. + # ── Stage PR (test) pages ────────────────────────────────────────────── + # Download from the caller's run (deploy.yml) or build locally. + # Either way, re-upload as 'visual-pr-pages' within this run so the + # matrix jobs can download it without needing cross-run artifact access. - name: Build PR pages locally if: ${{ !inputs.pr-artifact-name }} run: | @@ -46,12 +53,21 @@ jobs: env: NODE_ENV: production - - name: Download PR pages artifact + - name: Download PR pages artifact from caller run if: ${{ inputs.pr-artifact-name }} uses: actions/download-artifact@v4 with: name: ${{ inputs.pr-artifact-name }} path: pages/lib/static-default + github-token: ${{ github.token }} + run-id: ${{ inputs.caller-run-id }} + + - name: Upload PR pages artifact (for matrix jobs) + uses: actions/upload-artifact@v4 + with: + name: visual-pr-pages + path: pages/lib/static-default + retention-days: 1 # ── Build baseline (main) pages ──────────────────────────────────────── # Use a git worktree so the baseline has its own directory and its own @@ -85,14 +101,6 @@ jobs: path: pages/lib/static-visual-baseline retention-days: 1 - - name: Upload PR pages artifact - if: ${{ !inputs.pr-artifact-name }} - uses: actions/upload-artifact@v4 - with: - name: visual-pr-pages - path: pages/lib/static-default - retention-days: 1 - visual: name: Visual regression (${{ matrix.browser }}) needs: build-baseline @@ -130,7 +138,7 @@ jobs: - name: Download PR pages artifact uses: actions/download-artifact@v4 with: - name: ${{ inputs.pr-artifact-name || 'visual-pr-pages' }} + name: visual-pr-pages path: pages/lib/static-default - name: Download baseline artifact From 9e1088639f56352e366955ff9772032b1c42b792 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 20:27:19 +0200 Subject: [PATCH 032/135] Fix workflows --- .github/workflows/deploy.yml | 57 +++++++++++++++++++++++- .github/workflows/visual-regression.yml | 59 ++++++++++--------------- 2 files changed, 78 insertions(+), 38 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8705c33048..8a3b4459b7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,6 +53,58 @@ jobs: name: dev-pages-react${{ matrix.react }} path: pages/lib/static-default + # Build the baseline (main branch) pages in parallel with quick-build. + # The result is passed to the visual regression workflow so it doesn't + # need to rebuild the baseline for each browser. + build-baseline: + name: Build baseline pages + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm i + + # Use a git worktree so the baseline has its own directory and its own + # node_modules. This means a PR that changes package-lock.json will still + # produce a correct baseline: the baseline installs from main's lockfile + # and the PR build installs from the PR's lockfile, so both sides use the + # dependency versions that are correct for their respective source trees. + - name: Create baseline worktree from origin/main + run: git worktree add /tmp/baseline origin/main + + - name: Install baseline dependencies + run: npm i + working-directory: /tmp/baseline + + - name: Build baseline pages + run: npx gulp quick-build + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Bundle baseline pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-visual-baseline + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Upload baseline artifact + uses: actions/upload-artifact@v4 + with: + name: visual-baseline-pages + path: pages/lib/static-visual-baseline + retention-days: 1 + deploy: needs: quick-build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} @@ -68,10 +120,11 @@ jobs: visual: name: Visual regression - needs: quick-build + needs: [quick-build, build-baseline] if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} uses: ./.github/workflows/visual-regression.yml secrets: inherit with: pr-artifact-name: dev-pages-react18 - caller-run-id: ${{ github.run_id }} \ No newline at end of file + baseline-artifact-name: visual-baseline-pages + caller-run-id: ${{ github.run_id }} diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index bedbb5435e..5fab2453e2 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -7,6 +7,10 @@ on: description: 'Name of the artifact containing PR pages (built by quick-build job). If not provided, pages will be built locally.' required: false type: string + baseline-artifact-name: + description: 'Name of the artifact containing baseline pages (built by build-baseline job in the caller workflow).' + required: true + type: string caller-run-id: description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' required: false @@ -22,15 +26,13 @@ permissions: actions: read jobs: - # Build the baseline (main branch) pages once and share them across all browser jobs. - # Also stages the PR pages artifact within this run so all jobs use the same run context. - build-baseline: - name: Build baseline pages + # Stage the PR pages within this run so matrix jobs can download them without + # needing cross-run artifact access. Runs in parallel with stage-baseline. + stage-pr-pages: + name: Stage PR pages runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 @@ -41,10 +43,6 @@ jobs: - name: Install dependencies run: npm i - # ── Stage PR (test) pages ────────────────────────────────────────────── - # Download from the caller's run (deploy.yml) or build locally. - # Either way, re-upload as 'visual-pr-pages' within this run so the - # matrix jobs can download it without needing cross-run artifact access. - name: Build PR pages locally if: ${{ !inputs.pr-artifact-name }} run: | @@ -69,32 +67,21 @@ jobs: path: pages/lib/static-default retention-days: 1 - # ── Build baseline (main) pages ──────────────────────────────────────── - # Use a git worktree so the baseline has its own directory and its own - # node_modules. This means a PR that changes package-lock.json will still - # produce a correct baseline: the baseline installs from main's lockfile - # and the PR build installs from the PR's lockfile, so both sides use the - # dependency versions that are correct for their respective source trees. - - name: Create baseline worktree from origin/main - run: git worktree add /tmp/baseline origin/main - - - name: Install baseline dependencies - run: npm i - working-directory: /tmp/baseline - - - name: Build baseline pages - run: npx gulp quick-build - working-directory: /tmp/baseline - env: - NODE_ENV: production - - - name: Bundle baseline pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-visual-baseline - working-directory: /tmp/baseline - env: - NODE_ENV: production + # Stage the baseline pages within this run so matrix jobs can download them + # without needing cross-run artifact access. Runs in parallel with stage-pr-pages. + stage-baseline: + name: Stage baseline pages + runs-on: ubuntu-latest + steps: + - name: Download baseline artifact from caller run + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.baseline-artifact-name }} + path: pages/lib/static-visual-baseline + github-token: ${{ github.token }} + run-id: ${{ inputs.caller-run-id }} - - name: Upload baseline artifact + - name: Upload baseline artifact (for matrix jobs) uses: actions/upload-artifact@v4 with: name: visual-baseline-pages @@ -103,7 +90,7 @@ jobs: visual: name: Visual regression (${{ matrix.browser }}) - needs: build-baseline + needs: [stage-pr-pages, stage-baseline] runs-on: ${{ matrix.os }} strategy: fail-fast: false From 5c7e8ea108bf8af6fa64e69e5100e65bc3694e4b Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 21:22:00 +0200 Subject: [PATCH 033/135] Fix workflow --- .github/workflows/visual-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 5fab2453e2..9b68239196 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -14,7 +14,7 @@ on: caller-run-id: description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' required: false - type: string + type: number defaults: run: From c42c2e11d0334797394e8cae3453672cc8760309 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 21:41:50 +0200 Subject: [PATCH 034/135] Fix workflow --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8a3b4459b7..45f7ac8fbb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -121,7 +121,7 @@ jobs: visual: name: Visual regression needs: [quick-build, build-baseline] - if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} uses: ./.github/workflows/visual-regression.yml secrets: inherit with: From a1a4d9249ced30203cbc625749c7be61d1358dd3 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 18 May 2026 21:47:43 +0200 Subject: [PATCH 035/135] Fix workflow --- .github/workflows/visual-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 9b68239196..5fab2453e2 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -14,7 +14,7 @@ on: caller-run-id: description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' required: false - type: number + type: string defaults: run: From df04eb67dda894e4ef9d55fdfb55dd18d1b2aa14 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 19 May 2026 01:56:14 +0200 Subject: [PATCH 036/135] Fix workflow --- .github/workflows/deploy.yml | 55 +---------------------- .github/workflows/visual-regression.yml | 59 ++++++++++++++++++------- 2 files changed, 43 insertions(+), 71 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 45f7ac8fbb..2add3494cd 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,58 +53,6 @@ jobs: name: dev-pages-react${{ matrix.react }} path: pages/lib/static-default - # Build the baseline (main branch) pages in parallel with quick-build. - # The result is passed to the visual regression workflow so it doesn't - # need to rebuild the baseline for each browser. - build-baseline: - name: Build baseline pages - if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm - - - name: Install dependencies - run: npm i - - # Use a git worktree so the baseline has its own directory and its own - # node_modules. This means a PR that changes package-lock.json will still - # produce a correct baseline: the baseline installs from main's lockfile - # and the PR build installs from the PR's lockfile, so both sides use the - # dependency versions that are correct for their respective source trees. - - name: Create baseline worktree from origin/main - run: git worktree add /tmp/baseline origin/main - - - name: Install baseline dependencies - run: npm i - working-directory: /tmp/baseline - - - name: Build baseline pages - run: npx gulp quick-build - working-directory: /tmp/baseline - env: - NODE_ENV: production - - - name: Bundle baseline pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-visual-baseline - working-directory: /tmp/baseline - env: - NODE_ENV: production - - - name: Upload baseline artifact - uses: actions/upload-artifact@v4 - with: - name: visual-baseline-pages - path: pages/lib/static-visual-baseline - retention-days: 1 - deploy: needs: quick-build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} @@ -120,11 +68,10 @@ jobs: visual: name: Visual regression - needs: [quick-build, build-baseline] + needs: quick-build if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} uses: ./.github/workflows/visual-regression.yml secrets: inherit with: pr-artifact-name: dev-pages-react18 - baseline-artifact-name: visual-baseline-pages caller-run-id: ${{ github.run_id }} diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 5fab2453e2..8c3c44c0ff 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -7,10 +7,6 @@ on: description: 'Name of the artifact containing PR pages (built by quick-build job). If not provided, pages will be built locally.' required: false type: string - baseline-artifact-name: - description: 'Name of the artifact containing baseline pages (built by build-baseline job in the caller workflow).' - required: true - type: string caller-run-id: description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' required: false @@ -27,7 +23,7 @@ permissions: jobs: # Stage the PR pages within this run so matrix jobs can download them without - # needing cross-run artifact access. Runs in parallel with stage-baseline. + # needing cross-run artifact access. Runs in parallel with build-baseline. stage-pr-pages: name: Stage PR pages runs-on: ubuntu-latest @@ -67,21 +63,50 @@ jobs: path: pages/lib/static-default retention-days: 1 - # Stage the baseline pages within this run so matrix jobs can download them - # without needing cross-run artifact access. Runs in parallel with stage-pr-pages. - stage-baseline: - name: Stage baseline pages + # Build the baseline (main branch) pages once and share them across all browser jobs. + # Runs in parallel with stage-pr-pages. + build-baseline: + name: Build baseline pages runs-on: ubuntu-latest steps: - - name: Download baseline artifact from caller run - uses: actions/download-artifact@v4 + - uses: actions/checkout@v4 with: - name: ${{ inputs.baseline-artifact-name }} - path: pages/lib/static-visual-baseline - github-token: ${{ github.token }} - run-id: ${{ inputs.caller-run-id }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm i + + # Use a git worktree so the baseline has its own directory and its own + # node_modules. This means a PR that changes package-lock.json will still + # produce a correct baseline: the baseline installs from main's lockfile + # and the PR build installs from the PR's lockfile, so both sides use the + # dependency versions that are correct for their respective source trees. + - name: Create baseline worktree from origin/main + run: git worktree add /tmp/baseline origin/main + + - name: Install baseline dependencies + run: npm i + working-directory: /tmp/baseline + + - name: Build baseline pages + run: npx gulp quick-build + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Bundle baseline pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-visual-baseline + working-directory: /tmp/baseline + env: + NODE_ENV: production - - name: Upload baseline artifact (for matrix jobs) + - name: Upload baseline artifact uses: actions/upload-artifact@v4 with: name: visual-baseline-pages @@ -90,7 +115,7 @@ jobs: visual: name: Visual regression (${{ matrix.browser }}) - needs: [stage-pr-pages, stage-baseline] + needs: [stage-pr-pages, build-baseline] runs-on: ${{ matrix.os }} strategy: fail-fast: false From 4b394dc8d097dd48df5986326c4fc47ff1c16c69 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 19 May 2026 02:07:06 +0200 Subject: [PATCH 037/135] Fix workflow --- .github/workflows/visual-regression.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 8c3c44c0ff..4f62e0fa16 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -101,7 +101,7 @@ jobs: NODE_ENV: production - name: Bundle baseline pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-visual-baseline + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline working-directory: /tmp/baseline env: NODE_ENV: production From cfb6a3a747f3a143e198b4e4fffa18a4e58469e0 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 19 May 2026 12:01:18 +0200 Subject: [PATCH 038/135] Add delay between retries in Safari --- build-tools/visual/setup.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/build-tools/visual/setup.js b/build-tools/visual/setup.js index 79050cab6a..a9013a3d61 100644 --- a/build-tools/visual/setup.js +++ b/build-tools/visual/setup.js @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/* global jest */ +/* global jest, afterEach */ const { configure } = require('@cloudscape-design/browser-test-tools/use-browser'); const isSafari = process.env.BROWSER === 'safari'; @@ -18,3 +18,11 @@ configure({ }); jest.retryTimes(2, { logErrorsBeforeRetry: true }); + +// Safari's WebDriver needs a moment to fully release a session before a new one +// can be created. Without this delay, retried tests hit "already paired" errors. +if (isSafari) { + afterEach(async () => { + await new Promise(resolve => setTimeout(resolve, 1000)); + }); +} From e5f7cc6777c6b0522b1eb43be04a4b89f62380a2 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 19 May 2026 12:21:48 +0200 Subject: [PATCH 039/135] Fine tune Safari delay --- build-tools/visual/setup.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build-tools/visual/setup.js b/build-tools/visual/setup.js index a9013a3d61..e6d166d7f3 100644 --- a/build-tools/visual/setup.js +++ b/build-tools/visual/setup.js @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/* global jest, afterEach */ +/* global jest, beforeEach */ const { configure } = require('@cloudscape-design/browser-test-tools/use-browser'); const isSafari = process.env.BROWSER === 'safari'; @@ -20,9 +20,9 @@ configure({ jest.retryTimes(2, { logErrorsBeforeRetry: true }); // Safari's WebDriver needs a moment to fully release a session before a new one -// can be created. Without this delay, retried tests hit "already paired" errors. +// can be created. Without this delay, the next test hits "already paired" errors. if (isSafari) { - afterEach(async () => { - await new Promise(resolve => setTimeout(resolve, 1000)); + beforeEach(async () => { + await new Promise(resolve => setTimeout(resolve, 5000)); }); } From 2ee27a9aa6a78463f27379816471a9d984727f37 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 19 May 2026 14:00:13 +0200 Subject: [PATCH 040/135] Release Safari session between tests --- build-tools/visual/global-setup.js | 6 ++---- build-tools/visual/setup.js | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/build-tools/visual/global-setup.js b/build-tools/visual/global-setup.js index f27f7d04cb..b4acf683c9 100644 --- a/build-tools/visual/global-setup.js +++ b/build-tools/visual/global-setup.js @@ -3,18 +3,16 @@ const { spawn } = require('child_process'); const waitOn = require('wait-on'); -let driverProcess; - module.exports = async () => { if (process.env.BROWSER === 'safari') { - driverProcess = spawn('safaridriver', ['--port', '4444']); + const driverProcess = spawn('safaridriver', ['--port', '4444']); driverProcess.on('error', err => { throw err; }); await waitOn({ resources: ['http-get://localhost:4444/status'], timeout: 10000 }); + global.__DRIVER_PROCESS__ = driverProcess; } else { const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); await startWebdriver(); } - global.__DRIVER_PROCESS__ = driverProcess; }; diff --git a/build-tools/visual/setup.js b/build-tools/visual/setup.js index e6d166d7f3..d8e7ab5779 100644 --- a/build-tools/visual/setup.js +++ b/build-tools/visual/setup.js @@ -1,6 +1,8 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 /* global jest, beforeEach */ +const { spawn } = require('child_process'); +const waitOn = require('wait-on'); const { configure } = require('@cloudscape-design/browser-test-tools/use-browser'); const isSafari = process.env.BROWSER === 'safari'; @@ -19,10 +21,18 @@ configure({ jest.retryTimes(2, { logErrorsBeforeRetry: true }); -// Safari's WebDriver needs a moment to fully release a session before a new one -// can be created. Without this delay, the next test hits "already paired" errors. +// Local safaridriver only supports one session at a time and doesn't reliably +// release the session lock between tests. Restarting the process before each +// test guarantees a clean state. This is not needed with BrowserStack. if (isSafari) { + let safariDriverProcess; + beforeEach(async () => { - await new Promise(resolve => setTimeout(resolve, 5000)); + if (safariDriverProcess) { + safariDriverProcess.kill(); + await new Promise(resolve => setTimeout(resolve, 500)); + } + safariDriverProcess = spawn('safaridriver', ['--port', '4444']); + await waitOn({ resources: ['http-get://localhost:4444/status'], timeout: 10000 }); }); } From 911ede4d462adc1db1dbeaec0eca422a576af59a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 19 May 2026 17:07:46 +0200 Subject: [PATCH 041/135] Do not retry with Safari --- build-tools/visual/setup.js | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/build-tools/visual/setup.js b/build-tools/visual/setup.js index d8e7ab5779..c63a18416f 100644 --- a/build-tools/visual/setup.js +++ b/build-tools/visual/setup.js @@ -1,8 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -/* global jest, beforeEach */ -const { spawn } = require('child_process'); -const waitOn = require('wait-on'); +/* global jest */ const { configure } = require('@cloudscape-design/browser-test-tools/use-browser'); const isSafari = process.env.BROWSER === 'safari'; @@ -19,20 +17,9 @@ configure({ }, }); -jest.retryTimes(2, { logErrorsBeforeRetry: true }); - -// Local safaridriver only supports one session at a time and doesn't reliably -// release the session lock between tests. Restarting the process before each -// test guarantees a clean state. This is not needed with BrowserStack. -if (isSafari) { - let safariDriverProcess; - - beforeEach(async () => { - if (safariDriverProcess) { - safariDriverProcess.kill(); - await new Promise(resolve => setTimeout(resolve, 500)); - } - safariDriverProcess = spawn('safaridriver', ['--port', '4444']); - await waitOn({ resources: ['http-get://localhost:4444/status'], timeout: 10000 }); - }); +// Retries help with flaky tests, but Safari's single-session constraint means +// a retry can hit "already paired" if the previous attempt's session hasn't +// fully released. Disable retries for Safari. +if (!isSafari) { + jest.retryTimes(2, { logErrorsBeforeRetry: true }); } From c580c7fa55262e46602c9a184364025799ddfa0d Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 19 May 2026 18:43:41 +0200 Subject: [PATCH 042/135] Change target directory --- build-tools/tasks/index.js | 5 -- build-tools/tasks/visual-definitions.js | 8 -- build-tools/tasks/visual.js | 107 ------------------------ tsconfig.visual-definitions.json | 2 +- 4 files changed, 1 insertion(+), 121 deletions(-) delete mode 100644 build-tools/tasks/visual-definitions.js delete mode 100644 build-tools/tasks/visual.js diff --git a/build-tools/tasks/index.js b/build-tools/tasks/index.js index 413695be14..d7db3c0784 100644 --- a/build-tools/tasks/index.js +++ b/build-tools/tasks/index.js @@ -21,10 +21,5 @@ module.exports = { themeableSource: require('./themeable-source'), bundleVendorFiles: require('./bundle-vendor-files'), sizeLimit: require('./size-limit'), -<<<<<<< HEAD testDefinitions: require('./test-definitions'), -======= - visual: require('./visual'), - visualDefinitions: require('./visual-definitions'), ->>>>>>> 4213557f5 (chore: Export visual test definitions) }; diff --git a/build-tools/tasks/visual-definitions.js b/build-tools/tasks/visual-definitions.js deleted file mode 100644 index 34dfe16adf..0000000000 --- a/build-tools/tasks/visual-definitions.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -const execa = require('execa'); -const { task } = require('../utils/gulp-utils'); - -module.exports = task('visual-definitions', () => - execa('tsc', ['-p', 'tsconfig.visual-definitions.json'], { stdio: 'inherit' }) -); diff --git a/build-tools/tasks/visual.js b/build-tools/tasks/visual.js deleted file mode 100644 index 0864790afb..0000000000 --- a/build-tools/tasks/visual.js +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -const execa = require('execa'); -const path = require('path'); -const fs = require('fs'); -const waitOn = require('wait-on'); -const { task } = require('../utils/gulp-utils.js'); -const { parseArgs } = require('node:util'); - -const BASELINE_WORKTREE = '/tmp/visual-baseline'; -const BASELINE_OUTPUT = path.resolve('pages/lib/static-visual-baseline'); -const TEST_OUTPUT = path.resolve('pages/lib/static-default'); - -// Port assignments: -// 8080 — test build (PR / local changes) -// 8081 — baseline build (main branch) -const TEST_PORT = 8080; -const BASELINE_PORT = 8081; - -/** - * Serves a pre-built static directory using webpack-dev-server in static mode. - */ -function serveStatic(dir, port) { - return execa( - 'node_modules/.bin/webpack', - ['serve', '--config', 'pages/webpack.config.integ.cjs', '--port', String(port), '--static', dir, '--no-hot'], - { env: { ...process.env, NODE_ENV: 'development' } } - ); -} - -/** - * Builds the dev pages from the source tree at `cwd` into `outputPath`. - * Uses the node_modules present in `cwd`. - */ -async function buildPages(cwd, outputPath) { - await execa('npx', ['gulp', 'quick-build'], { - stdio: 'inherit', - cwd, - env: { ...process.env, NODE_ENV: 'production' }, - }); - await execa( - path.join(cwd, 'node_modules/.bin/webpack'), - ['--config', 'pages/webpack.config.integ.cjs', '--output-path', outputPath], - { stdio: 'inherit', cwd, env: { ...process.env, NODE_ENV: 'production' } } - ); -} - -module.exports = task('test:visual', async () => { - const options = { - shard: { type: 'string' }, - // Pass --skip-build to skip the build steps when artifacts are already present. - skipBuild: { type: 'boolean' }, - }; - const { shard, skipBuild } = parseArgs({ options, strict: false }).values; - - const cwd = process.cwd(); - - if (!skipBuild) { - // ── 1. Build the test (PR) pages ──────────────────────────────────────── - console.log('Building test pages (current branch)…'); - await buildPages(cwd, TEST_OUTPUT); - - // ── 2. Build the baseline (main) pages ────────────────────────────────── - // Create a worktree for origin/main so it gets its own node_modules. - // This correctly handles PRs that change package-lock.json: each side - // installs from its own lockfile. - console.log('Setting up baseline worktree from origin/main…'); - if (fs.existsSync(BASELINE_WORKTREE)) { - await execa('git', ['worktree', 'remove', '--force', BASELINE_WORKTREE]); - } - await execa('git', ['worktree', 'add', BASELINE_WORKTREE, 'origin/main']); - - try { - console.log('Installing baseline dependencies…'); - await execa('npm', ['ci'], { stdio: 'inherit', cwd: BASELINE_WORKTREE }); - - console.log('Building baseline pages (origin/main)…'); - await buildPages(BASELINE_WORKTREE, BASELINE_OUTPUT); - } finally { - await execa('git', ['worktree', 'remove', '--force', BASELINE_WORKTREE]); - } - } - - // ── 3. Start both static servers ────────────────────────────────────────── - console.log(`Starting test server on :${TEST_PORT} (${TEST_OUTPUT})…`); - const testServer = serveStatic(TEST_OUTPUT, TEST_PORT); - - console.log(`Starting baseline server on :${BASELINE_PORT} (${BASELINE_OUTPUT})…`); - const baselineServer = serveStatic(BASELINE_OUTPUT, BASELINE_PORT); - - try { - await waitOn({ resources: [`http://localhost:${TEST_PORT}`, `http://localhost:${BASELINE_PORT}`] }); - - // ── 4. Run visual tests ────────────────────────────────────────────────── - const jestArgs = ['-c', 'jest.visual.config.js']; - if (shard) { - jestArgs.push(`--shard=${shard}`); - } - await execa('jest', jestArgs, { - stdio: 'inherit', - env: { ...process.env, NODE_OPTIONS: '--experimental-vm-modules' }, - }); - } finally { - testServer.cancel(); - baselineServer.cancel(); - } -}); diff --git a/tsconfig.visual-definitions.json b/tsconfig.visual-definitions.json index a31ccd02d3..fbc322c92a 100644 --- a/tsconfig.visual-definitions.json +++ b/tsconfig.visual-definitions.json @@ -12,7 +12,7 @@ "sourceMap": true, "inlineSources": true, "rootDir": "test/visual", - "outDir": "lib/visual-test-definitions" + "outDir": "lib/test-definitions" }, "include": ["test/visual/definitions", "test/visual/types.ts"], "exclude": [] From 10d9a9e73bf335adf29f90dffc41b469439ae0ee Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 21 May 2026 16:35:14 +0200 Subject: [PATCH 043/135] Remove local testing setup --- gulpfile.js | 4 ---- tsconfig.visual-definitions.json | 19 ------------------- 2 files changed, 23 deletions(-) delete mode 100644 tsconfig.visual-definitions.json diff --git a/gulpfile.js b/gulpfile.js index 09e0833868..6e3f389082 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,8 +19,6 @@ const { generateI18nMessages, integ, motion, - visual, - visualDefinitions, copyFiles, themeableSource, bundleVendorFiles, @@ -45,8 +43,6 @@ exports['test:unit'] = unit; exports['test:integ'] = integ; exports['test:a11y'] = a11y; exports['test:motion'] = motion; -exports['test:visual'] = visual; -exports['build:visual-definitions'] = visualDefinitions; exports.watch = () => { watch( diff --git a/tsconfig.visual-definitions.json b/tsconfig.visual-definitions.json deleted file mode 100644 index fbc322c92a..0000000000 --- a/tsconfig.visual-definitions.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "lib": ["ES2021", "DOM"], - "target": "ES2019", - "types": [], - "module": "CommonJS", - "moduleResolution": "node", - "esModuleInterop": true, - "strict": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "inlineSources": true, - "rootDir": "test/visual", - "outDir": "lib/test-definitions" - }, - "include": ["test/visual/definitions", "test/visual/types.ts"], - "exclude": [] -} From 596ae2337fb231d2398248e9b022bd13e56925e8 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 21 May 2026 16:44:41 +0200 Subject: [PATCH 044/135] Refactor --- docs/RUNNING_TESTS.md | 4 +- .../visual/compare-screenshots.ts | 4 +- test/{visual => }/visual.test.ts | 2 +- test/visual/definitions/action-card.ts | 31 ---------------- test/visual/definitions/alert.ts | 37 ------------------- test/visual/definitions/index.ts | 10 ----- test/visual/types.ts | 28 -------------- 7 files changed, 6 insertions(+), 110 deletions(-) rename test/{ => definitions}/visual/compare-screenshots.ts (96%) rename test/{visual => }/visual.test.ts (70%) delete mode 100644 test/visual/definitions/action-card.ts delete mode 100644 test/visual/definitions/alert.ts delete mode 100644 test/visual/definitions/index.ts delete mode 100644 test/visual/types.ts diff --git a/docs/RUNNING_TESTS.md b/docs/RUNNING_TESTS.md index c5c483e981..6e0eb84503 100644 --- a/docs/RUNNING_TESTS.md +++ b/docs/RUNNING_TESTS.md @@ -86,7 +86,7 @@ NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.con ### Adding tests for a new component -Create `test/visual/definitions/.ts`: +Create `test/definitions/visual/.ts`: ```ts import { TestSuite } from '../types'; @@ -104,7 +104,7 @@ const suite: TestSuite = { export default suite; ``` -Then import and add it to `test/visual/definitions/index.ts`: +Then import and add it to `test/definitions/visual/index.ts`: ```ts import myComponent from './my-component'; diff --git a/test/visual/compare-screenshots.ts b/test/definitions/visual/compare-screenshots.ts similarity index 96% rename from test/visual/compare-screenshots.ts rename to test/definitions/visual/compare-screenshots.ts index 8847e10d21..f81b8afc56 100644 --- a/test/visual/compare-screenshots.ts +++ b/test/definitions/visual/compare-screenshots.ts @@ -7,7 +7,7 @@ import { parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; import useBrowser from '@cloudscape-design/browser-test-tools/use-browser'; -import { TestDefinition, TestSuite } from './types'; +import { TestDefinition, TestSuite } from '../types'; const screenshotAreaSelector = '.screenshot-area'; const defaultWindowSize = { width: 1600, height: 800 }; @@ -97,6 +97,8 @@ function runSingleTest(testDef: TestDefinition) { test( testDef.description, + // useBrowser is not a React hook, despite the name + // eslint-disable-next-line react-hooks/rules-of-hooks useBrowser(windowSize, async browser => { const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const newScreenshot = await captureScreenshot(browser, newUrl, testDef, testDef.setup); diff --git a/test/visual/visual.test.ts b/test/visual.test.ts similarity index 70% rename from test/visual/visual.test.ts rename to test/visual.test.ts index 06ef0fa8a4..bc7369d6a9 100644 --- a/test/visual/visual.test.ts +++ b/test/visual.test.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from './compare-screenshots'; import { allSuites } from './definitions'; +import { runTestSuites } from './definitions/visual/compare-screenshots'; runTestSuites(allSuites); diff --git a/test/visual/definitions/action-card.ts b/test/visual/definitions/action-card.ts deleted file mode 100644 index 8730ac9504..0000000000 --- a/test/visual/definitions/action-card.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'action-card', - tests: [ - { - description: 'permutations', - path: 'action-card/permutations', - screenshotType: 'permutations', - }, - { - description: 'variant permutations', - path: 'action-card/variant-permutations', - screenshotType: 'permutations', - }, - { - description: 'padding permutations', - path: 'action-card/padding-permutations', - screenshotType: 'permutations', - }, - { - description: 'simple', - path: 'action-card/simple', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/visual/definitions/alert.ts b/test/visual/definitions/alert.ts deleted file mode 100644 index 30792c0d2d..0000000000 --- a/test/visual/definitions/alert.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'alert', - tests: [ - { - description: 'simple', - path: 'alert/simple', - screenshotType: 'screenshotArea', - }, - { - description: 'style custom page', - path: 'alert/style-custom-types', - screenshotType: 'screenshotArea', - }, - ...[600, 1280].map(width => ({ - description: `width ${width}px`, - tests: [ - { - description: 'permutations', - path: 'alert/permutations', - screenshotType: 'permutations' as const, - }, - { - description: 'custom types', - path: 'alert/style-custom-types', - screenshotType: 'screenshotArea' as const, - }, - ], - })), - ], -}; - -export default suite; diff --git a/test/visual/definitions/index.ts b/test/visual/definitions/index.ts deleted file mode 100644 index 318ce7c68b..0000000000 --- a/test/visual/definitions/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Each component has its own test definition file. -// Import them here manually to form the full test suite. -import { TestSuite } from '../types'; -import actionCard from './action-card'; -import alert from './alert'; - -export const allSuites: TestSuite[] = [actionCard, alert]; diff --git a/test/visual/types.ts b/test/visual/types.ts deleted file mode 100644 index c4c23b622f..0000000000 --- a/test/visual/types.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import type { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; - -export interface ScreenshotTestConfiguration { - width?: number; - height?: number; -} - -export type TestCallback = (page: ScreenshotPageObject) => Promise; - -// 'screenshotArea' — captures the .screenshot-area element on a focused page. -// 'permutations' — captures the entire page and crops permutations out of it. -export type ScreenshotType = 'screenshotArea' | 'permutations'; - -export interface TestDefinition { - description: string; - path: string; - screenshotType: ScreenshotType; - queryParams?: Record; - configuration?: ScreenshotTestConfiguration; - setup?: TestCallback; -} - -export interface TestSuite { - description: string; - tests: Array; -} From 56086de726d78771d8183052222cf4dcb151d79e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 27 May 2026 10:34:20 +0200 Subject: [PATCH 045/135] Refactor --- test/definitions/utils.ts | 72 +++++++++++ .../definitions/visual/compare-screenshots.ts | 115 ------------------ test/visual.test.ts | 2 +- 3 files changed, 73 insertions(+), 116 deletions(-) create mode 100644 test/definitions/utils.ts delete mode 100644 test/definitions/visual/compare-screenshots.ts diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts new file mode 100644 index 0000000000..2a650f79ed --- /dev/null +++ b/test/definitions/utils.ts @@ -0,0 +1,72 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; +import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; +import useBrowser from '@cloudscape-design/browser-test-tools/use-browser'; + +import { TestDefinition, TestSuite } from './types'; + +const screenshotAreaSelector = '.screenshot-area'; +const defaultWindowSize = { width: 1600, height: 800 }; + +// NEW_HOST serves the PR's pages, OLD_HOST serves the baseline (main) pages. +const newHost = process.env.NEW_HOST || 'http://localhost:8080'; +const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; + +function buildUrl(host: string, path: string, queryParams?: Record): string { + const params = new URLSearchParams(queryParams); + const qs = params.toString(); + return `${host}/#/${path}${qs ? `?${qs}` : ''}`; +} + +function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinition { + return (item as TestDefinition).path !== undefined; +} + +export function runTestSuites(suites: Array) { + for (const item of suites) { + if (isTestDefinition(item)) { + runSingleTest(item); + } else { + describe(item.description, () => { + runTestSuites(item.tests); + }); + } + } +} + +function runSingleTest(testDef: TestDefinition) { + const windowSize = { ...defaultWindowSize, ...testDef.configuration }; + + test( + testDef.description, + // useBrowser is not a React hook, despite the name + // eslint-disable-next-line react-hooks/rules-of-hooks + useBrowser(windowSize, async browser => { + const page = new ScreenshotPageObject(browser); + + const capture = async (host: string) => { + await browser.url(buildUrl(host, testDef.path, testDef.queryParams)); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup(page); + } + return testDef.screenshotType === 'permutations' + ? page.capturePermutations() + : page.captureBySelector(screenshotAreaSelector); + }; + + const newScreenshots = await capture(newHost); + const oldScreenshots = await capture(oldHost); + + const newArr = Array.isArray(newScreenshots) ? newScreenshots : [newScreenshots]; + const oldArr = Array.isArray(oldScreenshots) ? oldScreenshots : [oldScreenshots]; + + expect(newArr.length).toBe(oldArr.length); + for (let i = 0; i < newArr.length; i++) { + const { diffPixels } = await cropAndCompare(newArr[i], oldArr[i]); + expect(diffPixels).toBe(0); + } + }) + ); +} diff --git a/test/definitions/visual/compare-screenshots.ts b/test/definitions/visual/compare-screenshots.ts deleted file mode 100644 index f81b8afc56..0000000000 --- a/test/definitions/visual/compare-screenshots.ts +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import pixelmatch from 'pixelmatch'; -import { PNG } from 'pngjs'; - -import { parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; -import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; -import useBrowser from '@cloudscape-design/browser-test-tools/use-browser'; - -import { TestDefinition, TestSuite } from '../types'; - -const screenshotAreaSelector = '.screenshot-area'; -const defaultWindowSize = { width: 1600, height: 800 }; - -// NEW_HOST serves the PR's pages, OLD_HOST serves the baseline (main) pages. -const newHost = process.env.NEW_HOST || 'http://localhost:8080'; -const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; - -/** - * Captures the .screenshot-area element on a focused page. - * Uses a standard ScreenshotPageObject (no forced scroll-and-merge). - */ -async function captureScreenshotArea(browser: WebdriverIO.Browser, url: string): Promise { - await browser.url(url); - const page = new ScreenshotPageObject(browser); - await page.waitForVisible(screenshotAreaSelector); - const { image } = await page.captureBySelector(screenshotAreaSelector); - return image; -} - -/** - * Captures the full page as a PNG for permutation pages. - * Uses fullPageScreenshot which handles pages taller than the viewport. - */ -async function capturePermutations(browser: WebdriverIO.Browser, url: string): Promise { - await browser.url(url); - const page = new ScreenshotPageObject(browser); - await page.waitForVisible(screenshotAreaSelector); - const base64 = await page.fullPageScreenshot(); - return parsePng(base64); -} - -async function captureScreenshot( - browser: WebdriverIO.Browser, - url: string, - testDef: TestDefinition, - setup?: (page: ScreenshotPageObject) => Promise -): Promise { - if (setup) { - await browser.url(url); - const page = new ScreenshotPageObject(browser); - await page.waitForVisible(screenshotAreaSelector); - await setup(page); - if (testDef.screenshotType === 'permutations') { - const base64 = await page.fullPageScreenshot(); - return parsePng(base64); - } - const { image } = await page.captureBySelector(screenshotAreaSelector); - return image; - } - if (testDef.screenshotType === 'permutations') { - return capturePermutations(browser, url); - } - return captureScreenshotArea(browser, url); -} - -function buildUrl(host: string, path: string, queryParams?: Record): string { - const params = new URLSearchParams(queryParams); - const qs = params.toString(); - return `${host}/#/${path}${qs ? `?${qs}` : ''}`; -} - -function compareImages(newImage: PNG, oldImage: PNG): number { - const { width, height } = newImage; - const diff = new PNG({ width, height }); - return pixelmatch(newImage.data, oldImage.data, diff.data, width, height, { threshold: 0.1 }); -} - -function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinition { - return (item as TestDefinition).path !== undefined; -} - -export function runTestSuites(suites: Array) { - for (const item of suites) { - if (isTestDefinition(item)) { - runSingleTest(item); - } else { - describe(item.description, () => { - runTestSuites(item.tests); - }); - } - } -} - -function runSingleTest(testDef: TestDefinition) { - const windowSize = { ...defaultWindowSize, ...testDef.configuration }; - - test( - testDef.description, - // useBrowser is not a React hook, despite the name - // eslint-disable-next-line react-hooks/rules-of-hooks - useBrowser(windowSize, async browser => { - const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); - const newScreenshot = await captureScreenshot(browser, newUrl, testDef, testDef.setup); - - const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - const oldScreenshot = await captureScreenshot(browser, oldUrl, testDef, testDef.setup); - const diffPixels = compareImages(newScreenshot, oldScreenshot); - expect(diffPixels).toBe(0); - }) - ); -} - -// Export the capture functions for use in custom setup callbacks if needed. -export { captureScreenshotArea, capturePermutations }; diff --git a/test/visual.test.ts b/test/visual.test.ts index bc7369d6a9..4e6e992f4f 100644 --- a/test/visual.test.ts +++ b/test/visual.test.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { allSuites } from './definitions'; -import { runTestSuites } from './definitions/visual/compare-screenshots'; +import { runTestSuites } from './definitions/utils'; runTestSuites(allSuites); From 7bfe43ee054960d2f55f005860f69e01591b5c76 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 27 May 2026 10:38:40 +0200 Subject: [PATCH 046/135] Fix setup --- tsconfig.integ.json | 2 +- tsconfig.test-definitions.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.integ.json b/tsconfig.integ.json index e816f749e9..784a3d2f1f 100644 --- a/tsconfig.integ.json +++ b/tsconfig.integ.json @@ -11,5 +11,5 @@ "resolveJsonModule": true, "moduleResolution": "node" }, - "include": ["**/__integ__/**/*.ts", "**/__a11y__/**/*.ts", "**/__motion__/**/*.ts", "types"] + "include": ["**/__integ__/**/*.ts", "**/__a11y__/**/*.ts", "**/__motion__/**/*.ts", "test/definitions/utils.ts", "test/visual.test.ts", "types"] } diff --git a/tsconfig.test-definitions.json b/tsconfig.test-definitions.json index 30e82b4047..3f5961a78b 100644 --- a/tsconfig.test-definitions.json +++ b/tsconfig.test-definitions.json @@ -16,5 +16,5 @@ "skipLibCheck": true }, "include": ["test/definitions", "test/types.ts"], - "exclude": [] + "exclude": ["test/definitions/utils.ts"] } From 009f97781ec8df977b28eccccf731b42d852b2c6 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 27 May 2026 10:49:40 +0200 Subject: [PATCH 047/135] Fix setup --- docs/RUNNING_TESTS.md | 2 +- eslint.config.mjs | 2 +- jest.visual.config.js | 2 +- test/definitions/utils.ts | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/RUNNING_TESTS.md b/docs/RUNNING_TESTS.md index 6e0eb84503..c7832dca72 100644 --- a/docs/RUNNING_TESTS.md +++ b/docs/RUNNING_TESTS.md @@ -68,7 +68,7 @@ They compare permutation pages between the PR build and a baseline build of `mai 1. The PR pages are built and served on port 8080. 2. A git worktree of `origin/main` is created, its dependencies installed, and its pages built and served on port 8081. -3. The single test runner (`test/visual/visual.test.ts`) iterates over all test definitions, captures the `.screenshot-area` element from both servers for each test, and fails if any pixels differ. +3. The single test runner (`test/visual.test.ts`) iterates over all test definitions, captures the `.screenshot-area` element from both servers for each test, and fails if any pixels differ. ### Running locally diff --git a/eslint.config.mjs b/eslint.config.mjs index f03eb9ce60..b92a169696 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -225,7 +225,7 @@ export default tsEslint.config( }, }, { - files: ['**/__integ__/**', '**/__motion__/**', '**/__a11y__/**', 'test/visual/**'], + files: ['**/__integ__/**', '**/__motion__/**', '**/__a11y__/**', 'test/definitions/**'], rules: { // useBrowser is not a hook 'react-hooks/rules-of-hooks': 'off', diff --git a/jest.visual.config.js b/jest.visual.config.js index 0d3abc58fd..097054bb69 100644 --- a/jest.visual.config.js +++ b/jest.visual.config.js @@ -23,5 +23,5 @@ module.exports = { globalTeardown: '/build-tools/visual/global-teardown.js', setupFilesAfterEnv: [path.join(__dirname, 'build-tools', 'visual', 'setup.js')], moduleFileExtensions: ['js', 'ts'], - testMatch: ['/test/visual/visual.test.ts'], + testMatch: ['/test/visual.test.ts'], }; diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 2a650f79ed..b29d3a8543 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -40,8 +40,6 @@ function runSingleTest(testDef: TestDefinition) { test( testDef.description, - // useBrowser is not a React hook, despite the name - // eslint-disable-next-line react-hooks/rules-of-hooks useBrowser(windowSize, async browser => { const page = new ScreenshotPageObject(browser); From eede7d95d5a600f6a0e4d794c26374081b44387a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 27 May 2026 11:59:05 +0200 Subject: [PATCH 048/135] Create one single browser session for all tests --- test/definitions/utils.ts | 88 ++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 33 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index b29d3a8543..253d5c008b 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -1,8 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; -import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; -import useBrowser from '@cloudscape-design/browser-test-tools/use-browser'; +import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; import { TestDefinition, TestSuite } from './types'; @@ -23,48 +22,71 @@ function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinit return (item as TestDefinition).path !== undefined; } +/** + * Registers all test suites. Creates a single shared browser session for the + * entire suite rather than spinning up a new session per test, which is the + * main source of overhead compared to AWS-UI-IntegrationTests. + */ export function runTestSuites(suites: Array) { + let browser: WebdriverIO.Browser; + + beforeAll(async () => { + const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); + const isSafari = process.env.BROWSER === 'safari'; + const browserName = isSafari ? 'Safari' : 'ChromeHeadlessIntegration'; + const seleniumUrl = isSafari ? 'http://localhost:4444' : 'http://localhost:9515'; + const creator = getBrowserCreator(browserName, 'local', { seleniumUrl }); + browser = await creator.getBrowser({ width: defaultWindowSize.width, height: defaultWindowSize.height }); + }); + + afterAll(async () => { + await browser?.deleteSession(); + }); + + registerSuites(suites, () => browser); +} + +function registerSuites(suites: Array, getBrowser: () => WebdriverIO.Browser) { for (const item of suites) { if (isTestDefinition(item)) { - runSingleTest(item); + registerTest(item, getBrowser); } else { describe(item.description, () => { - runTestSuites(item.tests); + registerSuites(item.tests, getBrowser); }); } } } -function runSingleTest(testDef: TestDefinition) { +function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { const windowSize = { ...defaultWindowSize, ...testDef.configuration }; - test( - testDef.description, - useBrowser(windowSize, async browser => { - const page = new ScreenshotPageObject(browser); - - const capture = async (host: string) => { - await browser.url(buildUrl(host, testDef.path, testDef.queryParams)); - await page.waitForVisible(screenshotAreaSelector); - if (testDef.setup) { - await testDef.setup(page); - } - return testDef.screenshotType === 'permutations' - ? page.capturePermutations() - : page.captureBySelector(screenshotAreaSelector); - }; - - const newScreenshots = await capture(newHost); - const oldScreenshots = await capture(oldHost); - - const newArr = Array.isArray(newScreenshots) ? newScreenshots : [newScreenshots]; - const oldArr = Array.isArray(oldScreenshots) ? oldScreenshots : [oldScreenshots]; - - expect(newArr.length).toBe(oldArr.length); - for (let i = 0; i < newArr.length; i++) { - const { diffPixels } = await cropAndCompare(newArr[i], oldArr[i]); - expect(diffPixels).toBe(0); + test(testDef.description, async () => { + const browser = getBrowser(); + await browser.setWindowSize(windowSize.width, windowSize.height); + const page = new ScreenshotPageObject(browser); + + const capture = async (host: string) => { + await browser.url(buildUrl(host, testDef.path, testDef.queryParams)); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup(page); } - }) - ); + return testDef.screenshotType === 'permutations' + ? page.capturePermutations() + : page.captureBySelector(screenshotAreaSelector); + }; + + const newScreenshots = await capture(newHost); + const oldScreenshots = await capture(oldHost); + + const newArr: ScreenshotWithOffset[] = Array.isArray(newScreenshots) ? newScreenshots : [newScreenshots]; + const oldArr: ScreenshotWithOffset[] = Array.isArray(oldScreenshots) ? oldScreenshots : [oldScreenshots]; + + expect(newArr.length).toBe(oldArr.length); + for (let i = 0; i < newArr.length; i++) { + const { diffPixels } = await cropAndCompare(newArr[i], oldArr[i]); + expect(diffPixels).toBe(0); + } + }); } From 2ae6d2ae560ce8cbd258e72bb1278d8024cfdd4d Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 27 May 2026 12:35:06 +0200 Subject: [PATCH 049/135] Try to fix Safari --- test/definitions/utils.ts | 115 +++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 45 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 253d5c008b..369026bd02 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -12,41 +12,55 @@ const defaultWindowSize = { width: 1600, height: 800 }; const newHost = process.env.NEW_HOST || 'http://localhost:8080'; const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; +const isSafari = process.env.BROWSER === 'safari'; + function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams(queryParams); const qs = params.toString(); return `${host}/#/${path}${qs ? `?${qs}` : ''}`; } +async function createBrowser(windowSize = defaultWindowSize): Promise { + const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); + const browserName = isSafari ? 'Safari' : 'ChromeHeadlessIntegration'; + const seleniumUrl = isSafari ? 'http://localhost:4444' : 'http://localhost:9515'; + const creator = getBrowserCreator(browserName, 'local', { seleniumUrl }); + return creator.getBrowser({ width: windowSize.width, height: windowSize.height }); +} + function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinition { return (item as TestDefinition).path !== undefined; } /** - * Registers all test suites. Creates a single shared browser session for the - * entire suite rather than spinning up a new session per test, which is the - * main source of overhead compared to AWS-UI-IntegrationTests. + * Registers all test suites. For Chrome, creates a single shared browser + * session for the entire suite to avoid per-test session overhead. + * + * Safari only supports one WebDriver session at a time and is sensitive to + * session teardown timing, so for Safari we create a fresh session per test. */ export function runTestSuites(suites: Array) { - let browser: WebdriverIO.Browser; - - beforeAll(async () => { - const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); - const isSafari = process.env.BROWSER === 'safari'; - const browserName = isSafari ? 'Safari' : 'ChromeHeadlessIntegration'; - const seleniumUrl = isSafari ? 'http://localhost:4444' : 'http://localhost:9515'; - const creator = getBrowserCreator(browserName, 'local', { seleniumUrl }); - browser = await creator.getBrowser({ width: defaultWindowSize.width, height: defaultWindowSize.height }); - }); - - afterAll(async () => { - await browser?.deleteSession(); - }); - - registerSuites(suites, () => browser); + if (isSafari) { + // Per-test sessions: safe for Safari's single-session constraint. + registerSuites(suites, null); + } else { + // Shared session: one browser for all tests in this worker. + let browser: WebdriverIO.Browser; + + beforeAll(async () => { + browser = await createBrowser(); + }); + + afterAll(async () => { + await browser?.deleteSession(); + }); + + registerSuites(suites, () => browser); + } } -function registerSuites(suites: Array, getBrowser: () => WebdriverIO.Browser) { +// getBrowser === null means "create a fresh session per test" (Safari mode). +function registerSuites(suites: Array, getBrowser: (() => WebdriverIO.Browser) | null) { for (const item of suites) { if (isTestDefinition(item)) { registerTest(item, getBrowser); @@ -58,35 +72,46 @@ function registerSuites(suites: Array, getBrowser: ( } } -function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { +function registerTest(testDef: TestDefinition, getBrowser: (() => WebdriverIO.Browser) | null) { const windowSize = { ...defaultWindowSize, ...testDef.configuration }; test(testDef.description, async () => { - const browser = getBrowser(); - await browser.setWindowSize(windowSize.width, windowSize.height); - const page = new ScreenshotPageObject(browser); - - const capture = async (host: string) => { - await browser.url(buildUrl(host, testDef.path, testDef.queryParams)); - await page.waitForVisible(screenshotAreaSelector); - if (testDef.setup) { - await testDef.setup(page); - } - return testDef.screenshotType === 'permutations' - ? page.capturePermutations() - : page.captureBySelector(screenshotAreaSelector); - }; - - const newScreenshots = await capture(newHost); - const oldScreenshots = await capture(oldHost); - - const newArr: ScreenshotWithOffset[] = Array.isArray(newScreenshots) ? newScreenshots : [newScreenshots]; - const oldArr: ScreenshotWithOffset[] = Array.isArray(oldScreenshots) ? oldScreenshots : [oldScreenshots]; + // Safari: create and destroy a session per test. + // Chrome: reuse the shared session, just resize the window. + const browser = getBrowser ? getBrowser() : await createBrowser(windowSize); + if (getBrowser) { + await browser.setWindowSize(windowSize.width, windowSize.height); + } - expect(newArr.length).toBe(oldArr.length); - for (let i = 0; i < newArr.length; i++) { - const { diffPixels } = await cropAndCompare(newArr[i], oldArr[i]); - expect(diffPixels).toBe(0); + try { + const page = new ScreenshotPageObject(browser); + + const capture = async (host: string) => { + await browser.url(buildUrl(host, testDef.path, testDef.queryParams)); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup(page); + } + return testDef.screenshotType === 'permutations' + ? page.capturePermutations() + : page.captureBySelector(screenshotAreaSelector); + }; + + const newScreenshots = await capture(newHost); + const oldScreenshots = await capture(oldHost); + + const newArr: ScreenshotWithOffset[] = Array.isArray(newScreenshots) ? newScreenshots : [newScreenshots]; + const oldArr: ScreenshotWithOffset[] = Array.isArray(oldScreenshots) ? oldScreenshots : [oldScreenshots]; + + expect(newArr.length).toBe(oldArr.length); + for (let i = 0; i < newArr.length; i++) { + const { diffPixels } = await cropAndCompare(newArr[i], oldArr[i]); + expect(diffPixels).toBe(0); + } + } finally { + if (!getBrowser) { + await browser.deleteSession(); + } } }); } From e4045ad6523ae265629c001b3ca257339d57d0b7 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 27 May 2026 12:56:37 +0200 Subject: [PATCH 050/135] Try again --- build-tools/visual/global-setup.js | 11 +++ test/definitions/utils.ts | 112 ++++++++++++----------------- 2 files changed, 55 insertions(+), 68 deletions(-) diff --git a/build-tools/visual/global-setup.js b/build-tools/visual/global-setup.js index b4acf683c9..e4082c183d 100644 --- a/build-tools/visual/global-setup.js +++ b/build-tools/visual/global-setup.js @@ -5,6 +5,17 @@ const waitOn = require('wait-on'); module.exports = async () => { if (process.env.BROWSER === 'safari') { + // Kill any lingering safaridriver process from a previous run to ensure + // no stale sessions exist (Safari only supports one session at a time). + const { execSync } = require('child_process'); + try { + execSync('pkill -f safaridriver', { stdio: 'ignore' }); + // Give the OS a moment to release the port. + await new Promise(resolve => setTimeout(resolve, 1000)); + } catch { + // No existing process — that's fine. + } + const driverProcess = spawn('safaridriver', ['--port', '4444']); driverProcess.on('error', err => { throw err; diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 369026bd02..7597a465e2 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -20,47 +20,34 @@ function buildUrl(host: string, path: string, queryParams?: Record { - const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); - const browserName = isSafari ? 'Safari' : 'ChromeHeadlessIntegration'; - const seleniumUrl = isSafari ? 'http://localhost:4444' : 'http://localhost:9515'; - const creator = getBrowserCreator(browserName, 'local', { seleniumUrl }); - return creator.getBrowser({ width: windowSize.width, height: windowSize.height }); -} - function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinition { return (item as TestDefinition).path !== undefined; } /** - * Registers all test suites. For Chrome, creates a single shared browser - * session for the entire suite to avoid per-test session overhead. - * - * Safari only supports one WebDriver session at a time and is sensitive to - * session teardown timing, so for Safari we create a fresh session per test. + * Registers all test suites with a single shared browser session per worker. + * This avoids the per-test session creation overhead that made tests slow. + * Safari runs with maxWorkers: 1, so there's only ever one session. */ export function runTestSuites(suites: Array) { - if (isSafari) { - // Per-test sessions: safe for Safari's single-session constraint. - registerSuites(suites, null); - } else { - // Shared session: one browser for all tests in this worker. - let browser: WebdriverIO.Browser; - - beforeAll(async () => { - browser = await createBrowser(); - }); - - afterAll(async () => { - await browser?.deleteSession(); - }); - - registerSuites(suites, () => browser); - } + let browser: WebdriverIO.Browser; + + beforeAll(async () => { + const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); + const browserName = isSafari ? 'Safari' : 'ChromeHeadlessIntegration'; + const seleniumUrl = isSafari ? 'http://localhost:4444' : 'http://localhost:9515'; + const creator = getBrowserCreator(browserName, 'local', { seleniumUrl }); + browser = await creator.getBrowser({ width: defaultWindowSize.width, height: defaultWindowSize.height }); + }); + + afterAll(async () => { + await browser?.deleteSession(); + }); + + registerSuites(suites, () => browser); } -// getBrowser === null means "create a fresh session per test" (Safari mode). -function registerSuites(suites: Array, getBrowser: (() => WebdriverIO.Browser) | null) { +function registerSuites(suites: Array, getBrowser: () => WebdriverIO.Browser) { for (const item of suites) { if (isTestDefinition(item)) { registerTest(item, getBrowser); @@ -72,46 +59,35 @@ function registerSuites(suites: Array, getBrowser: ( } } -function registerTest(testDef: TestDefinition, getBrowser: (() => WebdriverIO.Browser) | null) { +function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { const windowSize = { ...defaultWindowSize, ...testDef.configuration }; test(testDef.description, async () => { - // Safari: create and destroy a session per test. - // Chrome: reuse the shared session, just resize the window. - const browser = getBrowser ? getBrowser() : await createBrowser(windowSize); - if (getBrowser) { - await browser.setWindowSize(windowSize.width, windowSize.height); - } - - try { - const page = new ScreenshotPageObject(browser); - - const capture = async (host: string) => { - await browser.url(buildUrl(host, testDef.path, testDef.queryParams)); - await page.waitForVisible(screenshotAreaSelector); - if (testDef.setup) { - await testDef.setup(page); - } - return testDef.screenshotType === 'permutations' - ? page.capturePermutations() - : page.captureBySelector(screenshotAreaSelector); - }; - - const newScreenshots = await capture(newHost); - const oldScreenshots = await capture(oldHost); - - const newArr: ScreenshotWithOffset[] = Array.isArray(newScreenshots) ? newScreenshots : [newScreenshots]; - const oldArr: ScreenshotWithOffset[] = Array.isArray(oldScreenshots) ? oldScreenshots : [oldScreenshots]; - - expect(newArr.length).toBe(oldArr.length); - for (let i = 0; i < newArr.length; i++) { - const { diffPixels } = await cropAndCompare(newArr[i], oldArr[i]); - expect(diffPixels).toBe(0); - } - } finally { - if (!getBrowser) { - await browser.deleteSession(); + const browser = getBrowser(); + await browser.setWindowSize(windowSize.width, windowSize.height); + const page = new ScreenshotPageObject(browser); + + const capture = async (host: string) => { + await browser.url(buildUrl(host, testDef.path, testDef.queryParams)); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup(page); } + return testDef.screenshotType === 'permutations' + ? page.capturePermutations() + : page.captureBySelector(screenshotAreaSelector); + }; + + const newScreenshots = await capture(newHost); + const oldScreenshots = await capture(oldHost); + + const newArr: ScreenshotWithOffset[] = Array.isArray(newScreenshots) ? newScreenshots : [newScreenshots]; + const oldArr: ScreenshotWithOffset[] = Array.isArray(oldScreenshots) ? oldScreenshots : [oldScreenshots]; + + expect(newArr.length).toBe(oldArr.length); + for (let i = 0; i < newArr.length; i++) { + const { diffPixels } = await cropAndCompare(newArr[i], oldArr[i]); + expect(diffPixels).toBe(0); } }); } From a40d76822370e229f6393c9217f41aa798d98e40 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 27 May 2026 15:52:13 +0200 Subject: [PATCH 051/135] Start safaridriver from workflow --- .github/workflows/visual-regression.yml | 4 +++- build-tools/visual/global-setup.js | 18 +----------------- build-tools/visual/global-teardown.js | 7 ++----- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 4f62e0fa16..a763c7868a 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -142,7 +142,9 @@ jobs: - name: Enable SafariDriver if: matrix.browser == 'safari' - run: sudo safaridriver --enable + run: | + sudo safaridriver --enable + safaridriver --port 4444 & - name: Install dependencies run: npm i diff --git a/build-tools/visual/global-setup.js b/build-tools/visual/global-setup.js index e4082c183d..a1c19b7022 100644 --- a/build-tools/visual/global-setup.js +++ b/build-tools/visual/global-setup.js @@ -1,27 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -const { spawn } = require('child_process'); const waitOn = require('wait-on'); module.exports = async () => { if (process.env.BROWSER === 'safari') { - // Kill any lingering safaridriver process from a previous run to ensure - // no stale sessions exist (Safari only supports one session at a time). - const { execSync } = require('child_process'); - try { - execSync('pkill -f safaridriver', { stdio: 'ignore' }); - // Give the OS a moment to release the port. - await new Promise(resolve => setTimeout(resolve, 1000)); - } catch { - // No existing process — that's fine. - } - - const driverProcess = spawn('safaridriver', ['--port', '4444']); - driverProcess.on('error', err => { - throw err; - }); + // safaridriver is started by the CI workflow on port 4444. await waitOn({ resources: ['http-get://localhost:4444/status'], timeout: 10000 }); - global.__DRIVER_PROCESS__ = driverProcess; } else { const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); await startWebdriver(); diff --git a/build-tools/visual/global-teardown.js b/build-tools/visual/global-teardown.js index 366c3f7660..61f45caa02 100644 --- a/build-tools/visual/global-teardown.js +++ b/build-tools/visual/global-teardown.js @@ -1,12 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 module.exports = () => { - if (process.env.BROWSER === 'safari') { - if (global.__DRIVER_PROCESS__) { - global.__DRIVER_PROCESS__.kill(); - } - } else { + if (process.env.BROWSER !== 'safari') { const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); shutdownWebdriver(); } + // Safari: safaridriver is managed by the CI workflow, nothing to tear down. }; From 6efec5b9ae2c905d90e859de3af85005be246605 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 27 May 2026 17:05:43 +0200 Subject: [PATCH 052/135] Move Safaridriver initialization back to local test setup --- .github/workflows/visual-regression.yml | 8 +++----- build-tools/visual/global-setup.js | 7 ++++++- build-tools/visual/global-teardown.js | 7 +++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index a763c7868a..50891f2558 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -142,9 +142,7 @@ jobs: - name: Enable SafariDriver if: matrix.browser == 'safari' - run: | - sudo safaridriver --enable - safaridriver --port 4444 & + run: sudo safaridriver --enable - name: Install dependencies run: npm i @@ -163,10 +161,10 @@ jobs: # ── Run tests ───────────────────────────────────────────────────────── - name: Start test server (port 8080) - run: npx serve --no-clipboard --listen 8080 pages/lib/static-default & + run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-default & - name: Start baseline server (port 8081) - run: npx serve --no-clipboard --listen 8081 pages/lib/static-visual-baseline & + run: npx --yes serve --no-clipboard --listen 8081 pages/lib/static-visual-baseline & - name: Wait for servers to be ready run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 diff --git a/build-tools/visual/global-setup.js b/build-tools/visual/global-setup.js index a1c19b7022..b4acf683c9 100644 --- a/build-tools/visual/global-setup.js +++ b/build-tools/visual/global-setup.js @@ -1,11 +1,16 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +const { spawn } = require('child_process'); const waitOn = require('wait-on'); module.exports = async () => { if (process.env.BROWSER === 'safari') { - // safaridriver is started by the CI workflow on port 4444. + const driverProcess = spawn('safaridriver', ['--port', '4444']); + driverProcess.on('error', err => { + throw err; + }); await waitOn({ resources: ['http-get://localhost:4444/status'], timeout: 10000 }); + global.__DRIVER_PROCESS__ = driverProcess; } else { const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); await startWebdriver(); diff --git a/build-tools/visual/global-teardown.js b/build-tools/visual/global-teardown.js index 61f45caa02..366c3f7660 100644 --- a/build-tools/visual/global-teardown.js +++ b/build-tools/visual/global-teardown.js @@ -1,9 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 module.exports = () => { - if (process.env.BROWSER !== 'safari') { + if (process.env.BROWSER === 'safari') { + if (global.__DRIVER_PROCESS__) { + global.__DRIVER_PROCESS__.kill(); + } + } else { const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); shutdownWebdriver(); } - // Safari: safaridriver is managed by the CI workflow, nothing to tear down. }; From aebe762fe5077c6670dbdbfb1ab86942d54627fb Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 28 May 2026 10:23:36 +0200 Subject: [PATCH 053/135] Remove outdated npm script --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 6e78ba7d82..e9bace4031 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "test:a11y": "gulp test:a11y", "test:integ": "gulp test:integ", "test:motion": "gulp test:motion", - "test:visual": "gulp test:visual", "lint": "npm-run-all --parallel lint:*", "lint:eslint": "eslint .", "lint:stylelint": "stylelint --ignore-path .gitignore '{src,pages}/**/*.{css,scss}'", From 68ab5636a4c836041ea201bad27e430477fe64bf Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 09:02:08 +0200 Subject: [PATCH 054/135] Remove Safari setup --- .github/workflows/visual-regression.yml | 7 ---- build-tools/visual/global-setup.js | 15 ++------- build-tools/visual/global-teardown.js | 11 ++----- build-tools/visual/setup.js | 15 ++------- jest.visual.config.js | 4 +-- test/definitions/utils.ts | 43 +++++++++++-------------- 6 files changed, 28 insertions(+), 67 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 50891f2558..40493f7e9e 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -123,8 +123,6 @@ jobs: include: - browser: chrome os: ubuntu-latest - - browser: safari - os: macos-latest steps: - uses: actions/checkout@v4 @@ -135,15 +133,10 @@ jobs: cache: npm - name: Setup Chrome and ChromeDriver - if: matrix.browser == 'chrome' uses: browser-actions/setup-chrome@v1 with: chrome-version: stable - - name: Enable SafariDriver - if: matrix.browser == 'safari' - run: sudo safaridriver --enable - - name: Install dependencies run: npm i diff --git a/build-tools/visual/global-setup.js b/build-tools/visual/global-setup.js index b4acf683c9..52ce2f271c 100644 --- a/build-tools/visual/global-setup.js +++ b/build-tools/visual/global-setup.js @@ -1,18 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -const { spawn } = require('child_process'); -const waitOn = require('wait-on'); module.exports = async () => { - if (process.env.BROWSER === 'safari') { - const driverProcess = spawn('safaridriver', ['--port', '4444']); - driverProcess.on('error', err => { - throw err; - }); - await waitOn({ resources: ['http-get://localhost:4444/status'], timeout: 10000 }); - global.__DRIVER_PROCESS__ = driverProcess; - } else { - const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); - await startWebdriver(); - } + const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); + await startWebdriver(); }; diff --git a/build-tools/visual/global-teardown.js b/build-tools/visual/global-teardown.js index 366c3f7660..0fa05eebfe 100644 --- a/build-tools/visual/global-teardown.js +++ b/build-tools/visual/global-teardown.js @@ -1,12 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 + module.exports = () => { - if (process.env.BROWSER === 'safari') { - if (global.__DRIVER_PROCESS__) { - global.__DRIVER_PROCESS__.kill(); - } - } else { - const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); - shutdownWebdriver(); - } + const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); + shutdownWebdriver(); }; diff --git a/build-tools/visual/setup.js b/build-tools/visual/setup.js index c63a18416f..d52cd606fb 100644 --- a/build-tools/visual/setup.js +++ b/build-tools/visual/setup.js @@ -3,23 +3,14 @@ /* global jest */ const { configure } = require('@cloudscape-design/browser-test-tools/use-browser'); -const isSafari = process.env.BROWSER === 'safari'; - -// The PR build (the code under test) is served on port 8080. -// The baseline build (main branch, same node_modules) is served on port 8081. configure({ - browserName: isSafari ? 'Safari' : 'ChromeHeadlessIntegration', + browserName: 'ChromeHeadlessIntegration', browserCreatorOptions: { - seleniumUrl: isSafari ? 'http://localhost:4444' : 'http://localhost:9515', + seleniumUrl: 'http://localhost:9515', }, webdriverOptions: { baseUrl: 'http://localhost:8080', }, }); -// Retries help with flaky tests, but Safari's single-session constraint means -// a retry can hit "already paired" if the previous attempt's session hasn't -// fully released. Disable retries for Safari. -if (!isSafari) { - jest.retryTimes(2, { logErrorsBeforeRetry: true }); -} +jest.retryTimes(2, { logErrorsBeforeRetry: true }); diff --git a/jest.visual.config.js b/jest.visual.config.js index 097054bb69..a4d06d46ae 100644 --- a/jest.visual.config.js +++ b/jest.visual.config.js @@ -16,9 +16,7 @@ module.exports = { }, reporters: ['default', 'github-actions'], testTimeout: 120_000, // 2min — pages can be tall and slow to capture - // Safari's WebDriver only supports one concurrent session, so tests must run serially. - // Chrome can run multiple workers to speed things up. - maxWorkers: process.env.BROWSER === 'safari' ? 1 : os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), + maxWorkers: os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), globalSetup: '/build-tools/visual/global-setup.js', globalTeardown: '/build-tools/visual/global-teardown.js', setupFilesAfterEnv: [path.join(__dirname, 'build-tools', 'visual', 'setup.js')], diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 7597a465e2..2467c3801b 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; -import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; +import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; import { TestDefinition, TestSuite } from './types'; @@ -12,8 +12,6 @@ const defaultWindowSize = { width: 1600, height: 800 }; const newHost = process.env.NEW_HOST || 'http://localhost:8080'; const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; -const isSafari = process.env.BROWSER === 'safari'; - function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams(queryParams); const qs = params.toString(); @@ -26,17 +24,16 @@ function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinit /** * Registers all test suites with a single shared browser session per worker. - * This avoids the per-test session creation overhead that made tests slow. - * Safari runs with maxWorkers: 1, so there's only ever one session. + * This avoids the per-test session creation overhead. */ export function runTestSuites(suites: Array) { let browser: WebdriverIO.Browser; beforeAll(async () => { const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); - const browserName = isSafari ? 'Safari' : 'ChromeHeadlessIntegration'; - const seleniumUrl = isSafari ? 'http://localhost:4444' : 'http://localhost:9515'; - const creator = getBrowserCreator(browserName, 'local', { seleniumUrl }); + const creator = getBrowserCreator('ChromeHeadlessIntegration', 'local', { + seleniumUrl: 'http://localhost:9515', + }); browser = await creator.getBrowser({ width: defaultWindowSize.width, height: defaultWindowSize.height }); }); @@ -60,11 +57,13 @@ function registerSuites(suites: Array, getBrowser: ( } function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { - const windowSize = { ...defaultWindowSize, ...testDef.configuration }; - test(testDef.description, async () => { const browser = getBrowser(); - await browser.setWindowSize(windowSize.width, windowSize.height); + // Only resize if the test needs a non-default window size. + if (testDef.configuration) { + const windowSize = { ...defaultWindowSize, ...testDef.configuration }; + await browser.setWindowSize(windowSize.width, windowSize.height); + } const page = new ScreenshotPageObject(browser); const capture = async (host: string) => { @@ -73,21 +72,17 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro if (testDef.setup) { await testDef.setup(page); } - return testDef.screenshotType === 'permutations' - ? page.capturePermutations() - : page.captureBySelector(screenshotAreaSelector); + // For screenshotArea pages the element fits in the viewport, so we use + // viewportOnly to avoid the expensive scroll-and-merge full-page capture. + // Permutations pages can be taller than the viewport and need the full strategy. + const viewportOnly = testDef.screenshotType !== 'permutations'; + return page.captureBySelector(screenshotAreaSelector, { viewportOnly }); }; - const newScreenshots = await capture(newHost); - const oldScreenshots = await capture(oldHost); + const newScreenshot = await capture(newHost); + const oldScreenshot = await capture(oldHost); - const newArr: ScreenshotWithOffset[] = Array.isArray(newScreenshots) ? newScreenshots : [newScreenshots]; - const oldArr: ScreenshotWithOffset[] = Array.isArray(oldScreenshots) ? oldScreenshots : [oldScreenshots]; - - expect(newArr.length).toBe(oldArr.length); - for (let i = 0; i < newArr.length; i++) { - const { diffPixels } = await cropAndCompare(newArr[i], oldArr[i]); - expect(diffPixels).toBe(0); - } + const { diffPixels } = await cropAndCompare(newScreenshot, oldScreenshot); + expect(diffPixels).toBe(0); }); } From 6c6c43bd91c5afce0b4dd97d0795df913b38212d Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 09:13:40 +0200 Subject: [PATCH 055/135] use captureScreenshotArea for permutation comparison unless there is a failure --- test/definitions/utils.ts | 65 +++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 2467c3801b..a1e8a3bd1f 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; -import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; +import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; import { TestDefinition, TestSuite } from './types'; @@ -56,33 +56,72 @@ function registerSuites(suites: Array, getBrowser: ( } } +/** + * Captures the .screenshot-area element using a viewport-only screenshot (fast). + */ +async function captureScreenshotArea( + browser: WebdriverIO.Browser, + page: ScreenshotPageObject, + url: string, + setup?: (page: ScreenshotPageObject) => Promise +): Promise { + await browser.url(url); + await page.waitForVisible(screenshotAreaSelector); + if (setup) { + await setup(page); + } + return page.captureBySelector(screenshotAreaSelector, { viewportOnly: true }); +} + function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { test(testDef.description, async () => { const browser = getBrowser(); - // Only resize if the test needs a non-default window size. if (testDef.configuration) { const windowSize = { ...defaultWindowSize, ...testDef.configuration }; await browser.setWindowSize(windowSize.width, windowSize.height); } const page = new ScreenshotPageObject(browser); - const capture = async (host: string) => { - await browser.url(buildUrl(host, testDef.path, testDef.queryParams)); + const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); + const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); + + // Fast path: compare the screenshot area (viewport-only, no scroll-and-merge). + const newScreenshot = await captureScreenshotArea(browser, page, newUrl, testDef.setup); + const oldScreenshot = await captureScreenshotArea(browser, page, oldUrl, testDef.setup); + const { diffPixels } = await cropAndCompare(newScreenshot, oldScreenshot); + + if (diffPixels === 0) { + return; + } + + // For permutations pages, a screenshot-area diff might be a false positive + // caused by content extending beyond the viewport. Re-capture using the + // full capturePermutations strategy which resizes the window to fit all + // content and returns individual permutation crops for precise comparison. + if (testDef.screenshotType === 'permutations') { + await browser.url(newUrl); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { await testDef.setup(page); } - // For screenshotArea pages the element fits in the viewport, so we use - // viewportOnly to avoid the expensive scroll-and-merge full-page capture. - // Permutations pages can be taller than the viewport and need the full strategy. - const viewportOnly = testDef.screenshotType !== 'permutations'; - return page.captureBySelector(screenshotAreaSelector, { viewportOnly }); - }; + const newPermutations = await page.capturePermutations(); - const newScreenshot = await capture(newHost); - const oldScreenshot = await capture(oldHost); + await browser.url(oldUrl); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup(page); + } + const oldPermutations = await page.capturePermutations(); - const { diffPixels } = await cropAndCompare(newScreenshot, oldScreenshot); + expect(newPermutations.length).toBe(oldPermutations.length); + for (let i = 0; i < newPermutations.length; i++) { + const { diffPixels: permDiff } = await cropAndCompare(newPermutations[i], oldPermutations[i]); + expect(permDiff).toBe(0); + } + return; + } + + // For screenshotArea type, the diff is a real failure. expect(diffPixels).toBe(0); }); } From ac78f9cdeb351f1c73a4e44463103c731c8ceff9 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 09:38:28 +0200 Subject: [PATCH 056/135] Add more test definitions --- test/definitions/index.ts | 3 +- test/definitions/visual/app-layout.ts | 586 ++++++++++++++++++++++++++ 2 files changed, 588 insertions(+), 1 deletion(-) create mode 100644 test/definitions/visual/app-layout.ts diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 91e90a89f7..6ff236cfa6 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -6,5 +6,6 @@ import { TestSuite } from './types'; import actionCard from './visual/action-card'; import alert from './visual/alert'; +import appLayout from './visual/app-layout'; -export const allSuites: TestSuite[] = [actionCard, alert]; +export const allSuites: TestSuite[] = [actionCard, alert, appLayout]; diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts new file mode 100644 index 0000000000..936f07152f --- /dev/null +++ b/test/definitions/visual/app-layout.ts @@ -0,0 +1,586 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +function responsiveTests(width: number): TestSuite { + return { + description: `width ${width}px`, + componentName: 'app-layout', + tests: [ + { + description: 'default', + path: 'app-layout/default', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'navigation drawer is open', + path: 'app-layout/with-wizard', + screenshotType: 'screenshotArea', + configuration: { width }, + setup: async page => { + await page.click('[aria-label="Open navigation"]'); + }, + }, + { + description: 'wizard', + path: 'app-layout/with-wizard', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with wizard and table', + path: 'app-layout/with-wizard-and-table', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with wizard, table, and breadcrumbs', + path: 'app-layout/with-wizard-and-table', + screenshotType: 'screenshotArea', + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true' }, + }, + { + description: 'notifications', + path: 'app-layout/with-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'breadcrumbs', + path: 'app-layout/with-breadcrumbs', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'notifications and breadcrumbs', + path: 'app-layout/with-breadcrumbs-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'dashboard content type', + path: 'app-layout/dashboard-content-type', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'fixed header and footer', + path: 'app-layout/with-fixed-header-footer', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disableBodyScroll - empty', + path: 'app-layout/legacy-nav-empty', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disableBodyScroll - with content', + path: 'app-layout/legacy-nav-scrollable', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disableBodyScroll - with split panel', + path: 'app-layout/legacy-nav-scrollable-with-split-panel', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disable paddings', + path: 'app-layout/disable-paddings', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disable paddings with breadcrumbs', + path: 'app-layout/disable-paddings-breadcrumbs', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'sticky notifications', + path: 'app-layout/with-sticky-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'sticky notifications scrolled down', + path: 'app-layout/with-sticky-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + setup: async page => { + await page.windowScrollTo({ top: 2000 }); + }, + }, + { + description: 'layout without panels', + path: 'app-layout/no-panels', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'layout without panels but with notifications', + path: 'app-layout/no-panels-with-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with drawers', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with empty drawers', + path: 'app-layout/with-drawers-empty', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with open drawer', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + configuration: { width }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + }, + }, + ], + }; +} + +const suite: TestSuite = { + description: 'AppLayout', + componentName: 'app-layout', + tests: [ + // ── Responsive tests at multiple breakpoints ────────────────────────── + responsiveTests(600), + responsiveTests(1280), + responsiveTests(1400), + responsiveTests(1920), + responsiveTests(2540), + + // ── General tests ───────────────────────────────────────────────────── + { + description: 'no scrollbars at 320px', + path: 'app-layout/default', + screenshotType: 'screenshotArea', + configuration: { width: 320 }, + }, + { + description: 'drawer buttons alignment', + path: 'app-layout/default', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + setup: async page => { + await page.click('[aria-label="Open tools"]'); + }, + }, + { + description: 'disable paddings - navigation closed', + path: 'app-layout/disable-paddings', + screenshotType: 'screenshotArea', + configuration: { width: 1280 }, + setup: async page => { + await page.click('[aria-label="Close navigation"]'); + }, + }, + { + description: 'panels stacking on mobile', + path: 'app-layout/all-panels-open', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + }, + { + description: 'wrapping long words', + path: 'app-layout/text-wrap', + screenshotType: 'screenshotArea', + }, + { + description: 'fill content area', + path: 'app-layout/fill-content-area', + screenshotType: 'screenshotArea', + }, + { + description: 'with tools and drawers', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + queryParams: { hasTools: 'true' }, + }, + { + description: 'with open drawer and open side split panel', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { splitPanelPosition: 'side' }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + await page.click('[aria-label="Open panel"]'); + }, + }, + + // ── Content paddings ────────────────────────────────────────────────── + { + description: 'Content paddings', + tests: [ + ...(['true', 'false'] as const).flatMap(toolsEnabled => + (['true', 'false'] as const).flatMap(splitPanelEnabled => + (['bottom', 'side'] as const).map(splitPanelPosition => ({ + description: `toolsEnabled=${toolsEnabled} splitPanelEnabled=${splitPanelEnabled} splitPanelPosition=${splitPanelPosition}`, + path: 'app-layout/with-split-panel', + screenshotType: 'screenshotArea' as const, + queryParams: { toolsEnabled, splitPanelEnabled, splitPanelPosition }, + })) + ) + ), + ...[1500, 600].map(width => ({ + description: `with split panel and disabled content paddings - width=${width}`, + path: 'app-layout/disable-paddings-with-split-panel', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { splitPanelOpen: 'true', splitPanelPosition: 'side' }, + })), + ], + }, + + // ── Drawers ─────────────────────────────────────────────────────────── + { + description: 'Drawers', + tests: [ + { + description: 'with split panel', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[aria-label="Pro help trigger button"]'); + }, + }, + { + description: 'with tooltip on hover', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[aria-label="Pro help trigger button"]'); + }, + }, + { + description: 'with custom scrollable drawer content', + path: 'app-layout/with-drawers-scrollable', + screenshotType: 'screenshotArea', + queryParams: { sideNavFill: 'false' }, + setup: async page => { + await page.click('[aria-label="Chat trigger button"]'); + }, + }, + ], + }, + + // ── Headers ─────────────────────────────────────────────────────────── + { + description: 'Headers', + tests: [600, 1280].flatMap(width => [ + { + description: `alignment with full-page table (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: `alignment with full-page table in sticky state (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `alignment with full-page table in sticky state with sticky notifications (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { stickyNotifications: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `high contrast header variant in landing page (${width}px)`, + path: 'app-layout/landing-page', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), + }, + + // ── High contrast header variant ────────────────────────────────────── + { + description: 'High contrast header variant', + tests: [ + ...[1400, 600].flatMap(width => [ + { + description: `with breadcrumbs and notifications at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', hasContainer: 'true' }, + }, + { + description: `without overlap at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { disableOverlap: 'true' }, + }, + { + description: `with content layout at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + hasContainer: 'true', + hasContentLayout: 'true', + }, + }, + ]), + ], + }, + + // ── Multiple instances ───────────────────────────────────────────────── + { + description: 'Multiple instances', + tests: [600, 1280].flatMap(width => [ + { + description: `simple (${width}px)`, + path: 'app-layout/multi-layout-simple', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: `iframe (${width}px)`, + path: 'app-layout/multi-layout-iframe', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), + }, + + // ── Z-index (absolute components) ───────────────────────────────────── + { + description: 'Z-index', + tests: [ + ...[600, 1280].flatMap(width => [ + { + description: `button dropdown (${width}px)`, + path: 'app-layout/with-absolute-components', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + setup: async page => { + await page.click('button=Button dropdown'); + await page.click('[data-testid="2"]'); + await page.windowScrollTo({ top: 300 }); + }, + } as TestDefinition, + { + description: `select (${width}px)`, + path: 'app-layout/with-absolute-components', + screenshotType: 'screenshotArea' as const, + configuration: { width, height: 800 }, + setup: async page => { + await page.click('[data-testid="select-demo"] button'); + await page.windowScrollTo({ top: 300 }); + }, + } as TestDefinition, + { + description: `split-panel and full-page table (${width}px)`, + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), + { + description: 'split-panel and full-page with open navigation (600px)', + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'screenshotArea' as const, + configuration: { width: 600 }, + setup: async page => { + await page.click('button[aria-label="Open navigation"]'); + }, + }, + { + description: 'split-panel and full-page with open tools (600px)', + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'screenshotArea' as const, + configuration: { width: 600 }, + setup: async page => { + await page.click('button[aria-label="Open tools"]'); + }, + }, + ], + }, + + // ── Toolbar ─────────────────────────────────────────────────────────── + { + description: 'Toolbar', + tests: [ + { + description: 'multiple nested instances (no breadcrumbs dedup)', + path: 'app-layout-toolbar/multi-layout-with-hidden-instances', + screenshotType: 'screenshotArea', + }, + { + description: 'no toolbar', + path: 'app-layout-toolbar/without-toolbar', + screenshotType: 'screenshotArea', + }, + ], + }, + + // ── Max content width ───────────────────────────────────────────────── + { + description: 'Max content width', + tests: [ + { + description: 'maxContentWidth set to Number.MAX_VALUE', + path: 'app-layout/refresh-content-width', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 700 }, + setup: async page => { + await page.click('[data-test-id="button_width-number-max_value"]'); + }, + }, + ], + }, + + // ── Sticky table header with split panel ────────────────────────────── + { + description: 'Sticky header with split panel', + tests: [ + { + description: 'scrolling to bottom with closed split panel (1 table row)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-1"]'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'scrolling to bottom with closed split panel (30 table rows)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky with open split panel (1 table row)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-1"]'); + await page.click('aria/Open panel'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky with open split panel (30 table rows)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.click('aria/Open panel'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky when mounting and unmounting a second table', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.click('aria/Open panel'); + await page.windowScrollTo({ top: 0 }); + await page.click('aria/Close panel'); + await page.scrollToBottom('html'); + }, + }, + ], + }, + + // ── Flashbar ────────────────────────────────────────────────────────── + { + description: 'Flashbar', + tests: [true, false].flatMap(disableContentPaddings => + [true, false].flatMap(stickyNotifications => + [true, false].flatMap(stickyTableHeader => + [true, false].map(stackNotifications => ({ + description: `disableContentPaddings: ${disableContentPaddings}, stickyNotifications: ${stickyNotifications}, stickyTableHeader: ${stickyTableHeader}, stackNotifications: ${stackNotifications}`, + path: 'app-layout/with-stacked-notifications-and-table', + screenshotType: 'screenshotArea' as const, + configuration: { width: 1280, height: 900 }, + setup: async ( + page: import('@cloudscape-design/browser-test-tools/page-objects').ScreenshotPageObject + ) => { + if (!disableContentPaddings) { + await page.click('[data-id="toggle-content-paddings"]'); + } + if (stickyNotifications) { + await page.click('[data-id="toggle-sticky-notifications"]'); + } + if (!stickyTableHeader) { + await page.click('[data-id="toggle-sticky-table-header"]'); + } + if (!stackNotifications) { + await page.click('[data-id="toggle-stack-items"]'); + } + await page.click('[data-id="add-notification"]'); + await page.click('[data-id="add-notification"]'); + }, + })) + ) + ) + ), + }, + + // ── Transitions ─────────────────────────────────────────────────────── + { + description: 'Transitions', + tests: [ + { + description: 'transition from 400px to 1800px', + path: 'app-layout/default', + screenshotType: 'screenshotArea', + configuration: { width: 400, height: 400 }, + setup: async page => { + await page.setWindowSize({ width: 1800, height: 400 }); + }, + }, + { + description: 'transition from 1800px to 400px', + path: 'app-layout/default', + screenshotType: 'screenshotArea', + configuration: { width: 1800, height: 400 }, + setup: async page => { + await page.setWindowSize({ width: 400, height: 400 }); + }, + }, + ], + }, + ], +}; + +export default suite; From 68e2fe71c9796352bf4c4e5d477bd8d23568179a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 09:47:07 +0200 Subject: [PATCH 057/135] Refine config --- tsconfig.integ.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.integ.json b/tsconfig.integ.json index 784a3d2f1f..e23c746e71 100644 --- a/tsconfig.integ.json +++ b/tsconfig.integ.json @@ -5,6 +5,7 @@ "types": ["jest"], "noEmit": true, "strict": true, + "isolatedModules": true, "sourceMap": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true, From 963d42d3009cc72176e651b92ae9e2f05f6ac301 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 10:46:16 +0200 Subject: [PATCH 058/135] Shard tests --- .github/workflows/visual-regression.yml | 13 +++++-------- jest.visual.config.js | 2 +- test/visual.test.ts | 6 ------ test/visual/action-card.test.ts | 6 ++++++ test/visual/alert.test.ts | 6 ++++++ test/visual/app-layout.test.ts | 6 ++++++ tsconfig.integ.json | 2 +- 7 files changed, 25 insertions(+), 16 deletions(-) delete mode 100644 test/visual.test.ts create mode 100644 test/visual/action-card.test.ts create mode 100644 test/visual/alert.test.ts create mode 100644 test/visual/app-layout.test.ts diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 40493f7e9e..86f0fdc7ea 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -114,15 +114,13 @@ jobs: retention-days: 1 visual: - name: Visual regression (${{ matrix.browser }}) + name: Visual regression (shard ${{ matrix.shard }}) needs: [stage-pr-pages, build-baseline] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - include: - - browser: chrome - os: ubuntu-latest + shard: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 @@ -163,15 +161,14 @@ jobs: run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/4 env: TZ: UTC - BROWSER: ${{ matrix.browser }} - name: Upload diff artifacts if: failure() uses: actions/upload-artifact@v4 with: - name: visual-regression-diffs-${{ matrix.browser }} + name: visual-regression-diffs-shard-${{ matrix.shard }} path: visual-regression-output/ retention-days: 14 diff --git a/jest.visual.config.js b/jest.visual.config.js index a4d06d46ae..69a5b71f8b 100644 --- a/jest.visual.config.js +++ b/jest.visual.config.js @@ -21,5 +21,5 @@ module.exports = { globalTeardown: '/build-tools/visual/global-teardown.js', setupFilesAfterEnv: [path.join(__dirname, 'build-tools', 'visual', 'setup.js')], moduleFileExtensions: ['js', 'ts'], - testMatch: ['/test/visual.test.ts'], + testMatch: ['/test/visual/**/*.test.ts'], }; diff --git a/test/visual.test.ts b/test/visual.test.ts deleted file mode 100644 index 4e6e992f4f..0000000000 --- a/test/visual.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { allSuites } from './definitions'; -import { runTestSuites } from './definitions/utils'; - -runTestSuites(allSuites); diff --git a/test/visual/action-card.test.ts b/test/visual/action-card.test.ts new file mode 100644 index 0000000000..8505dad23b --- /dev/null +++ b/test/visual/action-card.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import actionCard from '../definitions/visual/action-card'; + +runTestSuites([actionCard]); diff --git a/test/visual/alert.test.ts b/test/visual/alert.test.ts new file mode 100644 index 0000000000..431e4907e4 --- /dev/null +++ b/test/visual/alert.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import alert from '../definitions/visual/alert'; + +runTestSuites([alert]); diff --git a/test/visual/app-layout.test.ts b/test/visual/app-layout.test.ts new file mode 100644 index 0000000000..93500354cc --- /dev/null +++ b/test/visual/app-layout.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import appLayout from '../definitions/visual/app-layout'; + +runTestSuites([appLayout]); diff --git a/tsconfig.integ.json b/tsconfig.integ.json index e23c746e71..b3f5080284 100644 --- a/tsconfig.integ.json +++ b/tsconfig.integ.json @@ -12,5 +12,5 @@ "resolveJsonModule": true, "moduleResolution": "node" }, - "include": ["**/__integ__/**/*.ts", "**/__a11y__/**/*.ts", "**/__motion__/**/*.ts", "test/definitions/utils.ts", "test/visual.test.ts", "types"] + "include": ["**/__integ__/**/*.ts", "**/__a11y__/**/*.ts", "**/__motion__/**/*.ts", "test/definitions/utils.ts", "test/visual/**/*.test.ts", "types"] } From e1f74ad3110fc4337fc3dc6b663540bbb1e9bc5a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 11:55:07 +0200 Subject: [PATCH 059/135] Fix config --- jest.visual.config.js | 2 +- tsconfig.integ.json | 3 +-- tsconfig.visual.json | 7 +++++++ 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 tsconfig.visual.json diff --git a/jest.visual.config.js b/jest.visual.config.js index 69a5b71f8b..bd04fe5d73 100644 --- a/jest.visual.config.js +++ b/jest.visual.config.js @@ -10,7 +10,7 @@ module.exports = { '^.+\\.tsx?$': [ 'ts-jest', { - tsconfig: 'tsconfig.integ.json', + tsconfig: 'tsconfig.visual.json', }, ], }, diff --git a/tsconfig.integ.json b/tsconfig.integ.json index b3f5080284..e816f749e9 100644 --- a/tsconfig.integ.json +++ b/tsconfig.integ.json @@ -5,12 +5,11 @@ "types": ["jest"], "noEmit": true, "strict": true, - "isolatedModules": true, "sourceMap": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true, "resolveJsonModule": true, "moduleResolution": "node" }, - "include": ["**/__integ__/**/*.ts", "**/__a11y__/**/*.ts", "**/__motion__/**/*.ts", "test/definitions/utils.ts", "test/visual/**/*.test.ts", "types"] + "include": ["**/__integ__/**/*.ts", "**/__a11y__/**/*.ts", "**/__motion__/**/*.ts", "types"] } diff --git a/tsconfig.visual.json b/tsconfig.visual.json new file mode 100644 index 0000000000..41f554c24f --- /dev/null +++ b/tsconfig.visual.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.integ.json", + "compilerOptions": { + "isolatedModules": true + }, + "include": ["test/definitions/utils.ts", "test/visual/**/*.test.ts", "types"] +} From a41f94c2eafec8b11a241c16c5fb1fcdee22b536 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 12:03:21 +0200 Subject: [PATCH 060/135] Fix config --- tsconfig.visual.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/tsconfig.visual.json b/tsconfig.visual.json index 41f554c24f..be61d962ef 100644 --- a/tsconfig.visual.json +++ b/tsconfig.visual.json @@ -1,7 +1,4 @@ { "extends": "./tsconfig.integ.json", - "compilerOptions": { - "isolatedModules": true - }, "include": ["test/definitions/utils.ts", "test/visual/**/*.test.ts", "types"] } From 3c153d119e2a2671d664f55e25617a667c4da07f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 15:15:22 +0200 Subject: [PATCH 061/135] Increase timeout --- jest.visual.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.visual.config.js b/jest.visual.config.js index bd04fe5d73..19418a86dd 100644 --- a/jest.visual.config.js +++ b/jest.visual.config.js @@ -15,7 +15,7 @@ module.exports = { ], }, reporters: ['default', 'github-actions'], - testTimeout: 120_000, // 2min — pages can be tall and slow to capture + testTimeout: 240_000, // 4min — pages can be tall and slow to capture maxWorkers: os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), globalSetup: '/build-tools/visual/global-setup.js', globalTeardown: '/build-tools/visual/global-teardown.js', From f0650970007d949632f7103ff0069436257da615 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 15:32:46 +0200 Subject: [PATCH 062/135] Use 3 shards --- .github/workflows/visual-regression.yml | 4 ++-- test/definitions/visual/app-layout.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 86f0fdc7ea..e004f354d7 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -120,7 +120,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4] + shard: [1, 2, 3] steps: - uses: actions/checkout@v4 @@ -161,7 +161,7 @@ jobs: run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/4 + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/3 env: TZ: UTC diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts index 936f07152f..12c76d9138 100644 --- a/test/definitions/visual/app-layout.ts +++ b/test/definitions/visual/app-layout.ts @@ -1,8 +1,11 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import createWrapper from '../../../lib/components/test-utils/selectors'; import { TestDefinition, TestSuite } from '../types'; +const wrapper = createWrapper(); + function responsiveTests(width: number): TestSuite { return { description: `width ${width}px`, @@ -257,7 +260,7 @@ const suite: TestSuite = { path: 'app-layout/with-drawers', screenshotType: 'screenshotArea', setup: async page => { - await page.click('[aria-label="Pro help trigger button"]'); + await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); }, }, { @@ -265,7 +268,7 @@ const suite: TestSuite = { path: 'app-layout/with-drawers', screenshotType: 'screenshotArea', setup: async page => { - await page.hoverElement('[aria-label="Pro help trigger button"]'); + await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); }, }, { @@ -274,7 +277,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', queryParams: { sideNavFill: 'false' }, setup: async page => { - await page.click('[aria-label="Chat trigger button"]'); + await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); }, }, ], From de46e47ea7c3d725d7ce5433fd878604d71ad250 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 15:32:58 +0200 Subject: [PATCH 063/135] Fix tests --- test/definitions/utils.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index a1e8a3bd1f..c5b237dc73 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -63,8 +63,12 @@ async function captureScreenshotArea( browser: WebdriverIO.Browser, page: ScreenshotPageObject, url: string, + windowSize: { width: number; height: number } | undefined, setup?: (page: ScreenshotPageObject) => Promise ): Promise { + if (windowSize) { + await browser.setWindowSize(windowSize.width, windowSize.height); + } await browser.url(url); await page.waitForVisible(screenshotAreaSelector); if (setup) { @@ -76,18 +80,15 @@ async function captureScreenshotArea( function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { test(testDef.description, async () => { const browser = getBrowser(); - if (testDef.configuration) { - const windowSize = { ...defaultWindowSize, ...testDef.configuration }; - await browser.setWindowSize(windowSize.width, windowSize.height); - } + const windowSize = testDef.configuration ? { ...defaultWindowSize, ...testDef.configuration } : undefined; const page = new ScreenshotPageObject(browser); const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); // Fast path: compare the screenshot area (viewport-only, no scroll-and-merge). - const newScreenshot = await captureScreenshotArea(browser, page, newUrl, testDef.setup); - const oldScreenshot = await captureScreenshotArea(browser, page, oldUrl, testDef.setup); + const newScreenshot = await captureScreenshotArea(browser, page, newUrl, windowSize, testDef.setup); + const oldScreenshot = await captureScreenshotArea(browser, page, oldUrl, windowSize, testDef.setup); const { diffPixels } = await cropAndCompare(newScreenshot, oldScreenshot); if (diffPixels === 0) { From b8f16c756271b36efe3861a81f1395207b7bab59 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 29 May 2026 23:56:13 +0200 Subject: [PATCH 064/135] Build the test selectors before running the tests --- .github/workflows/visual-regression.yml | 38 ++++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index e004f354d7..ed5739b0e3 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -4,12 +4,12 @@ on: workflow_call: inputs: pr-artifact-name: - description: 'Name of the artifact containing PR pages (built by quick-build job). If not provided, pages will be built locally.' - required: false + description: 'Name of the artifact containing PR pages (built by the caller workflow).' + required: true type: string caller-run-id: description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' - required: false + required: true type: string defaults: @@ -22,8 +22,8 @@ permissions: actions: read jobs: - # Stage the PR pages within this run so matrix jobs can download them without - # needing cross-run artifact access. Runs in parallel with build-baseline. + # Stage the PR pages and build test utils for the visual regression shards. + # Runs in parallel with build-baseline. stage-pr-pages: name: Stage PR pages runs-on: ubuntu-latest @@ -39,16 +39,7 @@ jobs: - name: Install dependencies run: npm i - - name: Build PR pages locally - if: ${{ !inputs.pr-artifact-name }} - run: | - npx gulp quick-build - node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path pages/lib/static-default - env: - NODE_ENV: production - - name: Download PR pages artifact from caller run - if: ${{ inputs.pr-artifact-name }} uses: actions/download-artifact@v4 with: name: ${{ inputs.pr-artifact-name }} @@ -56,6 +47,11 @@ jobs: github-token: ${{ github.token }} run-id: ${{ inputs.caller-run-id }} + - name: Build (test utils selectors) + run: npx gulp quick-build + env: + NODE_ENV: production + - name: Upload PR pages artifact (for matrix jobs) uses: actions/upload-artifact@v4 with: @@ -63,6 +59,13 @@ jobs: path: pages/lib/static-default retention-days: 1 + - name: Upload test utils artifact + uses: actions/upload-artifact@v4 + with: + name: visual-test-utils + path: lib/components/test-utils + retention-days: 1 + # Build the baseline (main branch) pages once and share them across all browser jobs. # Runs in parallel with stage-pr-pages. build-baseline: @@ -150,7 +153,14 @@ jobs: name: visual-baseline-pages path: pages/lib/static-visual-baseline + - name: Download test utils artifact + uses: actions/download-artifact@v4 + with: + name: visual-test-utils + path: lib/components/test-utils + # ── Run tests ───────────────────────────────────────────────────────── + - name: Start test server (port 8080) run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-default & From 14714bb89f72e6f864a163516b01de2ff364358f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 30 May 2026 00:35:48 +0200 Subject: [PATCH 065/135] Optimizations --- .github/workflows/deploy.yml | 8 +++++++ .github/workflows/visual-regression.yml | 31 ++++++------------------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2add3494cd..df56701906 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,6 +53,13 @@ jobs: name: dev-pages-react${{ matrix.react }} path: pages/lib/static-default + - name: Upload test utils selectors artifact + if: matrix.react == 18 + uses: actions/upload-artifact@v4 + with: + name: test-utils-selectors + path: lib/components/test-utils + deploy: needs: quick-build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} @@ -74,4 +81,5 @@ jobs: secrets: inherit with: pr-artifact-name: dev-pages-react18 + test-utils-artifact-name: test-utils-selectors caller-run-id: ${{ github.run_id }} diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index ed5739b0e3..71349ddb28 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -7,6 +7,10 @@ on: description: 'Name of the artifact containing PR pages (built by the caller workflow).' required: true type: string + test-utils-artifact-name: + description: 'Name of the artifact containing test-utils selectors.' + required: true + type: string caller-run-id: description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' required: true @@ -28,17 +32,6 @@ jobs: name: Stage PR pages runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm - - - name: Install dependencies - run: npm i - - name: Download PR pages artifact from caller run uses: actions/download-artifact@v4 with: @@ -47,11 +40,6 @@ jobs: github-token: ${{ github.token }} run-id: ${{ inputs.caller-run-id }} - - name: Build (test utils selectors) - run: npx gulp quick-build - env: - NODE_ENV: production - - name: Upload PR pages artifact (for matrix jobs) uses: actions/upload-artifact@v4 with: @@ -59,13 +47,6 @@ jobs: path: pages/lib/static-default retention-days: 1 - - name: Upload test utils artifact - uses: actions/upload-artifact@v4 - with: - name: visual-test-utils - path: lib/components/test-utils - retention-days: 1 - # Build the baseline (main branch) pages once and share them across all browser jobs. # Runs in parallel with stage-pr-pages. build-baseline: @@ -156,8 +137,10 @@ jobs: - name: Download test utils artifact uses: actions/download-artifact@v4 with: - name: visual-test-utils + name: ${{ inputs.test-utils-artifact-name }} path: lib/components/test-utils + github-token: ${{ github.token }} + run-id: ${{ inputs.caller-run-id }} # ── Run tests ───────────────────────────────────────────────────────── From 1a063b1400db19bd4655c11b8661ba236d63400f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 30 May 2026 00:38:30 +0200 Subject: [PATCH 066/135] Upload the entire lib/components directory --- .github/workflows/deploy.yml | 2 +- .github/workflows/visual-regression.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index df56701906..36964ddc1f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -58,7 +58,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-utils-selectors - path: lib/components/test-utils + path: lib/components deploy: needs: quick-build diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 71349ddb28..b1333cf1f4 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -138,7 +138,7 @@ jobs: uses: actions/download-artifact@v4 with: name: ${{ inputs.test-utils-artifact-name }} - path: lib/components/test-utils + path: lib/components github-token: ${{ github.token }} run-id: ${{ inputs.caller-run-id }} From dc4fea381f7a18aedcc9c8ef7413d55483c3ac59 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 30 May 2026 00:48:38 +0200 Subject: [PATCH 067/135] Remove unnecessary step --- .github/workflows/visual-regression.yml | 28 ++++--------------------- 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index b1333cf1f4..a35b2fa877 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -26,29 +26,7 @@ permissions: actions: read jobs: - # Stage the PR pages and build test utils for the visual regression shards. - # Runs in parallel with build-baseline. - stage-pr-pages: - name: Stage PR pages - runs-on: ubuntu-latest - steps: - - name: Download PR pages artifact from caller run - uses: actions/download-artifact@v4 - with: - name: ${{ inputs.pr-artifact-name }} - path: pages/lib/static-default - github-token: ${{ github.token }} - run-id: ${{ inputs.caller-run-id }} - - - name: Upload PR pages artifact (for matrix jobs) - uses: actions/upload-artifact@v4 - with: - name: visual-pr-pages - path: pages/lib/static-default - retention-days: 1 - # Build the baseline (main branch) pages once and share them across all browser jobs. - # Runs in parallel with stage-pr-pages. build-baseline: name: Build baseline pages runs-on: ubuntu-latest @@ -99,7 +77,7 @@ jobs: visual: name: Visual regression (shard ${{ matrix.shard }}) - needs: [stage-pr-pages, build-baseline] + needs: [build-baseline] runs-on: ubuntu-latest strategy: fail-fast: false @@ -125,8 +103,10 @@ jobs: - name: Download PR pages artifact uses: actions/download-artifact@v4 with: - name: visual-pr-pages + name: ${{ inputs.pr-artifact-name }} path: pages/lib/static-default + github-token: ${{ github.token }} + run-id: ${{ inputs.caller-run-id }} - name: Download baseline artifact uses: actions/download-artifact@v4 From 5c18355d56f1f37a37cc944326a4b4404a130d7a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 30 May 2026 01:07:41 +0200 Subject: [PATCH 068/135] Add more components tests --- .github/workflows/visual-regression.yml | 4 +- test/definitions/index.ts | 6 +- test/definitions/visual/area-chart.ts | 136 ++++++++++++++++++++ test/definitions/visual/attribute-editor.ts | 31 +++++ test/definitions/visual/autosuggest.ts | 70 ++++++++++ test/definitions/visual/badge.ts | 23 ++++ test/visual/area-chart.test.ts | 6 + test/visual/attribute-editor.test.ts | 5 + test/visual/autosuggest.test.ts | 6 + test/visual/badge.test.ts | 6 + 10 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 test/definitions/visual/area-chart.ts create mode 100644 test/definitions/visual/attribute-editor.ts create mode 100644 test/definitions/visual/autosuggest.ts create mode 100644 test/definitions/visual/badge.ts create mode 100644 test/visual/area-chart.test.ts create mode 100644 test/visual/attribute-editor.test.ts create mode 100644 test/visual/autosuggest.test.ts create mode 100644 test/visual/badge.test.ts diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index a35b2fa877..adb4d3ed5b 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -82,7 +82,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2, 3] + shard: [1, 2, 3, 4, 5, 6] steps: - uses: actions/checkout@v4 @@ -134,7 +134,7 @@ jobs: run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/3 + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/6 env: TZ: UTC diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 6ff236cfa6..5f6d790d65 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -7,5 +7,9 @@ import { TestSuite } from './types'; import actionCard from './visual/action-card'; import alert from './visual/alert'; import appLayout from './visual/app-layout'; +import areaChart from './visual/area-chart'; +import attributeEditor from './visual/attribute-editor'; +import autosuggest from './visual/autosuggest'; +import badge from './visual/badge'; -export const allSuites: TestSuite[] = [actionCard, alert, appLayout]; +export const allSuites: TestSuite[] = [actionCard, alert, appLayout, areaChart, attributeEditor, autosuggest, badge]; diff --git a/test/definitions/visual/area-chart.ts b/test/definitions/visual/area-chart.ts new file mode 100644 index 0000000000..10855239f6 --- /dev/null +++ b/test/definitions/visual/area-chart.ts @@ -0,0 +1,136 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_FILTER_TRIGGER = '#linear-latency-chart button'; +const TEST_CHART_TOOLTIP_HEADER = '#linear-latency-chart h2'; + +const suite: TestSuite = { + description: 'Area chart', + componentName: 'area-chart', + tests: [ + { + description: 'permutations', + path: 'area-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'fit-height', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit-height no filter, no legend', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFilter: 'true', hideLegend: 'true' }, + }, + { + description: 'fit-height, no legend', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideLegend: 'true' }, + }, + { + description: 'chart plot has a focus outline', + path: 'area-chart/test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + }, + }, + { + description: 'can navigate along X axis highlighting all series with keyboard', + path: 'area-chart/test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can navigate a specific series with keyboard', + path: 'area-chart/test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowDown']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'selects correct series when navigated back from legend', + path: 'area-chart/test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.keys(['Tab']); + await page.keys(['Tab']); + await page.keys(['ArrowRight']); + await page.keys(['Shift', 'Tab']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover for all data points at a given X coordinate with keyboard', + path: 'area-chart/test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'can pin popover for a point in a specific series with keyboard', + path: 'area-chart/test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowDown']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'area-chart/test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('[aria-label="Linear latency chart"]', 200, 50); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/attribute-editor.ts b/test/definitions/visual/attribute-editor.ts new file mode 100644 index 0000000000..33da3fad86 --- /dev/null +++ b/test/definitions/visual/attribute-editor.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Attribute Editor', + componentName: 'attribute-editor', + tests: [360, 768, 992].flatMap(width => [ + { + description: `permutations at ${width}px`, + path: 'attribute-editor/permutations', + screenshotType: 'permutations' as const, + configuration: { width }, + }, + { + description: `customizable-footer at ${width}px`, + path: 'attribute-editor/customizable-footer', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: `with long select at ${width}px`, + path: 'attribute-editor/select-with-long-value', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/autosuggest.ts b/test/definitions/visual/autosuggest.ts new file mode 100644 index 0000000000..92f4d46897 --- /dev/null +++ b/test/definitions/visual/autosuggest.ts @@ -0,0 +1,70 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import createWrapper from '../../../lib/components/test-utils/selectors'; +import { TestDefinition, TestSuite } from '../types'; + +const wrapper = createWrapper(); + +const suite: TestSuite = { + description: 'Autosuggest', + componentName: 'autosuggest', + tests: [ + { + description: 'permutations', + path: 'autosuggest/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'permutations for async properties', + path: 'autosuggest/permutations-async', + screenshotType: 'permutations', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Displays options with groups correctly', + path: 'autosuggest/scenarios', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Correctly displays dropdown regions', + path: 'autosuggest/regions-scenarios', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Long virtual list - navigate to last item', + path: 'autosuggest/virtual-scroll', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); + await page.keys(['ArrowUp']); + }, + }, + ...[true, false].map( + virtualScroll => + ({ + description: `with custom renderOption (virtualScroll=${virtualScroll})`, + path: 'autosuggest/custom-render-option', + screenshotType: 'screenshotArea' as const, + queryParams: { virtualScroll: String(virtualScroll) }, + setup: async page => { + await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); + await page.keys(['ArrowDown']); + }, + }) as TestDefinition + ), + ], +}; + +export default suite; diff --git a/test/definitions/visual/badge.ts b/test/definitions/visual/badge.ts new file mode 100644 index 0000000000..7d405ed843 --- /dev/null +++ b/test/definitions/visual/badge.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Badge', + componentName: 'badge', + tests: [ + { + description: 'permutation page', + path: 'badge/permutations', + screenshotType: 'permutations', + }, + { + description: 'style custom page', + path: 'badge/style-custom-types', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/visual/area-chart.test.ts b/test/visual/area-chart.test.ts new file mode 100644 index 0000000000..51d6631016 --- /dev/null +++ b/test/visual/area-chart.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import areaChart from '../definitions/visual/area-chart'; + +runTestSuites([areaChart]); diff --git a/test/visual/attribute-editor.test.ts b/test/visual/attribute-editor.test.ts new file mode 100644 index 0000000000..f57e8785b2 --- /dev/null +++ b/test/visual/attribute-editor.test.ts @@ -0,0 +1,5 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import attributeEditor from '../definitions/visual/attribute-editor'; +runTestSuites([attributeEditor]); diff --git a/test/visual/autosuggest.test.ts b/test/visual/autosuggest.test.ts new file mode 100644 index 0000000000..454c00c648 --- /dev/null +++ b/test/visual/autosuggest.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import autosuggest from '../definitions/visual/autosuggest'; + +runTestSuites([autosuggest]); diff --git a/test/visual/badge.test.ts b/test/visual/badge.test.ts new file mode 100644 index 0000000000..6b24838a42 --- /dev/null +++ b/test/visual/badge.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import badge from '../definitions/visual/badge'; + +runTestSuites([badge]); From ff7434063a8babaf835d9dd9fe852705cc2d9119 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 30 May 2026 08:36:55 +0200 Subject: [PATCH 069/135] Split app layout test definition files --- .github/workflows/visual-regression.yml | 4 +- test/definitions/index.ts | 28 +- .../visual/app-layout-content-paddings.ts | 30 + test/definitions/visual/app-layout-drawers.ts | 41 ++ .../definitions/visual/app-layout-flashbar.ts | 39 ++ test/definitions/visual/app-layout-header.ts | 85 +++ test/definitions/visual/app-layout-multi.ts | 25 + .../visual/app-layout-responsive.ts | 169 ++++++ ...-layout-sticky-table-header-split-panel.ts | 78 +++ test/definitions/visual/app-layout-toolbar.ts | 23 + test/definitions/visual/app-layout-z-index.ts | 60 ++ test/definitions/visual/app-layout.ts | 539 +----------------- .../app-layout-content-paddings.test.ts | 6 + test/visual/app-layout-drawers.test.ts | 6 + test/visual/app-layout-flashbar.test.ts | 6 + test/visual/app-layout-header.test.ts | 6 + test/visual/app-layout-multi.test.ts | 6 + test/visual/app-layout-responsive.test.ts | 6 + ...ut-sticky-table-header-split-panel.test.ts | 6 + test/visual/app-layout-toolbar.test.ts | 6 + test/visual/app-layout-z-index.test.ts | 6 + test/visual/app-layout.test.ts | 4 +- 22 files changed, 662 insertions(+), 517 deletions(-) create mode 100644 test/definitions/visual/app-layout-content-paddings.ts create mode 100644 test/definitions/visual/app-layout-drawers.ts create mode 100644 test/definitions/visual/app-layout-flashbar.ts create mode 100644 test/definitions/visual/app-layout-header.ts create mode 100644 test/definitions/visual/app-layout-multi.ts create mode 100644 test/definitions/visual/app-layout-responsive.ts create mode 100644 test/definitions/visual/app-layout-sticky-table-header-split-panel.ts create mode 100644 test/definitions/visual/app-layout-toolbar.ts create mode 100644 test/definitions/visual/app-layout-z-index.ts create mode 100644 test/visual/app-layout-content-paddings.test.ts create mode 100644 test/visual/app-layout-drawers.test.ts create mode 100644 test/visual/app-layout-flashbar.test.ts create mode 100644 test/visual/app-layout-header.test.ts create mode 100644 test/visual/app-layout-multi.test.ts create mode 100644 test/visual/app-layout-responsive.test.ts create mode 100644 test/visual/app-layout-sticky-table-header-split-panel.test.ts create mode 100644 test/visual/app-layout-toolbar.test.ts create mode 100644 test/visual/app-layout-z-index.test.ts diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index adb4d3ed5b..8e03c9967c 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -82,7 +82,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4, 5, 6] + shard: [1, 2, 3, 4, 5, 6, 7, 8] steps: - uses: actions/checkout@v4 @@ -134,7 +134,7 @@ jobs: run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/6 + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/8 env: TZ: UTC diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 5f6d790d65..17dc1902a0 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -7,9 +7,35 @@ import { TestSuite } from './types'; import actionCard from './visual/action-card'; import alert from './visual/alert'; import appLayout from './visual/app-layout'; +import appLayoutContentPaddings from './visual/app-layout-content-paddings'; +import appLayoutDrawers from './visual/app-layout-drawers'; +import appLayoutFlashbar from './visual/app-layout-flashbar'; +import appLayoutHeader from './visual/app-layout-header'; +import appLayoutMulti from './visual/app-layout-multi'; +import appLayoutResponsive from './visual/app-layout-responsive'; +import appLayoutStickyTableHeaderSplitPanel from './visual/app-layout-sticky-table-header-split-panel'; +import appLayoutToolbar from './visual/app-layout-toolbar'; +import appLayoutZIndex from './visual/app-layout-z-index'; import areaChart from './visual/area-chart'; import attributeEditor from './visual/attribute-editor'; import autosuggest from './visual/autosuggest'; import badge from './visual/badge'; -export const allSuites: TestSuite[] = [actionCard, alert, appLayout, areaChart, attributeEditor, autosuggest, badge]; +export const allSuites: TestSuite[] = [ + actionCard, + alert, + appLayout, + appLayoutContentPaddings, + appLayoutDrawers, + appLayoutFlashbar, + appLayoutHeader, + appLayoutMulti, + appLayoutResponsive, + appLayoutStickyTableHeaderSplitPanel, + appLayoutToolbar, + appLayoutZIndex, + areaChart, + attributeEditor, + autosuggest, + badge, +]; diff --git a/test/definitions/visual/app-layout-content-paddings.ts b/test/definitions/visual/app-layout-content-paddings.ts new file mode 100644 index 0000000000..068e5dd08e --- /dev/null +++ b/test/definitions/visual/app-layout-content-paddings.ts @@ -0,0 +1,30 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Content paddings', + componentName: 'app-layout', + tests: [ + ...(['true', 'false'] as const).flatMap(toolsEnabled => + (['true', 'false'] as const).flatMap(splitPanelEnabled => + (['bottom', 'side'] as const).map(splitPanelPosition => ({ + description: `toolsEnabled=${toolsEnabled} splitPanelEnabled=${splitPanelEnabled} splitPanelPosition=${splitPanelPosition}`, + path: 'app-layout/with-split-panel', + screenshotType: 'screenshotArea' as const, + queryParams: { toolsEnabled, splitPanelEnabled, splitPanelPosition }, + })) + ) + ), + ...[1500, 600].map(width => ({ + description: `with split panel and disabled content paddings - width=${width}`, + path: 'app-layout/disable-paddings-with-split-panel', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { splitPanelOpen: 'true', splitPanelPosition: 'side' }, + })), + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-drawers.ts b/test/definitions/visual/app-layout-drawers.ts new file mode 100644 index 0000000000..2af69c2ca1 --- /dev/null +++ b/test/definitions/visual/app-layout-drawers.ts @@ -0,0 +1,41 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import createWrapper from '../../../lib/components/test-utils/selectors'; +import { TestSuite } from '../types'; + +const wrapper = createWrapper(); + +const suite: TestSuite = { + description: 'Drawers', + componentName: 'app-layout', + tests: [ + { + description: 'with split panel', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); + }, + }, + { + description: 'with tooltip on hover', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); + }, + }, + { + description: 'with custom scrollable drawer content', + path: 'app-layout/with-drawers-scrollable', + screenshotType: 'screenshotArea', + queryParams: { sideNavFill: 'false' }, + setup: async page => { + await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-flashbar.ts b/test/definitions/visual/app-layout-flashbar.ts new file mode 100644 index 0000000000..5849a5cbf5 --- /dev/null +++ b/test/definitions/visual/app-layout-flashbar.ts @@ -0,0 +1,39 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Flashbar', + componentName: 'app-layout', + tests: [true, false].flatMap(disableContentPaddings => + [true, false].flatMap(stickyNotifications => + [true, false].flatMap(stickyTableHeader => + [true, false].map(stackNotifications => ({ + description: `disableContentPaddings: ${disableContentPaddings}, stickyNotifications: ${stickyNotifications}, stickyTableHeader: ${stickyTableHeader}, stackNotifications: ${stackNotifications}`, + path: 'app-layout/with-stacked-notifications-and-table', + screenshotType: 'screenshotArea' as const, + configuration: { width: 1280, height: 900 }, + setup: async (page: import('@cloudscape-design/browser-test-tools/page-objects').ScreenshotPageObject) => { + if (!disableContentPaddings) { + await page.click('[data-id="toggle-content-paddings"]'); + } + if (stickyNotifications) { + await page.click('[data-id="toggle-sticky-notifications"]'); + } + if (!stickyTableHeader) { + await page.click('[data-id="toggle-sticky-table-header"]'); + } + if (!stackNotifications) { + await page.click('[data-id="toggle-stack-items"]'); + } + await page.click('[data-id="add-notification"]'); + await page.click('[data-id="add-notification"]'); + }, + })) + ) + ) + ), +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-header.ts b/test/definitions/visual/app-layout-header.ts new file mode 100644 index 0000000000..55f674860c --- /dev/null +++ b/test/definitions/visual/app-layout-header.ts @@ -0,0 +1,85 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Headers', + componentName: 'app-layout', + tests: [ + // ── Headers ─────────────────────────────────────────────────────────── + { + description: 'Headers', + tests: [600, 1280].flatMap(width => [ + { + description: `alignment with full-page table (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: `alignment with full-page table in sticky state (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `alignment with full-page table in sticky state with sticky notifications (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { stickyNotifications: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `high contrast header variant in landing page (${width}px)`, + path: 'app-layout/landing-page', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), + }, + + // ── High contrast header variant ────────────────────────────────────── + { + description: 'High contrast header variant', + tests: [ + ...[1400, 600].flatMap(width => [ + { + description: `with breadcrumbs and notifications at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', hasContainer: 'true' }, + } as TestDefinition, + { + description: `without overlap at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { disableOverlap: 'true' }, + } as TestDefinition, + { + description: `with content layout at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + hasContainer: 'true', + hasContentLayout: 'true', + }, + } as TestDefinition, + ]), + ], + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-multi.ts b/test/definitions/visual/app-layout-multi.ts new file mode 100644 index 0000000000..7efbbcd473 --- /dev/null +++ b/test/definitions/visual/app-layout-multi.ts @@ -0,0 +1,25 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Multiple instances', + componentName: 'app-layout', + tests: [600, 1280].flatMap(width => [ + { + description: `simple (${width}px)`, + path: 'app-layout/multi-layout-simple', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: `iframe (${width}px)`, + path: 'app-layout/multi-layout-iframe', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-responsive.ts b/test/definitions/visual/app-layout-responsive.ts new file mode 100644 index 0000000000..8f227a5814 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive.ts @@ -0,0 +1,169 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +function responsiveTests(width: number): TestSuite { + return { + description: `width ${width}px`, + componentName: 'app-layout', + tests: [ + { + description: 'default', + path: 'app-layout/default', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'navigation drawer is open', + path: 'app-layout/with-wizard', + screenshotType: 'screenshotArea', + configuration: { width }, + setup: async page => { + await page.click('[aria-label="Open navigation"]'); + }, + }, + { + description: 'wizard', + path: 'app-layout/with-wizard', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with wizard and table', + path: 'app-layout/with-wizard-and-table', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with wizard, table, and breadcrumbs', + path: 'app-layout/with-wizard-and-table', + screenshotType: 'screenshotArea', + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true' }, + }, + { + description: 'notifications', + path: 'app-layout/with-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'breadcrumbs', + path: 'app-layout/with-breadcrumbs', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'notifications and breadcrumbs', + path: 'app-layout/with-breadcrumbs-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'dashboard content type', + path: 'app-layout/dashboard-content-type', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'fixed header and footer', + path: 'app-layout/with-fixed-header-footer', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disableBodyScroll - empty', + path: 'app-layout/legacy-nav-empty', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disableBodyScroll - with content', + path: 'app-layout/legacy-nav-scrollable', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disableBodyScroll - with split panel', + path: 'app-layout/legacy-nav-scrollable-with-split-panel', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disable paddings', + path: 'app-layout/disable-paddings', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'disable paddings with breadcrumbs', + path: 'app-layout/disable-paddings-breadcrumbs', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'sticky notifications', + path: 'app-layout/with-sticky-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'sticky notifications scrolled down', + path: 'app-layout/with-sticky-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + setup: async page => { + await page.windowScrollTo({ top: 2000 }); + }, + }, + { + description: 'layout without panels', + path: 'app-layout/no-panels', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'layout without panels but with notifications', + path: 'app-layout/no-panels-with-notifications', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with drawers', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with empty drawers', + path: 'app-layout/with-drawers-empty', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: 'with open drawer', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + configuration: { width }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + }, + }, + ], + }; +} + +const suite: TestSuite = { + description: 'AppLayout responsive', + componentName: 'app-layout', + tests: [ + responsiveTests(600), + responsiveTests(1280), + responsiveTests(1400), + responsiveTests(1920), + responsiveTests(2540), + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts new file mode 100644 index 0000000000..91628802ff --- /dev/null +++ b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts @@ -0,0 +1,78 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Sticky header with split panel', + componentName: 'app-layout', + tests: [ + { + description: 'scrolling to bottom with closed split panel (1 table row)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-1"]'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'scrolling to bottom with closed split panel (30 table rows)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky with open split panel (1 table row)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-1"]'); + await page.click('aria/Open panel'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky with open split panel (30 table rows)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.click('aria/Open panel'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky when mounting and unmounting a second table', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.click('aria/Open panel'); + await page.windowScrollTo({ top: 0 }); + await page.click('aria/Close panel'); + await page.scrollToBottom('html'); + }, + }, + // ── Max content width ───────────────────────────────────────────────── + { + description: 'maxContentWidth set to Number.MAX_VALUE', + path: 'app-layout/refresh-content-width', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 700 }, + setup: async page => { + await page.click('[data-test-id="button_width-number-max_value"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-toolbar.ts b/test/definitions/visual/app-layout-toolbar.ts new file mode 100644 index 0000000000..dc9f5861fe --- /dev/null +++ b/test/definitions/visual/app-layout-toolbar.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Toolbar', + componentName: 'app-layout', + tests: [ + { + description: 'multiple nested instances (no breadcrumbs dedup)', + path: 'app-layout-toolbar/multi-layout-with-hidden-instances', + screenshotType: 'screenshotArea', + }, + { + description: 'no toolbar', + path: 'app-layout-toolbar/without-toolbar', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-z-index.ts b/test/definitions/visual/app-layout-z-index.ts new file mode 100644 index 0000000000..7765a496a9 --- /dev/null +++ b/test/definitions/visual/app-layout-z-index.ts @@ -0,0 +1,60 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Z-index', + componentName: 'app-layout', + tests: [ + ...[600, 1280].flatMap(width => [ + { + description: `button dropdown (${width}px)`, + path: 'app-layout/with-absolute-components', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + setup: async page => { + await page.click('button=Button dropdown'); + await page.click('[data-testid="2"]'); + await page.windowScrollTo({ top: 300 }); + }, + } as TestDefinition, + { + description: `select (${width}px)`, + path: 'app-layout/with-absolute-components', + screenshotType: 'screenshotArea' as const, + configuration: { width, height: 800 }, + setup: async page => { + await page.click('[data-testid="select-demo"] button'); + await page.windowScrollTo({ top: 300 }); + }, + } as TestDefinition, + { + description: `split-panel and full-page table (${width}px)`, + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), + { + description: 'split-panel and full-page with open navigation (600px)', + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'screenshotArea' as const, + configuration: { width: 600 }, + setup: async page => { + await page.click('button[aria-label="Open navigation"]'); + }, + }, + { + description: 'split-panel and full-page with open tools (600px)', + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'screenshotArea' as const, + configuration: { width: 600 }, + setup: async page => { + await page.click('button[aria-label="Open tools"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts index 12c76d9138..c2f6a41f5c 100644 --- a/test/definitions/visual/app-layout.ts +++ b/test/definitions/visual/app-layout.ts @@ -1,174 +1,12 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import createWrapper from '../../../lib/components/test-utils/selectors'; -import { TestDefinition, TestSuite } from '../types'; - -const wrapper = createWrapper(); - -function responsiveTests(width: number): TestSuite { - return { - description: `width ${width}px`, - componentName: 'app-layout', - tests: [ - { - description: 'default', - path: 'app-layout/default', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'navigation drawer is open', - path: 'app-layout/with-wizard', - screenshotType: 'screenshotArea', - configuration: { width }, - setup: async page => { - await page.click('[aria-label="Open navigation"]'); - }, - }, - { - description: 'wizard', - path: 'app-layout/with-wizard', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'with wizard and table', - path: 'app-layout/with-wizard-and-table', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'with wizard, table, and breadcrumbs', - path: 'app-layout/with-wizard-and-table', - screenshotType: 'screenshotArea', - configuration: { width }, - queryParams: { hasBreadcrumbs: 'true' }, - }, - { - description: 'notifications', - path: 'app-layout/with-notifications', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'breadcrumbs', - path: 'app-layout/with-breadcrumbs', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'notifications and breadcrumbs', - path: 'app-layout/with-breadcrumbs-notifications', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'dashboard content type', - path: 'app-layout/dashboard-content-type', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'fixed header and footer', - path: 'app-layout/with-fixed-header-footer', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'disableBodyScroll - empty', - path: 'app-layout/legacy-nav-empty', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'disableBodyScroll - with content', - path: 'app-layout/legacy-nav-scrollable', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'disableBodyScroll - with split panel', - path: 'app-layout/legacy-nav-scrollable-with-split-panel', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'disable paddings', - path: 'app-layout/disable-paddings', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'disable paddings with breadcrumbs', - path: 'app-layout/disable-paddings-breadcrumbs', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'sticky notifications', - path: 'app-layout/with-sticky-notifications', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'sticky notifications scrolled down', - path: 'app-layout/with-sticky-notifications', - screenshotType: 'screenshotArea', - configuration: { width }, - setup: async page => { - await page.windowScrollTo({ top: 2000 }); - }, - }, - { - description: 'layout without panels', - path: 'app-layout/no-panels', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'layout without panels but with notifications', - path: 'app-layout/no-panels-with-notifications', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'with drawers', - path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'with empty drawers', - path: 'app-layout/with-drawers-empty', - screenshotType: 'screenshotArea', - configuration: { width }, - }, - { - description: 'with open drawer', - path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', - configuration: { width }, - setup: async page => { - await page.click('[aria-label="Security trigger button"]'); - }, - }, - ], - }; -} +import { TestSuite } from '../types'; const suite: TestSuite = { description: 'AppLayout', componentName: 'app-layout', tests: [ - // ── Responsive tests at multiple breakpoints ────────────────────────── - responsiveTests(600), - responsiveTests(1280), - responsiveTests(1400), - responsiveTests(1920), - responsiveTests(2540), - - // ── General tests ───────────────────────────────────────────────────── { description: 'no scrollbars at 320px', path: 'app-layout/default', @@ -227,361 +65,38 @@ const suite: TestSuite = { }, }, - // ── Content paddings ────────────────────────────────────────────────── - { - description: 'Content paddings', - tests: [ - ...(['true', 'false'] as const).flatMap(toolsEnabled => - (['true', 'false'] as const).flatMap(splitPanelEnabled => - (['bottom', 'side'] as const).map(splitPanelPosition => ({ - description: `toolsEnabled=${toolsEnabled} splitPanelEnabled=${splitPanelEnabled} splitPanelPosition=${splitPanelPosition}`, - path: 'app-layout/with-split-panel', - screenshotType: 'screenshotArea' as const, - queryParams: { toolsEnabled, splitPanelEnabled, splitPanelPosition }, - })) - ) - ), - ...[1500, 600].map(width => ({ - description: `with split panel and disabled content paddings - width=${width}`, - path: 'app-layout/disable-paddings-with-split-panel', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { splitPanelOpen: 'true', splitPanelPosition: 'side' }, - })), - ], - }, - - // ── Drawers ─────────────────────────────────────────────────────────── - { - description: 'Drawers', - tests: [ - { - description: 'with split panel', - path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', - setup: async page => { - await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); - }, - }, - { - description: 'with tooltip on hover', - path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', - setup: async page => { - await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); - }, - }, - { - description: 'with custom scrollable drawer content', - path: 'app-layout/with-drawers-scrollable', - screenshotType: 'screenshotArea', - queryParams: { sideNavFill: 'false' }, - setup: async page => { - await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); - }, - }, - ], - }, - - // ── Headers ─────────────────────────────────────────────────────────── - { - description: 'Headers', - tests: [600, 1280].flatMap(width => [ - { - description: `alignment with full-page table (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - { - description: `alignment with full-page table in sticky state (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - setup: async page => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `alignment with full-page table in sticky state with sticky notifications (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { stickyNotifications: 'true' }, - setup: async page => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `high contrast header variant in landing page (${width}px)`, - path: 'app-layout/landing-page', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - ]), - }, - - // ── High contrast header variant ────────────────────────────────────── + // regression for https://github.com/cloudscape-design/components/pull/1612 { - description: 'High contrast header variant', - tests: [ - ...[1400, 600].flatMap(width => [ - { - description: `with breadcrumbs and notifications at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', hasContainer: 'true' }, - }, - { - description: `without overlap at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { disableOverlap: 'true' }, - }, - { - description: `with content layout at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - hasContainer: 'true', - hasContentLayout: 'true', - }, - }, - ]), - ], - }, - - // ── Multiple instances ───────────────────────────────────────────────── - { - description: 'Multiple instances', - tests: [600, 1280].flatMap(width => [ - { - description: `simple (${width}px)`, - path: 'app-layout/multi-layout-simple', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - { - description: `iframe (${width}px)`, - path: 'app-layout/multi-layout-iframe', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - ]), - }, - - // ── Z-index (absolute components) ───────────────────────────────────── - { - description: 'Z-index', - tests: [ - ...[600, 1280].flatMap(width => [ - { - description: `button dropdown (${width}px)`, - path: 'app-layout/with-absolute-components', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - setup: async page => { - await page.click('button=Button dropdown'); - await page.click('[data-testid="2"]'); - await page.windowScrollTo({ top: 300 }); - }, - } as TestDefinition, - { - description: `select (${width}px)`, - path: 'app-layout/with-absolute-components', - screenshotType: 'screenshotArea' as const, - configuration: { width, height: 800 }, - setup: async page => { - await page.click('[data-testid="select-demo"] button'); - await page.windowScrollTo({ top: 300 }); - }, - } as TestDefinition, - { - description: `split-panel and full-page table (${width}px)`, - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - ]), - { - description: 'split-panel and full-page with open navigation (600px)', - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'screenshotArea' as const, - configuration: { width: 600 }, - setup: async page => { - await page.click('button[aria-label="Open navigation"]'); - }, - }, - { - description: 'split-panel and full-page with open tools (600px)', - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'screenshotArea' as const, - configuration: { width: 600 }, - setup: async page => { - await page.click('button[aria-label="Open tools"]'); - }, - }, - ], - }, - - // ── Toolbar ─────────────────────────────────────────────────────────── - { - description: 'Toolbar', - tests: [ - { - description: 'multiple nested instances (no breadcrumbs dedup)', - path: 'app-layout-toolbar/multi-layout-with-hidden-instances', - screenshotType: 'screenshotArea', - }, - { - description: 'no toolbar', - path: 'app-layout-toolbar/without-toolbar', - screenshotType: 'screenshotArea', - }, - ], - }, - - // ── Max content width ───────────────────────────────────────────────── - { - description: 'Max content width', - tests: [ - { - description: 'maxContentWidth set to Number.MAX_VALUE', - path: 'app-layout/refresh-content-width', - screenshotType: 'screenshotArea', - configuration: { width: 1280, height: 700 }, - setup: async page => { - await page.click('[data-test-id="button_width-number-max_value"]'); - }, - }, - ], - }, - - // ── Sticky table header with split panel ────────────────────────────── - { - description: 'Sticky header with split panel', - tests: [ - { - description: 'scrolling to bottom with closed split panel (1 table row)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-1"]'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'scrolling to bottom with closed split panel (30 table rows)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky with open split panel (1 table row)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-1"]'); - await page.click('aria/Open panel'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky with open split panel (30 table rows)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.click('aria/Open panel'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky when mounting and unmounting a second table', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.click('aria/Open panel'); - await page.windowScrollTo({ top: 0 }); - await page.click('aria/Close panel'); - await page.scrollToBottom('html'); - }, - }, - ], + description: 'with open drawer and open side split panel after resize', + path: 'app-layout/with-drawers', + screenshotType: 'screenshotArea', + configuration: { width: 1500 }, + queryParams: { splitPanelPosition: 'side' }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + await page.click('[aria-label="Open panel"]'); + await page.setWindowSize({ width: 1400, height: 800 }); + }, }, - // ── Flashbar ────────────────────────────────────────────────────────── + // ── Transitions ─────────────────────────────────────────────────────── { - description: 'Flashbar', - tests: [true, false].flatMap(disableContentPaddings => - [true, false].flatMap(stickyNotifications => - [true, false].flatMap(stickyTableHeader => - [true, false].map(stackNotifications => ({ - description: `disableContentPaddings: ${disableContentPaddings}, stickyNotifications: ${stickyNotifications}, stickyTableHeader: ${stickyTableHeader}, stackNotifications: ${stackNotifications}`, - path: 'app-layout/with-stacked-notifications-and-table', - screenshotType: 'screenshotArea' as const, - configuration: { width: 1280, height: 900 }, - setup: async ( - page: import('@cloudscape-design/browser-test-tools/page-objects').ScreenshotPageObject - ) => { - if (!disableContentPaddings) { - await page.click('[data-id="toggle-content-paddings"]'); - } - if (stickyNotifications) { - await page.click('[data-id="toggle-sticky-notifications"]'); - } - if (!stickyTableHeader) { - await page.click('[data-id="toggle-sticky-table-header"]'); - } - if (!stackNotifications) { - await page.click('[data-id="toggle-stack-items"]'); - } - await page.click('[data-id="add-notification"]'); - await page.click('[data-id="add-notification"]'); - }, - })) - ) - ) - ), + description: 'transition from 400px to 1800px', + path: 'app-layout/default', + screenshotType: 'screenshotArea', + configuration: { width: 400, height: 400 }, + setup: async page => { + await page.setWindowSize({ width: 1800, height: 400 }); + }, }, - - // ── Transitions ─────────────────────────────────────────────────────── { - description: 'Transitions', - tests: [ - { - description: 'transition from 400px to 1800px', - path: 'app-layout/default', - screenshotType: 'screenshotArea', - configuration: { width: 400, height: 400 }, - setup: async page => { - await page.setWindowSize({ width: 1800, height: 400 }); - }, - }, - { - description: 'transition from 1800px to 400px', - path: 'app-layout/default', - screenshotType: 'screenshotArea', - configuration: { width: 1800, height: 400 }, - setup: async page => { - await page.setWindowSize({ width: 400, height: 400 }); - }, - }, - ], + description: 'transition from 1800px to 400px', + path: 'app-layout/default', + screenshotType: 'screenshotArea', + configuration: { width: 1800, height: 400 }, + setup: async page => { + await page.setWindowSize({ width: 400, height: 400 }); + }, }, ], }; diff --git a/test/visual/app-layout-content-paddings.test.ts b/test/visual/app-layout-content-paddings.test.ts new file mode 100644 index 0000000000..e566d79817 --- /dev/null +++ b/test/visual/app-layout-content-paddings.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-content-paddings'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-drawers.test.ts b/test/visual/app-layout-drawers.test.ts new file mode 100644 index 0000000000..c66454010d --- /dev/null +++ b/test/visual/app-layout-drawers.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-drawers'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-flashbar.test.ts b/test/visual/app-layout-flashbar.test.ts new file mode 100644 index 0000000000..333642e5f3 --- /dev/null +++ b/test/visual/app-layout-flashbar.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-flashbar'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-header.test.ts b/test/visual/app-layout-header.test.ts new file mode 100644 index 0000000000..682f71ffe2 --- /dev/null +++ b/test/visual/app-layout-header.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-header'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-multi.test.ts b/test/visual/app-layout-multi.test.ts new file mode 100644 index 0000000000..244019c8fb --- /dev/null +++ b/test/visual/app-layout-multi.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-multi'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive.test.ts b/test/visual/app-layout-responsive.test.ts new file mode 100644 index 0000000000..668d4b3522 --- /dev/null +++ b/test/visual/app-layout-responsive.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-sticky-table-header-split-panel.test.ts b/test/visual/app-layout-sticky-table-header-split-panel.test.ts new file mode 100644 index 0000000000..c1ad3016a1 --- /dev/null +++ b/test/visual/app-layout-sticky-table-header-split-panel.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-sticky-table-header-split-panel'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-toolbar.test.ts b/test/visual/app-layout-toolbar.test.ts new file mode 100644 index 0000000000..398d6386f8 --- /dev/null +++ b/test/visual/app-layout-toolbar.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-toolbar'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-z-index.test.ts b/test/visual/app-layout-z-index.test.ts new file mode 100644 index 0000000000..5f69f77b71 --- /dev/null +++ b/test/visual/app-layout-z-index.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-z-index'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout.test.ts b/test/visual/app-layout.test.ts index 93500354cc..21c3a6ce25 100644 --- a/test/visual/app-layout.test.ts +++ b/test/visual/app-layout.test.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { runTestSuites } from '../definitions/utils'; -import appLayout from '../definitions/visual/app-layout'; +import suite from '../definitions/visual/app-layout'; -runTestSuites([appLayout]); +runTestSuites([suite]); From d2de71667a1923dcb62f89f389bd4d9250d81f43 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 30 May 2026 08:58:50 +0200 Subject: [PATCH 070/135] Add viewport screenshot type --- test/definitions/types.ts | 2 +- test/definitions/utils.ts | 28 +++++++----- .../visual/app-layout-content-paddings.ts | 4 +- test/definitions/visual/app-layout-drawers.ts | 6 +-- test/definitions/visual/app-layout-header.ts | 8 ++-- test/definitions/visual/app-layout-multi.ts | 4 +- .../visual/app-layout-responsive.ts | 44 +++++++++---------- ...-layout-sticky-table-header-split-panel.ts | 12 ++--- test/definitions/visual/app-layout-toolbar.ts | 4 +- test/definitions/visual/app-layout-z-index.ts | 10 ++--- test/definitions/visual/app-layout.ts | 22 +++++----- test/definitions/visual/area-chart.ts | 14 +++--- 12 files changed, 83 insertions(+), 75 deletions(-) diff --git a/test/definitions/types.ts b/test/definitions/types.ts index 8cdca9996a..9f8a187c48 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -9,7 +9,7 @@ export interface ScreenshotTestConfiguration { // 'screenshotArea' — captures the .screenshot-area element on the page. // 'permutations' — captures the entire page and crops permutations out of it. -export type ScreenshotType = 'screenshotArea' | 'permutations'; +export type ScreenshotType = 'screenshotArea' | 'permutations' | 'viewport'; export interface TestDefinition { description: string; diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index c5b237dc73..683e50cc80 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -57,22 +57,25 @@ function registerSuites(suites: Array, getBrowser: ( } /** - * Captures the .screenshot-area element using a viewport-only screenshot (fast). + * Captures a screenshot based on the test's screenshotType. */ -async function captureScreenshotArea( +async function capture( browser: WebdriverIO.Browser, page: ScreenshotPageObject, url: string, - windowSize: { width: number; height: number } | undefined, - setup?: (page: ScreenshotPageObject) => Promise + testDef: TestDefinition, + windowSize: { width: number; height: number } | undefined ): Promise { if (windowSize) { await browser.setWindowSize(windowSize.width, windowSize.height); } await browser.url(url); await page.waitForVisible(screenshotAreaSelector); - if (setup) { - await setup(page); + if (testDef.setup) { + await testDef.setup(page); + } + if (testDef.screenshotType === 'viewport') { + return page.captureViewport(); } return page.captureBySelector(screenshotAreaSelector, { viewportOnly: true }); } @@ -86,9 +89,8 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - // Fast path: compare the screenshot area (viewport-only, no scroll-and-merge). - const newScreenshot = await captureScreenshotArea(browser, page, newUrl, windowSize, testDef.setup); - const oldScreenshot = await captureScreenshotArea(browser, page, oldUrl, windowSize, testDef.setup); + const newScreenshot = await capture(browser, page, newUrl, testDef, windowSize); + const oldScreenshot = await capture(browser, page, oldUrl, testDef, windowSize); const { diffPixels } = await cropAndCompare(newScreenshot, oldScreenshot); if (diffPixels === 0) { @@ -100,6 +102,9 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro // full capturePermutations strategy which resizes the window to fit all // content and returns individual permutation crops for precise comparison. if (testDef.screenshotType === 'permutations') { + if (windowSize) { + await browser.setWindowSize(windowSize.width, windowSize.height); + } await browser.url(newUrl); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { @@ -107,6 +112,9 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro } const newPermutations = await page.capturePermutations(); + if (windowSize) { + await browser.setWindowSize(windowSize.width, windowSize.height); + } await browser.url(oldUrl); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { @@ -122,7 +130,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro return; } - // For screenshotArea type, the diff is a real failure. + // For screenshotArea and viewport types, the diff is a real failure. expect(diffPixels).toBe(0); }); } diff --git a/test/definitions/visual/app-layout-content-paddings.ts b/test/definitions/visual/app-layout-content-paddings.ts index 068e5dd08e..e76d055749 100644 --- a/test/definitions/visual/app-layout-content-paddings.ts +++ b/test/definitions/visual/app-layout-content-paddings.ts @@ -12,7 +12,7 @@ const suite: TestSuite = { (['bottom', 'side'] as const).map(splitPanelPosition => ({ description: `toolsEnabled=${toolsEnabled} splitPanelEnabled=${splitPanelEnabled} splitPanelPosition=${splitPanelPosition}`, path: 'app-layout/with-split-panel', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, queryParams: { toolsEnabled, splitPanelEnabled, splitPanelPosition }, })) ) @@ -20,7 +20,7 @@ const suite: TestSuite = { ...[1500, 600].map(width => ({ description: `with split panel and disabled content paddings - width=${width}`, path: 'app-layout/disable-paddings-with-split-panel', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, queryParams: { splitPanelOpen: 'true', splitPanelPosition: 'side' }, })), diff --git a/test/definitions/visual/app-layout-drawers.ts b/test/definitions/visual/app-layout-drawers.ts index 2af69c2ca1..39c2089783 100644 --- a/test/definitions/visual/app-layout-drawers.ts +++ b/test/definitions/visual/app-layout-drawers.ts @@ -13,7 +13,7 @@ const suite: TestSuite = { { description: 'with split panel', path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', setup: async page => { await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); }, @@ -21,7 +21,7 @@ const suite: TestSuite = { { description: 'with tooltip on hover', path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', setup: async page => { await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); }, @@ -29,7 +29,7 @@ const suite: TestSuite = { { description: 'with custom scrollable drawer content', path: 'app-layout/with-drawers-scrollable', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', queryParams: { sideNavFill: 'false' }, setup: async page => { await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); diff --git a/test/definitions/visual/app-layout-header.ts b/test/definitions/visual/app-layout-header.ts index 55f674860c..3a128a266e 100644 --- a/test/definitions/visual/app-layout-header.ts +++ b/test/definitions/visual/app-layout-header.ts @@ -14,13 +14,13 @@ const suite: TestSuite = { { description: `alignment with full-page table (${width}px)`, path: 'app-layout/with-table', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, }, { description: `alignment with full-page table in sticky state (${width}px)`, path: 'app-layout/with-table', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, setup: async page => { await page.windowScrollTo({ top: 200 }); @@ -29,7 +29,7 @@ const suite: TestSuite = { { description: `alignment with full-page table in sticky state with sticky notifications (${width}px)`, path: 'app-layout/with-table', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, queryParams: { stickyNotifications: 'true' }, setup: async page => { @@ -39,7 +39,7 @@ const suite: TestSuite = { { description: `high contrast header variant in landing page (${width}px)`, path: 'app-layout/landing-page', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, }, ]), diff --git a/test/definitions/visual/app-layout-multi.ts b/test/definitions/visual/app-layout-multi.ts index 7efbbcd473..babf9733cf 100644 --- a/test/definitions/visual/app-layout-multi.ts +++ b/test/definitions/visual/app-layout-multi.ts @@ -10,13 +10,13 @@ const suite: TestSuite = { { description: `simple (${width}px)`, path: 'app-layout/multi-layout-simple', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, }, { description: `iframe (${width}px)`, path: 'app-layout/multi-layout-iframe', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, }, ]), diff --git a/test/definitions/visual/app-layout-responsive.ts b/test/definitions/visual/app-layout-responsive.ts index 8f227a5814..8a53da6a09 100644 --- a/test/definitions/visual/app-layout-responsive.ts +++ b/test/definitions/visual/app-layout-responsive.ts @@ -11,13 +11,13 @@ function responsiveTests(width: number): TestSuite { { description: 'default', path: 'app-layout/default', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'navigation drawer is open', path: 'app-layout/with-wizard', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, setup: async page => { await page.click('[aria-label="Open navigation"]'); @@ -26,92 +26,92 @@ function responsiveTests(width: number): TestSuite { { description: 'wizard', path: 'app-layout/with-wizard', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'with wizard and table', path: 'app-layout/with-wizard-and-table', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'with wizard, table, and breadcrumbs', path: 'app-layout/with-wizard-and-table', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, queryParams: { hasBreadcrumbs: 'true' }, }, { description: 'notifications', path: 'app-layout/with-notifications', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'breadcrumbs', path: 'app-layout/with-breadcrumbs', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'notifications and breadcrumbs', path: 'app-layout/with-breadcrumbs-notifications', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'dashboard content type', path: 'app-layout/dashboard-content-type', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'fixed header and footer', path: 'app-layout/with-fixed-header-footer', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'disableBodyScroll - empty', path: 'app-layout/legacy-nav-empty', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'disableBodyScroll - with content', path: 'app-layout/legacy-nav-scrollable', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'disableBodyScroll - with split panel', path: 'app-layout/legacy-nav-scrollable-with-split-panel', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'disable paddings', path: 'app-layout/disable-paddings', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'disable paddings with breadcrumbs', path: 'app-layout/disable-paddings-breadcrumbs', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'sticky notifications', path: 'app-layout/with-sticky-notifications', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'sticky notifications scrolled down', path: 'app-layout/with-sticky-notifications', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, setup: async page => { await page.windowScrollTo({ top: 2000 }); @@ -120,31 +120,31 @@ function responsiveTests(width: number): TestSuite { { description: 'layout without panels', path: 'app-layout/no-panels', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'layout without panels but with notifications', path: 'app-layout/no-panels-with-notifications', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'with drawers', path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'with empty drawers', path: 'app-layout/with-drawers-empty', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, }, { description: 'with open drawer', path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width }, setup: async page => { await page.click('[aria-label="Security trigger button"]'); diff --git a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts index 91628802ff..6a0b899686 100644 --- a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts +++ b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts @@ -10,7 +10,7 @@ const suite: TestSuite = { { description: 'scrolling to bottom with closed split panel (1 table row)', path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, setup: async page => { await page.click('[data-testid="set-item-count-to-1"]'); @@ -20,7 +20,7 @@ const suite: TestSuite = { { description: 'scrolling to bottom with closed split panel (30 table rows)', path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, setup: async page => { await page.click('[data-testid="set-item-count-to-30"]'); @@ -30,7 +30,7 @@ const suite: TestSuite = { { description: 'header stays sticky with open split panel (1 table row)', path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, setup: async page => { await page.click('[data-testid="set-item-count-to-1"]'); @@ -41,7 +41,7 @@ const suite: TestSuite = { { description: 'header stays sticky with open split panel (30 table rows)', path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, setup: async page => { await page.click('[data-testid="set-item-count-to-30"]'); @@ -52,7 +52,7 @@ const suite: TestSuite = { { description: 'header stays sticky when mounting and unmounting a second table', path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, setup: async page => { await page.click('[data-testid="set-item-count-to-30"]'); @@ -66,7 +66,7 @@ const suite: TestSuite = { { description: 'maxContentWidth set to Number.MAX_VALUE', path: 'app-layout/refresh-content-width', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1280, height: 700 }, setup: async page => { await page.click('[data-test-id="button_width-number-max_value"]'); diff --git a/test/definitions/visual/app-layout-toolbar.ts b/test/definitions/visual/app-layout-toolbar.ts index dc9f5861fe..fb174b0a71 100644 --- a/test/definitions/visual/app-layout-toolbar.ts +++ b/test/definitions/visual/app-layout-toolbar.ts @@ -10,12 +10,12 @@ const suite: TestSuite = { { description: 'multiple nested instances (no breadcrumbs dedup)', path: 'app-layout-toolbar/multi-layout-with-hidden-instances', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', }, { description: 'no toolbar', path: 'app-layout-toolbar/without-toolbar', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', }, ], }; diff --git a/test/definitions/visual/app-layout-z-index.ts b/test/definitions/visual/app-layout-z-index.ts index 7765a496a9..b0dacf8bc5 100644 --- a/test/definitions/visual/app-layout-z-index.ts +++ b/test/definitions/visual/app-layout-z-index.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { { description: `button dropdown (${width}px)`, path: 'app-layout/with-absolute-components', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, setup: async page => { await page.click('button=Button dropdown'); @@ -22,7 +22,7 @@ const suite: TestSuite = { { description: `select (${width}px)`, path: 'app-layout/with-absolute-components', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width, height: 800 }, setup: async page => { await page.click('[data-testid="select-demo"] button'); @@ -32,14 +32,14 @@ const suite: TestSuite = { { description: `split-panel and full-page table (${width}px)`, path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width }, }, ]), { description: 'split-panel and full-page with open navigation (600px)', path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width: 600 }, setup: async page => { await page.click('button[aria-label="Open navigation"]'); @@ -48,7 +48,7 @@ const suite: TestSuite = { { description: 'split-panel and full-page with open tools (600px)', path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'screenshotArea' as const, + screenshotType: 'viewport' as const, configuration: { width: 600 }, setup: async page => { await page.click('button[aria-label="Open tools"]'); diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts index c2f6a41f5c..683efdcc25 100644 --- a/test/definitions/visual/app-layout.ts +++ b/test/definitions/visual/app-layout.ts @@ -10,13 +10,13 @@ const suite: TestSuite = { { description: 'no scrollbars at 320px', path: 'app-layout/default', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 320 }, }, { description: 'drawer buttons alignment', path: 'app-layout/default', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 800 }, setup: async page => { await page.click('[aria-label="Open tools"]'); @@ -25,7 +25,7 @@ const suite: TestSuite = { { description: 'disable paddings - navigation closed', path: 'app-layout/disable-paddings', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1280 }, setup: async page => { await page.click('[aria-label="Close navigation"]'); @@ -34,29 +34,29 @@ const suite: TestSuite = { { description: 'panels stacking on mobile', path: 'app-layout/all-panels-open', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 600 }, }, { description: 'wrapping long words', path: 'app-layout/text-wrap', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', }, { description: 'fill content area', path: 'app-layout/fill-content-area', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', }, { description: 'with tools and drawers', path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', queryParams: { hasTools: 'true' }, }, { description: 'with open drawer and open side split panel', path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1400 }, queryParams: { splitPanelPosition: 'side' }, setup: async page => { @@ -69,7 +69,7 @@ const suite: TestSuite = { { description: 'with open drawer and open side split panel after resize', path: 'app-layout/with-drawers', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1500 }, queryParams: { splitPanelPosition: 'side' }, setup: async page => { @@ -83,7 +83,7 @@ const suite: TestSuite = { { description: 'transition from 400px to 1800px', path: 'app-layout/default', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 400, height: 400 }, setup: async page => { await page.setWindowSize({ width: 1800, height: 400 }); @@ -92,7 +92,7 @@ const suite: TestSuite = { { description: 'transition from 1800px to 400px', path: 'app-layout/default', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 1800, height: 400 }, setup: async page => { await page.setWindowSize({ width: 400, height: 400 }); diff --git a/test/definitions/visual/area-chart.ts b/test/definitions/visual/area-chart.ts index 10855239f6..cfa0b5e47b 100644 --- a/test/definitions/visual/area-chart.ts +++ b/test/definitions/visual/area-chart.ts @@ -35,7 +35,7 @@ const suite: TestSuite = { { description: 'chart plot has a focus outline', path: 'area-chart/test', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 800, height: 800 }, setup: async page => { await page.click(TEST_CHART_FILTER_TRIGGER); @@ -46,7 +46,7 @@ const suite: TestSuite = { { description: 'can navigate along X axis highlighting all series with keyboard', path: 'area-chart/test', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 800, height: 800 }, setup: async page => { await page.click(TEST_CHART_FILTER_TRIGGER); @@ -59,7 +59,7 @@ const suite: TestSuite = { { description: 'can navigate a specific series with keyboard', path: 'area-chart/test', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 800, height: 800 }, setup: async page => { await page.click(TEST_CHART_FILTER_TRIGGER); @@ -74,7 +74,7 @@ const suite: TestSuite = { { description: 'selects correct series when navigated back from legend', path: 'area-chart/test', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 800, height: 800 }, setup: async page => { await page.click(TEST_CHART_FILTER_TRIGGER); @@ -90,7 +90,7 @@ const suite: TestSuite = { { description: 'can pin popover for all data points at a given X coordinate with keyboard', path: 'area-chart/test', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 800, height: 800 }, setup: async page => { await page.click(TEST_CHART_FILTER_TRIGGER); @@ -106,7 +106,7 @@ const suite: TestSuite = { { description: 'can pin popover for a point in a specific series with keyboard', path: 'area-chart/test', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 800, height: 800 }, setup: async page => { await page.click(TEST_CHART_FILTER_TRIGGER); @@ -123,7 +123,7 @@ const suite: TestSuite = { { description: 'shows popover on hover', path: 'area-chart/test', - screenshotType: 'screenshotArea', + screenshotType: 'viewport', configuration: { width: 800, height: 800 }, setup: async page => { await page.hoverElement('[aria-label="Linear latency chart"]', 200, 50); From 67d68efb226c446ca5c14ac67427c6d498ba39d3 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 5 Jun 2026 06:35:49 +0200 Subject: [PATCH 071/135] Split app layout responsive test definitions --- test/definitions/index.ts | 12 ++++++++-- .../visual/app-layout-responsive-1280.ts | 7 ++++++ .../visual/app-layout-responsive-1400.ts | 7 ++++++ .../visual/app-layout-responsive-1920.ts | 7 ++++++ .../visual/app-layout-responsive-2540.ts | 7 ++++++ .../visual/app-layout-responsive-600.ts | 7 ++++++ ...sive.ts => app-layout-responsive-tests.ts} | 22 +++++-------------- .../visual/app-layout-responsive-1280.test.ts | 6 +++++ .../visual/app-layout-responsive-1400.test.ts | 6 +++++ .../visual/app-layout-responsive-1920.test.ts | 6 +++++ .../visual/app-layout-responsive-2540.test.ts | 6 +++++ ...t.ts => app-layout-responsive-600.test.ts} | 2 +- 12 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 test/definitions/visual/app-layout-responsive-1280.ts create mode 100644 test/definitions/visual/app-layout-responsive-1400.ts create mode 100644 test/definitions/visual/app-layout-responsive-1920.ts create mode 100644 test/definitions/visual/app-layout-responsive-2540.ts create mode 100644 test/definitions/visual/app-layout-responsive-600.ts rename test/definitions/visual/{app-layout-responsive.ts => app-layout-responsive-tests.ts} (92%) create mode 100644 test/visual/app-layout-responsive-1280.test.ts create mode 100644 test/visual/app-layout-responsive-1400.test.ts create mode 100644 test/visual/app-layout-responsive-1920.test.ts create mode 100644 test/visual/app-layout-responsive-2540.test.ts rename test/visual/{app-layout-responsive.test.ts => app-layout-responsive-600.test.ts} (73%) diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 17dc1902a0..3bd6fa66e2 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -12,7 +12,11 @@ import appLayoutDrawers from './visual/app-layout-drawers'; import appLayoutFlashbar from './visual/app-layout-flashbar'; import appLayoutHeader from './visual/app-layout-header'; import appLayoutMulti from './visual/app-layout-multi'; -import appLayoutResponsive from './visual/app-layout-responsive'; +import appLayoutResponsive600 from './visual/app-layout-responsive-600'; +import appLayoutResponsive1280 from './visual/app-layout-responsive-1280'; +import appLayoutResponsive1400 from './visual/app-layout-responsive-1400'; +import appLayoutResponsive1920 from './visual/app-layout-responsive-1920'; +import appLayoutResponsive2540 from './visual/app-layout-responsive-2540'; import appLayoutStickyTableHeaderSplitPanel from './visual/app-layout-sticky-table-header-split-panel'; import appLayoutToolbar from './visual/app-layout-toolbar'; import appLayoutZIndex from './visual/app-layout-z-index'; @@ -30,7 +34,11 @@ export const allSuites: TestSuite[] = [ appLayoutFlashbar, appLayoutHeader, appLayoutMulti, - appLayoutResponsive, + appLayoutResponsive600, + appLayoutResponsive1280, + appLayoutResponsive1400, + appLayoutResponsive1920, + appLayoutResponsive2540, appLayoutStickyTableHeaderSplitPanel, appLayoutToolbar, appLayoutZIndex, diff --git a/test/definitions/visual/app-layout-responsive-1280.ts b/test/definitions/visual/app-layout-responsive-1280.ts new file mode 100644 index 0000000000..d5fe823f54 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1280.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1280); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1400.ts b/test/definitions/visual/app-layout-responsive-1400.ts new file mode 100644 index 0000000000..1cb519005c --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1400.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1400); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1920.ts b/test/definitions/visual/app-layout-responsive-1920.ts new file mode 100644 index 0000000000..88b8c6caf3 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1920.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1920); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-2540.ts b/test/definitions/visual/app-layout-responsive-2540.ts new file mode 100644 index 0000000000..9dd62c00db --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-2540.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(2540); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-600.ts b/test/definitions/visual/app-layout-responsive-600.ts new file mode 100644 index 0000000000..f6fd3665cc --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-600.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(600); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive.ts b/test/definitions/visual/app-layout-responsive-tests.ts similarity index 92% rename from test/definitions/visual/app-layout-responsive.ts rename to test/definitions/visual/app-layout-responsive-tests.ts index 8a53da6a09..f0ac91d121 100644 --- a/test/definitions/visual/app-layout-responsive.ts +++ b/test/definitions/visual/app-layout-responsive-tests.ts @@ -3,9 +3,13 @@ import { TestSuite } from '../types'; -function responsiveTests(width: number): TestSuite { +/** + * Shared responsive test scenarios for app-layout. Each width gets its own + * definition file and test runner so that Jest sharding can parallelize them. + */ +export function responsiveTests(width: number): TestSuite { return { - description: `width ${width}px`, + description: `AppLayout responsive width ${width}px`, componentName: 'app-layout', tests: [ { @@ -153,17 +157,3 @@ function responsiveTests(width: number): TestSuite { ], }; } - -const suite: TestSuite = { - description: 'AppLayout responsive', - componentName: 'app-layout', - tests: [ - responsiveTests(600), - responsiveTests(1280), - responsiveTests(1400), - responsiveTests(1920), - responsiveTests(2540), - ], -}; - -export default suite; diff --git a/test/visual/app-layout-responsive-1280.test.ts b/test/visual/app-layout-responsive-1280.test.ts new file mode 100644 index 0000000000..0324b2bf39 --- /dev/null +++ b/test/visual/app-layout-responsive-1280.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-1280'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1400.test.ts b/test/visual/app-layout-responsive-1400.test.ts new file mode 100644 index 0000000000..745372c34e --- /dev/null +++ b/test/visual/app-layout-responsive-1400.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-1400'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1920.test.ts b/test/visual/app-layout-responsive-1920.test.ts new file mode 100644 index 0000000000..bee5fc8763 --- /dev/null +++ b/test/visual/app-layout-responsive-1920.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-1920'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-2540.test.ts b/test/visual/app-layout-responsive-2540.test.ts new file mode 100644 index 0000000000..48bc8580da --- /dev/null +++ b/test/visual/app-layout-responsive-2540.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-2540'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive.test.ts b/test/visual/app-layout-responsive-600.test.ts similarity index 73% rename from test/visual/app-layout-responsive.test.ts rename to test/visual/app-layout-responsive-600.test.ts index 668d4b3522..cd9243cb32 100644 --- a/test/visual/app-layout-responsive.test.ts +++ b/test/visual/app-layout-responsive-600.test.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive'; +import suite from '../definitions/visual/app-layout-responsive-600'; runTestSuites([suite]); From 6a15aaf4296d552ba4869aaaacf66dadf55b6f47 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 5 Jun 2026 06:59:26 +0200 Subject: [PATCH 072/135] Autogenerate test files --- .github/workflows/visual-regression.yml | 3 + .gitignore | 1 + build-tools/visual/generate-tests.js | 71 +++++++++++++++++++ test/visual/action-card.test.ts | 6 -- test/visual/alert.test.ts | 6 -- .../app-layout-content-paddings.test.ts | 6 -- test/visual/app-layout-drawers.test.ts | 6 -- test/visual/app-layout-flashbar.test.ts | 6 -- test/visual/app-layout-header.test.ts | 6 -- test/visual/app-layout-multi.test.ts | 6 -- .../visual/app-layout-responsive-1280.test.ts | 6 -- .../visual/app-layout-responsive-1400.test.ts | 6 -- .../visual/app-layout-responsive-1920.test.ts | 6 -- .../visual/app-layout-responsive-2540.test.ts | 6 -- test/visual/app-layout-responsive-600.test.ts | 6 -- ...ut-sticky-table-header-split-panel.test.ts | 6 -- test/visual/app-layout-toolbar.test.ts | 6 -- test/visual/app-layout-z-index.test.ts | 6 -- test/visual/app-layout.test.ts | 6 -- test/visual/area-chart.test.ts | 6 -- test/visual/attribute-editor.test.ts | 5 -- test/visual/autosuggest.test.ts | 6 -- test/visual/badge.test.ts | 6 -- 23 files changed, 75 insertions(+), 119 deletions(-) create mode 100644 build-tools/visual/generate-tests.js delete mode 100644 test/visual/action-card.test.ts delete mode 100644 test/visual/alert.test.ts delete mode 100644 test/visual/app-layout-content-paddings.test.ts delete mode 100644 test/visual/app-layout-drawers.test.ts delete mode 100644 test/visual/app-layout-flashbar.test.ts delete mode 100644 test/visual/app-layout-header.test.ts delete mode 100644 test/visual/app-layout-multi.test.ts delete mode 100644 test/visual/app-layout-responsive-1280.test.ts delete mode 100644 test/visual/app-layout-responsive-1400.test.ts delete mode 100644 test/visual/app-layout-responsive-1920.test.ts delete mode 100644 test/visual/app-layout-responsive-2540.test.ts delete mode 100644 test/visual/app-layout-responsive-600.test.ts delete mode 100644 test/visual/app-layout-sticky-table-header-split-panel.test.ts delete mode 100644 test/visual/app-layout-toolbar.test.ts delete mode 100644 test/visual/app-layout-z-index.test.ts delete mode 100644 test/visual/app-layout.test.ts delete mode 100644 test/visual/area-chart.test.ts delete mode 100644 test/visual/attribute-editor.test.ts delete mode 100644 test/visual/autosuggest.test.ts delete mode 100644 test/visual/badge.test.ts diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 8e03c9967c..007be5a144 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -124,6 +124,9 @@ jobs: # ── Run tests ───────────────────────────────────────────────────────── + - name: Generate visual test files + run: node build-tools/visual/generate-tests.js + - name: Start test server (port 8080) run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-default & diff --git a/.gitignore b/.gitignore index db3d62b944..3aa354895f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ coverage lib # generated sources src/index.ts +test/visual src/test-utils/dom/index.ts src/test-utils/selectors src/icon/generated diff --git a/build-tools/visual/generate-tests.js b/build-tools/visual/generate-tests.js new file mode 100644 index 0000000000..a56b996b3d --- /dev/null +++ b/build-tools/visual/generate-tests.js @@ -0,0 +1,71 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Auto-generates test/visual/*.test.ts files from test/definitions/visual/*.ts. + * + * Each definition file that exports a default TestSuite gets a corresponding + * test runner. Helper files (those without a default export, e.g. shared utils + * like app-layout-responsive-tests.ts) are skipped. + * + * Run this script before executing the visual test suite: + * node build-tools/visual/generate-tests.js + */ +const fs = require('fs'); +const path = require('path'); + +const definitionsDir = path.resolve(__dirname, '../../test/definitions/visual'); +const outputDir = path.resolve(__dirname, '../../test/visual'); + +// Files that are shared helpers (export named functions, not a default suite). +// These are detected by checking if the file contains "export default" or +// a conventional "const suite" pattern followed by "export default suite". +const HELPER_SUFFIXES = ['-tests']; + +function isHelperFile(basename) { + return HELPER_SUFFIXES.some(suffix => basename.endsWith(suffix)); +} + +function generate() { + // Ensure output directory exists + if (!fs.existsSync(outputDir)) { + fs.mkdirSync(outputDir, { recursive: true }); + } + + const files = fs.readdirSync(definitionsDir).filter(f => f.endsWith('.ts') && !f.endsWith('.d.ts')); + + const generated = []; + + for (const file of files) { + const basename = file.replace(/\.ts$/, ''); + + if (isHelperFile(basename)) { + continue; + } + + // Verify the file has a default export by scanning for the pattern + const content = fs.readFileSync(path.join(definitionsDir, file), 'utf-8'); + if (!content.includes('export default')) { + continue; + } + + const testContent = [ + '// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.', + '// SPDX-License-Identifier: Apache-2.0', + '// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually.', + `import { runTestSuites } from '../definitions/utils';`, + `import suite from '../definitions/visual/${basename}';`, + '', + 'runTestSuites([suite]);', + '', + ].join('\n'); + + const outputPath = path.join(outputDir, `${basename}.test.ts`); + fs.writeFileSync(outputPath, testContent); + generated.push(basename); + } + + console.log(`Generated ${generated.length} visual test files in test/visual/`); +} + +generate(); diff --git a/test/visual/action-card.test.ts b/test/visual/action-card.test.ts deleted file mode 100644 index 8505dad23b..0000000000 --- a/test/visual/action-card.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import actionCard from '../definitions/visual/action-card'; - -runTestSuites([actionCard]); diff --git a/test/visual/alert.test.ts b/test/visual/alert.test.ts deleted file mode 100644 index 431e4907e4..0000000000 --- a/test/visual/alert.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import alert from '../definitions/visual/alert'; - -runTestSuites([alert]); diff --git a/test/visual/app-layout-content-paddings.test.ts b/test/visual/app-layout-content-paddings.test.ts deleted file mode 100644 index e566d79817..0000000000 --- a/test/visual/app-layout-content-paddings.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-content-paddings'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-drawers.test.ts b/test/visual/app-layout-drawers.test.ts deleted file mode 100644 index c66454010d..0000000000 --- a/test/visual/app-layout-drawers.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-drawers'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-flashbar.test.ts b/test/visual/app-layout-flashbar.test.ts deleted file mode 100644 index 333642e5f3..0000000000 --- a/test/visual/app-layout-flashbar.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-flashbar'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-header.test.ts b/test/visual/app-layout-header.test.ts deleted file mode 100644 index 682f71ffe2..0000000000 --- a/test/visual/app-layout-header.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-header'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-multi.test.ts b/test/visual/app-layout-multi.test.ts deleted file mode 100644 index 244019c8fb..0000000000 --- a/test/visual/app-layout-multi.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-multi'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1280.test.ts b/test/visual/app-layout-responsive-1280.test.ts deleted file mode 100644 index 0324b2bf39..0000000000 --- a/test/visual/app-layout-responsive-1280.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-1280'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1400.test.ts b/test/visual/app-layout-responsive-1400.test.ts deleted file mode 100644 index 745372c34e..0000000000 --- a/test/visual/app-layout-responsive-1400.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-1400'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1920.test.ts b/test/visual/app-layout-responsive-1920.test.ts deleted file mode 100644 index bee5fc8763..0000000000 --- a/test/visual/app-layout-responsive-1920.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-1920'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-2540.test.ts b/test/visual/app-layout-responsive-2540.test.ts deleted file mode 100644 index 48bc8580da..0000000000 --- a/test/visual/app-layout-responsive-2540.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-2540'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-600.test.ts b/test/visual/app-layout-responsive-600.test.ts deleted file mode 100644 index cd9243cb32..0000000000 --- a/test/visual/app-layout-responsive-600.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-600'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-sticky-table-header-split-panel.test.ts b/test/visual/app-layout-sticky-table-header-split-panel.test.ts deleted file mode 100644 index c1ad3016a1..0000000000 --- a/test/visual/app-layout-sticky-table-header-split-panel.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-sticky-table-header-split-panel'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-toolbar.test.ts b/test/visual/app-layout-toolbar.test.ts deleted file mode 100644 index 398d6386f8..0000000000 --- a/test/visual/app-layout-toolbar.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-toolbar'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-z-index.test.ts b/test/visual/app-layout-z-index.test.ts deleted file mode 100644 index 5f69f77b71..0000000000 --- a/test/visual/app-layout-z-index.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-z-index'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout.test.ts b/test/visual/app-layout.test.ts deleted file mode 100644 index 21c3a6ce25..0000000000 --- a/test/visual/app-layout.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout'; - -runTestSuites([suite]); diff --git a/test/visual/area-chart.test.ts b/test/visual/area-chart.test.ts deleted file mode 100644 index 51d6631016..0000000000 --- a/test/visual/area-chart.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import areaChart from '../definitions/visual/area-chart'; - -runTestSuites([areaChart]); diff --git a/test/visual/attribute-editor.test.ts b/test/visual/attribute-editor.test.ts deleted file mode 100644 index f57e8785b2..0000000000 --- a/test/visual/attribute-editor.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import attributeEditor from '../definitions/visual/attribute-editor'; -runTestSuites([attributeEditor]); diff --git a/test/visual/autosuggest.test.ts b/test/visual/autosuggest.test.ts deleted file mode 100644 index 454c00c648..0000000000 --- a/test/visual/autosuggest.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import autosuggest from '../definitions/visual/autosuggest'; - -runTestSuites([autosuggest]); diff --git a/test/visual/badge.test.ts b/test/visual/badge.test.ts deleted file mode 100644 index 6b24838a42..0000000000 --- a/test/visual/badge.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import badge from '../definitions/visual/badge'; - -runTestSuites([badge]); From 950d87c890cfc9459a3fee8a547d6cd3d884a3d2 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 5 Jun 2026 07:12:36 +0200 Subject: [PATCH 073/135] Generate Allure reports --- .github/workflows/visual-regression.yml | 34 ++ .gitignore | 2 + jest.visual.config.js | 11 +- package-lock.json | 737 ++++++++++++++++++++++++ package.json | 1 + 5 files changed, 784 insertions(+), 1 deletion(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 007be5a144..0b4e20a816 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -148,3 +148,37 @@ jobs: name: visual-regression-diffs-shard-${{ matrix.shard }} path: visual-regression-output/ retention-days: 14 + + - name: Upload Allure results + if: always() + uses: actions/upload-artifact@v4 + with: + name: allure-results-shard-${{ matrix.shard }} + path: allure-results/ + retention-days: 3 + + report: + name: Generate Allure Report + if: always() + needs: [visual] + runs-on: ubuntu-latest + steps: + - name: Download all Allure results + uses: actions/download-artifact@v4 + with: + pattern: allure-results-shard-* + path: allure-results + merge-multiple: true + + - name: Generate Allure HTML report + uses: simple-elf/allure-report-action@v1.12 + with: + allure_results: allure-results + allure_report: allure-report + + - name: Upload Allure report + uses: actions/upload-artifact@v4 + with: + name: allure-report + path: allure-report/ + retention-days: 14 diff --git a/.gitignore b/.gitignore index 3aa354895f..38d6053611 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ lib # generated sources src/index.ts test/visual +allure-results +allure-report src/test-utils/dom/index.ts src/test-utils/selectors src/icon/generated diff --git a/jest.visual.config.js b/jest.visual.config.js index 19418a86dd..5cb7f033fe 100644 --- a/jest.visual.config.js +++ b/jest.visual.config.js @@ -14,7 +14,16 @@ module.exports = { }, ], }, - reporters: ['default', 'github-actions'], + reporters: [ + 'default', + 'github-actions', + [ + 'jest-allure2-reporter', + { + resultsDir: 'allure-results', + }, + ], + ], testTimeout: 240_000, // 4min — pages can be tall and slow to capture maxWorkers: os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), globalSetup: '/build-tools/visual/global-setup.js', diff --git a/package-lock.json b/package-lock.json index a1ace81398..89a7c13f6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,6 +88,7 @@ "html-webpack-plugin": "^5.5.0", "husky": "^9.0.0", "jest": "^29.7.0", + "jest-allure2-reporter": "^2.3.0", "jest-environment-jsdom": "^29.7.0", "lint-staged": "^15.2.2", "loader-utils": "^3.2.1", @@ -2111,6 +2112,13 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@flatten-js/interval-tree": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.4.tgz", + "integrity": "sha512-o4emRDDvGdkwX18BSVSXH8q27qAL7Z2WDHSN75C8xyRSE4A8UOkig0mWSGoT5M5KaTHZxoLmalFwOTQmbRusUg==", + "dev": true, + "license": "MIT" + }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.3.4", "license": "MIT", @@ -5915,6 +5923,19 @@ "dev": true, "license": "MIT" }, + "node_modules/allure-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/allure-store/-/allure-store-1.2.0.tgz", + "integrity": "sha512-YUKLBrYA/qmZTqBgmC/ZgoTBTcbaFLV6OcQ/DsruWX5YwqVSCJUmMWi5cdGDzw5QL+b9EindJJ6KVHYjIL66Ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "properties": "^1.2.1" + }, + "engines": { + "node": ">=16.14.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "dev": true, @@ -6867,6 +6888,13 @@ "node": ">=8" } }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -6982,6 +7010,94 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bunyamin": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/bunyamin/-/bunyamin-1.6.3.tgz", + "integrity": "sha512-m1hAijFhu8pFiidsVc0XEDic46uxPK+mKNLqkb5mluNx0nTolNzx/DjwMqHChQWCgfOLMjKYJJ2uPTQLE6t4Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@flatten-js/interval-tree": "^1.1.2", + "multi-sort-stream": "^1.0.4", + "stream-json": "^1.7.5", + "trace-event-lib": "^1.3.1" + }, + "engines": { + "node": ">=14.18.2" + }, + "peerDependencies": { + "@types/bunyan": "^1.8.8", + "bunyan": "^1.8.15 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@types/bunyan": { + "optional": true + }, + "bunyan": { + "optional": true + } + } + }, + "node_modules/bunyan": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.5.tgz", + "integrity": "sha512-Jvl74TdxCN6rSP9W1I6+UOUtwslTDqsSFkDqZlFb/ilaSvQ+bZAnXT/GT97IZ5L+Vph0joPZPhxUyn6FLNmFAA==", + "dev": true, + "engines": [ + "node >=0.10.0" + ], + "license": "MIT", + "dependencies": { + "exeunt": "1.1.0" + }, + "bin": { + "bunyan": "bin/bunyan" + }, + "optionalDependencies": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, + "node_modules/bunyan-debug-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bunyan-debug-stream/-/bunyan-debug-stream-3.1.1.tgz", + "integrity": "sha512-LfMcz4yKM6s9BP5dfT63Prb5B2hAjReLAfQzLbNQF7qBHtn3P1v+/yn0SZ6UAr4PC3VZRX/QzK7HYkkY0ytokQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "bunyan": "*" + }, + "peerDependenciesMeta": { + "bunyan": { + "optional": true + } + } + }, + "node_modules/bunyan-debug-stream/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/bytes": { "version": "3.1.2", "dev": true, @@ -9063,6 +9179,21 @@ "tslib": "^2.0.3" } }, + "node_modules/dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "nan": "^2.14.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "dev": true, @@ -9104,6 +9235,16 @@ "dev": true, "license": "MIT" }, + "node_modules/easy-stack": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/edge-paths": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", @@ -9339,6 +9480,16 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, "node_modules/es-abstract": { "version": "1.24.0", "dev": true, @@ -10175,6 +10326,16 @@ "node": ">= 0.6" } }, + "node_modules/event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/event-target-shim": { "version": "5.0.1", "dev": true, @@ -10232,6 +10393,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/exeunt": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", + "integrity": "sha512-dd++Yn/0Fp+gtJ04YHov7MeAii+LFivJc6KqnJNfplzLVUkUDrfKoQDTLlCgzcW15vY5hKlHasWeIsQJ8agHsw==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=0.10" + } + }, "node_modules/exit": { "version": "0.1.2", "dev": true, @@ -11209,6 +11380,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/funpermaproxy": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/funpermaproxy/-/funpermaproxy-1.1.0.tgz", + "integrity": "sha512-2Sp1hWuO8m5fqeFDusyhKqYPT+7rGLw34N3qonDcdRP8+n7M7Gl/yKp/q7oCxnnJ6pWCectOmLFJpsMU/++KrQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.3.0" + } + }, "node_modules/geckodriver": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-6.1.0.tgz", @@ -12390,6 +12571,19 @@ "node": ">=4" } }, + "node_modules/import-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", + "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/import-local": { "version": "3.2.0", "dev": true, @@ -13206,6 +13400,59 @@ } } }, + "node_modules/jest-allure2-reporter": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/jest-allure2-reporter/-/jest-allure2-reporter-2.3.0.tgz", + "integrity": "sha512-snDc5geSUcMbIegjqGuEPyYrZZxlajUH61Z+3EHQsJEF+m50lPonX9KQdqDC+xksCuwpM6mxGuBjQDGOIa2w6w==", + "dev": true, + "license": "MIT", + "workspaces": [ + "e2e", + "package-e2e" + ], + "dependencies": { + "allure-store": "^1.1.0", + "bunyamin": "^1.6.1", + "handlebars": "^4.7.8", + "import-from": "^4.0.0", + "jest-metadata": "^1.6.0", + "lodash": "^4.17.21", + "node-fetch": "^2.6.7", + "pkg-up": "^3.1.0", + "properties": "^1.2.1", + "stacktrace-js": "^2.0.2", + "strip-ansi": "^6.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=16.20.0" + }, + "peerDependencies": { + "jest": ">=27.2.5", + "jest-docblock": ">=27.2.5" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-docblock": { + "optional": true + } + } + }, + "node_modules/jest-allure2-reporter/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-changed-files": { "version": "29.7.0", "dev": true, @@ -13655,6 +13902,63 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/jest-environment-emit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-emit/-/jest-environment-emit-1.2.0.tgz", + "integrity": "sha512-dSFBrRuIiWbHK2LSUA6CutXpMcNGjjuhvxFLF+TVz5tYFAAH0eesrZgrQ3UtOptajDYNt/fIGRqtlHqGq/bLbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bunyamin": "^1.5.2", + "bunyan": "^2.0.5", + "bunyan-debug-stream": "^3.1.0", + "funpermaproxy": "^1.1.0", + "lodash.merge": "^4.6.2", + "node-ipc": "9.2.1", + "strip-ansi": "^6.0.0", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16.14.0" + }, + "peerDependencies": { + "@jest/environment": ">=27.2.5", + "@jest/types": ">=27.2.5", + "jest": ">=27.2.5", + "jest-environment-jsdom": ">=27.2.5", + "jest-environment-node": ">=27.2.5" + }, + "peerDependenciesMeta": { + "@jest/environment": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "jest": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "jest-environment-node": { + "optional": true + } + } + }, + "node_modules/jest-environment-emit/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-environment-jsdom": { "version": "29.7.0", "dev": true, @@ -13904,6 +14208,67 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/jest-metadata": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jest-metadata/-/jest-metadata-1.6.0.tgz", + "integrity": "sha512-penaOkD6tN0Vpqd+9xnbS+iYSLqaZpsx08gz44mOBvyNGBHPglnNKOsBMr3cbIe0bFYGlnouDy4N5SfLtNgVBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bunyamin": "^1.5.2", + "funpermaproxy": "^1.1.0", + "jest-environment-emit": "^1.0.8", + "lodash.merge": "^4.6.2", + "lodash.snakecase": "^4.1.1", + "node-ipc": "9.2.1", + "strip-ansi": "^6.0.0", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16.14.0" + }, + "peerDependencies": { + "@jest/environment": ">=27.2.5", + "@jest/reporters": ">=27.2.5", + "@jest/types": ">=27.2.5", + "jest": ">=27.2.5", + "jest-environment-jsdom": ">=27.2.5", + "jest-environment-node": ">=27.2.5" + }, + "peerDependenciesMeta": { + "@jest/environment": { + "optional": true + }, + "@jest/reporters": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "jest": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "jest-environment-node": { + "optional": true + } + } + }, + "node_modules/jest-metadata/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-mock": { "version": "29.7.0", "dev": true, @@ -14419,6 +14784,29 @@ "node": ">= 20" } }, + "node_modules/js-message": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/js-queue": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz", + "integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "easy-stack": "^1.0.1" + }, + "engines": { + "node": ">=1.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "license": "MIT" @@ -15092,6 +15480,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "dev": true, @@ -15575,6 +15970,20 @@ "dev": true, "license": "MIT" }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mnth": { "version": "2.0.0", "license": "MIT", @@ -15613,6 +16022,13 @@ "dev": true, "license": "MIT" }, + "node_modules/multi-sort-stream": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multi-sort-stream/-/multi-sort-stream-1.0.4.tgz", + "integrity": "sha512-hAZ8JOEQFbgdLe8HWZbb7gdZg0/yAIHF00Qfo3kd0rXFv96nXe+/bPTrKHZ2QMHugGX4FiAyET1Lt+jiB+7Qlg==", + "dev": true, + "license": "bsd" + }, "node_modules/multicast-dns": { "version": "7.2.5", "dev": true, @@ -15666,6 +16082,78 @@ "node": ">= 10.13.0" } }, + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mv/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/nan": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz", + "integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "5.1.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz", @@ -15698,6 +16186,17 @@ "dev": true, "license": "MIT" }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "ncp": "bin/ncp" + } + }, "node_modules/negotiator": { "version": "0.6.4", "dev": true, @@ -15741,11 +16240,72 @@ "license": "MIT", "optional": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "dev": true, "license": "MIT" }, + "node_modules/node-ipc": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz", + "integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-pubsub": "4.3.0", + "js-message": "1.0.7", + "js-queue": "2.0.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -16720,6 +17280,85 @@ "node": ">=8" } }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-up/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-up/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/pkijs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", @@ -17621,6 +18260,16 @@ "version": "16.13.1", "license": "MIT" }, + "node_modules/properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/properties/-/properties-1.2.1.tgz", + "integrity": "sha512-qYNxyMj1JeW54i/EWEFsM1cVwxJbtgPp8+0Wg9XjNaK6VE/c4oRi6PNu5p7w1mNXEIQIjV5Wwn8v8Gz82/QzdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "dev": true, @@ -18798,6 +19447,14 @@ ], "license": "MIT" }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/safe-push-apply": { "version": "1.0.0", "dev": true, @@ -19707,6 +20364,16 @@ "dev": true, "license": "MIT" }, + "node_modules/stack-generator": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", + "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "stackframe": "^1.3.4" + } + }, "node_modules/stack-utils": { "version": "2.0.6", "dev": true, @@ -19726,6 +20393,46 @@ "node": ">=8" } }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stacktrace-gps": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", + "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "0.5.6", + "stackframe": "^1.3.4" + } + }, + "node_modules/stacktrace-gps/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stacktrace-js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", + "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-stack-parser": "^2.0.6", + "stack-generator": "^2.0.5", + "stacktrace-gps": "^3.0.4" + } + }, "node_modules/statuses": { "version": "2.0.1", "dev": true, @@ -19746,6 +20453,13 @@ "node": ">= 0.4" } }, + "node_modules/stream-chain": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/stream-composer": { "version": "1.0.2", "dev": true, @@ -19768,6 +20482,16 @@ "dev": true, "license": "MIT" }, + "node_modules/stream-json": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", + "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "stream-chain": "^2.2.5" + } + }, "node_modules/stream-shift": { "version": "1.0.3", "dev": true, @@ -20943,6 +21667,19 @@ "node": ">=12" } }, + "node_modules/trace-event-lib": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/trace-event-lib/-/trace-event-lib-1.4.1.tgz", + "integrity": "sha512-TOgFolKG8JFY+9d5EohGWMvwvteRafcyfPWWNIqcuD1W/FUvxWcy2MSCZ/beYHM63oYPHYHCd3tkbgCctHVP7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/tree-dump": { "version": "1.0.3", "dev": true, diff --git a/package.json b/package.json index e9bace4031..1fd30fccf7 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "html-webpack-plugin": "^5.5.0", "husky": "^9.0.0", "jest": "^29.7.0", + "jest-allure2-reporter": "^2.3.0", "jest-environment-jsdom": "^29.7.0", "lint-staged": "^15.2.2", "loader-utils": "^3.2.1", From f3191083a39c646a91bd129e581f11cfc35d72ec Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 5 Jun 2026 19:30:35 +0200 Subject: [PATCH 074/135] Increase to 10 shards --- .github/workflows/visual-regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 0b4e20a816..4942a136f9 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -82,7 +82,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4, 5, 6, 7, 8] + shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] steps: - uses: actions/checkout@v4 @@ -137,7 +137,7 @@ jobs: run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/8 + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/10 env: TZ: UTC From 19eab2bd0f93273309d4af2291d11f22628f630b Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 5 Jun 2026 19:33:04 +0200 Subject: [PATCH 075/135] Download Allure directly --- .github/workflows/visual-regression.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 4942a136f9..6c37dafdb5 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -170,11 +170,13 @@ jobs: path: allure-results merge-multiple: true + - name: Install Allure CLI + run: | + curl -sL https://github.com/allure-framework/allure2/releases/download/2.32.0/allure-2.32.0.tgz | tar -xz + echo "$PWD/allure-2.32.0/bin" >> $GITHUB_PATH + - name: Generate Allure HTML report - uses: simple-elf/allure-report-action@v1.12 - with: - allure_results: allure-results - allure_report: allure-report + run: allure generate allure-results --clean -o allure-report - name: Upload Allure report uses: actions/upload-artifact@v4 From dda1ccafa079ab88f91cdc9ea838b25199442266 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 5 Jun 2026 23:22:46 +0200 Subject: [PATCH 076/135] Deploy Allure results --- .github/workflows/visual-regression.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 6c37dafdb5..ded9ff9344 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -24,6 +24,7 @@ permissions: id-token: write contents: read actions: read + deployments: write jobs: # Build the baseline (main branch) pages once and share them across all browser jobs. @@ -178,9 +179,19 @@ jobs: - name: Generate Allure HTML report run: allure generate allure-results --clean -o allure-report - - name: Upload Allure report + - name: Upload Allure report artifact uses: actions/upload-artifact@v4 with: name: allure-report path: allure-report/ retention-days: 14 + + deploy-report: + name: Deploy Allure Report + if: always() + needs: [report] + uses: cloudscape-design/actions/.github/workflows/deploy.yml@main + secrets: inherit + with: + artifact-name: allure-report + deployment-path: allure-report From 4961b1b85e3ed6a53f3f0e842c4303df40e0513f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 5 Jun 2026 23:22:59 +0200 Subject: [PATCH 077/135] Add image diffs to Allure reports --- test/definitions/utils.ts | 71 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 683e50cc80..1b4dd0fee2 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -1,5 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import * as crypto from 'crypto'; +import * as fs from 'fs'; +import * as path from 'path'; + import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; @@ -12,6 +16,8 @@ const defaultWindowSize = { width: 1600, height: 800 }; const newHost = process.env.NEW_HOST || 'http://localhost:8080'; const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; +const allureResultsDir = path.resolve(process.cwd(), 'allure-results'); + function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams(queryParams); const qs = params.toString(); @@ -22,6 +28,55 @@ function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinit return (item as TestDefinition).path !== undefined; } +/** + * Writes a PNG buffer to the allure-results directory and returns the filename. + * Allure picks up files in this directory and attaches them to the report. + */ +function writeAllureAttachment(buffer: Buffer): string { + if (!fs.existsSync(allureResultsDir)) { + fs.mkdirSync(allureResultsDir, { recursive: true }); + } + const uuid = crypto.randomUUID(); + const filename = `${uuid}-attachment.png`; + fs.writeFileSync(path.join(allureResultsDir, filename), buffer); + return filename; +} + +/** + * Attaches visual diff images (new, baseline, diff) to the Allure results + * for the current test by writing an attachment JSON file. + */ +function attachDiffImages( + result: { firstImage: Buffer; secondImage: Buffer; diffImage: Buffer | null }, + testName: string +): void { + const newFile = writeAllureAttachment(result.firstImage); + const baselineFile = writeAllureAttachment(result.secondImage); + + const attachments: Array<{ name: string; source: string; type: string }> = [ + { name: `${testName} — new (PR)`, source: newFile, type: 'image/png' }, + { name: `${testName} — baseline (main)`, source: baselineFile, type: 'image/png' }, + ]; + + if (result.diffImage) { + const diffFile = writeAllureAttachment(result.diffImage); + attachments.push({ name: `${testName} — diff`, source: diffFile, type: 'image/png' }); + } + + // Write a container JSON that Allure merges into the test result. + // jest-allure2-reporter reads attachment files from allure-results/. + const containerUuid = crypto.randomUUID(); + const containerFile = path.join(allureResultsDir, `${containerUuid}-container.json`); + fs.writeFileSync( + containerFile, + JSON.stringify({ + uuid: containerUuid, + name: testName, + attachments, + }) + ); +} + /** * Registers all test suites with a single shared browser session per worker. * This avoids the per-test session creation overhead. @@ -91,9 +146,9 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const newScreenshot = await capture(browser, page, newUrl, testDef, windowSize); const oldScreenshot = await capture(browser, page, oldUrl, testDef, windowSize); - const { diffPixels } = await cropAndCompare(newScreenshot, oldScreenshot); + const result = await cropAndCompare(newScreenshot, oldScreenshot); - if (diffPixels === 0) { + if (result.diffPixels === 0) { return; } @@ -124,13 +179,19 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro expect(newPermutations.length).toBe(oldPermutations.length); for (let i = 0; i < newPermutations.length; i++) { - const { diffPixels: permDiff } = await cropAndCompare(newPermutations[i], oldPermutations[i]); - expect(permDiff).toBe(0); + const permResult = await cropAndCompare(newPermutations[i], oldPermutations[i]); + if (permResult.diffPixels !== 0) { + attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`); + } + expect(permResult.diffPixels).toBe(0); } return; } + // Attach diff images to Allure report for visual inspection. + attachDiffImages(result, testDef.description); + // For screenshotArea and viewport types, the diff is a real failure. - expect(diffPixels).toBe(0); + expect(result.diffPixels).toBe(0); }); } From 036e37d4e54745382b7bf511e4a3f383ba4bb319 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 5 Jun 2026 23:25:33 +0200 Subject: [PATCH 078/135] Use Allure 3 --- .github/workflows/visual-regression.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index ded9ff9344..977c6cd96d 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -164,6 +164,11 @@ jobs: needs: [visual] runs-on: ubuntu-latest steps: + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Download all Allure results uses: actions/download-artifact@v4 with: @@ -171,13 +176,8 @@ jobs: path: allure-results merge-multiple: true - - name: Install Allure CLI - run: | - curl -sL https://github.com/allure-framework/allure2/releases/download/2.32.0/allure-2.32.0.tgz | tar -xz - echo "$PWD/allure-2.32.0/bin" >> $GITHUB_PATH - - name: Generate Allure HTML report - run: allure generate allure-results --clean -o allure-report + run: npx --yes allure generate allure-results -o allure-report - name: Upload Allure report artifact uses: actions/upload-artifact@v4 From 1576564fdd82e6c87adb6f6495e2123a08d4eda1 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 6 Jun 2026 08:28:36 +0200 Subject: [PATCH 079/135] Fix image attachments --- jest.visual.config.js | 16 +- package-lock.json | 829 +++++--------------------------------- package.json | 2 +- test/definitions/utils.ts | 58 +-- 4 files changed, 108 insertions(+), 797 deletions(-) diff --git a/jest.visual.config.js b/jest.visual.config.js index 5cb7f033fe..98938768b3 100644 --- a/jest.visual.config.js +++ b/jest.visual.config.js @@ -5,7 +5,10 @@ const os = require('os'); module.exports = { verbose: true, - testEnvironment: 'node', + testEnvironment: 'allure-jest/node', + testEnvironmentOptions: { + resultsDir: 'allure-results', + }, transform: { '^.+\\.tsx?$': [ 'ts-jest', @@ -14,16 +17,7 @@ module.exports = { }, ], }, - reporters: [ - 'default', - 'github-actions', - [ - 'jest-allure2-reporter', - { - resultsDir: 'allure-results', - }, - ], - ], + reporters: ['default', 'github-actions'], testTimeout: 240_000, // 4min — pages can be tall and slow to capture maxWorkers: os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), globalSetup: '/build-tools/visual/global-setup.js', diff --git a/package-lock.json b/package-lock.json index 89a7c13f6f..6ddd0880a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,6 +61,7 @@ "@types/react-test-renderer": "^16.9.12", "@types/react-transition-group": "^4.4.4", "@types/webpack-env": "^1.16.3", + "allure-jest": "^3.9.0", "axe-core": "^4.7.2", "babel-jest": "^29.7.0", "change-case": "^4.1.2", @@ -88,7 +89,6 @@ "html-webpack-plugin": "^5.5.0", "husky": "^9.0.0", "jest": "^29.7.0", - "jest-allure2-reporter": "^2.3.0", "jest-environment-jsdom": "^29.7.0", "lint-staged": "^15.2.2", "loader-utils": "^3.2.1", @@ -2112,13 +2112,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@flatten-js/interval-tree": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@flatten-js/interval-tree/-/interval-tree-1.1.4.tgz", - "integrity": "sha512-o4emRDDvGdkwX18BSVSXH8q27qAL7Z2WDHSN75C8xyRSE4A8UOkig0mWSGoT5M5KaTHZxoLmalFwOTQmbRusUg==", - "dev": true, - "license": "MIT" - }, "node_modules/@formatjs/ecma402-abstract": { "version": "2.3.4", "license": "MIT", @@ -5923,17 +5916,56 @@ "dev": true, "license": "MIT" }, - "node_modules/allure-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/allure-store/-/allure-store-1.2.0.tgz", - "integrity": "sha512-YUKLBrYA/qmZTqBgmC/ZgoTBTcbaFLV6OcQ/DsruWX5YwqVSCJUmMWi5cdGDzw5QL+b9EindJJ6KVHYjIL66Ww==", + "node_modules/allure-jest": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/allure-jest/-/allure-jest-3.9.0.tgz", + "integrity": "sha512-hEW4DKjvb3engGoHUPQaDEdyrFkUxQnqULiSQAehL1eDEggqdPbQro86Nch8Cj1yuIqUTn9UP1FMuuuwl/5jnQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "properties": "^1.2.1" + "allure-js-commons": "3.9.0" }, - "engines": { - "node": ">=16.14.0" + "peerDependencies": { + "jest": ">=24.8.0", + "jest-circus": ">=24.8.0", + "jest-cli": ">=24.8.0", + "jest-environment-jsdom": ">=24.8.0", + "jest-environment-node": ">=24.8.0" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-circus": { + "optional": true + }, + "jest-cli": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "jest-environment-node": { + "optional": true + } + } + }, + "node_modules/allure-js-commons": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.9.0.tgz", + "integrity": "sha512-uVQcGE6MWIvGR/zW1XEUwHXUQa1EJKY0Cah+0TZK1qKuw6ptyhftDr34XE3wExTyCZirRrI98dbRtPeYYuyI+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "md5": "^2.3.0" + }, + "peerDependencies": { + "allure-playwright": "3.9.0" + }, + "peerDependenciesMeta": { + "allure-playwright": { + "optional": true + } } }, "node_modules/ansi-escapes": { @@ -6888,13 +6920,6 @@ "node": ">=8" } }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/browserslist": { "version": "4.28.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", @@ -7010,94 +7035,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bunyamin": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/bunyamin/-/bunyamin-1.6.3.tgz", - "integrity": "sha512-m1hAijFhu8pFiidsVc0XEDic46uxPK+mKNLqkb5mluNx0nTolNzx/DjwMqHChQWCgfOLMjKYJJ2uPTQLE6t4Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@flatten-js/interval-tree": "^1.1.2", - "multi-sort-stream": "^1.0.4", - "stream-json": "^1.7.5", - "trace-event-lib": "^1.3.1" - }, - "engines": { - "node": ">=14.18.2" - }, - "peerDependencies": { - "@types/bunyan": "^1.8.8", - "bunyan": "^1.8.15 || ^2.0.0" - }, - "peerDependenciesMeta": { - "@types/bunyan": { - "optional": true - }, - "bunyan": { - "optional": true - } - } - }, - "node_modules/bunyan": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-2.0.5.tgz", - "integrity": "sha512-Jvl74TdxCN6rSP9W1I6+UOUtwslTDqsSFkDqZlFb/ilaSvQ+bZAnXT/GT97IZ5L+Vph0joPZPhxUyn6FLNmFAA==", - "dev": true, - "engines": [ - "node >=0.10.0" - ], - "license": "MIT", - "dependencies": { - "exeunt": "1.1.0" - }, - "bin": { - "bunyan": "bin/bunyan" - }, - "optionalDependencies": { - "dtrace-provider": "~0.8", - "moment": "^2.19.3", - "mv": "~2", - "safe-json-stringify": "~1" - } - }, - "node_modules/bunyan-debug-stream": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/bunyan-debug-stream/-/bunyan-debug-stream-3.1.1.tgz", - "integrity": "sha512-LfMcz4yKM6s9BP5dfT63Prb5B2hAjReLAfQzLbNQF7qBHtn3P1v+/yn0SZ6UAr4PC3VZRX/QzK7HYkkY0ytokQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2" - }, - "engines": { - "node": ">=0.12.0" - }, - "peerDependencies": { - "bunyan": "*" - }, - "peerDependenciesMeta": { - "bunyan": { - "optional": true - } - } - }, - "node_modules/bunyan-debug-stream/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/bytes": { "version": "3.1.2", "dev": true, @@ -7293,6 +7230,16 @@ "node": ">=10" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/cheerio": { "version": "1.1.0", "dev": true, @@ -8011,6 +7958,16 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/css-declaration-sorter": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.1.tgz", @@ -9179,21 +9136,6 @@ "tslib": "^2.0.3" } }, - "node_modules/dtrace-provider": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", - "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", - "dev": true, - "hasInstallScript": true, - "license": "BSD-2-Clause", - "optional": true, - "dependencies": { - "nan": "^2.14.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "dev": true, @@ -9235,16 +9177,6 @@ "dev": true, "license": "MIT" }, - "node_modules/easy-stack": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", - "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/edge-paths": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", @@ -9480,16 +9412,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, "node_modules/es-abstract": { "version": "1.24.0", "dev": true, @@ -10326,16 +10248,6 @@ "node": ">= 0.6" } }, - "node_modules/event-pubsub": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", - "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", - "dev": true, - "license": "Unlicense", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/event-target-shim": { "version": "5.0.1", "dev": true, @@ -10393,16 +10305,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/exeunt": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/exeunt/-/exeunt-1.1.0.tgz", - "integrity": "sha512-dd++Yn/0Fp+gtJ04YHov7MeAii+LFivJc6KqnJNfplzLVUkUDrfKoQDTLlCgzcW15vY5hKlHasWeIsQJ8agHsw==", - "dev": true, - "license": "MPL-2.0", - "engines": { - "node": ">=0.10" - } - }, "node_modules/exit": { "version": "0.1.2", "dev": true, @@ -11380,16 +11282,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/funpermaproxy": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/funpermaproxy/-/funpermaproxy-1.1.0.tgz", - "integrity": "sha512-2Sp1hWuO8m5fqeFDusyhKqYPT+7rGLw34N3qonDcdRP8+n7M7Gl/yKp/q7oCxnnJ6pWCectOmLFJpsMU/++KrQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.3.0" - } - }, "node_modules/geckodriver": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-6.1.0.tgz", @@ -12571,19 +12463,6 @@ "node": ">=4" } }, - "node_modules/import-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", - "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/import-local": { "version": "3.2.0", "dev": true, @@ -12811,6 +12690,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, "node_modules/is-builtin-module": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", @@ -13400,59 +13286,6 @@ } } }, - "node_modules/jest-allure2-reporter": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/jest-allure2-reporter/-/jest-allure2-reporter-2.3.0.tgz", - "integrity": "sha512-snDc5geSUcMbIegjqGuEPyYrZZxlajUH61Z+3EHQsJEF+m50lPonX9KQdqDC+xksCuwpM6mxGuBjQDGOIa2w6w==", - "dev": true, - "license": "MIT", - "workspaces": [ - "e2e", - "package-e2e" - ], - "dependencies": { - "allure-store": "^1.1.0", - "bunyamin": "^1.6.1", - "handlebars": "^4.7.8", - "import-from": "^4.0.0", - "jest-metadata": "^1.6.0", - "lodash": "^4.17.21", - "node-fetch": "^2.6.7", - "pkg-up": "^3.1.0", - "properties": "^1.2.1", - "stacktrace-js": "^2.0.2", - "strip-ansi": "^6.0.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">=16.20.0" - }, - "peerDependencies": { - "jest": ">=27.2.5", - "jest-docblock": ">=27.2.5" - }, - "peerDependenciesMeta": { - "jest": { - "optional": true - }, - "jest-docblock": { - "optional": true - } - } - }, - "node_modules/jest-allure2-reporter/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, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-changed-files": { "version": "29.7.0", "dev": true, @@ -13902,63 +13735,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-environment-emit": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-emit/-/jest-environment-emit-1.2.0.tgz", - "integrity": "sha512-dSFBrRuIiWbHK2LSUA6CutXpMcNGjjuhvxFLF+TVz5tYFAAH0eesrZgrQ3UtOptajDYNt/fIGRqtlHqGq/bLbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bunyamin": "^1.5.2", - "bunyan": "^2.0.5", - "bunyan-debug-stream": "^3.1.0", - "funpermaproxy": "^1.1.0", - "lodash.merge": "^4.6.2", - "node-ipc": "9.2.1", - "strip-ansi": "^6.0.0", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16.14.0" - }, - "peerDependencies": { - "@jest/environment": ">=27.2.5", - "@jest/types": ">=27.2.5", - "jest": ">=27.2.5", - "jest-environment-jsdom": ">=27.2.5", - "jest-environment-node": ">=27.2.5" - }, - "peerDependenciesMeta": { - "@jest/environment": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "jest": { - "optional": true - }, - "jest-environment-jsdom": { - "optional": true - }, - "jest-environment-node": { - "optional": true - } - } - }, - "node_modules/jest-environment-emit/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, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-environment-jsdom": { "version": "29.7.0", "dev": true, @@ -14208,75 +13984,14 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-metadata": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/jest-metadata/-/jest-metadata-1.6.0.tgz", - "integrity": "sha512-penaOkD6tN0Vpqd+9xnbS+iYSLqaZpsx08gz44mOBvyNGBHPglnNKOsBMr3cbIe0bFYGlnouDy4N5SfLtNgVBQ==", + "node_modules/jest-mock": { + "version": "29.7.0", "dev": true, "license": "MIT", "dependencies": { - "bunyamin": "^1.5.2", - "funpermaproxy": "^1.1.0", - "jest-environment-emit": "^1.0.8", - "lodash.merge": "^4.6.2", - "lodash.snakecase": "^4.1.1", - "node-ipc": "9.2.1", - "strip-ansi": "^6.0.0", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16.14.0" - }, - "peerDependencies": { - "@jest/environment": ">=27.2.5", - "@jest/reporters": ">=27.2.5", - "@jest/types": ">=27.2.5", - "jest": ">=27.2.5", - "jest-environment-jsdom": ">=27.2.5", - "jest-environment-node": ">=27.2.5" - }, - "peerDependenciesMeta": { - "@jest/environment": { - "optional": true - }, - "@jest/reporters": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "jest": { - "optional": true - }, - "jest-environment-jsdom": { - "optional": true - }, - "jest-environment-node": { - "optional": true - } - } - }, - "node_modules/jest-metadata/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, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -14784,29 +14499,6 @@ "node": ">= 20" } }, - "node_modules/js-message": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", - "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/js-queue": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz", - "integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "easy-stack": "^1.0.1" - }, - "engines": { - "node": ">=1.0.0" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "license": "MIT" @@ -15480,13 +15172,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.truncate": { "version": "4.4.2", "dev": true, @@ -15702,6 +15387,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/mdn-data": { "version": "2.12.2", "dev": true, @@ -15970,20 +15667,6 @@ "dev": true, "license": "MIT" }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/mnth": { "version": "2.0.0", "license": "MIT", @@ -16022,13 +15705,6 @@ "dev": true, "license": "MIT" }, - "node_modules/multi-sort-stream": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multi-sort-stream/-/multi-sort-stream-1.0.4.tgz", - "integrity": "sha512-hAZ8JOEQFbgdLe8HWZbb7gdZg0/yAIHF00Qfo3kd0rXFv96nXe+/bPTrKHZ2QMHugGX4FiAyET1Lt+jiB+7Qlg==", - "dev": true, - "license": "bsd" - }, "node_modules/multicast-dns": { "version": "7.2.5", "dev": true, @@ -16082,78 +15758,6 @@ "node": ">= 10.13.0" } }, - "node_modules/mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/mv/node_modules/glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mv/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/mv/node_modules/rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "optional": true, - "dependencies": { - "glob": "^6.0.1" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/nan": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz", - "integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/nanoid": { "version": "5.1.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz", @@ -16186,17 +15790,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", - "dev": true, - "license": "MIT", - "optional": true, - "bin": { - "ncp": "bin/ncp" - } - }, "node_modules/negotiator": { "version": "0.6.4", "dev": true, @@ -16240,72 +15833,11 @@ "license": "MIT", "optional": true }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/node-int64": { "version": "0.4.0", "dev": true, "license": "MIT" }, - "node_modules/node-ipc": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz", - "integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "event-pubsub": "4.3.0", - "js-message": "1.0.7", - "js-queue": "2.0.2" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -17280,85 +16812,6 @@ "node": ">=8" } }, - "node_modules/pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-up/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-up/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/pkijs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", @@ -18260,16 +17713,6 @@ "version": "16.13.1", "license": "MIT" }, - "node_modules/properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/properties/-/properties-1.2.1.tgz", - "integrity": "sha512-qYNxyMj1JeW54i/EWEFsM1cVwxJbtgPp8+0Wg9XjNaK6VE/c4oRi6PNu5p7w1mNXEIQIjV5Wwn8v8Gz82/QzdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "dev": true, @@ -19447,14 +18890,6 @@ ], "license": "MIT" }, - "node_modules/safe-json-stringify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", - "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/safe-push-apply": { "version": "1.0.0", "dev": true, @@ -20364,16 +19799,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stack-generator": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", - "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, "node_modules/stack-utils": { "version": "2.0.6", "dev": true, @@ -20393,46 +19818,6 @@ "node": ">=8" } }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "dev": true, - "license": "MIT" - }, - "node_modules/stacktrace-gps": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", - "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "0.5.6", - "stackframe": "^1.3.4" - } - }, - "node_modules/stacktrace-gps/node_modules/source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stacktrace-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", - "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "error-stack-parser": "^2.0.6", - "stack-generator": "^2.0.5", - "stacktrace-gps": "^3.0.4" - } - }, "node_modules/statuses": { "version": "2.0.1", "dev": true, @@ -20453,13 +19838,6 @@ "node": ">= 0.4" } }, - "node_modules/stream-chain": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", - "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/stream-composer": { "version": "1.0.2", "dev": true, @@ -20482,16 +19860,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stream-json": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", - "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "stream-chain": "^2.2.5" - } - }, "node_modules/stream-shift": { "version": "1.0.3", "dev": true, @@ -21667,19 +21035,6 @@ "node": ">=12" } }, - "node_modules/trace-event-lib": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/trace-event-lib/-/trace-event-lib-1.4.1.tgz", - "integrity": "sha512-TOgFolKG8JFY+9d5EohGWMvwvteRafcyfPWWNIqcuD1W/FUvxWcy2MSCZ/beYHM63oYPHYHCd3tkbgCctHVP7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-process-hrtime": "^1.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/tree-dump": { "version": "1.0.3", "dev": true, diff --git a/package.json b/package.json index 1fd30fccf7..1fbbc996f0 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "@types/react-test-renderer": "^16.9.12", "@types/react-transition-group": "^4.4.4", "@types/webpack-env": "^1.16.3", + "allure-jest": "^3.9.0", "axe-core": "^4.7.2", "babel-jest": "^29.7.0", "change-case": "^4.1.2", @@ -111,7 +112,6 @@ "html-webpack-plugin": "^5.5.0", "husky": "^9.0.0", "jest": "^29.7.0", - "jest-allure2-reporter": "^2.3.0", "jest-environment-jsdom": "^29.7.0", "lint-staged": "^15.2.2", "loader-utils": "^3.2.1", diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 1b4dd0fee2..67e38799c7 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -1,8 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import * as crypto from 'crypto'; -import * as fs from 'fs'; -import * as path from 'path'; +import { attachment, ContentType } from 'allure-js-commons'; import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; @@ -16,8 +14,6 @@ const defaultWindowSize = { width: 1600, height: 800 }; const newHost = process.env.NEW_HOST || 'http://localhost:8080'; const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; -const allureResultsDir = path.resolve(process.cwd(), 'allure-results'); - function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams(queryParams); const qs = params.toString(); @@ -29,52 +25,18 @@ function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinit } /** - * Writes a PNG buffer to the allure-results directory and returns the filename. - * Allure picks up files in this directory and attaches them to the report. + * Attaches visual diff images (new, baseline, diff) to the Allure report + * via the allure-js-commons runtime API. */ -function writeAllureAttachment(buffer: Buffer): string { - if (!fs.existsSync(allureResultsDir)) { - fs.mkdirSync(allureResultsDir, { recursive: true }); - } - const uuid = crypto.randomUUID(); - const filename = `${uuid}-attachment.png`; - fs.writeFileSync(path.join(allureResultsDir, filename), buffer); - return filename; -} - -/** - * Attaches visual diff images (new, baseline, diff) to the Allure results - * for the current test by writing an attachment JSON file. - */ -function attachDiffImages( +async function attachDiffImages( result: { firstImage: Buffer; secondImage: Buffer; diffImage: Buffer | null }, testName: string -): void { - const newFile = writeAllureAttachment(result.firstImage); - const baselineFile = writeAllureAttachment(result.secondImage); - - const attachments: Array<{ name: string; source: string; type: string }> = [ - { name: `${testName} — new (PR)`, source: newFile, type: 'image/png' }, - { name: `${testName} — baseline (main)`, source: baselineFile, type: 'image/png' }, - ]; - +): Promise { + await attachment(`${testName} — new (PR)`, result.firstImage, ContentType.PNG); + await attachment(`${testName} — baseline (main)`, result.secondImage, ContentType.PNG); if (result.diffImage) { - const diffFile = writeAllureAttachment(result.diffImage); - attachments.push({ name: `${testName} — diff`, source: diffFile, type: 'image/png' }); + await attachment(`${testName} — diff`, result.diffImage, ContentType.PNG); } - - // Write a container JSON that Allure merges into the test result. - // jest-allure2-reporter reads attachment files from allure-results/. - const containerUuid = crypto.randomUUID(); - const containerFile = path.join(allureResultsDir, `${containerUuid}-container.json`); - fs.writeFileSync( - containerFile, - JSON.stringify({ - uuid: containerUuid, - name: testName, - attachments, - }) - ); } /** @@ -181,7 +143,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro for (let i = 0; i < newPermutations.length; i++) { const permResult = await cropAndCompare(newPermutations[i], oldPermutations[i]); if (permResult.diffPixels !== 0) { - attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`); + await attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`); } expect(permResult.diffPixels).toBe(0); } @@ -189,7 +151,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro } // Attach diff images to Allure report for visual inspection. - attachDiffImages(result, testDef.description); + await attachDiffImages(result, testDef.description); // For screenshotArea and viewport types, the diff is a real failure. expect(result.diffPixels).toBe(0); From 480ac3311c277c04b2fc22b22602a20312d02c40 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 6 Jun 2026 20:07:54 +0200 Subject: [PATCH 080/135] Optimize comparison --- test/definitions/utils.ts | 55 ++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 67e38799c7..1bffb732df 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { attachment, ContentType } from 'allure-js-commons'; -import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; +import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; import { TestDefinition, TestSuite } from './types'; @@ -14,6 +14,13 @@ const defaultWindowSize = { width: 1600, height: 800 }; const newHost = process.env.NEW_HOST || 'http://localhost:8080'; const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; +interface RawCapture { + /** The raw base64-encoded PNG string from WebDriver (before decoding). */ + rawBase64: string; + /** The fully parsed screenshot with offset metadata (lazily resolved). */ + screenshot: () => Promise; +} + function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams(queryParams); const qs = params.toString(); @@ -74,15 +81,16 @@ function registerSuites(suites: Array, getBrowser: ( } /** - * Captures a screenshot based on the test's screenshotType. + * Captures a screenshot and returns both the raw PNG base64 and the parsed result. + * Having the raw base64 allows a fast byte-equality check before expensive pixel decoding. */ -async function capture( +async function captureRaw( browser: WebdriverIO.Browser, page: ScreenshotPageObject, url: string, testDef: TestDefinition, windowSize: { width: number; height: number } | undefined -): Promise { +): Promise { if (windowSize) { await browser.setWindowSize(windowSize.width, windowSize.height); } @@ -91,10 +99,29 @@ async function capture( if (testDef.setup) { await testDef.setup(page); } + if (testDef.screenshotType === 'viewport') { - return page.captureViewport(); + const { height, width } = await page.getViewportSize(); + const rawBase64 = await browser.takeScreenshot(); + return { + rawBase64, + screenshot: async () => { + const image = await parsePng(rawBase64); + return { image, offset: { top: 0, left: 0 }, height, width }; + }, + }; } - return page.captureBySelector(screenshotAreaSelector, { viewportOnly: true }); + + // screenshotArea / permutations — capture by selector with viewportOnly + const box = await page.getBoundingBox(screenshotAreaSelector); + const rawBase64 = await browser.takeScreenshot(); + return { + rawBase64, + screenshot: async () => { + const image = await parsePng(rawBase64); + return { image, offset: { top: box.top, left: box.left }, height: box.height, width: box.width }; + }, + }; } function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { @@ -106,9 +133,19 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - const newScreenshot = await capture(browser, page, newUrl, testDef, windowSize); - const oldScreenshot = await capture(browser, page, oldUrl, testDef, windowSize); - const result = await cropAndCompare(newScreenshot, oldScreenshot); + const newCapture = await captureRaw(browser, page, newUrl, testDef, windowSize); + const oldCapture = await captureRaw(browser, page, oldUrl, testDef, windowSize); + + // Fast path: if the raw PNG bytes are identical, the images are guaranteed + // to be the same. This skips the expensive crop + pixelmatch decode path + // for the common case (no visual difference). + if (newCapture.rawBase64 === oldCapture.rawBase64) { + return; + } + + // Raw bytes differ — could be a real diff or just offset/crop differences. + // Fall through to full pixel comparison. + const result = await cropAndCompare(await newCapture.screenshot(), await oldCapture.screenshot()); if (result.diffPixels === 0) { return; From b776692175e660516f63a94a4051bc5d43dd06fa Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 6 Jun 2026 20:55:25 +0200 Subject: [PATCH 081/135] Export types --- .gitignore | 1 + build-tools/tasks/package-json.js | 4 ++ build-tools/visual/generate-tests.js | 63 +++++++++++++++++++++------- docs/RUNNING_TESTS.md | 10 ++--- test/definitions/index.ts | 49 ---------------------- 5 files changed, 59 insertions(+), 68 deletions(-) delete mode 100644 test/definitions/index.ts diff --git a/.gitignore b/.gitignore index 38d6053611..1f46343d58 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ lib # generated sources src/index.ts test/visual +test/definitions/index.ts allure-results allure-report src/test-utils/dom/index.ts diff --git a/build-tools/tasks/package-json.js b/build-tools/tasks/package-json.js index a7c69a53f5..2c447682a1 100644 --- a/build-tools/tasks/package-json.js +++ b/build-tools/tasks/package-json.js @@ -103,6 +103,10 @@ const devPagesPackageJson = generatePackageJson(path.join(workspace.targetPath, const testDefinitionsPackageJson = generatePackageJson(path.join(workspace.targetPath, 'test-definitions'), { name: '@cloudscape-design/test-definitions', + exports: { + '.': './index.js', + './types': './types.js', + }, }); module.exports = parallel([ diff --git a/build-tools/visual/generate-tests.js b/build-tools/visual/generate-tests.js index a56b996b3d..f82b54d037 100644 --- a/build-tools/visual/generate-tests.js +++ b/build-tools/visual/generate-tests.js @@ -2,11 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 /** - * Auto-generates test/visual/*.test.ts files from test/definitions/visual/*.ts. + * Auto-generates: + * 1. test/visual/*.test.ts — one test runner per definition file (for Jest sharding) + * 2. test/definitions/index.ts — barrel that exports allSuites from all definitions * - * Each definition file that exports a default TestSuite gets a corresponding - * test runner. Helper files (those without a default export, e.g. shared utils - * like app-layout-responsive-tests.ts) are skipped. + * Each definition file that exports a default TestSuite gets included. + * Helper files (those without a default export, e.g. shared utils like + * app-layout-responsive-tests.ts) are skipped. * * Run this script before executing the visual test suite: * node build-tools/visual/generate-tests.js @@ -15,26 +17,29 @@ const fs = require('fs'); const path = require('path'); const definitionsDir = path.resolve(__dirname, '../../test/definitions/visual'); -const outputDir = path.resolve(__dirname, '../../test/visual'); +const testOutputDir = path.resolve(__dirname, '../../test/visual'); +const indexOutputPath = path.resolve(__dirname, '../../test/definitions/index.ts'); // Files that are shared helpers (export named functions, not a default suite). -// These are detected by checking if the file contains "export default" or -// a conventional "const suite" pattern followed by "export default suite". const HELPER_SUFFIXES = ['-tests']; function isHelperFile(basename) { return HELPER_SUFFIXES.some(suffix => basename.endsWith(suffix)); } +function toCamelCase(basename) { + return basename.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase()); +} + function generate() { // Ensure output directory exists - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); + if (!fs.existsSync(testOutputDir)) { + fs.mkdirSync(testOutputDir, { recursive: true }); } const files = fs.readdirSync(definitionsDir).filter(f => f.endsWith('.ts') && !f.endsWith('.d.ts')); - const generated = []; + const suiteFiles = []; for (const file of files) { const basename = file.replace(/\.ts$/, ''); @@ -49,6 +54,9 @@ function generate() { continue; } + suiteFiles.push(basename); + + // Generate test/visual/.test.ts const testContent = [ '// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.', '// SPDX-License-Identifier: Apache-2.0', @@ -60,12 +68,39 @@ function generate() { '', ].join('\n'); - const outputPath = path.join(outputDir, `${basename}.test.ts`); - fs.writeFileSync(outputPath, testContent); - generated.push(basename); + fs.writeFileSync(path.join(testOutputDir, `${basename}.test.ts`), testContent); } - console.log(`Generated ${generated.length} visual test files in test/visual/`); + // Generate test/definitions/index.ts + // Sort by module path for consistent import ordering + suiteFiles.sort(); + + const imports = suiteFiles.map(basename => { + const varName = toCamelCase(basename); + return `import ${varName} from './visual/${basename}';`; + }); + + const arrayEntries = suiteFiles.map(basename => ` ${toCamelCase(basename)},`); + + const indexContent = [ + '// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.', + '// SPDX-License-Identifier: Apache-2.0', + '// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually.', + '', + `import { TestSuite } from './types';`, + `export { TestSuite, TestDefinition, ScreenshotType, ScreenshotTestConfiguration } from './types';`, + ...imports, + '', + 'export const allSuites: TestSuite[] = [', + ...arrayEntries, + '];', + '', + ].join('\n'); + + fs.writeFileSync(indexOutputPath, indexContent); + + console.log(`Generated ${suiteFiles.length} visual test files in test/visual/`); + console.log(`Generated test/definitions/index.ts with ${suiteFiles.length} suites`); } generate(); diff --git a/docs/RUNNING_TESTS.md b/docs/RUNNING_TESTS.md index c7832dca72..4db82e361b 100644 --- a/docs/RUNNING_TESTS.md +++ b/docs/RUNNING_TESTS.md @@ -104,14 +104,14 @@ const suite: TestSuite = { export default suite; ``` -Then import and add it to `test/definitions/visual/index.ts`: +Then run the generation script to pick it up automatically: -```ts -import myComponent from './my-component'; - -export const allSuites: TestSuite[] = [..., myComponent]; +```bash +node build-tools/visual/generate-tests.js ``` +This generates both the test runner (`test/visual/my-component.test.ts`) and updates `test/definitions/index.ts`. No manual imports needed. + ### Reviewing failures If the CI job fails, download the `visual-regression-diffs` artifact from the Actions summary. diff --git a/test/definitions/index.ts b/test/definitions/index.ts deleted file mode 100644 index 3bd6fa66e2..0000000000 --- a/test/definitions/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Each component has its own test definition file. -// Import them here manually to form the full test suite. -import { TestSuite } from './types'; -import actionCard from './visual/action-card'; -import alert from './visual/alert'; -import appLayout from './visual/app-layout'; -import appLayoutContentPaddings from './visual/app-layout-content-paddings'; -import appLayoutDrawers from './visual/app-layout-drawers'; -import appLayoutFlashbar from './visual/app-layout-flashbar'; -import appLayoutHeader from './visual/app-layout-header'; -import appLayoutMulti from './visual/app-layout-multi'; -import appLayoutResponsive600 from './visual/app-layout-responsive-600'; -import appLayoutResponsive1280 from './visual/app-layout-responsive-1280'; -import appLayoutResponsive1400 from './visual/app-layout-responsive-1400'; -import appLayoutResponsive1920 from './visual/app-layout-responsive-1920'; -import appLayoutResponsive2540 from './visual/app-layout-responsive-2540'; -import appLayoutStickyTableHeaderSplitPanel from './visual/app-layout-sticky-table-header-split-panel'; -import appLayoutToolbar from './visual/app-layout-toolbar'; -import appLayoutZIndex from './visual/app-layout-z-index'; -import areaChart from './visual/area-chart'; -import attributeEditor from './visual/attribute-editor'; -import autosuggest from './visual/autosuggest'; -import badge from './visual/badge'; - -export const allSuites: TestSuite[] = [ - actionCard, - alert, - appLayout, - appLayoutContentPaddings, - appLayoutDrawers, - appLayoutFlashbar, - appLayoutHeader, - appLayoutMulti, - appLayoutResponsive600, - appLayoutResponsive1280, - appLayoutResponsive1400, - appLayoutResponsive1920, - appLayoutResponsive2540, - appLayoutStickyTableHeaderSplitPanel, - appLayoutToolbar, - appLayoutZIndex, - areaChart, - attributeEditor, - autosuggest, - badge, -]; From 780db53c8d0bcc0a6fcdba8272a48fa667fb5098 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 9 Jun 2026 00:07:55 +0200 Subject: [PATCH 082/135] Remove new test definitions --- .../visual/app-layout-content-paddings.ts | 30 ---- test/definitions/visual/app-layout-drawers.ts | 41 ----- .../definitions/visual/app-layout-flashbar.ts | 39 ----- test/definitions/visual/app-layout-header.ts | 85 ---------- test/definitions/visual/app-layout-multi.ts | 25 --- .../visual/app-layout-responsive-1280.ts | 7 - .../visual/app-layout-responsive-1400.ts | 7 - .../visual/app-layout-responsive-1920.ts | 7 - .../visual/app-layout-responsive-2540.ts | 7 - .../visual/app-layout-responsive-600.ts | 7 - .../visual/app-layout-responsive-tests.ts | 159 ------------------ ...-layout-sticky-table-header-split-panel.ts | 78 --------- test/definitions/visual/app-layout-toolbar.ts | 23 --- test/definitions/visual/app-layout-z-index.ts | 60 ------- test/definitions/visual/app-layout.ts | 104 ------------ test/definitions/visual/area-chart.ts | 136 --------------- test/definitions/visual/attribute-editor.ts | 31 ---- test/definitions/visual/autosuggest.ts | 70 -------- test/definitions/visual/badge.ts | 23 --- 19 files changed, 939 deletions(-) delete mode 100644 test/definitions/visual/app-layout-content-paddings.ts delete mode 100644 test/definitions/visual/app-layout-drawers.ts delete mode 100644 test/definitions/visual/app-layout-flashbar.ts delete mode 100644 test/definitions/visual/app-layout-header.ts delete mode 100644 test/definitions/visual/app-layout-multi.ts delete mode 100644 test/definitions/visual/app-layout-responsive-1280.ts delete mode 100644 test/definitions/visual/app-layout-responsive-1400.ts delete mode 100644 test/definitions/visual/app-layout-responsive-1920.ts delete mode 100644 test/definitions/visual/app-layout-responsive-2540.ts delete mode 100644 test/definitions/visual/app-layout-responsive-600.ts delete mode 100644 test/definitions/visual/app-layout-responsive-tests.ts delete mode 100644 test/definitions/visual/app-layout-sticky-table-header-split-panel.ts delete mode 100644 test/definitions/visual/app-layout-toolbar.ts delete mode 100644 test/definitions/visual/app-layout-z-index.ts delete mode 100644 test/definitions/visual/app-layout.ts delete mode 100644 test/definitions/visual/area-chart.ts delete mode 100644 test/definitions/visual/attribute-editor.ts delete mode 100644 test/definitions/visual/autosuggest.ts delete mode 100644 test/definitions/visual/badge.ts diff --git a/test/definitions/visual/app-layout-content-paddings.ts b/test/definitions/visual/app-layout-content-paddings.ts deleted file mode 100644 index e76d055749..0000000000 --- a/test/definitions/visual/app-layout-content-paddings.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Content paddings', - componentName: 'app-layout', - tests: [ - ...(['true', 'false'] as const).flatMap(toolsEnabled => - (['true', 'false'] as const).flatMap(splitPanelEnabled => - (['bottom', 'side'] as const).map(splitPanelPosition => ({ - description: `toolsEnabled=${toolsEnabled} splitPanelEnabled=${splitPanelEnabled} splitPanelPosition=${splitPanelPosition}`, - path: 'app-layout/with-split-panel', - screenshotType: 'viewport' as const, - queryParams: { toolsEnabled, splitPanelEnabled, splitPanelPosition }, - })) - ) - ), - ...[1500, 600].map(width => ({ - description: `with split panel and disabled content paddings - width=${width}`, - path: 'app-layout/disable-paddings-with-split-panel', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { splitPanelOpen: 'true', splitPanelPosition: 'side' }, - })), - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-drawers.ts b/test/definitions/visual/app-layout-drawers.ts deleted file mode 100644 index 39c2089783..0000000000 --- a/test/definitions/visual/app-layout-drawers.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import createWrapper from '../../../lib/components/test-utils/selectors'; -import { TestSuite } from '../types'; - -const wrapper = createWrapper(); - -const suite: TestSuite = { - description: 'Drawers', - componentName: 'app-layout', - tests: [ - { - description: 'with split panel', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - setup: async page => { - await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); - }, - }, - { - description: 'with tooltip on hover', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - setup: async page => { - await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); - }, - }, - { - description: 'with custom scrollable drawer content', - path: 'app-layout/with-drawers-scrollable', - screenshotType: 'viewport', - queryParams: { sideNavFill: 'false' }, - setup: async page => { - await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-flashbar.ts b/test/definitions/visual/app-layout-flashbar.ts deleted file mode 100644 index 5849a5cbf5..0000000000 --- a/test/definitions/visual/app-layout-flashbar.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Flashbar', - componentName: 'app-layout', - tests: [true, false].flatMap(disableContentPaddings => - [true, false].flatMap(stickyNotifications => - [true, false].flatMap(stickyTableHeader => - [true, false].map(stackNotifications => ({ - description: `disableContentPaddings: ${disableContentPaddings}, stickyNotifications: ${stickyNotifications}, stickyTableHeader: ${stickyTableHeader}, stackNotifications: ${stackNotifications}`, - path: 'app-layout/with-stacked-notifications-and-table', - screenshotType: 'screenshotArea' as const, - configuration: { width: 1280, height: 900 }, - setup: async (page: import('@cloudscape-design/browser-test-tools/page-objects').ScreenshotPageObject) => { - if (!disableContentPaddings) { - await page.click('[data-id="toggle-content-paddings"]'); - } - if (stickyNotifications) { - await page.click('[data-id="toggle-sticky-notifications"]'); - } - if (!stickyTableHeader) { - await page.click('[data-id="toggle-sticky-table-header"]'); - } - if (!stackNotifications) { - await page.click('[data-id="toggle-stack-items"]'); - } - await page.click('[data-id="add-notification"]'); - await page.click('[data-id="add-notification"]'); - }, - })) - ) - ) - ), -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-header.ts b/test/definitions/visual/app-layout-header.ts deleted file mode 100644 index 3a128a266e..0000000000 --- a/test/definitions/visual/app-layout-header.ts +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestDefinition, TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Headers', - componentName: 'app-layout', - tests: [ - // ── Headers ─────────────────────────────────────────────────────────── - { - description: 'Headers', - tests: [600, 1280].flatMap(width => [ - { - description: `alignment with full-page table (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - { - description: `alignment with full-page table in sticky state (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'viewport' as const, - configuration: { width }, - setup: async page => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `alignment with full-page table in sticky state with sticky notifications (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { stickyNotifications: 'true' }, - setup: async page => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `high contrast header variant in landing page (${width}px)`, - path: 'app-layout/landing-page', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - ]), - }, - - // ── High contrast header variant ────────────────────────────────────── - { - description: 'High contrast header variant', - tests: [ - ...[1400, 600].flatMap(width => [ - { - description: `with breadcrumbs and notifications at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', hasContainer: 'true' }, - } as TestDefinition, - { - description: `without overlap at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { disableOverlap: 'true' }, - } as TestDefinition, - { - description: `with content layout at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - hasContainer: 'true', - hasContentLayout: 'true', - }, - } as TestDefinition, - ]), - ], - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-multi.ts b/test/definitions/visual/app-layout-multi.ts deleted file mode 100644 index babf9733cf..0000000000 --- a/test/definitions/visual/app-layout-multi.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Multiple instances', - componentName: 'app-layout', - tests: [600, 1280].flatMap(width => [ - { - description: `simple (${width}px)`, - path: 'app-layout/multi-layout-simple', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - { - description: `iframe (${width}px)`, - path: 'app-layout/multi-layout-iframe', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - ]), -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1280.ts b/test/definitions/visual/app-layout-responsive-1280.ts deleted file mode 100644 index d5fe823f54..0000000000 --- a/test/definitions/visual/app-layout-responsive-1280.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(1280); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1400.ts b/test/definitions/visual/app-layout-responsive-1400.ts deleted file mode 100644 index 1cb519005c..0000000000 --- a/test/definitions/visual/app-layout-responsive-1400.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(1400); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1920.ts b/test/definitions/visual/app-layout-responsive-1920.ts deleted file mode 100644 index 88b8c6caf3..0000000000 --- a/test/definitions/visual/app-layout-responsive-1920.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(1920); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-2540.ts b/test/definitions/visual/app-layout-responsive-2540.ts deleted file mode 100644 index 9dd62c00db..0000000000 --- a/test/definitions/visual/app-layout-responsive-2540.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(2540); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-600.ts b/test/definitions/visual/app-layout-responsive-600.ts deleted file mode 100644 index f6fd3665cc..0000000000 --- a/test/definitions/visual/app-layout-responsive-600.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(600); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-tests.ts b/test/definitions/visual/app-layout-responsive-tests.ts deleted file mode 100644 index f0ac91d121..0000000000 --- a/test/definitions/visual/app-layout-responsive-tests.ts +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -/** - * Shared responsive test scenarios for app-layout. Each width gets its own - * definition file and test runner so that Jest sharding can parallelize them. - */ -export function responsiveTests(width: number): TestSuite { - return { - description: `AppLayout responsive width ${width}px`, - componentName: 'app-layout', - tests: [ - { - description: 'default', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'navigation drawer is open', - path: 'app-layout/with-wizard', - screenshotType: 'viewport', - configuration: { width }, - setup: async page => { - await page.click('[aria-label="Open navigation"]'); - }, - }, - { - description: 'wizard', - path: 'app-layout/with-wizard', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with wizard and table', - path: 'app-layout/with-wizard-and-table', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with wizard, table, and breadcrumbs', - path: 'app-layout/with-wizard-and-table', - screenshotType: 'viewport', - configuration: { width }, - queryParams: { hasBreadcrumbs: 'true' }, - }, - { - description: 'notifications', - path: 'app-layout/with-notifications', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'breadcrumbs', - path: 'app-layout/with-breadcrumbs', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'notifications and breadcrumbs', - path: 'app-layout/with-breadcrumbs-notifications', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'dashboard content type', - path: 'app-layout/dashboard-content-type', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'fixed header and footer', - path: 'app-layout/with-fixed-header-footer', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disableBodyScroll - empty', - path: 'app-layout/legacy-nav-empty', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disableBodyScroll - with content', - path: 'app-layout/legacy-nav-scrollable', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disableBodyScroll - with split panel', - path: 'app-layout/legacy-nav-scrollable-with-split-panel', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disable paddings', - path: 'app-layout/disable-paddings', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disable paddings with breadcrumbs', - path: 'app-layout/disable-paddings-breadcrumbs', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'sticky notifications', - path: 'app-layout/with-sticky-notifications', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'sticky notifications scrolled down', - path: 'app-layout/with-sticky-notifications', - screenshotType: 'viewport', - configuration: { width }, - setup: async page => { - await page.windowScrollTo({ top: 2000 }); - }, - }, - { - description: 'layout without panels', - path: 'app-layout/no-panels', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'layout without panels but with notifications', - path: 'app-layout/no-panels-with-notifications', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with drawers', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with empty drawers', - path: 'app-layout/with-drawers-empty', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with open drawer', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - configuration: { width }, - setup: async page => { - await page.click('[aria-label="Security trigger button"]'); - }, - }, - ], - }; -} diff --git a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts deleted file mode 100644 index 6a0b899686..0000000000 --- a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Sticky header with split panel', - componentName: 'app-layout', - tests: [ - { - description: 'scrolling to bottom with closed split panel (1 table row)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-1"]'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'scrolling to bottom with closed split panel (30 table rows)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky with open split panel (1 table row)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-1"]'); - await page.click('aria/Open panel'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky with open split panel (30 table rows)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.click('aria/Open panel'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky when mounting and unmounting a second table', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async page => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.click('aria/Open panel'); - await page.windowScrollTo({ top: 0 }); - await page.click('aria/Close panel'); - await page.scrollToBottom('html'); - }, - }, - // ── Max content width ───────────────────────────────────────────────── - { - description: 'maxContentWidth set to Number.MAX_VALUE', - path: 'app-layout/refresh-content-width', - screenshotType: 'viewport', - configuration: { width: 1280, height: 700 }, - setup: async page => { - await page.click('[data-test-id="button_width-number-max_value"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-toolbar.ts b/test/definitions/visual/app-layout-toolbar.ts deleted file mode 100644 index fb174b0a71..0000000000 --- a/test/definitions/visual/app-layout-toolbar.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Toolbar', - componentName: 'app-layout', - tests: [ - { - description: 'multiple nested instances (no breadcrumbs dedup)', - path: 'app-layout-toolbar/multi-layout-with-hidden-instances', - screenshotType: 'viewport', - }, - { - description: 'no toolbar', - path: 'app-layout-toolbar/without-toolbar', - screenshotType: 'viewport', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-z-index.ts b/test/definitions/visual/app-layout-z-index.ts deleted file mode 100644 index b0dacf8bc5..0000000000 --- a/test/definitions/visual/app-layout-z-index.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestDefinition, TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Z-index', - componentName: 'app-layout', - tests: [ - ...[600, 1280].flatMap(width => [ - { - description: `button dropdown (${width}px)`, - path: 'app-layout/with-absolute-components', - screenshotType: 'viewport' as const, - configuration: { width }, - setup: async page => { - await page.click('button=Button dropdown'); - await page.click('[data-testid="2"]'); - await page.windowScrollTo({ top: 300 }); - }, - } as TestDefinition, - { - description: `select (${width}px)`, - path: 'app-layout/with-absolute-components', - screenshotType: 'viewport' as const, - configuration: { width, height: 800 }, - setup: async page => { - await page.click('[data-testid="select-demo"] button'); - await page.windowScrollTo({ top: 300 }); - }, - } as TestDefinition, - { - description: `split-panel and full-page table (${width}px)`, - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - ]), - { - description: 'split-panel and full-page with open navigation (600px)', - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, - configuration: { width: 600 }, - setup: async page => { - await page.click('button[aria-label="Open navigation"]'); - }, - }, - { - description: 'split-panel and full-page with open tools (600px)', - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, - configuration: { width: 600 }, - setup: async page => { - await page.click('button[aria-label="Open tools"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts deleted file mode 100644 index 683efdcc25..0000000000 --- a/test/definitions/visual/app-layout.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'AppLayout', - componentName: 'app-layout', - tests: [ - { - description: 'no scrollbars at 320px', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width: 320 }, - }, - { - description: 'drawer buttons alignment', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width: 800 }, - setup: async page => { - await page.click('[aria-label="Open tools"]'); - }, - }, - { - description: 'disable paddings - navigation closed', - path: 'app-layout/disable-paddings', - screenshotType: 'viewport', - configuration: { width: 1280 }, - setup: async page => { - await page.click('[aria-label="Close navigation"]'); - }, - }, - { - description: 'panels stacking on mobile', - path: 'app-layout/all-panels-open', - screenshotType: 'viewport', - configuration: { width: 600 }, - }, - { - description: 'wrapping long words', - path: 'app-layout/text-wrap', - screenshotType: 'viewport', - }, - { - description: 'fill content area', - path: 'app-layout/fill-content-area', - screenshotType: 'viewport', - }, - { - description: 'with tools and drawers', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - queryParams: { hasTools: 'true' }, - }, - { - description: 'with open drawer and open side split panel', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - configuration: { width: 1400 }, - queryParams: { splitPanelPosition: 'side' }, - setup: async page => { - await page.click('[aria-label="Security trigger button"]'); - await page.click('[aria-label="Open panel"]'); - }, - }, - - // regression for https://github.com/cloudscape-design/components/pull/1612 - { - description: 'with open drawer and open side split panel after resize', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - configuration: { width: 1500 }, - queryParams: { splitPanelPosition: 'side' }, - setup: async page => { - await page.click('[aria-label="Security trigger button"]'); - await page.click('[aria-label="Open panel"]'); - await page.setWindowSize({ width: 1400, height: 800 }); - }, - }, - - // ── Transitions ─────────────────────────────────────────────────────── - { - description: 'transition from 400px to 1800px', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width: 400, height: 400 }, - setup: async page => { - await page.setWindowSize({ width: 1800, height: 400 }); - }, - }, - { - description: 'transition from 1800px to 400px', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width: 1800, height: 400 }, - setup: async page => { - await page.setWindowSize({ width: 400, height: 400 }); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/area-chart.ts b/test/definitions/visual/area-chart.ts deleted file mode 100644 index cfa0b5e47b..0000000000 --- a/test/definitions/visual/area-chart.ts +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const TEST_CHART_FILTER_TRIGGER = '#linear-latency-chart button'; -const TEST_CHART_TOOLTIP_HEADER = '#linear-latency-chart h2'; - -const suite: TestSuite = { - description: 'Area chart', - componentName: 'area-chart', - tests: [ - { - description: 'permutations', - path: 'area-chart/permutations', - screenshotType: 'permutations', - }, - { - description: 'fit-height', - path: 'area-chart/fit-height', - screenshotType: 'screenshotArea', - }, - { - description: 'fit-height no filter, no legend', - path: 'area-chart/fit-height', - screenshotType: 'screenshotArea', - queryParams: { hideFilter: 'true', hideLegend: 'true' }, - }, - { - description: 'fit-height, no legend', - path: 'area-chart/fit-height', - screenshotType: 'screenshotArea', - queryParams: { hideLegend: 'true' }, - }, - { - description: 'chart plot has a focus outline', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async page => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - }, - }, - { - description: 'can navigate along X axis highlighting all series with keyboard', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async page => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'can navigate a specific series with keyboard', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async page => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowDown']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'selects correct series when navigated back from legend', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async page => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.keys(['Tab']); - await page.keys(['Tab']); - await page.keys(['ArrowRight']); - await page.keys(['Shift', 'Tab']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'can pin popover for all data points at a given X coordinate with keyboard', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async page => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - await page.keys(['Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - { - description: 'can pin popover for a point in a specific series with keyboard', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async page => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowDown']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - await page.keys(['Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - { - description: 'shows popover on hover', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async page => { - await page.hoverElement('[aria-label="Linear latency chart"]', 200, 50); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/attribute-editor.ts b/test/definitions/visual/attribute-editor.ts deleted file mode 100644 index 33da3fad86..0000000000 --- a/test/definitions/visual/attribute-editor.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Attribute Editor', - componentName: 'attribute-editor', - tests: [360, 768, 992].flatMap(width => [ - { - description: `permutations at ${width}px`, - path: 'attribute-editor/permutations', - screenshotType: 'permutations' as const, - configuration: { width }, - }, - { - description: `customizable-footer at ${width}px`, - path: 'attribute-editor/customizable-footer', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - { - description: `with long select at ${width}px`, - path: 'attribute-editor/select-with-long-value', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - ]), -}; - -export default suite; diff --git a/test/definitions/visual/autosuggest.ts b/test/definitions/visual/autosuggest.ts deleted file mode 100644 index 92f4d46897..0000000000 --- a/test/definitions/visual/autosuggest.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import createWrapper from '../../../lib/components/test-utils/selectors'; -import { TestDefinition, TestSuite } from '../types'; - -const wrapper = createWrapper(); - -const suite: TestSuite = { - description: 'Autosuggest', - componentName: 'autosuggest', - tests: [ - { - description: 'permutations', - path: 'autosuggest/permutations', - screenshotType: 'permutations', - setup: async page => { - await page.click('input'); - }, - }, - { - description: 'permutations for async properties', - path: 'autosuggest/permutations-async', - screenshotType: 'permutations', - setup: async page => { - await page.click('input'); - }, - }, - { - description: 'Displays options with groups correctly', - path: 'autosuggest/scenarios', - screenshotType: 'screenshotArea', - setup: async page => { - await page.click('input'); - }, - }, - { - description: 'Correctly displays dropdown regions', - path: 'autosuggest/regions-scenarios', - screenshotType: 'screenshotArea', - setup: async page => { - await page.click('input'); - }, - }, - { - description: 'Long virtual list - navigate to last item', - path: 'autosuggest/virtual-scroll', - screenshotType: 'screenshotArea', - setup: async page => { - await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); - await page.keys(['ArrowUp']); - }, - }, - ...[true, false].map( - virtualScroll => - ({ - description: `with custom renderOption (virtualScroll=${virtualScroll})`, - path: 'autosuggest/custom-render-option', - screenshotType: 'screenshotArea' as const, - queryParams: { virtualScroll: String(virtualScroll) }, - setup: async page => { - await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); - await page.keys(['ArrowDown']); - }, - }) as TestDefinition - ), - ], -}; - -export default suite; diff --git a/test/definitions/visual/badge.ts b/test/definitions/visual/badge.ts deleted file mode 100644 index 7d405ed843..0000000000 --- a/test/definitions/visual/badge.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Badge', - componentName: 'badge', - tests: [ - { - description: 'permutation page', - path: 'badge/permutations', - screenshotType: 'permutations', - }, - { - description: 'style custom page', - path: 'badge/style-custom-types', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; From f37634cc9f13482cb7a74949e6a84829d46d84ee Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 9 Jun 2026 00:14:58 +0200 Subject: [PATCH 083/135] Reduce to 1 shard --- .github/workflows/visual-regression.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 977c6cd96d..cca8a2bf97 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -83,7 +83,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + shard: [1] steps: - uses: actions/checkout@v4 @@ -93,7 +93,7 @@ jobs: node-version: 20 cache: npm - - name: Setup Chrome and ChromeDriver + - name: Set up Chrome and ChromeDriver uses: browser-actions/setup-chrome@v1 with: chrome-version: stable @@ -138,7 +138,7 @@ jobs: run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/10 + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/1 env: TZ: UTC From 5b6e7c47d4435550c0ad7e8d01dd59b619ff26fa Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 11 Jun 2026 09:40:21 +0200 Subject: [PATCH 084/135] Do not generate visual test files --- .github/workflows/visual-regression.yml | 3 - .gitignore | 2 - build-tools/visual/generate-tests.js | 106 ------------------------ test/definitions/index.ts | 10 +++ test/visual/action-card.test.ts | 7 ++ test/visual/alert.test.ts | 7 ++ 6 files changed, 24 insertions(+), 111 deletions(-) delete mode 100644 build-tools/visual/generate-tests.js create mode 100644 test/definitions/index.ts create mode 100644 test/visual/action-card.test.ts create mode 100644 test/visual/alert.test.ts diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index cca8a2bf97..b7eddf67d4 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -125,9 +125,6 @@ jobs: # ── Run tests ───────────────────────────────────────────────────────── - - name: Generate visual test files - run: node build-tools/visual/generate-tests.js - - name: Start test server (port 8080) run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-default & diff --git a/.gitignore b/.gitignore index 1f46343d58..9b14cf8ced 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ coverage lib # generated sources src/index.ts -test/visual -test/definitions/index.ts allure-results allure-report src/test-utils/dom/index.ts diff --git a/build-tools/visual/generate-tests.js b/build-tools/visual/generate-tests.js deleted file mode 100644 index f82b54d037..0000000000 --- a/build-tools/visual/generate-tests.js +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/** - * Auto-generates: - * 1. test/visual/*.test.ts — one test runner per definition file (for Jest sharding) - * 2. test/definitions/index.ts — barrel that exports allSuites from all definitions - * - * Each definition file that exports a default TestSuite gets included. - * Helper files (those without a default export, e.g. shared utils like - * app-layout-responsive-tests.ts) are skipped. - * - * Run this script before executing the visual test suite: - * node build-tools/visual/generate-tests.js - */ -const fs = require('fs'); -const path = require('path'); - -const definitionsDir = path.resolve(__dirname, '../../test/definitions/visual'); -const testOutputDir = path.resolve(__dirname, '../../test/visual'); -const indexOutputPath = path.resolve(__dirname, '../../test/definitions/index.ts'); - -// Files that are shared helpers (export named functions, not a default suite). -const HELPER_SUFFIXES = ['-tests']; - -function isHelperFile(basename) { - return HELPER_SUFFIXES.some(suffix => basename.endsWith(suffix)); -} - -function toCamelCase(basename) { - return basename.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase()); -} - -function generate() { - // Ensure output directory exists - if (!fs.existsSync(testOutputDir)) { - fs.mkdirSync(testOutputDir, { recursive: true }); - } - - const files = fs.readdirSync(definitionsDir).filter(f => f.endsWith('.ts') && !f.endsWith('.d.ts')); - - const suiteFiles = []; - - for (const file of files) { - const basename = file.replace(/\.ts$/, ''); - - if (isHelperFile(basename)) { - continue; - } - - // Verify the file has a default export by scanning for the pattern - const content = fs.readFileSync(path.join(definitionsDir, file), 'utf-8'); - if (!content.includes('export default')) { - continue; - } - - suiteFiles.push(basename); - - // Generate test/visual/.test.ts - const testContent = [ - '// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.', - '// SPDX-License-Identifier: Apache-2.0', - '// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually.', - `import { runTestSuites } from '../definitions/utils';`, - `import suite from '../definitions/visual/${basename}';`, - '', - 'runTestSuites([suite]);', - '', - ].join('\n'); - - fs.writeFileSync(path.join(testOutputDir, `${basename}.test.ts`), testContent); - } - - // Generate test/definitions/index.ts - // Sort by module path for consistent import ordering - suiteFiles.sort(); - - const imports = suiteFiles.map(basename => { - const varName = toCamelCase(basename); - return `import ${varName} from './visual/${basename}';`; - }); - - const arrayEntries = suiteFiles.map(basename => ` ${toCamelCase(basename)},`); - - const indexContent = [ - '// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.', - '// SPDX-License-Identifier: Apache-2.0', - '// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually.', - '', - `import { TestSuite } from './types';`, - `export { TestSuite, TestDefinition, ScreenshotType, ScreenshotTestConfiguration } from './types';`, - ...imports, - '', - 'export const allSuites: TestSuite[] = [', - ...arrayEntries, - '];', - '', - ].join('\n'); - - fs.writeFileSync(indexOutputPath, indexContent); - - console.log(`Generated ${suiteFiles.length} visual test files in test/visual/`); - console.log(`Generated test/definitions/index.ts with ${suiteFiles.length} suites`); -} - -generate(); diff --git a/test/definitions/index.ts b/test/definitions/index.ts new file mode 100644 index 0000000000..67970b0ab6 --- /dev/null +++ b/test/definitions/index.ts @@ -0,0 +1,10 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually. + +import { TestSuite } from './types'; +export { TestSuite, TestDefinition, ScreenshotType, ScreenshotTestConfiguration } from './types'; +import actionCard from './visual/action-card'; +import alert from './visual/alert'; + +export const allSuites: TestSuite[] = [actionCard, alert]; diff --git a/test/visual/action-card.test.ts b/test/visual/action-card.test.ts new file mode 100644 index 0000000000..9e62b89ebf --- /dev/null +++ b/test/visual/action-card.test.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually. +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/action-card'; + +runTestSuites([suite]); diff --git a/test/visual/alert.test.ts b/test/visual/alert.test.ts new file mode 100644 index 0000000000..95af4acc78 --- /dev/null +++ b/test/visual/alert.test.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually. +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/alert'; + +runTestSuites([suite]); From 6af11438d2e10dd020dfc6f63f24f816a9526684 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 11 Jun 2026 09:43:57 +0200 Subject: [PATCH 085/135] Attach images correctly --- test/definitions/utils.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 1bffb732df..121bae94a6 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { attachment, ContentType } from 'allure-js-commons'; +import { attachment } from 'allure-js-commons'; import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; @@ -32,18 +32,24 @@ function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinit } /** - * Attaches visual diff images (new, baseline, diff) to the Allure report - * via the allure-js-commons runtime API. + * Attaches a visual comparison to the Allure report using the built-in image diff viewer. + * Uses the `application/vnd.allure.image.diff` content type which Allure renders + * as a side-by-side/overlay comparison widget. */ async function attachDiffImages( result: { firstImage: Buffer; secondImage: Buffer; diffImage: Buffer | null }, testName: string ): Promise { - await attachment(`${testName} — new (PR)`, result.firstImage, ContentType.PNG); - await attachment(`${testName} — baseline (main)`, result.secondImage, ContentType.PNG); - if (result.diffImage) { - await attachment(`${testName} — diff`, result.diffImage, ContentType.PNG); - } + const diffPayload = JSON.stringify({ + expected: `data:image/png;base64,${result.secondImage.toString('base64')}`, + actual: `data:image/png;base64,${result.firstImage.toString('base64')}`, + diff: result.diffImage ? `data:image/png;base64,${result.diffImage.toString('base64')}` : undefined, + }); + + await attachment(testName, diffPayload, { + contentType: 'application/vnd.allure.image.diff', + fileExtension: 'imagediff', + } as any); } /** From d39c0e2dc49c251872c9874e2493dade17ec5862 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 11 Jun 2026 11:26:49 +0200 Subject: [PATCH 086/135] Always attach image to report --- test/definitions/utils.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 121bae94a6..002ae103da 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -52,6 +52,15 @@ async function attachDiffImages( } as any); } +/** + * Attaches a single screenshot to the Allure report for passing tests + * where both hosts produced identical images. + */ +async function attachScreenshot(rawBase64: string, testName: string): Promise { + const imageBuffer = Buffer.from(rawBase64, 'base64'); + await attachment(testName, imageBuffer, 'image/png'); +} + /** * Registers all test suites with a single shared browser session per worker. * This avoids the per-test session creation overhead. @@ -146,6 +155,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro // to be the same. This skips the expensive crop + pixelmatch decode path // for the common case (no visual difference). if (newCapture.rawBase64 === oldCapture.rawBase64) { + await attachScreenshot(newCapture.rawBase64, testDef.description); return; } @@ -153,6 +163,9 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro // Fall through to full pixel comparison. const result = await cropAndCompare(await newCapture.screenshot(), await oldCapture.screenshot()); + // Always attach the comparison for visibility in the report. + await attachDiffImages(result, testDef.description); + if (result.diffPixels === 0) { return; } @@ -193,9 +206,6 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro return; } - // Attach diff images to Allure report for visual inspection. - await attachDiffImages(result, testDef.description); - // For screenshotArea and viewport types, the diff is a real failure. expect(result.diffPixels).toBe(0); }); From 9c95fe33bdfde8619f1c2326496d1901b600e88d Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 11 Jun 2026 12:40:31 +0200 Subject: [PATCH 087/135] Make sure all permutations are compared --- test/definitions/types.ts | 6 +- test/definitions/utils.ts | 120 ++++++++++---------------------------- 2 files changed, 35 insertions(+), 91 deletions(-) diff --git a/test/definitions/types.ts b/test/definitions/types.ts index 9f8a187c48..adaaf9f23c 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -2,6 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import type { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; +import type createWrapper from '../../lib/components/test-utils/selectors'; + +export type Wrapper = ReturnType; + export interface ScreenshotTestConfiguration { width?: number; height?: number; @@ -17,7 +21,7 @@ export interface TestDefinition { screenshotType: ScreenshotType; queryParams?: Record; configuration?: ScreenshotTestConfiguration; - setup?: (page: ScreenshotPageObject) => Promise; + setup?: (page: ScreenshotPageObject, wrapper: Wrapper) => Promise; } export interface TestSuite { diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 002ae103da..446de85c17 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -2,9 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { attachment } from 'allure-js-commons'; -import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; +import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; +import createWrapper from '../../lib/components/test-utils/selectors'; import { TestDefinition, TestSuite } from './types'; const screenshotAreaSelector = '.screenshot-area'; @@ -14,12 +15,7 @@ const defaultWindowSize = { width: 1600, height: 800 }; const newHost = process.env.NEW_HOST || 'http://localhost:8080'; const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; -interface RawCapture { - /** The raw base64-encoded PNG string from WebDriver (before decoding). */ - rawBase64: string; - /** The fully parsed screenshot with offset metadata (lazily resolved). */ - screenshot: () => Promise; -} +const wrapper = createWrapper(); function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams(queryParams); @@ -33,8 +29,6 @@ function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinit /** * Attaches a visual comparison to the Allure report using the built-in image diff viewer. - * Uses the `application/vnd.allure.image.diff` content type which Allure renders - * as a side-by-side/overlay comparison widget. */ async function attachDiffImages( result: { firstImage: Buffer; secondImage: Buffer; diffImage: Buffer | null }, @@ -52,15 +46,6 @@ async function attachDiffImages( } as any); } -/** - * Attaches a single screenshot to the Allure report for passing tests - * where both hosts produced identical images. - */ -async function attachScreenshot(rawBase64: string, testName: string): Promise { - const imageBuffer = Buffer.from(rawBase64, 'base64'); - await attachment(testName, imageBuffer, 'image/png'); -} - /** * Registers all test suites with a single shared browser session per worker. * This avoids the per-test session creation overhead. @@ -96,47 +81,34 @@ function registerSuites(suites: Array, getBrowser: ( } /** - * Captures a screenshot and returns both the raw PNG base64 and the parsed result. - * Having the raw base64 allows a fast byte-equality check before expensive pixel decoding. + * Navigates to a URL, waits for the screenshot area, and runs any setup interactions. */ -async function captureRaw( +async function preparePage( browser: WebdriverIO.Browser, page: ScreenshotPageObject, url: string, testDef: TestDefinition, windowSize: { width: number; height: number } | undefined -): Promise { +): Promise { if (windowSize) { await browser.setWindowSize(windowSize.width, windowSize.height); } await browser.url(url); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { - await testDef.setup(page); + await testDef.setup(page, wrapper); } +} +/** + * Captures a screenshot based on the test's screenshotType using the + * ScreenshotPageObject methods from browser-test-tools. + */ +function capture(page: ScreenshotPageObject, testDef: TestDefinition): Promise { if (testDef.screenshotType === 'viewport') { - const { height, width } = await page.getViewportSize(); - const rawBase64 = await browser.takeScreenshot(); - return { - rawBase64, - screenshot: async () => { - const image = await parsePng(rawBase64); - return { image, offset: { top: 0, left: 0 }, height, width }; - }, - }; + return page.captureViewport(); } - - // screenshotArea / permutations — capture by selector with viewportOnly - const box = await page.getBoundingBox(screenshotAreaSelector); - const rawBase64 = await browser.takeScreenshot(); - return { - rawBase64, - screenshot: async () => { - const image = await parsePng(rawBase64); - return { image, offset: { top: box.top, left: box.left }, height: box.height, width: box.width }; - }, - }; + return page.captureBySelector(screenshotAreaSelector, { viewportOnly: true }); } function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { @@ -148,65 +120,33 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - const newCapture = await captureRaw(browser, page, newUrl, testDef, windowSize); - const oldCapture = await captureRaw(browser, page, oldUrl, testDef, windowSize); - - // Fast path: if the raw PNG bytes are identical, the images are guaranteed - // to be the same. This skips the expensive crop + pixelmatch decode path - // for the common case (no visual difference). - if (newCapture.rawBase64 === oldCapture.rawBase64) { - await attachScreenshot(newCapture.rawBase64, testDef.description); - return; - } + await preparePage(browser, page, newUrl, testDef, windowSize); + const newScreenshot = await capture(page, testDef); - // Raw bytes differ — could be a real diff or just offset/crop differences. - // Fall through to full pixel comparison. - const result = await cropAndCompare(await newCapture.screenshot(), await oldCapture.screenshot()); + await preparePage(browser, page, oldUrl, testDef, windowSize); + const oldScreenshot = await capture(page, testDef); - // Always attach the comparison for visibility in the report. - await attachDiffImages(result, testDef.description); - - if (result.diffPixels === 0) { - return; - } - - // For permutations pages, a screenshot-area diff might be a false positive - // caused by content extending beyond the viewport. Re-capture using the - // full capturePermutations strategy which resizes the window to fit all - // content and returns individual permutation crops for precise comparison. if (testDef.screenshotType === 'permutations') { - if (windowSize) { - await browser.setWindowSize(windowSize.width, windowSize.height); - } - await browser.url(newUrl); - await page.waitForVisible(screenshotAreaSelector); - if (testDef.setup) { - await testDef.setup(page); - } + await preparePage(browser, page, newUrl, testDef, windowSize); const newPermutations = await page.capturePermutations(); - if (windowSize) { - await browser.setWindowSize(windowSize.width, windowSize.height); - } - await browser.url(oldUrl); - await page.waitForVisible(screenshotAreaSelector); - if (testDef.setup) { - await testDef.setup(page); - } + await preparePage(browser, page, oldUrl, testDef, windowSize); const oldPermutations = await page.capturePermutations(); expect(newPermutations.length).toBe(oldPermutations.length); for (let i = 0; i < newPermutations.length; i++) { const permResult = await cropAndCompare(newPermutations[i], oldPermutations[i]); - if (permResult.diffPixels !== 0) { - await attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`); - } + await attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`); expect(permResult.diffPixels).toBe(0); } - return; - } + } else { + const result = await cropAndCompare(newScreenshot, oldScreenshot); - // For screenshotArea and viewport types, the diff is a real failure. - expect(result.diffPixels).toBe(0); + // Attach comparison to Allure report for visibility (pass or fail). + await attachDiffImages(result, testDef.description); + + // For screenshotArea and viewport types, the diff is a real failure. + expect(result.diffPixels).toBe(0); + } }); } From a36a79d0be09a76e250b3a90e8e63dd3a42ad72a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 12 Jun 2026 10:32:28 +0200 Subject: [PATCH 088/135] Adjust window size correctly --- test/definitions/utils.ts | 2 +- test/definitions/visual/alert.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 446de85c17..2d842bc099 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -9,7 +9,7 @@ import createWrapper from '../../lib/components/test-utils/selectors'; import { TestDefinition, TestSuite } from './types'; const screenshotAreaSelector = '.screenshot-area'; -const defaultWindowSize = { width: 1600, height: 800 }; +const defaultWindowSize = { width: 1200, height: 800 }; // NEW_HOST serves the PR's pages, OLD_HOST serves the baseline (main) pages. const newHost = process.env.NEW_HOST || 'http://localhost:8080'; diff --git a/test/definitions/visual/alert.ts b/test/definitions/visual/alert.ts index 3220c79e7e..a4169741e3 100644 --- a/test/definitions/visual/alert.ts +++ b/test/definitions/visual/alert.ts @@ -21,11 +21,13 @@ const suite: TestSuite = { description: `width ${width}px`, tests: [ { + configuration: { width }, description: 'permutations', path: 'alert/permutations', screenshotType: 'permutations' as const, }, { + configuration: { width }, description: 'custom types', path: 'alert/style-custom-types', screenshotType: 'screenshotArea' as const, From d854993e8ad1d10e078f4222cf2d07b4d9ea2929 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 12 Jun 2026 11:17:12 +0200 Subject: [PATCH 089/135] Attach permutation images in bulk --- test/definitions/utils.ts | 41 +++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 2d842bc099..cdd40a064c 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -101,8 +101,7 @@ async function preparePage( } /** - * Captures a screenshot based on the test's screenshotType using the - * ScreenshotPageObject methods from browser-test-tools. + * Captures a screenshot based on the test's screenshotType. */ function capture(page: ScreenshotPageObject, testDef: TestDefinition): Promise { if (testDef.screenshotType === 'viewport') { @@ -120,12 +119,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - await preparePage(browser, page, newUrl, testDef, windowSize); - const newScreenshot = await capture(page, testDef); - - await preparePage(browser, page, oldUrl, testDef, windowSize); - const oldScreenshot = await capture(page, testDef); - + // For permutations, go directly to capturePermutations (no extra navigation). if (testDef.screenshotType === 'permutations') { await preparePage(browser, page, newUrl, testDef, windowSize); const newPermutations = await page.capturePermutations(); @@ -134,19 +128,32 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const oldPermutations = await page.capturePermutations(); expect(newPermutations.length).toBe(oldPermutations.length); + const permFailures: number[] = []; + const attachmentPromises: Promise[] = []; for (let i = 0; i < newPermutations.length; i++) { const permResult = await cropAndCompare(newPermutations[i], oldPermutations[i]); - await attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`); - expect(permResult.diffPixels).toBe(0); + attachmentPromises.push(attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`)); + if (permResult.diffPixels !== 0) { + permFailures.push(i); + } } - } else { - const result = await cropAndCompare(newScreenshot, oldScreenshot); + await Promise.all(attachmentPromises); + expect(permFailures).toEqual([]); + return; + } - // Attach comparison to Allure report for visibility (pass or fail). - await attachDiffImages(result, testDef.description); + // For screenshotArea and viewport: capture from both hosts and compare. + await preparePage(browser, page, newUrl, testDef, windowSize); + const newScreenshot = await capture(page, testDef); - // For screenshotArea and viewport types, the diff is a real failure. - expect(result.diffPixels).toBe(0); - } + await preparePage(browser, page, oldUrl, testDef, windowSize); + const oldScreenshot = await capture(page, testDef); + + const result = await cropAndCompare(newScreenshot, oldScreenshot); + + // Always attach for visibility in the Allure report. + await attachDiffImages(result, testDef.description); + + expect(result.diffPixels).toBe(0); }); } From 477664c174691c21610951b09b4c1419ac2ecd6a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 12 Jun 2026 15:58:59 +0200 Subject: [PATCH 090/135] Refine permutation test descriptions --- test/definitions/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index cdd40a064c..f54930c358 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -132,7 +132,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const attachmentPromises: Promise[] = []; for (let i = 0; i < newPermutations.length; i++) { const permResult = await cropAndCompare(newPermutations[i], oldPermutations[i]); - attachmentPromises.push(attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`)); + attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i + 1}`)); if (permResult.diffPixels !== 0) { permFailures.push(i); } From 07e504fc9a7a0d033653a4e77846baf619f35fea Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 12 Jun 2026 16:07:58 +0200 Subject: [PATCH 091/135] Refine setting of window size --- test/definitions/utils.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index f54930c358..f4e9cf4cc2 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -88,11 +88,12 @@ async function preparePage( page: ScreenshotPageObject, url: string, testDef: TestDefinition, - windowSize: { width: number; height: number } | undefined + windowSize?: { width?: number; height?: number } ): Promise { - if (windowSize) { - await browser.setWindowSize(windowSize.width, windowSize.height); - } + await browser.setWindowSize( + windowSize?.width ?? defaultWindowSize.width, + windowSize?.height ?? defaultWindowSize.height + ); await browser.url(url); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { @@ -113,7 +114,6 @@ function capture(page: ScreenshotPageObject, testDef: TestDefinition): Promise WebdriverIO.Browser) { test(testDef.description, async () => { const browser = getBrowser(); - const windowSize = testDef.configuration ? { ...defaultWindowSize, ...testDef.configuration } : undefined; const page = new ScreenshotPageObject(browser); const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); @@ -121,10 +121,10 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro // For permutations, go directly to capturePermutations (no extra navigation). if (testDef.screenshotType === 'permutations') { - await preparePage(browser, page, newUrl, testDef, windowSize); + await preparePage(browser, page, newUrl, testDef, testDef.configuration); const newPermutations = await page.capturePermutations(); - await preparePage(browser, page, oldUrl, testDef, windowSize); + await preparePage(browser, page, oldUrl, testDef, testDef.configuration); const oldPermutations = await page.capturePermutations(); expect(newPermutations.length).toBe(oldPermutations.length); @@ -143,10 +143,10 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro } // For screenshotArea and viewport: capture from both hosts and compare. - await preparePage(browser, page, newUrl, testDef, windowSize); + await preparePage(browser, page, newUrl, testDef, testDef.configuration); const newScreenshot = await capture(page, testDef); - await preparePage(browser, page, oldUrl, testDef, windowSize); + await preparePage(browser, page, oldUrl, testDef, testDef.configuration); const oldScreenshot = await capture(page, testDef); const result = await cropAndCompare(newScreenshot, oldScreenshot); From b7810bbfe8083f2b4fdfbf61346ecdc413aaeae2 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 12 Jun 2026 20:37:41 +0200 Subject: [PATCH 092/135] Disable motion --- test/definitions/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index f4e9cf4cc2..84b9919c3e 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -94,7 +94,8 @@ async function preparePage( windowSize?.width ?? defaultWindowSize.width, windowSize?.height ?? defaultWindowSize.height ); - await browser.url(url); + const params = new URLSearchParams({ motionDisabled: 'true' }); + await browser.url(`${url}?${params.toString()}`); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { await testDef.setup(page, wrapper); From 612c136b32259a8ef348575a31e4521671c81155 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 12 Jun 2026 20:39:05 +0200 Subject: [PATCH 093/135] Do not capture only viewport by default --- test/definitions/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 84b9919c3e..6d0c15cdfe 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -109,7 +109,7 @@ function capture(page: ScreenshotPageObject, testDef: TestDefinition): Promise WebdriverIO.Browser) { From 2ffba7963431441b7e060458f3849ad41c11b84e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 12 Jun 2026 20:42:25 +0200 Subject: [PATCH 094/135] Optimize permutation comparison --- test/definitions/utils.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 6d0c15cdfe..862e1834d9 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -120,8 +120,18 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - // For permutations, go directly to capturePermutations (no extra navigation). + await preparePage(browser, page, newUrl, testDef, testDef.configuration); + const newScreenshot = await capture(page, testDef); + + await preparePage(browser, page, oldUrl, testDef, testDef.configuration); + const oldScreenshot = await capture(page, testDef); + + const result = await cropAndCompare(newScreenshot, oldScreenshot); + if (testDef.screenshotType === 'permutations') { + if (result.diffPixels === 0) { + return; + } await preparePage(browser, page, newUrl, testDef, testDef.configuration); const newPermutations = await page.capturePermutations(); @@ -143,15 +153,6 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro return; } - // For screenshotArea and viewport: capture from both hosts and compare. - await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newScreenshot = await capture(page, testDef); - - await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldScreenshot = await capture(page, testDef); - - const result = await cropAndCompare(newScreenshot, oldScreenshot); - // Always attach for visibility in the Allure report. await attachDiffImages(result, testDef.description); From e0e27d295106c4cff5a3c9d14fb8502f073428be Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 00:22:22 +0200 Subject: [PATCH 095/135] Include browser in Testdefinition setup callback --- package-lock.json | 1439 +++---------------------------------- package.json | 1 + test/definitions/types.ts | 12 +- test/definitions/utils.ts | 2 +- 4 files changed, 107 insertions(+), 1347 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ddd0880a8..5c7afe6633 100644 --- a/package-lock.json +++ b/package-lock.json @@ -127,6 +127,7 @@ "typescript": "^5.9.2", "typescript-eslint": "^8.44.0", "wait-on": "^8.0.2", + "webdriverio": "^9.28.0", "webpack": "^5.105.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.2.5" @@ -142,8 +143,6 @@ }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -158,8 +157,6 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -171,8 +168,6 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -185,8 +180,6 @@ }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -199,8 +192,6 @@ }, "node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -214,8 +205,6 @@ }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -224,8 +213,6 @@ }, "node_modules/@aws-crypto/util": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -236,8 +223,6 @@ }, "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -249,8 +234,6 @@ }, "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -263,8 +246,6 @@ }, "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -277,8 +258,6 @@ }, "node_modules/@aws-sdk/client-device-farm": { "version": "3.1014.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-device-farm/-/client-device-farm-3.1014.0.tgz", - "integrity": "sha512-BZAXBk6FZn4g1cQ4/MEtHnwM6d52fKN0i4AlKd1+QDzU0w2iLtEfBiB0BmRWAisRcR3oLvNQ6/mmxIWFNu1b0g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -328,8 +307,6 @@ }, "node_modules/@aws-sdk/core": { "version": "3.973.23", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.23.tgz", - "integrity": "sha512-aoJncvD1XvloZ9JLnKqTRL9dBy+Szkryoag9VT+V1TqsuUgIxV9cnBVM/hrDi2vE8bDqLiDR8nirdRcCdtJu0w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -353,8 +330,6 @@ }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.972.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.21.tgz", - "integrity": "sha512-BkAfKq8Bd4shCtec1usNz//urPJF/SZy14qJyxkSaRJQ/Vv1gVh0VZSTmS7aE6aLMELkFV5wHHrS9ZcdG8Kxsg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -370,8 +345,6 @@ }, "node_modules/@aws-sdk/credential-provider-http": { "version": "3.972.23", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.23.tgz", - "integrity": "sha512-4XZ3+Gu5DY8/n8zQFHBgcKTF7hWQl42G6CY9xfXVo2d25FM/lYkpmuzhYopYoPL1ITWkJ2OSBQfYEu5JRfHOhA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -392,8 +365,6 @@ }, "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.972.23", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.23.tgz", - "integrity": "sha512-PZLSmU0JFpNCDFReidBezsgL5ji9jOBry8CnZdw4Jj6d0K2z3Ftnp44NXgADqYx5BLMu/ZHujfeJReaDoV+IwQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -418,8 +389,6 @@ }, "node_modules/@aws-sdk/credential-provider-login": { "version": "3.972.23", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.23.tgz", - "integrity": "sha512-OmE/pSkbMM3dCj1HdOnZ5kXnKK+R/Yz+kbBugraBecp0pGAs21eEURfQRz+1N2gzIHLVyGIP1MEjk/uSrFsngg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -438,8 +407,6 @@ }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.972.24", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.24.tgz", - "integrity": "sha512-9Jwi7aps3AfUicJyF5udYadPypPpCwUZ6BSKr/QjRbVCpRVS1wc+1Q6AEZ/qz8J4JraeRd247pSzyMQSIHVebw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -462,8 +429,6 @@ }, "node_modules/@aws-sdk/credential-provider-process": { "version": "3.972.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.21.tgz", - "integrity": "sha512-nRxbeOJ1E1gVA0lNQezuMVndx+ZcuyaW/RB05pUsznN5BxykSlH6KkZ/7Ca/ubJf3i5N3p0gwNO5zgPSCzj+ww==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -480,8 +445,6 @@ }, "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.972.23", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.23.tgz", - "integrity": "sha512-APUccADuYPLL0f2htpM8Z4czabSmHOdo4r41W6lKEZdy++cNJ42Radqy6x4TopENzr3hR6WYMyhiuiqtbf/nAA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -500,8 +463,6 @@ }, "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.972.23", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.23.tgz", - "integrity": "sha512-H5JNqtIwOu/feInmMMWcK0dL5r897ReEn7n2m16Dd0DPD9gA2Hg8Cq4UDzZ/9OzaLh/uqBM6seixz0U6Fi2Eag==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -519,8 +480,6 @@ }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.8.tgz", - "integrity": "sha512-wAr2REfKsqoKQ+OkNqvOShnBoh+nkPurDKW7uAeVSu6kUECnWlSJiPvnoqxGlfousEY/v9LfS9sNc46hjSYDIQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -535,8 +494,6 @@ }, "node_modules/@aws-sdk/middleware-logger": { "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.8.tgz", - "integrity": "sha512-CWl5UCM57WUFaFi5kB7IBY1UmOeLvNZAZ2/OZ5l20ldiJ3TiIz1pC65gYj8X0BCPWkeR1E32mpsCk1L1I4n+lA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -550,8 +507,6 @@ }, "node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.8.tgz", - "integrity": "sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -567,8 +522,6 @@ }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.972.24", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.24.tgz", - "integrity": "sha512-dLTWy6IfAMhNiSEvMr07g/qZ54be6pLqlxVblbF6AzafmmGAzMMj8qMoY9B4+YgT+gY9IcuxZslNh03L6PyMCQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -587,8 +540,6 @@ }, "node_modules/@aws-sdk/nested-clients": { "version": "3.996.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.13.tgz", - "integrity": "sha512-ptZ1HF4yYHNJX8cgFF+8NdYO69XJKZn7ft0/ynV3c0hCbN+89fAbrLS+fqniU2tW8o9Kfqhj8FUh+IPXb2Qsuw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -637,8 +588,6 @@ }, "node_modules/@aws-sdk/region-config-resolver": { "version": "3.972.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.9.tgz", - "integrity": "sha512-eQ+dFU05ZRC/lC2XpYlYSPlXtX3VT8sn5toxN2Fv7EXlMoA2p9V7vUBKqHunfD4TRLpxUq8Y8Ol/nCqiv327Ng==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -654,8 +603,6 @@ }, "node_modules/@aws-sdk/token-providers": { "version": "3.1014.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1014.0.tgz", - "integrity": "sha512-gHTHNUoaOGNrSWkl32A7wFsU78jlNTlqMccLu0byUk5CysYYXaxNMIonIVr4YcykC7vgtDS5ABuz83giy6fzJA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -673,8 +620,6 @@ }, "node_modules/@aws-sdk/types": { "version": "3.973.6", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.6.tgz", - "integrity": "sha512-Atfcy4E++beKtwJHiDln2Nby8W/mam64opFPTiHEqgsthqeydFS1pY+OUlN1ouNOmf8ArPU/6cDS65anOP3KQw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -687,8 +632,6 @@ }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.996.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.5.tgz", - "integrity": "sha512-Uh93L5sXFNbyR5sEPMzUU8tJ++Ku97EY4udmC01nB8Zu+xfBPwpIwJ6F7snqQeq8h2pf+8SGN5/NoytfKgYPIw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -704,8 +647,6 @@ }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.965.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", - "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -717,8 +658,6 @@ }, "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.8.tgz", - "integrity": "sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -730,8 +669,6 @@ }, "node_modules/@aws-sdk/util-user-agent-node": { "version": "3.973.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.10.tgz", - "integrity": "sha512-E99zeTscCc+pTMfsvnfi6foPpKmdD1cZfOC7/P8UUrjsoQdg9VEWPRD+xdFduKnfPXwcvby58AlO9jwwF6U96g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -756,8 +693,6 @@ }, "node_modules/@aws-sdk/xml-builder": { "version": "3.972.21", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.21.tgz", - "integrity": "sha512-qxNiHUtlrsjTeSlrPWiFkWps7uD6YB4eKzg7eLAFH8jbiHTlt0ePNlo2Xu+WlftP38JIcMaIX4jTUjOlE2ySWw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -772,8 +707,6 @@ }, "node_modules/@aws/lambda-invoke-store": { "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", - "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -806,21 +739,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.6.tgz", - "integrity": "sha512-QdxmAo/ikZqqRGA8s43ww8lcql6naWRvEz0FFrl6MIlc7Gi6TroXnSdWa5U/kq6fzcpqpHesicQxFZIieZbyIA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.6", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.29.2", - "@babel/parser": "^7.29.3", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -1841,8 +1774,6 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1860,8 +1791,6 @@ }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -1870,8 +1799,6 @@ }, "node_modules/@eslint/compat": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-1.4.1.tgz", - "integrity": "sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1891,8 +1818,6 @@ }, "node_modules/@eslint/config-array": { "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1906,8 +1831,6 @@ }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -1919,8 +1842,6 @@ }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1932,8 +1853,6 @@ }, "node_modules/@eslint/core": { "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1945,8 +1864,6 @@ }, "node_modules/@eslint/eslintrc": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1969,15 +1886,11 @@ }, "node_modules/@eslint/eslintrc/node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1989,8 +1902,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/espree": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2007,8 +1918,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { @@ -2020,8 +1929,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -2029,20 +1936,8 @@ } }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.1.1", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -2053,8 +1948,6 @@ }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -2066,8 +1959,6 @@ }, "node_modules/@eslint/js": { "version": "9.39.3", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.3.tgz", - "integrity": "sha512-1B1VkCq6FuUNlQvlBYb+1jDu/gV297TIs/OeiaSR9l1H27SVW55ONE1e1Vp16NqP683+xEGzxYtv4XCiDPaQiw==", "dev": true, "license": "MIT", "engines": { @@ -2079,8 +1970,6 @@ }, "node_modules/@eslint/object-schema": { "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2089,9 +1978,8 @@ }, "node_modules/@eslint/plugin-kit": { "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" @@ -2102,9 +1990,8 @@ }, "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -2172,58 +2059,17 @@ "node": ">=10.13.0" } }, - "node_modules/@hapi/address": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", - "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@hapi/hoek": "^11.0.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@hapi/formula": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", - "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/@hapi/hoek": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", - "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/pinpoint": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", - "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==", + "version": "9.3.0", "dev": true, "license": "BSD-3-Clause" }, - "node_modules/@hapi/tlds": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.7.tgz", - "integrity": "sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@hapi/topo": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", - "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "version": "5.1.0", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@hapi/hoek": "^11.0.2" + "@hapi/hoek": "^9.0.0" } }, "node_modules/@html-validate/stylish": { @@ -2239,8 +2085,6 @@ }, "node_modules/@humanfs/core": { "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2249,8 +2093,6 @@ }, "node_modules/@humanfs/node": { "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2263,8 +2105,6 @@ }, "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2289,8 +2129,6 @@ }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2517,8 +2355,6 @@ }, "node_modules/@jest/diff-sequences": { "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", "dev": true, "license": "MIT", "peer": true, @@ -2581,8 +2417,6 @@ }, "node_modules/@jest/get-type": { "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", "dev": true, "license": "MIT", "peer": true, @@ -2606,8 +2440,6 @@ }, "node_modules/@jest/pattern": { "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", "dev": true, "license": "MIT", "dependencies": { @@ -2620,8 +2452,6 @@ }, "node_modules/@jest/pattern/node_modules/jest-regex-util": { "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", "engines": { @@ -2749,8 +2579,6 @@ }, "node_modules/@jest/reporters/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -2946,8 +2774,6 @@ }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, @@ -3028,14 +2854,10 @@ }, "node_modules/@material/material-color-utilities": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@material/material-color-utilities/-/material-color-utilities-0.3.0.tgz", - "integrity": "sha512-ztmtTd6xwnuh2/xu+Vb01btgV8SQWYCaK56CkRK8gEkWe5TuDyBcYJ0wgkMRn+2VcE9KUmhvkz+N9GHrqw/C0g==", "license": "Apache-2.0" }, "node_modules/@noble/hashes": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "dev": true, "license": "MIT", "engines": { @@ -3047,8 +2869,6 @@ }, "node_modules/@nodable/entities": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", "dev": true, "funding": [ { @@ -3125,27 +2945,6 @@ "@parcel/watcher-win32-x64": "2.5.1" } }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", - "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@parcel/watcher-darwin-arm64": { "version": "2.5.1", "cpu": [ @@ -3165,241 +2964,8 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", - "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", - "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", - "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", - "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", - "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", - "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", - "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", - "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", - "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", - "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@peculiar/asn1-cms": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.6.1.tgz", - "integrity": "sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3412,8 +2978,6 @@ }, "node_modules/@peculiar/asn1-csr": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.6.1.tgz", - "integrity": "sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==", "dev": true, "license": "MIT", "dependencies": { @@ -3425,8 +2989,6 @@ }, "node_modules/@peculiar/asn1-ecc": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.6.1.tgz", - "integrity": "sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==", "dev": true, "license": "MIT", "dependencies": { @@ -3438,8 +3000,6 @@ }, "node_modules/@peculiar/asn1-pfx": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.6.1.tgz", - "integrity": "sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==", "dev": true, "license": "MIT", "dependencies": { @@ -3453,8 +3013,6 @@ }, "node_modules/@peculiar/asn1-pkcs8": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.6.1.tgz", - "integrity": "sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==", "dev": true, "license": "MIT", "dependencies": { @@ -3466,8 +3024,6 @@ }, "node_modules/@peculiar/asn1-pkcs9": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.6.1.tgz", - "integrity": "sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==", "dev": true, "license": "MIT", "dependencies": { @@ -3483,8 +3039,6 @@ }, "node_modules/@peculiar/asn1-rsa": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.6.1.tgz", - "integrity": "sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==", "dev": true, "license": "MIT", "dependencies": { @@ -3496,8 +3050,6 @@ }, "node_modules/@peculiar/asn1-schema": { "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.6.0.tgz", - "integrity": "sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==", "dev": true, "license": "MIT", "dependencies": { @@ -3508,8 +3060,6 @@ }, "node_modules/@peculiar/asn1-x509": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.6.1.tgz", - "integrity": "sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==", "dev": true, "license": "MIT", "dependencies": { @@ -3521,8 +3071,6 @@ }, "node_modules/@peculiar/asn1-x509-attr": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.6.1.tgz", - "integrity": "sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3534,8 +3082,6 @@ }, "node_modules/@peculiar/x509": { "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", - "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", "dev": true, "license": "MIT", "dependencies": { @@ -3577,8 +3123,6 @@ }, "node_modules/@promptbook/utils": { "version": "0.69.5", - "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.69.5.tgz", - "integrity": "sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==", "dev": true, "funding": [ { @@ -3597,8 +3141,6 @@ }, "node_modules/@puppeteer/browsers": { "version": "2.13.2", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz", - "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3619,8 +3161,6 @@ }, "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, "license": "ISC", "dependencies": { @@ -3634,8 +3174,6 @@ }, "node_modules/@puppeteer/browsers/node_modules/semver": { "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -3647,8 +3185,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -3660,8 +3196,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -3678,8 +3212,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -3744,6 +3276,24 @@ } } }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@sidvind/better-ajv-errors": { "version": "2.1.3", "dev": true, @@ -3842,8 +3392,6 @@ }, "node_modules/@smithy/abort-controller": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.12.tgz", - "integrity": "sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3856,8 +3404,6 @@ }, "node_modules/@smithy/config-resolver": { "version": "4.4.13", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.13.tgz", - "integrity": "sha512-iIzMC5NmOUP6WL6o8iPBjFhUhBZ9pPjpUpQYWMUFQqKyXXzOftbfK8zcQCz/jFV1Psmf05BK5ypx4K2r4Tnwdg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3874,8 +3420,6 @@ }, "node_modules/@smithy/core": { "version": "3.23.12", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.12.tgz", - "integrity": "sha512-o9VycsYNtgC+Dy3I0yrwCqv9CWicDnke0L7EVOrZtJpjb2t0EjaEofmMrYc0T1Kn3yk32zm6cspxF9u9Bj7e5w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3896,8 +3440,6 @@ }, "node_modules/@smithy/credential-provider-imds": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.12.tgz", - "integrity": "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3913,8 +3455,6 @@ }, "node_modules/@smithy/fetch-http-handler": { "version": "5.3.15", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.15.tgz", - "integrity": "sha512-T4jFU5N/yiIfrtrsb9uOQn7RdELdM/7HbyLNr6uO/mpkj1ctiVs7CihVr51w4LyQlXWDpXFn4BElf1WmQvZu/A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3930,8 +3470,6 @@ }, "node_modules/@smithy/hash-node": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.12.tgz", - "integrity": "sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3946,8 +3484,6 @@ }, "node_modules/@smithy/invalid-dependency": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.12.tgz", - "integrity": "sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3960,8 +3496,6 @@ }, "node_modules/@smithy/is-array-buffer": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", - "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3973,8 +3507,6 @@ }, "node_modules/@smithy/middleware-content-length": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.12.tgz", - "integrity": "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3988,8 +3520,6 @@ }, "node_modules/@smithy/middleware-endpoint": { "version": "4.4.27", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.27.tgz", - "integrity": "sha512-T3TFfUgXQlpcg+UdzcAISdZpj4Z+XECZ/cefgA6wLBd6V4lRi0svN2hBouN/be9dXQ31X4sLWz3fAQDf+nt6BA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4008,8 +3538,6 @@ }, "node_modules/@smithy/middleware-retry": { "version": "4.4.44", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.44.tgz", - "integrity": "sha512-Y1Rav7m5CFRPQyM4CI0koD/bXjyjJu3EQxZZhtLGD88WIrBrQ7kqXM96ncd6rYnojwOo/u9MXu57JrEvu/nLrA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4029,8 +3557,6 @@ }, "node_modules/@smithy/middleware-serde": { "version": "4.2.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.15.tgz", - "integrity": "sha512-ExYhcltZSli0pgAKOpQQe1DLFBLryeZ22605y/YS+mQpdNWekum9Ujb/jMKfJKgjtz1AZldtwA/wCYuKJgjjlg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4045,8 +3571,6 @@ }, "node_modules/@smithy/middleware-stack": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.12.tgz", - "integrity": "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4059,8 +3583,6 @@ }, "node_modules/@smithy/node-config-provider": { "version": "4.3.12", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.12.tgz", - "integrity": "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4075,8 +3597,6 @@ }, "node_modules/@smithy/node-http-handler": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.5.0.tgz", - "integrity": "sha512-Rnq9vQWiR1+/I6NZZMNzJHV6pZYyEHt2ZnuV3MG8z2NNenC4i/8Kzttz7CjZiHSmsN5frhXhg17z3Zqjjhmz1A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4092,8 +3612,6 @@ }, "node_modules/@smithy/property-provider": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.12.tgz", - "integrity": "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4106,8 +3624,6 @@ }, "node_modules/@smithy/protocol-http": { "version": "5.3.12", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.12.tgz", - "integrity": "sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4120,8 +3636,6 @@ }, "node_modules/@smithy/querystring-builder": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.12.tgz", - "integrity": "sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4135,8 +3649,6 @@ }, "node_modules/@smithy/querystring-parser": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.12.tgz", - "integrity": "sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4149,8 +3661,6 @@ }, "node_modules/@smithy/service-error-classification": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.12.tgz", - "integrity": "sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4162,8 +3672,6 @@ }, "node_modules/@smithy/shared-ini-file-loader": { "version": "4.4.7", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.7.tgz", - "integrity": "sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4176,8 +3684,6 @@ }, "node_modules/@smithy/signature-v4": { "version": "5.3.12", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.12.tgz", - "integrity": "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4196,8 +3702,6 @@ }, "node_modules/@smithy/smithy-client": { "version": "4.12.7", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.7.tgz", - "integrity": "sha512-q3gqnwml60G44FECaEEsdQMplYhDMZYCtYhMCzadCnRnnHIobZJjegmdoUo6ieLQlPUzvrMdIJUpx6DoPmzANQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4215,8 +3719,6 @@ }, "node_modules/@smithy/types": { "version": "4.14.1", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.1.tgz", - "integrity": "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4228,8 +3730,6 @@ }, "node_modules/@smithy/url-parser": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.12.tgz", - "integrity": "sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4243,8 +3743,6 @@ }, "node_modules/@smithy/util-base64": { "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", - "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4258,8 +3756,6 @@ }, "node_modules/@smithy/util-body-length-browser": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", - "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4271,8 +3767,6 @@ }, "node_modules/@smithy/util-body-length-node": { "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", - "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4284,8 +3778,6 @@ }, "node_modules/@smithy/util-buffer-from": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", - "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4298,8 +3790,6 @@ }, "node_modules/@smithy/util-config-provider": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", - "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4311,8 +3801,6 @@ }, "node_modules/@smithy/util-defaults-mode-browser": { "version": "4.3.43", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.43.tgz", - "integrity": "sha512-Qd/0wCKMaXxev/z00TvNzGCH2jlKKKxXP1aDxB6oKwSQthe3Og2dMhSayGCnsma1bK/kQX1+X7SMP99t6FgiiQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4327,8 +3815,6 @@ }, "node_modules/@smithy/util-defaults-mode-node": { "version": "4.2.47", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.47.tgz", - "integrity": "sha512-qSRbYp1EQ7th+sPFuVcVO05AE0QH635hycdEXlpzIahqHHf2Fyd/Zl+8v0XYMJ3cgDVPa0lkMefU7oNUjAP+DQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4346,8 +3832,6 @@ }, "node_modules/@smithy/util-endpoints": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.3.tgz", - "integrity": "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4361,8 +3845,6 @@ }, "node_modules/@smithy/util-hex-encoding": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", - "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4374,8 +3856,6 @@ }, "node_modules/@smithy/util-middleware": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.12.tgz", - "integrity": "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4388,8 +3868,6 @@ }, "node_modules/@smithy/util-retry": { "version": "4.2.12", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.12.tgz", - "integrity": "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4403,8 +3881,6 @@ }, "node_modules/@smithy/util-stream": { "version": "4.5.20", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.20.tgz", - "integrity": "sha512-4yXLm5n/B5SRBR2p8cZ90Sbv4zL4NKsgxdzCzp/83cXw2KxLEumt5p+GAVyRNZgQOSrzXn9ARpO0lUe8XSlSDw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4423,8 +3899,6 @@ }, "node_modules/@smithy/util-uri-escape": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", - "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4436,8 +3910,6 @@ }, "node_modules/@smithy/util-utf8": { "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", - "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4450,8 +3922,6 @@ }, "node_modules/@smithy/uuid": { "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", - "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4461,13 +3931,6 @@ "node": ">=18.0.0" } }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, "node_modules/@testing-library/dom": { "version": "8.20.1", "dev": true, @@ -4549,8 +4012,6 @@ }, "node_modules/@tony.ganchev/eslint-plugin-header": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@tony.ganchev/eslint-plugin-header/-/eslint-plugin-header-3.3.3.tgz", - "integrity": "sha512-neFWBw6lW5KbNDm9CUoYroczS9cxHY0+ulnRN5VMJW0hwoh/Y8XN3K17izVWY8TPLOIoDZQkr3fRkYmvCNUQEA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -4559,8 +4020,6 @@ }, "node_modules/@tootallnate/once": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz", - "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", "dev": true, "license": "MIT", "engines": { @@ -4569,8 +4028,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, "license": "MIT" }, @@ -4667,8 +4124,6 @@ }, "node_modules/@types/d3-scale": { "version": "2.2.10", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.2.10.tgz", - "integrity": "sha512-j4V7qQ+CQzK2KpvI5NDejdPAcds+fTzNGqWXrCXv1zNR33HRir0bMdhzN1OHDQLNXYffW/zOr3FOS2qlHDEgrw==", "dev": true, "license": "MIT", "dependencies": { @@ -4713,8 +4168,6 @@ }, "node_modules/@types/express": { "version": "4.17.25", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", - "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "dev": true, "license": "MIT", "dependencies": { @@ -4847,8 +4300,6 @@ }, "node_modules/@types/minimatch": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", "dev": true, "license": "MIT" }, @@ -4862,8 +4313,6 @@ }, "node_modules/@types/pixelmatch": { "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@types/pixelmatch/-/pixelmatch-5.2.6.tgz", - "integrity": "sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg==", "dev": true, "license": "MIT", "dependencies": { @@ -4913,8 +4362,6 @@ }, "node_modules/@types/react-is": { "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-zts4lhQn5ia0cF/y2+3V6Riu0MAfez9/LJYavdM8TvcVl+S91A/7VWxyBT8hbRuWspmuCaiGI0F41OJYGrKhRA==", "dev": true, "license": "MIT", "dependencies": { @@ -5036,8 +4483,6 @@ }, "node_modules/@types/which": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", - "integrity": "sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==", "dev": true, "license": "MIT" }, @@ -5064,8 +4509,6 @@ }, "node_modules/@types/yauzl": { "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, "license": "MIT", "optional": true, @@ -5075,8 +4518,6 @@ }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", - "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -5104,8 +4545,6 @@ }, "node_modules/@typescript-eslint/parser": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", - "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", "dev": true, "license": "MIT", "dependencies": { @@ -5129,8 +4568,6 @@ }, "node_modules/@typescript-eslint/project-service": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", - "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", "dev": true, "license": "MIT", "dependencies": { @@ -5151,8 +4588,6 @@ }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", - "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", "dev": true, "license": "MIT", "dependencies": { @@ -5169,8 +4604,6 @@ }, "node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", - "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", "dev": true, "license": "MIT", "engines": { @@ -5186,8 +4619,6 @@ }, "node_modules/@typescript-eslint/type-utils": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", - "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", "dev": true, "license": "MIT", "dependencies": { @@ -5211,8 +4642,6 @@ }, "node_modules/@typescript-eslint/types": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", - "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", "dev": true, "license": "MIT", "engines": { @@ -5225,8 +4654,6 @@ }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", - "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", "dev": true, "license": "MIT", "dependencies": { @@ -5253,8 +4680,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -5263,8 +4688,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { @@ -5279,8 +4702,6 @@ }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -5292,8 +4713,6 @@ }, "node_modules/@typescript-eslint/utils": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", - "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5316,8 +4735,6 @@ }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", - "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", "dev": true, "license": "MIT", "dependencies": { @@ -5334,8 +4751,6 @@ }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", - "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5347,15 +4762,11 @@ }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, "license": "ISC" }, "node_modules/@vitest/pretty-format": { "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", - "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", "dev": true, "license": "MIT", "peer": true, @@ -5368,8 +4779,6 @@ }, "node_modules/@vitest/snapshot": { "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", - "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", "dev": true, "license": "MIT", "peer": true, @@ -5384,22 +4793,18 @@ }, "node_modules/@vitest/snapshot/node_modules/pathe": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT", "peer": true }, "node_modules/@wdio/config": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.24.0.tgz", - "integrity": "sha512-rcHu0eG16rSEmHL0sEKDcr/vYFmGhQ5GOlmlx54r+1sgh6sf136q+kth4169s16XqviWGW3LjZbUfpTK29pGtw==", + "version": "9.28.0", "dev": true, "license": "MIT", "dependencies": { "@wdio/logger": "9.18.0", - "@wdio/types": "9.24.0", - "@wdio/utils": "9.24.0", + "@wdio/types": "9.28.0", + "@wdio/utils": "9.28.0", "deepmerge-ts": "^7.0.3", "glob": "^10.2.2", "import-meta-resolve": "^4.0.0", @@ -5410,9 +4815,7 @@ } }, "node_modules/@wdio/config/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.1", "dev": true, "license": "MIT", "dependencies": { @@ -5421,9 +4824,6 @@ }, "node_modules/@wdio/config/node_modules/glob": { "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -5443,8 +4843,6 @@ }, "node_modules/@wdio/config/node_modules/minimatch": { "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { @@ -5459,8 +4857,6 @@ }, "node_modules/@wdio/globals": { "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.23.0.tgz", - "integrity": "sha512-OmwPKV8c5ecLqo+EkytN7oUeYfNmRI4uOXGIR1ybP7AK5Zz+l9R0dGfoadEuwi1aZXAL0vwuhtq3p0OL3dfqHQ==", "dev": true, "license": "MIT", "engines": { @@ -5481,8 +4877,6 @@ }, "node_modules/@wdio/logger": { "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.18.0.tgz", - "integrity": "sha512-HdzDrRs+ywAqbXGKqe1i/bLtCv47plz4TvsHFH3j729OooT5VH38ctFn5aLXgECmiAKDkmH/A6kOq2Zh5DIxww==", "dev": true, "license": "MIT", "dependencies": { @@ -5508,9 +4902,7 @@ } }, "node_modules/@wdio/protocols": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.24.0.tgz", - "integrity": "sha512-ozQKYddBLT4TRvU9J+fGrhVUtx3iDAe+KNCJcTDMFMxNSdDMR2xFQdNp8HLHypspk58oXTYCvz6ZYjySthhqsw==", + "version": "9.28.0", "dev": true, "license": "MIT" }, @@ -5526,9 +4918,7 @@ } }, "node_modules/@wdio/types": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.24.0.tgz", - "integrity": "sha512-PYYunNl8Uq1r8YMJAK6ReRy/V/XIrCSyj5cpCtR5EqCL6heETOORFj7gt4uPnzidfgbtMBcCru0LgjjlMiH1UQ==", + "version": "9.28.0", "dev": true, "license": "MIT", "dependencies": { @@ -5539,15 +4929,13 @@ } }, "node_modules/@wdio/utils": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.24.0.tgz", - "integrity": "sha512-6WhtzC5SNCGRBTkaObX6A07Ofnnyyf+TQH/d/fuhZRqvBknrP4AMMZF+PFxGl1fwdySWdBn+gV2QLE+52Byowg==", + "version": "9.28.0", "dev": true, "license": "MIT", "dependencies": { "@puppeteer/browsers": "^2.2.0", "@wdio/logger": "9.18.0", - "@wdio/types": "9.24.0", + "@wdio/types": "9.28.0", "decamelize": "^6.0.0", "deepmerge-ts": "^7.0.3", "edgedriver": "^6.1.2", @@ -5747,9 +5135,7 @@ "license": "Apache-2.0" }, "node_modules/@zip.js/zip.js": { - "version": "2.8.21", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.8.21.tgz", - "integrity": "sha512-fkyzXISE3IMrstDO1AgPkJCx14MYHP/suIGiAovEYEuBjq3mffsuL6aMV7ohOSjW4rXtuACuUfpA3GtITgdtYg==", + "version": "2.8.26", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -5820,8 +5206,6 @@ }, "node_modules/acorn-import-phases": { "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, "license": "MIT", "engines": { @@ -5863,8 +5247,6 @@ }, "node_modules/ajv": { "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { @@ -5896,8 +5278,6 @@ }, "node_modules/ajv-formats/node_modules/ajv": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { @@ -5918,8 +5298,6 @@ }, "node_modules/allure-jest": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/allure-jest/-/allure-jest-3.9.0.tgz", - "integrity": "sha512-hEW4DKjvb3engGoHUPQaDEdyrFkUxQnqULiSQAehL1eDEggqdPbQro86Nch8Cj1yuIqUTn9UP1FMuuuwl/5jnQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5952,8 +5330,6 @@ }, "node_modules/allure-js-commons": { "version": "3.9.0", - "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.9.0.tgz", - "integrity": "sha512-uVQcGE6MWIvGR/zW1XEUwHXUQa1EJKY0Cah+0TZK1qKuw6ptyhftDr34XE3wExTyCZirRrI98dbRtPeYYuyI+g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -6029,8 +5405,6 @@ }, "node_modules/anymatch/node_modules/picomatch": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -6076,8 +5450,6 @@ }, "node_modules/archiver-utils/node_modules/brace-expansion": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -6086,8 +5458,6 @@ }, "node_modules/archiver-utils/node_modules/glob": { "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -6107,8 +5477,6 @@ }, "node_modules/archiver-utils/node_modules/minimatch": { "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { @@ -6184,8 +5552,6 @@ }, "node_modules/array-differ": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", - "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", "dev": true, "license": "MIT", "engines": { @@ -6340,8 +5706,6 @@ }, "node_modules/arrify": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", - "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "dev": true, "license": "MIT", "engines": { @@ -6350,8 +5714,6 @@ }, "node_modules/asn1js": { "version": "3.0.10", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", - "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -6365,8 +5727,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -6486,8 +5846,6 @@ }, "node_modules/axios": { "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", "dev": true, "license": "MIT", "dependencies": { @@ -6498,8 +5856,6 @@ }, "node_modules/axios/node_modules/proxy-from-env": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", - "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", "dev": true, "license": "MIT", "engines": { @@ -6635,8 +5991,6 @@ }, "node_modules/bare-events": { "version": "2.8.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", - "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -6651,8 +6005,6 @@ }, "node_modules/bare-fs": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.4.tgz", - "integrity": "sha512-POK4oplfA7P7gqvetNmCs4CNtm9fNsx+IAh7jH7GgU0OJdge2rso0R20TNWVq6VoWcCvsTdlNDaleLHGaKx8CA==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -6677,8 +6029,6 @@ }, "node_modules/bare-os": { "version": "3.6.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", - "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -6688,8 +6038,6 @@ }, "node_modules/bare-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", - "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -6699,8 +6047,6 @@ }, "node_modules/bare-stream": { "version": "2.8.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.8.0.tgz", - "integrity": "sha512-reUN0M2sHRqCdG4lUK3Fw8w98eeUIZHL5c3H7Mbhk2yVBL+oofgaIp0ieLfD5QXwPCypBpmEEKU2WZKzbAk8GA==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -6723,8 +6069,6 @@ }, "node_modules/bare-url": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", - "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -6753,8 +6097,6 @@ }, "node_modules/baseline-browser-mapping": { "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -6763,8 +6105,6 @@ }, "node_modules/basic-ftp": { "version": "5.3.1", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", - "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", "dev": true, "license": "MIT", "engines": { @@ -6799,8 +6139,6 @@ }, "node_modules/body-parser": { "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "dev": true, "license": "MIT", "dependencies": { @@ -6832,8 +6170,6 @@ }, "node_modules/body-parser/node_modules/http-errors": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6869,8 +6205,6 @@ }, "node_modules/body-parser/node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { @@ -6893,15 +6227,11 @@ }, "node_modules/bowser": { "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "dev": true, "license": "MIT" }, "node_modules/brace-expansion": { "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -6922,8 +6252,6 @@ }, "node_modules/browserslist": { "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -6939,6 +6267,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -7010,8 +6339,6 @@ }, "node_modules/builtin-modules": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz", - "integrity": "sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==", "dev": true, "license": "MIT", "engines": { @@ -7053,8 +6380,6 @@ }, "node_modules/bytestreamjs": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", - "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -7149,8 +6474,6 @@ }, "node_modules/caniuse-api": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", - "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, "license": "MIT", "dependencies": { @@ -7162,8 +6485,6 @@ }, "node_modules/caniuse-lite": { "version": "1.0.30001768", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001768.tgz", - "integrity": "sha512-qY3aDRZC5nWPgHUgIB84WL+nySuo19wk0VJpp/XI9T34lrvkyhRvNVOFJOp2kxClQhiFBu+TaUSudf6oa3vkSA==", "dev": true, "funding": [ { @@ -7232,8 +6553,6 @@ }, "node_modules/charenc": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -7350,8 +6669,6 @@ }, "node_modules/chromium-bidi": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", - "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -7644,8 +6961,6 @@ }, "node_modules/compression": { "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, "license": "MIT", "dependencies": { @@ -7762,8 +7077,6 @@ }, "node_modules/copy-webpack-plugin": { "version": "14.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-14.0.0.tgz", - "integrity": "sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==", "dev": true, "license": "MIT", "dependencies": { @@ -7786,9 +7099,8 @@ }, "node_modules/core-js-compat": { "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", - "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.25.3" }, @@ -7833,20 +7145,8 @@ "license": "Python-2.0" }, "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.1.1", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } - ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -7960,8 +7260,6 @@ }, "node_modules/crypt": { "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -7970,8 +7268,6 @@ }, "node_modules/css-declaration-sorter": { "version": "7.3.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.1.tgz", - "integrity": "sha512-gz6x+KkgNCjxq3Var03pRYLhyNfwhkKF1g/yoLgDNtFvVu0/fOLV9C8fFEZRjACp/XQLumjAYo7JVjzH3wLbxA==", "dev": true, "license": "ISC", "engines": { @@ -8036,8 +7332,6 @@ }, "node_modules/css-minimizer-webpack-plugin": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-8.0.0.tgz", - "integrity": "sha512-9bEpzHs8gEq6/cbEj418jXL/YWjBUD2YTLLk905Npt2JODqnRITin0+So5Vx4Dp5vyi2Lpt9pp2QHzQ7fdxNrw==", "dev": true, "license": "MIT", "dependencies": { @@ -8081,8 +7375,6 @@ }, "node_modules/css-minimizer-webpack-plugin/node_modules/@jest/schemas": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", "dependencies": { @@ -8094,8 +7386,6 @@ }, "node_modules/css-minimizer-webpack-plugin/node_modules/@jest/types": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, "license": "MIT", "dependencies": { @@ -8113,15 +7403,11 @@ }, "node_modules/css-minimizer-webpack-plugin/node_modules/@sinclair/typebox": { "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, "node_modules/css-minimizer-webpack-plugin/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -8137,8 +7423,6 @@ }, "node_modules/css-minimizer-webpack-plugin/node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { @@ -8150,8 +7434,6 @@ }, "node_modules/css-minimizer-webpack-plugin/node_modules/ci-info": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -8166,8 +7448,6 @@ }, "node_modules/css-minimizer-webpack-plugin/node_modules/jest-util": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", "dependencies": { @@ -8184,8 +7464,6 @@ }, "node_modules/css-minimizer-webpack-plugin/node_modules/jest-worker": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "license": "MIT", "dependencies": { @@ -8201,8 +7479,6 @@ }, "node_modules/css-minimizer-webpack-plugin/node_modules/supports-color": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8245,8 +7521,6 @@ }, "node_modules/css-tokenize": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/css-tokenize/-/css-tokenize-1.0.1.tgz", - "integrity": "sha512-gLmmbJdwH9HLY4bcA17lnZ8GgPwEXRbvxBJGHnkiB6gLhRpTzjkjtMIvz7YORGW/Ptv2oMk8b5g+u7mRD6Dd7A==", "dev": true, "license": "MIT", "dependencies": { @@ -8256,8 +7530,6 @@ }, "node_modules/css-tokenize/node_modules/readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "dev": true, "license": "MIT", "dependencies": { @@ -8269,8 +7541,6 @@ }, "node_modules/css-tokenize/node_modules/string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", "dev": true, "license": "MIT" }, @@ -8317,8 +7587,6 @@ }, "node_modules/cssnano": { "version": "7.1.2", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.2.tgz", - "integrity": "sha512-HYOPBsNvoiFeR1eghKD5C3ASm64v9YVyJB4Ivnl2gqKoQYvjjN/G0rztvKQq8OxocUtC6sjqY8jwYngIB4AByA==", "dev": true, "license": "MIT", "dependencies": { @@ -8338,8 +7606,6 @@ }, "node_modules/cssnano-preset-default": { "version": "7.0.10", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.10.tgz", - "integrity": "sha512-6ZBjW0Lf1K1Z+0OKUAUpEN62tSXmYChXWi2NAA0afxEVsj9a+MbcB1l5qel6BHJHmULai2fCGRthCeKSFbScpA==", "dev": true, "license": "MIT", "dependencies": { @@ -8383,8 +7649,6 @@ }, "node_modules/cssnano-preset-default/node_modules/postcss-discard-empty": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.1.tgz", - "integrity": "sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==", "dev": true, "license": "MIT", "engines": { @@ -8396,8 +7660,6 @@ }, "node_modules/cssnano-utils": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.1.tgz", - "integrity": "sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==", "dev": true, "license": "MIT", "engines": { @@ -8553,8 +7815,6 @@ }, "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, "license": "MIT", "engines": { @@ -8638,8 +7898,6 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -8656,8 +7914,6 @@ }, "node_modules/decamelize": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.1.tgz", - "integrity": "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==", "dev": true, "license": "MIT", "engines": { @@ -8686,8 +7942,6 @@ }, "node_modules/deep-eql": { "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", "peer": true, @@ -8746,8 +8000,6 @@ }, "node_modules/deepmerge-ts": { "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -8756,8 +8008,6 @@ }, "node_modules/default-browser": { "version": "5.4.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", - "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", "dev": true, "license": "MIT", "dependencies": { @@ -8827,8 +8077,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -8900,8 +8148,6 @@ }, "node_modules/devtools-protocol": { "version": "0.0.1608973", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz", - "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==", "dev": true, "license": "BSD-3-Clause" }, @@ -8937,8 +8183,6 @@ }, "node_modules/doiuse": { "version": "6.0.6", - "resolved": "https://registry.npmjs.org/doiuse/-/doiuse-6.0.6.tgz", - "integrity": "sha512-XuPRslcWHhQJ+WjCjimRUcNfhZvOiC0610FsY6WeSlzXvoZYtm6iOpR9K0N4wRoM/lP4i7LatT+IhltAzouSOw==", "dev": true, "license": "MIT", "dependencies": { @@ -8960,8 +8204,6 @@ }, "node_modules/doiuse/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, "license": "ISC", "dependencies": { @@ -8975,8 +8217,6 @@ }, "node_modules/doiuse/node_modules/source-map": { "version": "0.7.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", - "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -8985,8 +8225,6 @@ }, "node_modules/doiuse/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, "license": "MIT", "dependencies": { @@ -8998,8 +8236,6 @@ }, "node_modules/doiuse/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, "license": "MIT", "dependencies": { @@ -9016,8 +8252,6 @@ }, "node_modules/doiuse/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, "license": "MIT", "dependencies": { @@ -9179,8 +8413,6 @@ }, "node_modules/edge-paths": { "version": "3.0.5", - "resolved": "https://registry.npmjs.org/edge-paths/-/edge-paths-3.0.5.tgz", - "integrity": "sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==", "dev": true, "license": "MIT", "dependencies": { @@ -9196,8 +8428,6 @@ }, "node_modules/edgedriver": { "version": "6.3.0", - "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-6.3.0.tgz", - "integrity": "sha512-ggEQL+oEyIcM4nP2QC3AtCQ04o4kDNefRM3hja0odvlPSnsaxiruMxEZ93v3gDCKWYW6BXUr51PPradb+3nffw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9220,8 +8450,6 @@ }, "node_modules/edgedriver/node_modules/agent-base": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -9230,8 +8458,6 @@ }, "node_modules/edgedriver/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, "license": "MIT", "dependencies": { @@ -9244,8 +8470,6 @@ }, "node_modules/edgedriver/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, "license": "MIT", "dependencies": { @@ -9258,8 +8482,6 @@ }, "node_modules/edgedriver/node_modules/isexe": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", - "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -9268,8 +8490,6 @@ }, "node_modules/edgedriver/node_modules/which": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", - "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "dev": true, "license": "ISC", "dependencies": { @@ -9289,8 +8509,6 @@ }, "node_modules/electron-to-chromium": { "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", "dev": true, "license": "ISC" }, @@ -9351,8 +8569,6 @@ }, "node_modules/enhanced-resolve": { "version": "5.19.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", - "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, "license": "MIT", "dependencies": { @@ -9547,8 +8763,6 @@ }, "node_modules/es-module-lexer": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, @@ -9692,8 +8906,6 @@ }, "node_modules/eslint": { "version": "9.39.3", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", - "integrity": "sha512-VmQ+sifHUbI/IcSopBCF/HO3YiHQx/AVd3UVyYL6weuwW+HvON9VYn5l6Zl1WZzPWXPNZrSQpxwkkZ/VuvJZzg==", "dev": true, "license": "MIT", "dependencies": { @@ -9752,8 +8964,6 @@ }, "node_modules/eslint-config-prettier": { "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", "dev": true, "license": "MIT", "bin": { @@ -9792,8 +9002,6 @@ }, "node_modules/eslint-plugin-no-unsanitized": { "version": "4.1.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-no-unsanitized/-/eslint-plugin-no-unsanitized-4.1.5.tgz", - "integrity": "sha512-MSB4hXPVFQrI8weqzs6gzl7reP2k/qSjtCoL2vUMSDejIIq9YL1ZKvq5/ORBXab/PvfBBrWO2jWviYpL+4Ghfg==", "dev": true, "license": "MPL-2.0", "peerDependencies": { @@ -9862,8 +9070,6 @@ }, "node_modules/eslint-plugin-react-hooks": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, "license": "MIT", "engines": { @@ -9886,8 +9092,6 @@ }, "node_modules/eslint-plugin-react/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -9923,9 +9127,8 @@ }, "node_modules/eslint-plugin-unicorn": { "version": "60.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-60.0.0.tgz", - "integrity": "sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "@eslint-community/eslint-utils": "^4.7.0", @@ -9958,14 +9161,11 @@ }, "node_modules/eslint-plugin-unicorn/node_modules/change-case": { "version": "5.4.4", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", - "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/eslint-plugin-unicorn/node_modules/ci-info": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", "dev": true, "funding": [ { @@ -9973,14 +9173,13 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/eslint-plugin-unicorn/node_modules/indent-string": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", "dev": true, "license": "MIT", "engines": { @@ -10003,8 +9202,6 @@ }, "node_modules/eslint-plugin-unicorn/node_modules/strip-indent": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", "dev": true, "license": "MIT", "dependencies": { @@ -10019,8 +9216,6 @@ }, "node_modules/eslint-scope": { "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10047,8 +9242,6 @@ }, "node_modules/eslint/node_modules/@eslint/plugin-kit": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -10076,8 +9269,6 @@ }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -10089,8 +9280,6 @@ }, "node_modules/eslint/node_modules/espree": { "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10144,8 +9333,6 @@ }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -10340,8 +9527,6 @@ }, "node_modules/expect-webdriverio": { "version": "5.6.4", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.6.4.tgz", - "integrity": "sha512-Bkoqs+39fHwjos51qab7ZWmvZrYNBbzgSAIykH2CrgLOLhHJXzC30DP9lZq2MsmaUsbBnN5c5m8VqAhOHTrCRw==", "dev": true, "license": "MIT", "peer": true, @@ -10373,8 +9558,6 @@ }, "node_modules/expect-webdriverio/node_modules/@jest/expect-utils": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", "dev": true, "license": "MIT", "peer": true, @@ -10387,8 +9570,6 @@ }, "node_modules/expect-webdriverio/node_modules/@jest/schemas": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", "peer": true, @@ -10401,8 +9582,6 @@ }, "node_modules/expect-webdriverio/node_modules/@jest/types": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, "license": "MIT", "peer": true, @@ -10421,16 +9600,12 @@ }, "node_modules/expect-webdriverio/node_modules/@sinclair/typebox": { "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true }, "node_modules/expect-webdriverio/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "peer": true, @@ -10447,8 +9622,6 @@ }, "node_modules/expect-webdriverio/node_modules/ci-info": { "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -10464,8 +9637,6 @@ }, "node_modules/expect-webdriverio/node_modules/expect": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", "dev": true, "license": "MIT", "peer": true, @@ -10483,8 +9654,6 @@ }, "node_modules/expect-webdriverio/node_modules/jest-diff": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", "dev": true, "license": "MIT", "peer": true, @@ -10500,8 +9669,6 @@ }, "node_modules/expect-webdriverio/node_modules/jest-matcher-utils": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", "dev": true, "license": "MIT", "peer": true, @@ -10517,8 +9684,6 @@ }, "node_modules/expect-webdriverio/node_modules/jest-message-util": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", "peer": true, @@ -10539,8 +9704,6 @@ }, "node_modules/expect-webdriverio/node_modules/jest-mock": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", "peer": true, @@ -10555,8 +9718,6 @@ }, "node_modules/expect-webdriverio/node_modules/jest-util": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", "peer": true, @@ -10574,8 +9735,6 @@ }, "node_modules/expect-webdriverio/node_modules/pretty-format": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", "peer": true, @@ -10590,8 +9749,6 @@ }, "node_modules/expect-webdriverio/node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", "peer": true, @@ -10604,8 +9761,6 @@ }, "node_modules/express": { "version": "4.22.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", - "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -10664,8 +9819,6 @@ }, "node_modules/express/node_modules/path-to-regexp": { "version": "0.1.13", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", - "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "dev": true, "license": "MIT" }, @@ -10676,8 +9829,6 @@ }, "node_modules/extract-zip": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10697,8 +9848,6 @@ }, "node_modules/extract-zip/node_modules/get-stream": { "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "license": "MIT", "dependencies": { @@ -10764,8 +9913,6 @@ }, "node_modules/fast-uri": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "dev": true, "funding": [ { @@ -10781,8 +9928,6 @@ }, "node_modules/fast-xml-builder": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", "dev": true, "funding": [ { @@ -10798,8 +9943,6 @@ }, "node_modules/fast-xml-parser": { "version": "5.7.2", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.2.tgz", - "integrity": "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==", "dev": true, "funding": [ { @@ -10859,8 +10002,6 @@ }, "node_modules/fd-slicer": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "license": "MIT", "dependencies": { @@ -10869,8 +10010,6 @@ }, "node_modules/fdir": { "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { @@ -10887,8 +10026,6 @@ }, "node_modules/file-entry-cache": { "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10953,8 +10090,6 @@ }, "node_modules/find-up-simple": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", - "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", "dev": true, "license": "MIT", "engines": { @@ -11011,8 +10146,6 @@ }, "node_modules/flat-cache": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { @@ -11025,8 +10158,6 @@ }, "node_modules/flatted": { "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -11041,8 +10172,6 @@ }, "node_modules/follow-redirects": { "version": "1.16.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", - "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "dev": true, "funding": [ { @@ -11120,17 +10249,15 @@ } }, "node_modules/form-data": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", - "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "version": "4.0.5", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.4", - "mime-types": "^2.1.35" + "hasown": "^2.0.2", + "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" @@ -11284,8 +10411,6 @@ }, "node_modules/geckodriver": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-6.1.0.tgz", - "integrity": "sha512-ZRXLa4ZaYTTgUO4Eefw+RsQCleugU2QLb1ME7qTYxxuRj51yAhfnXaItXNs5/vUzfIaDHuZ+YnSF005hfp07nQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -11306,8 +10431,6 @@ }, "node_modules/geckodriver/node_modules/agent-base": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -11316,8 +10439,6 @@ }, "node_modules/geckodriver/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, "license": "MIT", "dependencies": { @@ -11330,8 +10451,6 @@ }, "node_modules/geckodriver/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, "license": "MIT", "dependencies": { @@ -11409,9 +10528,7 @@ } }, "node_modules/get-port": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", - "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "version": "7.2.0", "dev": true, "license": "MIT", "engines": { @@ -11462,8 +10579,6 @@ }, "node_modules/get-uri": { "version": "6.0.5", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", - "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", "dev": true, "license": "MIT", "dependencies": { @@ -11477,8 +10592,6 @@ }, "node_modules/glob": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", - "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -11530,8 +10643,6 @@ }, "node_modules/glob-to-regexp": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true, "license": "BSD-2-Clause" }, @@ -11625,9 +10736,8 @@ }, "node_modules/globals": { "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -11789,8 +10899,6 @@ }, "node_modules/handlebars": { "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11879,9 +10987,7 @@ } }, "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "version": "2.0.2", "dev": true, "license": "MIT", "dependencies": { @@ -12092,8 +11198,6 @@ }, "node_modules/html-validate/node_modules/ajv": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { @@ -12109,8 +11213,6 @@ }, "node_modules/html-validate/node_modules/brace-expansion": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -12119,8 +11221,6 @@ }, "node_modules/html-validate/node_modules/glob": { "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -12153,8 +11253,6 @@ }, "node_modules/html-validate/node_modules/minimatch": { "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { @@ -12211,8 +11309,6 @@ }, "node_modules/htmlfy": { "version": "0.8.1", - "resolved": "https://registry.npmjs.org/htmlfy/-/htmlfy-0.8.1.tgz", - "integrity": "sha512-xWROBw9+MEGwxpotll0h672KCaLrKKiCYzsyN8ZgL9cQbVumFnyvsk2JqiB9ELAV1GLj1GG/jxZUjV9OZZi/yQ==", "dev": true, "license": "MIT" }, @@ -12297,9 +11393,7 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz", - "integrity": "sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==", + "version": "2.0.9", "dev": true, "license": "MIT", "dependencies": { @@ -12435,8 +11529,6 @@ }, "node_modules/immutable": { "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", "dev": true, "license": "MIT" }, @@ -12483,8 +11575,6 @@ }, "node_modules/import-meta-resolve": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", - "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", "dev": true, "license": "MIT", "funding": { @@ -12568,8 +11658,6 @@ }, "node_modules/ip-address": { "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "dev": true, "license": "MIT", "engines": { @@ -12692,15 +11780,11 @@ }, "node_modules/is-buffer": { "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", "dev": true, "license": "MIT" }, "node_modules/is-builtin-module": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", - "integrity": "sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==", "dev": true, "license": "MIT", "dependencies": { @@ -13582,8 +12666,6 @@ }, "node_modules/jest-config/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -14209,8 +13291,6 @@ }, "node_modules/jest-runtime/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -14333,8 +13413,6 @@ }, "node_modules/jest-util/node_modules/picomatch": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -14472,8 +13550,6 @@ }, "node_modules/jiti": { "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", "bin": { @@ -14481,22 +13557,15 @@ } }, "node_modules/joi": { - "version": "18.2.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz", - "integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==", + "version": "17.13.3", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@hapi/address": "^5.1.1", - "@hapi/formula": "^3.0.2", - "@hapi/hoek": "^11.0.7", - "@hapi/pinpoint": "^2.0.1", - "@hapi/tlds": "^1.1.1", - "@hapi/topo": "^6.0.2", - "@standard-schema/spec": "^1.1.0" - }, - "engines": { - "node": ">= 20" + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" } }, "node_modules/js-tokens": { @@ -14505,8 +13574,6 @@ }, "node_modules/js-yaml": { "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -14574,8 +13641,6 @@ }, "node_modules/json-buffer": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, @@ -14688,8 +13753,6 @@ }, "node_modules/keyv": { "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { @@ -14734,14 +13797,12 @@ } }, "node_modules/launch-editor": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", - "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==", + "version": "2.10.0", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^1.1.1", - "shell-quote": "^1.8.4" + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" } }, "node_modules/lazystream": { @@ -14842,8 +13903,6 @@ }, "node_modules/lilconfig": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "license": "MIT", "engines": { @@ -15079,8 +14138,6 @@ }, "node_modules/loader-runner": { "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, "license": "MIT", "engines": { @@ -15101,8 +14158,6 @@ }, "node_modules/locate-app": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.5.0.tgz", - "integrity": "sha512-xIqbzPMBYArJRmPGUZD9CzV9wOqmVtQnaAn3wrj3s6WYW0bQvPI7x+sPYUGmDTYMHefVK//zc6HEYZ1qnxIK+Q==", "dev": true, "funding": [ { @@ -15123,8 +14178,6 @@ }, "node_modules/locate-app/node_modules/type-fest": { "version": "4.26.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz", - "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -15147,8 +14200,6 @@ }, "node_modules/lodash": { "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, @@ -15179,8 +14230,6 @@ }, "node_modules/lodash.uniq": { "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true, "license": "MIT" }, @@ -15316,8 +14365,6 @@ }, "node_modules/magic-string": { "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { @@ -15389,8 +14436,6 @@ }, "node_modules/md5": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", - "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -15491,8 +14536,6 @@ }, "node_modules/micromatch/node_modules/picomatch": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -15585,8 +14628,6 @@ }, "node_modules/minimatch": { "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -15601,8 +14642,6 @@ }, "node_modules/minimatch/node_modules/balanced-match": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.3.tgz", - "integrity": "sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g==", "dev": true, "license": "MIT", "engines": { @@ -15611,8 +14650,6 @@ }, "node_modules/minimatch/node_modules/brace-expansion": { "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", "dev": true, "license": "MIT", "dependencies": { @@ -15632,8 +14669,6 @@ }, "node_modules/minipass": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -15662,8 +14697,6 @@ }, "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, "license": "MIT" }, @@ -15683,9 +14716,7 @@ "license": "MIT" }, "node_modules/modern-tar": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.4.tgz", - "integrity": "sha512-5ixBi7pY+H8z3MKExsipXPq6S/Q27KpSY0K+NnIyLQLr58mNeZVhT9TkYcqa74H52DabOyrmGLhT5D7TZ/x26Q==", + "version": "0.7.6", "dev": true, "license": "MIT", "engines": { @@ -15719,8 +14750,6 @@ }, "node_modules/multimatch": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", - "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", "dev": true, "license": "MIT", "dependencies": { @@ -15739,8 +14768,6 @@ }, "node_modules/multimatch/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -15759,9 +14786,9 @@ } }, "node_modules/nanoid": { - "version": "5.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz", - "integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==", + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", "dev": true, "funding": [ { @@ -15805,8 +14832,6 @@ }, "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, "license": "MIT", "engines": { @@ -15840,8 +14865,6 @@ }, "node_modules/node-releases": { "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT" }, @@ -15985,8 +15008,6 @@ }, "node_modules/npm-run-all/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -16242,8 +15263,6 @@ }, "node_modules/on-headers": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, "license": "MIT", "engines": { @@ -16382,8 +15401,6 @@ }, "node_modules/pac-proxy-agent": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", - "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, "license": "MIT", "dependencies": { @@ -16402,8 +15419,6 @@ }, "node_modules/pac-proxy-agent/node_modules/agent-base": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -16412,8 +15427,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -16426,8 +15439,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -16440,8 +15451,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -16643,8 +15652,6 @@ }, "node_modules/path-expression-matcher": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", "dev": true, "funding": [ { @@ -16740,8 +15747,6 @@ }, "node_modules/pend": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "dev": true, "license": "MIT" }, @@ -16752,8 +15757,6 @@ }, "node_modules/picomatch": { "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -16814,8 +15817,6 @@ }, "node_modules/pkijs": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", - "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -16856,8 +15857,6 @@ }, "node_modules/postcss": { "version": "8.5.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", - "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "dev": true, "funding": [ { @@ -16885,8 +15884,6 @@ }, "node_modules/postcss-calc": { "version": "10.1.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz", - "integrity": "sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==", "dev": true, "license": "MIT", "dependencies": { @@ -16902,8 +15899,6 @@ }, "node_modules/postcss-colormin": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.5.tgz", - "integrity": "sha512-ekIBP/nwzRWhEMmIxHHbXHcMdzd1HIUzBECaj5KEdLz9DVP2HzT065sEhvOx1dkLjYW7jyD0CngThx6bpFi2fA==", "dev": true, "license": "MIT", "dependencies": { @@ -16921,8 +15916,6 @@ }, "node_modules/postcss-convert-values": { "version": "7.0.8", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.8.tgz", - "integrity": "sha512-+XNKuPfkHTCEo499VzLMYn94TiL3r9YqRE3Ty+jP7UX4qjewUONey1t7CG21lrlTLN07GtGM8MqFVp86D4uKJg==", "dev": true, "license": "MIT", "dependencies": { @@ -16938,8 +15931,6 @@ }, "node_modules/postcss-discard-comments": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.5.tgz", - "integrity": "sha512-IR2Eja8WfYgN5n32vEGSctVQ1+JARfu4UH8M7bgGh1bC+xI/obsPJXaBpQF7MAByvgwZinhpHpdrmXtvVVlKcQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16954,8 +15945,6 @@ }, "node_modules/postcss-discard-duplicates": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.2.tgz", - "integrity": "sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==", "dev": true, "license": "MIT", "engines": { @@ -16978,8 +15967,6 @@ }, "node_modules/postcss-discard-overridden": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.1.tgz", - "integrity": "sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==", "dev": true, "license": "MIT", "engines": { @@ -17010,8 +15997,6 @@ }, "node_modules/postcss-merge-longhand": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.5.tgz", - "integrity": "sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==", "dev": true, "license": "MIT", "dependencies": { @@ -17027,8 +16012,6 @@ }, "node_modules/postcss-merge-rules": { "version": "7.0.7", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.7.tgz", - "integrity": "sha512-njWJrd/Ms6XViwowaaCc+/vqhPG3SmXn725AGrnl+BgTuRPEacjiLEaGq16J6XirMJbtKkTwnt67SS+e2WGoew==", "dev": true, "license": "MIT", "dependencies": { @@ -17046,8 +16029,6 @@ }, "node_modules/postcss-minify-font-values": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.1.tgz", - "integrity": "sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17062,8 +16043,6 @@ }, "node_modules/postcss-minify-gradients": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz", - "integrity": "sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==", "dev": true, "license": "MIT", "dependencies": { @@ -17080,8 +16059,6 @@ }, "node_modules/postcss-minify-params": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.5.tgz", - "integrity": "sha512-FGK9ky02h6Ighn3UihsyeAH5XmLEE2MSGH5Tc4tXMFtEDx7B+zTG6hD/+/cT+fbF7PbYojsmmWjyTwFwW1JKQQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17098,8 +16075,6 @@ }, "node_modules/postcss-minify-selectors": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.5.tgz", - "integrity": "sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug==", "dev": true, "license": "MIT", "dependencies": { @@ -17188,8 +16163,6 @@ }, "node_modules/postcss-normalize-charset": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.1.tgz", - "integrity": "sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==", "dev": true, "license": "MIT", "engines": { @@ -17201,8 +16174,6 @@ }, "node_modules/postcss-normalize-display-values": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.1.tgz", - "integrity": "sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17217,8 +16188,6 @@ }, "node_modules/postcss-normalize-positions": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.1.tgz", - "integrity": "sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17233,8 +16202,6 @@ }, "node_modules/postcss-normalize-repeat-style": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.1.tgz", - "integrity": "sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17249,8 +16216,6 @@ }, "node_modules/postcss-normalize-string": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.1.tgz", - "integrity": "sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17265,8 +16230,6 @@ }, "node_modules/postcss-normalize-timing-functions": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.1.tgz", - "integrity": "sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==", "dev": true, "license": "MIT", "dependencies": { @@ -17281,8 +16244,6 @@ }, "node_modules/postcss-normalize-unicode": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.5.tgz", - "integrity": "sha512-X6BBwiRxVaFHrb2WyBMddIeB5HBjJcAaUHyhLrM2FsxSq5TFqcHSsK7Zu1otag+o0ZphQGJewGH1tAyrD0zX1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -17298,8 +16259,6 @@ }, "node_modules/postcss-normalize-url": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.1.tgz", - "integrity": "sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17314,8 +16273,6 @@ }, "node_modules/postcss-normalize-whitespace": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.1.tgz", - "integrity": "sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==", "dev": true, "license": "MIT", "dependencies": { @@ -17330,8 +16287,6 @@ }, "node_modules/postcss-ordered-values": { "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.2.tgz", - "integrity": "sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==", "dev": true, "license": "MIT", "dependencies": { @@ -17347,8 +16302,6 @@ }, "node_modules/postcss-reduce-initial": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.5.tgz", - "integrity": "sha512-RHagHLidG8hTZcnr4FpyMB2jtgd/OcyAazjMhoy5qmWJOx1uxKh4ntk0Pb46ajKM0rkf32lRH4C8c9qQiPR6IA==", "dev": true, "license": "MIT", "dependencies": { @@ -17364,8 +16317,6 @@ }, "node_modules/postcss-reduce-transforms": { "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.1.tgz", - "integrity": "sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==", "dev": true, "license": "MIT", "dependencies": { @@ -17435,8 +16386,6 @@ }, "node_modules/postcss-selector-parser": { "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", - "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", "dependencies": { @@ -17457,8 +16406,6 @@ }, "node_modules/postcss-svgo": { "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.0.tgz", - "integrity": "sha512-KnAlfmhtoLz6IuU3Sij2ycusNs4jPW+QoFE5kuuUOK8awR6tMxZQrs5Ey3BUz7nFCzT3eqyFgqkyrHiaU2xx3w==", "dev": true, "license": "MIT", "dependencies": { @@ -17474,8 +16421,6 @@ }, "node_modules/postcss-svgo/node_modules/commander": { "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "license": "MIT", "engines": { @@ -17484,8 +16429,6 @@ }, "node_modules/postcss-svgo/node_modules/csso": { "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17498,8 +16441,6 @@ }, "node_modules/postcss-svgo/node_modules/csso/node_modules/css-tree": { "version": "2.2.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", - "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", "dev": true, "license": "MIT", "dependencies": { @@ -17513,15 +16454,11 @@ }, "node_modules/postcss-svgo/node_modules/mdn-data": { "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", "dev": true, "license": "CC0-1.0" }, "node_modules/postcss-svgo/node_modules/svgo": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", - "integrity": "sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==", "dev": true, "license": "MIT", "dependencies": { @@ -17546,8 +16483,6 @@ }, "node_modules/postcss-unique-selectors": { "version": "7.0.4", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz", - "integrity": "sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ==", "dev": true, "license": "MIT", "dependencies": { @@ -17592,8 +16527,6 @@ }, "node_modules/prettier": { "version": "3.6.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.1.tgz", - "integrity": "sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==", "dev": true, "license": "MIT", "bin": { @@ -17652,8 +16585,6 @@ }, "node_modules/pretty-format/node_modules/react-is": { "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, "license": "MIT" }, @@ -17672,8 +16603,6 @@ }, "node_modules/progress": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, "license": "MIT", "engines": { @@ -17735,8 +16664,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -17755,8 +16682,6 @@ }, "node_modules/proxy-agent/node_modules/agent-base": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -17765,8 +16690,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -17779,8 +16702,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -17793,8 +16714,6 @@ }, "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, "license": "ISC", "engines": { @@ -17898,8 +16817,6 @@ }, "node_modules/puppeteer-core": { "version": "24.43.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz", - "integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -17932,8 +16849,6 @@ }, "node_modules/pvtsutils": { "version": "1.3.6", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", - "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", "dev": true, "license": "MIT", "dependencies": { @@ -17942,8 +16857,6 @@ }, "node_modules/pvutils": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", - "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", "dev": true, "license": "MIT", "engines": { @@ -17952,8 +16865,6 @@ }, "node_modules/qs": { "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -18005,8 +16916,6 @@ }, "node_modules/raw-body": { "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "dev": true, "license": "MIT", "dependencies": { @@ -18021,8 +16930,6 @@ }, "node_modules/raw-body/node_modules/http-errors": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18042,8 +16949,6 @@ }, "node_modules/raw-body/node_modules/iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "license": "MIT", "dependencies": { @@ -18055,8 +16960,6 @@ }, "node_modules/raw-body/node_modules/statuses": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { @@ -18091,8 +16994,6 @@ "node_modules/react-dom18": { "name": "react-dom", "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dev": true, "license": "MIT", "dependencies": { @@ -18105,8 +17006,6 @@ }, "node_modules/react-dom18/node_modules/scheduler": { "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18115,8 +17014,6 @@ }, "node_modules/react-is": { "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "license": "MIT" }, "node_modules/react-router": { @@ -18210,8 +17107,6 @@ "node_modules/react18": { "name": "react", "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dev": true, "license": "MIT", "dependencies": { @@ -18268,8 +17163,6 @@ }, "node_modules/readdir-glob/node_modules/brace-expansion": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { @@ -18278,8 +17171,6 @@ }, "node_modules/readdir-glob/node_modules/minimatch": { "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "license": "ISC", "dependencies": { @@ -18302,8 +17193,6 @@ }, "node_modules/readdirp/node_modules/picomatch": { "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -18338,8 +17227,6 @@ }, "node_modules/reflect-metadata": { "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "dev": true, "license": "Apache-2.0" }, @@ -18366,8 +17253,6 @@ }, "node_modules/regexp-tree": { "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, "license": "MIT", "bin": { @@ -18395,8 +17280,6 @@ }, "node_modules/regjsparser": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -18408,8 +17291,6 @@ }, "node_modules/regjsparser/node_modules/jsesc": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "license": "MIT", "bin": { @@ -18702,8 +17583,6 @@ }, "node_modules/ret": { "version": "0.5.0", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", - "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", "dev": true, "license": "MIT", "engines": { @@ -18739,8 +17618,6 @@ }, "node_modules/rimraf": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.0.tgz", - "integrity": "sha512-DxdlA1bdNzkZK7JiNWH+BAx1x4tEJWoTofIopFo6qWUU94jYrFZ0ubY05TqH3nWPJ1nKa1JWVFDINZ3fnrle/A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -18759,8 +17636,6 @@ }, "node_modules/rollup": { "version": "3.30.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.30.0.tgz", - "integrity": "sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==", "dev": true, "license": "MIT", "bin": { @@ -18776,8 +17651,6 @@ }, "node_modules/rollup-plugin-license": { "version": "3.7.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-license/-/rollup-plugin-license-3.7.0.tgz", - "integrity": "sha512-RvvOIF+GH3fBR3wffgc/vmjQn6qOn72WjppWVDp/v+CLpT0BbcRBdSkPeeIOL6U5XccdYgSIMjUyXgxlKEEFcw==", "dev": true, "license": "MIT", "dependencies": { @@ -18840,8 +17713,6 @@ }, "node_modules/safaridriver": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-1.0.1.tgz", - "integrity": "sha512-jkg4434cYgtrIF2AeY/X0Wmd2W73cK5qIEFE3hDrrQenJH/2SDJIXGvPAigfvQTcE9+H31zkiNHbUqcihEiMRA==", "dev": true, "license": "MIT", "engines": { @@ -18928,8 +17799,6 @@ }, "node_modules/safe-regex2": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.0.0.tgz", - "integrity": "sha512-YwJwe5a51WlK7KbOJREPdjNrpViQBI3p4T50lfwPuDhZnE3XGVTlGvi+aolc5+RvxDD6bnUmjVsU9n1eboLUYw==", "dev": true, "funding": [ { @@ -19035,8 +17904,6 @@ }, "node_modules/sax": { "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", - "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -19064,8 +17931,6 @@ }, "node_modules/schema-utils": { "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "license": "MIT", "dependencies": { @@ -19084,8 +17949,6 @@ }, "node_modules/schema-utils/node_modules/ajv": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { @@ -19101,8 +17964,6 @@ }, "node_modules/schema-utils/node_modules/ajv-keywords": { "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", "dependencies": { @@ -19114,8 +17975,6 @@ }, "node_modules/schema-utils/node_modules/json-schema-traverse": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT" }, @@ -19126,8 +17985,6 @@ }, "node_modules/selfsigned": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", - "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", "dev": true, "license": "MIT", "dependencies": { @@ -19213,8 +18070,6 @@ }, "node_modules/serialize-error": { "version": "12.0.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-12.0.0.tgz", - "integrity": "sha512-ZYkZLAvKTKQXWuh5XpBw7CdbSzagarX39WyZ2H07CDLC5/KfsRGlIXV8d4+tfqX1M7916mRqR1QfNHSij+c9Pw==", "dev": true, "license": "MIT", "dependencies": { @@ -19229,8 +18084,6 @@ }, "node_modules/serialize-error/node_modules/type-fest": { "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { @@ -19242,8 +18095,6 @@ }, "node_modules/serialize-javascript": { "version": "7.0.5", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", - "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -19419,8 +18270,6 @@ }, "node_modules/shell-quote": { "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", "dev": true, "license": "MIT", "engines": { @@ -19572,8 +18421,6 @@ }, "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, "license": "MIT", "engines": { @@ -19602,8 +18449,6 @@ }, "node_modules/socks": { "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "dev": true, "license": "MIT", "dependencies": { @@ -19617,8 +18462,6 @@ }, "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, "license": "MIT", "dependencies": { @@ -19632,8 +18475,6 @@ }, "node_modules/socks-proxy-agent/node_modules/agent-base": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -19667,8 +18508,6 @@ }, "node_modules/spacetrim": { "version": "0.11.59", - "resolved": "https://registry.npmjs.org/spacetrim/-/spacetrim-0.11.59.tgz", - "integrity": "sha512-lLYsktklSRKprreOm7NXReW8YiX2VBjbgmXYEziOoGf/qsJqAEACaDvoTtUOycwjpaSh+bT8eu0KrJn7UNxiCg==", "dev": true, "funding": [ { @@ -19781,8 +18620,6 @@ }, "node_modules/split2": { "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "dev": true, "license": "ISC", "engines": { @@ -20168,8 +19005,6 @@ }, "node_modules/strnum": { "version": "2.2.3", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", - "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", "dev": true, "funding": [ { @@ -20181,8 +19016,6 @@ }, "node_modules/stylehacks": { "version": "7.0.7", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.7.tgz", - "integrity": "sha512-bJkD0JkEtbRrMFtwgpJyBbFIwfDDONQ1Ov3sDLZQP8HuJ73kBOyx66H4bOcAbVWmnfLdvQ0AJwXxOMkpujcO6g==", "dev": true, "license": "MIT", "dependencies": { @@ -20302,8 +19135,6 @@ }, "node_modules/stylelint-no-unsupported-browser-features": { "version": "8.1.1", - "resolved": "https://registry.npmjs.org/stylelint-no-unsupported-browser-features/-/stylelint-no-unsupported-browser-features-8.1.1.tgz", - "integrity": "sha512-sLEe6NUFoWL2vGt6IKKllQEKfKgVxmvTBFs1JVMHKKLWzxtWAXaqSxJvH+j0URM4vLQQqzC/wXc9Kp3XBNKdBw==", "dev": true, "license": "MIT", "dependencies": { @@ -20540,8 +19371,6 @@ }, "node_modules/svgo": { "version": "2.8.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.2.tgz", - "integrity": "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA==", "dev": true, "license": "MIT", "dependencies": { @@ -20684,8 +19513,6 @@ }, "node_modules/table/node_modules/ajv": { "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { @@ -20741,8 +19568,6 @@ }, "node_modules/tapable": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -20755,8 +19580,6 @@ }, "node_modules/tar-fs": { "version": "3.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", - "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", "dev": true, "license": "MIT", "dependencies": { @@ -20805,8 +19628,6 @@ }, "node_modules/terser-webpack-plugin": { "version": "5.3.17", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.17.tgz", - "integrity": "sha512-YR7PtUp6GMU91BgSJmlaX/rS2lGDbAF7D+Wtq7hRO+MiljNmodYvqslzCFiYVAgW+Qoaaia/QUIP4lGXufjdZw==", "dev": true, "license": "MIT", "dependencies": { @@ -20885,8 +19706,6 @@ }, "node_modules/test-exclude/node_modules/minimatch": { "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -20958,8 +19777,6 @@ }, "node_modules/tinyrainbow": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", - "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", "dev": true, "license": "MIT", "peer": true, @@ -21052,8 +19869,6 @@ }, "node_modules/ts-api-utils": { "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { @@ -21064,19 +19879,17 @@ } }, "node_modules/ts-jest": { - "version": "29.4.11", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", - "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==", + "version": "29.4.6", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.9", + "handlebars": "^4.7.8", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", - "semver": "^7.8.0", + "semver": "^7.7.3", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, @@ -21093,7 +19906,7 @@ "babel-jest": "^29.0.0 || ^30.0.0", "jest": "^29.0.0 || ^30.0.0", "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <7" + "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { "@babel/core": { @@ -21117,9 +19930,7 @@ } }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "version": "7.7.4", "dev": true, "license": "ISC", "bin": { @@ -21199,8 +20010,6 @@ }, "node_modules/tsyringe": { "version": "4.10.0", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", - "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", "dev": true, "license": "MIT", "dependencies": { @@ -21212,8 +20021,6 @@ }, "node_modules/tsyringe/node_modules/tslib": { "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, "license": "0BSD" }, @@ -21331,8 +20138,6 @@ }, "node_modules/typed-query-selector": { "version": "2.12.2", - "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", - "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", "dev": true, "license": "MIT" }, @@ -21344,8 +20149,6 @@ "node_modules/types-react-dom18": { "name": "@types/react-dom", "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -21355,8 +20158,6 @@ "node_modules/types-react18": { "name": "@types/react", "version": "18.3.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", - "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", "dev": true, "license": "MIT", "dependencies": { @@ -21366,8 +20167,6 @@ }, "node_modules/typescript": { "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", "bin": { @@ -21380,8 +20179,6 @@ }, "node_modules/typescript-eslint": { "version": "8.56.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.0.tgz", - "integrity": "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg==", "dev": true, "license": "MIT", "dependencies": { @@ -21404,8 +20201,6 @@ }, "node_modules/uglify-js": { "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, "license": "BSD-2-Clause", "optional": true, @@ -21472,9 +20267,7 @@ } }, "node_modules/undici": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", - "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "version": "7.24.0", "dev": true, "license": "MIT", "engines": { @@ -21504,8 +20297,6 @@ }, "node_modules/update-browserslist-db": { "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -21573,8 +20364,6 @@ }, "node_modules/userhome": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.1.tgz", - "integrity": "sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==", "dev": true, "license": "MIT", "engines": { @@ -21601,8 +20390,6 @@ }, "node_modules/uuid": { "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "license": "MIT", "bin": { @@ -21738,14 +20525,12 @@ } }, "node_modules/wait-on": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.5.tgz", - "integrity": "sha512-J3WlS0txVHkhLRb2FsmRg3dkMTCV1+M6Xra3Ho7HzZDHpE7DCOnoSoCJsZotrmW3uRMhvIJGSKUKrh/MeF4iag==", + "version": "8.0.3", "dev": true, "license": "MIT", "dependencies": { - "axios": "^1.12.1", - "joi": "^18.0.1", + "axios": "^1.8.2", + "joi": "^17.13.3", "lodash": "^4.17.21", "minimist": "^1.2.8", "rxjs": "^7.8.2" @@ -21759,8 +20544,6 @@ }, "node_modules/wait-port": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/wait-port/-/wait-port-1.1.0.tgz", - "integrity": "sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -21777,8 +20560,6 @@ }, "node_modules/wait-port/node_modules/chalk": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -21794,8 +20575,6 @@ }, "node_modules/wait-port/node_modules/commander": { "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, "license": "MIT", "engines": { @@ -21812,8 +20591,6 @@ }, "node_modules/watchpack": { "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "license": "MIT", "dependencies": { @@ -21833,19 +20610,17 @@ } }, "node_modules/webdriver": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.24.0.tgz", - "integrity": "sha512-2R31Ey83NzMsafkl4hdFq6GlIBvOODQMkueLjeRqYAITu3QCYiq9oqBdnWA6CdePuV4dbKlYsKRX0mwMiPclDA==", + "version": "9.28.0", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "9.24.0", + "@wdio/config": "9.28.0", "@wdio/logger": "9.18.0", - "@wdio/protocols": "9.24.0", - "@wdio/types": "9.24.0", - "@wdio/utils": "9.24.0", + "@wdio/protocols": "9.28.0", + "@wdio/types": "9.28.0", + "@wdio/utils": "9.28.0", "deepmerge-ts": "^7.0.3", "https-proxy-agent": "^7.0.6", "undici": "^6.21.3", @@ -21857,15 +20632,11 @@ }, "node_modules/webdriver-bidi-protocol": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", - "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", "dev": true, "license": "Apache-2.0" }, "node_modules/webdriver/node_modules/agent-base": { "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -21874,8 +20645,6 @@ }, "node_modules/webdriver/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, "license": "MIT", "dependencies": { @@ -21887,9 +20656,7 @@ } }, "node_modules/webdriver/node_modules/undici": { - "version": "6.27.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz", - "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==", + "version": "6.26.0", "dev": true, "license": "MIT", "engines": { @@ -21897,20 +20664,18 @@ } }, "node_modules/webdriverio": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.24.0.tgz", - "integrity": "sha512-LTJt6Z/iDM0ne/4ytd3BykoPv9CuJ+CAILOzlwFeMGn4Mj02i4Bk2Rg9o/jeJ89f52hnv4OPmNjD0e8nzWAy5g==", + "version": "9.28.0", "dev": true, "license": "MIT", "dependencies": { "@types/node": "^20.11.30", "@types/sinonjs__fake-timers": "^8.1.5", - "@wdio/config": "9.24.0", + "@wdio/config": "9.28.0", "@wdio/logger": "9.18.0", - "@wdio/protocols": "9.24.0", + "@wdio/protocols": "9.28.0", "@wdio/repl": "9.16.2", - "@wdio/types": "9.24.0", - "@wdio/utils": "9.24.0", + "@wdio/types": "9.28.0", + "@wdio/utils": "9.28.0", "archiver": "^7.0.1", "aria-query": "^5.3.0", "cheerio": "^1.0.0-rc.12", @@ -21927,7 +20692,7 @@ "rgb2hex": "0.2.5", "serialize-error": "^12.0.0", "urlpattern-polyfill": "^10.0.0", - "webdriver": "9.24.0" + "webdriver": "9.28.0" }, "engines": { "node": ">=18.20.0" @@ -21951,8 +20716,6 @@ }, "node_modules/webpack": { "version": "5.105.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", - "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, "license": "MIT", "dependencies": { @@ -22369,8 +21132,6 @@ }, "node_modules/wordwrap": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true, "license": "MIT" }, @@ -22469,8 +21230,6 @@ }, "node_modules/ws": { "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "license": "MIT", "engines": { @@ -22499,8 +21258,6 @@ }, "node_modules/xml-naming": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", "dev": true, "funding": [ { @@ -22535,8 +21292,6 @@ }, "node_modules/yaml": { "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, "license": "ISC", "bin": { @@ -22584,8 +21339,6 @@ }, "node_modules/yauzl": { "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "license": "MIT", "dependencies": { @@ -22595,8 +21348,6 @@ }, "node_modules/yauzl/node_modules/buffer-crc32": { "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, "license": "MIT", "engines": { @@ -22644,8 +21395,6 @@ }, "node_modules/zod": { "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index 1fbbc996f0..9af50e9da5 100644 --- a/package.json +++ b/package.json @@ -150,6 +150,7 @@ "typescript": "^5.9.2", "typescript-eslint": "^8.44.0", "wait-on": "^8.0.2", + "webdriverio": "^9.28.0", "webpack": "^5.105.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.2.5" diff --git a/test/definitions/types.ts b/test/definitions/types.ts index adaaf9f23c..2d12d9c1fa 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -1,5 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import { Browser } from 'webdriverio'; + import type { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; import type createWrapper from '../../lib/components/test-utils/selectors'; @@ -21,7 +23,15 @@ export interface TestDefinition { screenshotType: ScreenshotType; queryParams?: Record; configuration?: ScreenshotTestConfiguration; - setup?: (page: ScreenshotPageObject, wrapper: Wrapper) => Promise; + setup?: ({ + page, + wrapper, + browser, + }: { + page: ScreenshotPageObject; + wrapper: Wrapper; + browser: Browser; + }) => Promise; } export interface TestSuite { diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 862e1834d9..71173a4690 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -98,7 +98,7 @@ async function preparePage( await browser.url(`${url}?${params.toString()}`); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { - await testDef.setup(page, wrapper); + await testDef.setup({ page, wrapper, browser }); } } From 6c612747f1771f0e58d4248650c5b6aef1557736 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 12 Jun 2026 23:59:54 +0200 Subject: [PATCH 096/135] Add all components --- .github/workflows/visual-regression.yml | 4 +- test/definitions/index.ts | 206 +++++- test/definitions/visual/alert.ts | 2 - .../visual/app-layout-content-paddings.ts | 30 + test/definitions/visual/app-layout-drawers.ts | 38 + .../definitions/visual/app-layout-flashbar.ts | 39 ++ test/definitions/visual/app-layout-header.ts | 85 +++ test/definitions/visual/app-layout-multi.ts | 25 + .../visual/app-layout-responsive-1280.ts | 7 + .../visual/app-layout-responsive-1400.ts | 7 + .../visual/app-layout-responsive-1920.ts | 7 + .../visual/app-layout-responsive-2540.ts | 7 + .../visual/app-layout-responsive-600.ts | 7 + .../visual/app-layout-responsive-tests.ts | 159 +++++ ...-layout-sticky-table-header-split-panel.ts | 78 +++ test/definitions/visual/app-layout-toolbar.ts | 23 + test/definitions/visual/app-layout-z-index.ts | 60 ++ test/definitions/visual/app-layout.ts | 104 +++ test/definitions/visual/area-chart.ts | 136 ++++ test/definitions/visual/attribute-editor.ts | 31 + test/definitions/visual/autosuggest.ts | 67 ++ test/definitions/visual/badge.ts | 23 + test/definitions/visual/bar-chart.ts | 97 +++ test/definitions/visual/box.ts | 43 ++ test/definitions/visual/breadcrumb-group.ts | 46 ++ test/definitions/visual/button-dropdown.ts | 395 +++++++++++ test/definitions/visual/button-group.ts | 52 ++ test/definitions/visual/button.ts | 73 ++ test/definitions/visual/cards.ts | 49 ++ test/definitions/visual/checkbox.ts | 37 + test/definitions/visual/code-editor.ts | 68 ++ .../visual/collection-preferences.ts | 82 +++ test/definitions/visual/column-layout.ts | 61 ++ test/definitions/visual/container-sticky.ts | 125 ++++ test/definitions/visual/container.ts | 87 +++ .../visual/content-layout-permutations.ts | 661 ++++++++++++++++++ test/definitions/visual/content-layout.ts | 98 +++ test/definitions/visual/copy-to-clipboard.ts | 28 + test/definitions/visual/date-input.ts | 23 + test/definitions/visual/date-picker.ts | 61 ++ test/definitions/visual/date-range-picker.ts | 121 ++++ test/definitions/visual/divider.ts | 18 + test/definitions/visual/drawer.ts | 105 +++ test/definitions/visual/dropdown.ts | 83 +++ test/definitions/visual/expandable-section.ts | 124 ++++ test/definitions/visual/file-dropzone.ts | 18 + test/definitions/visual/file-input.ts | 18 + test/definitions/visual/file-token-group.ts | 18 + test/definitions/visual/file-upload.ts | 18 + test/definitions/visual/flashbar-stacked.ts | 125 ++++ test/definitions/visual/flashbar.ts | 67 ++ test/definitions/visual/form-field.ts | 61 ++ test/definitions/visual/form.ts | 23 + test/definitions/visual/grid.ts | 37 + test/definitions/visual/header.ts | 67 ++ test/definitions/visual/help-panel.ts | 37 + test/definitions/visual/icon.ts | 69 ++ test/definitions/visual/input.ts | 23 + test/definitions/visual/item-card.ts | 33 + test/definitions/visual/key-value-pairs.ts | 29 + test/definitions/visual/line-chart.ts | 156 +++++ test/definitions/visual/link.ts | 38 + test/definitions/visual/list.ts | 23 + .../visual/mixed-line-bar-chart.ts | 108 +++ test/definitions/visual/modal.ts | 163 +++++ test/definitions/visual/multiselect.ts | 132 ++++ test/definitions/visual/pagination.ts | 18 + test/definitions/visual/pie-chart.ts | 66 ++ test/definitions/visual/popover.ts | 197 ++++++ test/definitions/visual/progress-bar.ts | 31 + test/definitions/visual/prompt-input.ts | 46 ++ test/definitions/visual/property-filter.ts | 144 ++++ test/definitions/visual/radio-button.ts | 42 ++ test/definitions/visual/radio-group.ts | 49 ++ .../visual/s3-resource-selector.ts | 38 + test/definitions/visual/segmented-control.ts | 23 + test/definitions/visual/select.ts | 96 +++ test/definitions/visual/side-navigation.ts | 18 + test/definitions/visual/slider.ts | 19 + test/definitions/visual/space-between.ts | 28 + test/definitions/visual/spinner.ts | 23 + test/definitions/visual/split-panel.ts | 93 +++ test/definitions/visual/status-indicator.ts | 18 + test/definitions/visual/steps.ts | 18 + test/definitions/visual/table-cells.ts | 20 + test/definitions/visual/table-embedded.ts | 23 + .../visual/table-inline-editing.ts | 54 ++ .../visual/table-resizable-columns.ts | 69 ++ .../definitions/visual/table-sticky-header.ts | 44 ++ .../visual/table-sticky-scrollbar.ts | 31 + test/definitions/visual/table.ts | 130 ++++ test/definitions/visual/tabs.ts | 80 +++ test/definitions/visual/tag-editor.ts | 18 + test/definitions/visual/text-content.ts | 33 + test/definitions/visual/text-filter.ts | 18 + test/definitions/visual/textarea.ts | 77 ++ test/definitions/visual/tiles.ts | 23 + test/definitions/visual/time-input.ts | 18 + test/definitions/visual/toggle-button.ts | 26 + test/definitions/visual/toggle.ts | 37 + test/definitions/visual/token-group.ts | 23 + test/definitions/visual/top-navigation.ts | 54 ++ test/definitions/visual/tree-view.ts | 28 + test/definitions/visual/wizard.ts | 34 + test/visual/action-card.test.ts | 1 - test/visual/alert.test.ts | 1 - .../app-layout-content-paddings.test.ts | 6 + test/visual/app-layout-drawers.test.ts | 6 + test/visual/app-layout-flashbar.test.ts | 6 + test/visual/app-layout-header.test.ts | 6 + test/visual/app-layout-multi.test.ts | 6 + .../visual/app-layout-responsive-1280.test.ts | 6 + .../visual/app-layout-responsive-1400.test.ts | 6 + .../visual/app-layout-responsive-1920.test.ts | 6 + .../visual/app-layout-responsive-2540.test.ts | 6 + test/visual/app-layout-responsive-600.test.ts | 6 + ...ut-sticky-table-header-split-panel.test.ts | 6 + test/visual/app-layout-toolbar.test.ts | 6 + test/visual/app-layout-z-index.test.ts | 6 + test/visual/app-layout.test.ts | 6 + test/visual/area-chart.test.ts | 6 + test/visual/attribute-editor.test.ts | 6 + test/visual/autosuggest.test.ts | 6 + test/visual/badge.test.ts | 6 + test/visual/bar-chart.test.ts | 6 + test/visual/box.test.ts | 6 + test/visual/breadcrumb-group.test.ts | 6 + test/visual/button-dropdown.test.ts | 6 + test/visual/button-group.test.ts | 6 + test/visual/button.test.ts | 6 + test/visual/cards.test.ts | 6 + test/visual/checkbox.test.ts | 6 + test/visual/code-editor.test.ts | 6 + test/visual/collection-preferences.test.ts | 6 + test/visual/column-layout.test.ts | 6 + test/visual/container-sticky.test.ts | 6 + test/visual/container.test.ts | 6 + .../content-layout-permutations.test.ts | 6 + test/visual/content-layout.test.ts | 6 + test/visual/copy-to-clipboard.test.ts | 6 + test/visual/date-input.test.ts | 6 + test/visual/date-picker.test.ts | 6 + test/visual/date-range-picker.test.ts | 6 + test/visual/divider.test.ts | 6 + test/visual/drawer.test.ts | 6 + test/visual/dropdown.test.ts | 6 + test/visual/expandable-section.test.ts | 6 + test/visual/file-dropzone.test.ts | 6 + test/visual/file-input.test.ts | 6 + test/visual/file-token-group.test.ts | 6 + test/visual/file-upload.test.ts | 6 + test/visual/flashbar-stacked.test.ts | 6 + test/visual/flashbar.test.ts | 6 + test/visual/form-field.test.ts | 6 + test/visual/form.test.ts | 6 + test/visual/grid.test.ts | 6 + test/visual/header.test.ts | 6 + test/visual/help-panel.test.ts | 6 + test/visual/icon.test.ts | 6 + test/visual/input.test.ts | 6 + test/visual/item-card.test.ts | 6 + test/visual/key-value-pairs.test.ts | 6 + test/visual/line-chart.test.ts | 6 + test/visual/link.test.ts | 6 + test/visual/list.test.ts | 6 + test/visual/mixed-line-bar-chart.test.ts | 6 + test/visual/modal.test.ts | 6 + test/visual/multiselect.test.ts | 6 + test/visual/pagination.test.ts | 6 + test/visual/pie-chart.test.ts | 6 + test/visual/popover.test.ts | 6 + test/visual/progress-bar.test.ts | 6 + test/visual/prompt-input.test.ts | 6 + test/visual/property-filter.test.ts | 6 + test/visual/radio-button.test.ts | 6 + test/visual/radio-group.test.ts | 6 + test/visual/s3-resource-selector.test.ts | 6 + test/visual/segmented-control.test.ts | 6 + test/visual/select.test.ts | 6 + test/visual/side-navigation.test.ts | 6 + test/visual/slider.test.ts | 6 + test/visual/space-between.test.ts | 6 + test/visual/spinner.test.ts | 6 + test/visual/split-panel.test.ts | 6 + test/visual/status-indicator.test.ts | 6 + test/visual/steps.test.ts | 6 + test/visual/table-cells.test.ts | 6 + test/visual/table-embedded.test.ts | 6 + test/visual/table-inline-editing.test.ts | 6 + test/visual/table-resizable-columns.test.ts | 6 + test/visual/table-sticky-header.test.ts | 6 + test/visual/table-sticky-scrollbar.test.ts | 6 + test/visual/table.test.ts | 6 + test/visual/tabs.test.ts | 6 + test/visual/tag-editor.test.ts | 6 + test/visual/text-content.test.ts | 6 + test/visual/text-filter.test.ts | 6 + test/visual/textarea.test.ts | 6 + test/visual/tiles.test.ts | 6 + test/visual/time-input.test.ts | 6 + test/visual/toggle-button.test.ts | 6 + test/visual/toggle.test.ts | 6 + test/visual/token-group.test.ts | 6 + test/visual/top-navigation.test.ts | 6 + test/visual/tree-view.test.ts | 6 + test/visual/wizard.test.ts | 6 + 206 files changed, 7346 insertions(+), 8 deletions(-) create mode 100644 test/definitions/visual/app-layout-content-paddings.ts create mode 100644 test/definitions/visual/app-layout-drawers.ts create mode 100644 test/definitions/visual/app-layout-flashbar.ts create mode 100644 test/definitions/visual/app-layout-header.ts create mode 100644 test/definitions/visual/app-layout-multi.ts create mode 100644 test/definitions/visual/app-layout-responsive-1280.ts create mode 100644 test/definitions/visual/app-layout-responsive-1400.ts create mode 100644 test/definitions/visual/app-layout-responsive-1920.ts create mode 100644 test/definitions/visual/app-layout-responsive-2540.ts create mode 100644 test/definitions/visual/app-layout-responsive-600.ts create mode 100644 test/definitions/visual/app-layout-responsive-tests.ts create mode 100644 test/definitions/visual/app-layout-sticky-table-header-split-panel.ts create mode 100644 test/definitions/visual/app-layout-toolbar.ts create mode 100644 test/definitions/visual/app-layout-z-index.ts create mode 100644 test/definitions/visual/app-layout.ts create mode 100644 test/definitions/visual/area-chart.ts create mode 100644 test/definitions/visual/attribute-editor.ts create mode 100644 test/definitions/visual/autosuggest.ts create mode 100644 test/definitions/visual/badge.ts create mode 100644 test/definitions/visual/bar-chart.ts create mode 100644 test/definitions/visual/box.ts create mode 100644 test/definitions/visual/breadcrumb-group.ts create mode 100644 test/definitions/visual/button-dropdown.ts create mode 100644 test/definitions/visual/button-group.ts create mode 100644 test/definitions/visual/button.ts create mode 100644 test/definitions/visual/cards.ts create mode 100644 test/definitions/visual/checkbox.ts create mode 100644 test/definitions/visual/code-editor.ts create mode 100644 test/definitions/visual/collection-preferences.ts create mode 100644 test/definitions/visual/column-layout.ts create mode 100644 test/definitions/visual/container-sticky.ts create mode 100644 test/definitions/visual/container.ts create mode 100644 test/definitions/visual/content-layout-permutations.ts create mode 100644 test/definitions/visual/content-layout.ts create mode 100644 test/definitions/visual/copy-to-clipboard.ts create mode 100644 test/definitions/visual/date-input.ts create mode 100644 test/definitions/visual/date-picker.ts create mode 100644 test/definitions/visual/date-range-picker.ts create mode 100644 test/definitions/visual/divider.ts create mode 100644 test/definitions/visual/drawer.ts create mode 100644 test/definitions/visual/dropdown.ts create mode 100644 test/definitions/visual/expandable-section.ts create mode 100644 test/definitions/visual/file-dropzone.ts create mode 100644 test/definitions/visual/file-input.ts create mode 100644 test/definitions/visual/file-token-group.ts create mode 100644 test/definitions/visual/file-upload.ts create mode 100644 test/definitions/visual/flashbar-stacked.ts create mode 100644 test/definitions/visual/flashbar.ts create mode 100644 test/definitions/visual/form-field.ts create mode 100644 test/definitions/visual/form.ts create mode 100644 test/definitions/visual/grid.ts create mode 100644 test/definitions/visual/header.ts create mode 100644 test/definitions/visual/help-panel.ts create mode 100644 test/definitions/visual/icon.ts create mode 100644 test/definitions/visual/input.ts create mode 100644 test/definitions/visual/item-card.ts create mode 100644 test/definitions/visual/key-value-pairs.ts create mode 100644 test/definitions/visual/line-chart.ts create mode 100644 test/definitions/visual/link.ts create mode 100644 test/definitions/visual/list.ts create mode 100644 test/definitions/visual/mixed-line-bar-chart.ts create mode 100644 test/definitions/visual/modal.ts create mode 100644 test/definitions/visual/multiselect.ts create mode 100644 test/definitions/visual/pagination.ts create mode 100644 test/definitions/visual/pie-chart.ts create mode 100644 test/definitions/visual/popover.ts create mode 100644 test/definitions/visual/progress-bar.ts create mode 100644 test/definitions/visual/prompt-input.ts create mode 100644 test/definitions/visual/property-filter.ts create mode 100644 test/definitions/visual/radio-button.ts create mode 100644 test/definitions/visual/radio-group.ts create mode 100644 test/definitions/visual/s3-resource-selector.ts create mode 100644 test/definitions/visual/segmented-control.ts create mode 100644 test/definitions/visual/select.ts create mode 100644 test/definitions/visual/side-navigation.ts create mode 100644 test/definitions/visual/slider.ts create mode 100644 test/definitions/visual/space-between.ts create mode 100644 test/definitions/visual/spinner.ts create mode 100644 test/definitions/visual/split-panel.ts create mode 100644 test/definitions/visual/status-indicator.ts create mode 100644 test/definitions/visual/steps.ts create mode 100644 test/definitions/visual/table-cells.ts create mode 100644 test/definitions/visual/table-embedded.ts create mode 100644 test/definitions/visual/table-inline-editing.ts create mode 100644 test/definitions/visual/table-resizable-columns.ts create mode 100644 test/definitions/visual/table-sticky-header.ts create mode 100644 test/definitions/visual/table-sticky-scrollbar.ts create mode 100644 test/definitions/visual/table.ts create mode 100644 test/definitions/visual/tabs.ts create mode 100644 test/definitions/visual/tag-editor.ts create mode 100644 test/definitions/visual/text-content.ts create mode 100644 test/definitions/visual/text-filter.ts create mode 100644 test/definitions/visual/textarea.ts create mode 100644 test/definitions/visual/tiles.ts create mode 100644 test/definitions/visual/time-input.ts create mode 100644 test/definitions/visual/toggle-button.ts create mode 100644 test/definitions/visual/toggle.ts create mode 100644 test/definitions/visual/token-group.ts create mode 100644 test/definitions/visual/top-navigation.ts create mode 100644 test/definitions/visual/tree-view.ts create mode 100644 test/definitions/visual/wizard.ts create mode 100644 test/visual/app-layout-content-paddings.test.ts create mode 100644 test/visual/app-layout-drawers.test.ts create mode 100644 test/visual/app-layout-flashbar.test.ts create mode 100644 test/visual/app-layout-header.test.ts create mode 100644 test/visual/app-layout-multi.test.ts create mode 100644 test/visual/app-layout-responsive-1280.test.ts create mode 100644 test/visual/app-layout-responsive-1400.test.ts create mode 100644 test/visual/app-layout-responsive-1920.test.ts create mode 100644 test/visual/app-layout-responsive-2540.test.ts create mode 100644 test/visual/app-layout-responsive-600.test.ts create mode 100644 test/visual/app-layout-sticky-table-header-split-panel.test.ts create mode 100644 test/visual/app-layout-toolbar.test.ts create mode 100644 test/visual/app-layout-z-index.test.ts create mode 100644 test/visual/app-layout.test.ts create mode 100644 test/visual/area-chart.test.ts create mode 100644 test/visual/attribute-editor.test.ts create mode 100644 test/visual/autosuggest.test.ts create mode 100644 test/visual/badge.test.ts create mode 100644 test/visual/bar-chart.test.ts create mode 100644 test/visual/box.test.ts create mode 100644 test/visual/breadcrumb-group.test.ts create mode 100644 test/visual/button-dropdown.test.ts create mode 100644 test/visual/button-group.test.ts create mode 100644 test/visual/button.test.ts create mode 100644 test/visual/cards.test.ts create mode 100644 test/visual/checkbox.test.ts create mode 100644 test/visual/code-editor.test.ts create mode 100644 test/visual/collection-preferences.test.ts create mode 100644 test/visual/column-layout.test.ts create mode 100644 test/visual/container-sticky.test.ts create mode 100644 test/visual/container.test.ts create mode 100644 test/visual/content-layout-permutations.test.ts create mode 100644 test/visual/content-layout.test.ts create mode 100644 test/visual/copy-to-clipboard.test.ts create mode 100644 test/visual/date-input.test.ts create mode 100644 test/visual/date-picker.test.ts create mode 100644 test/visual/date-range-picker.test.ts create mode 100644 test/visual/divider.test.ts create mode 100644 test/visual/drawer.test.ts create mode 100644 test/visual/dropdown.test.ts create mode 100644 test/visual/expandable-section.test.ts create mode 100644 test/visual/file-dropzone.test.ts create mode 100644 test/visual/file-input.test.ts create mode 100644 test/visual/file-token-group.test.ts create mode 100644 test/visual/file-upload.test.ts create mode 100644 test/visual/flashbar-stacked.test.ts create mode 100644 test/visual/flashbar.test.ts create mode 100644 test/visual/form-field.test.ts create mode 100644 test/visual/form.test.ts create mode 100644 test/visual/grid.test.ts create mode 100644 test/visual/header.test.ts create mode 100644 test/visual/help-panel.test.ts create mode 100644 test/visual/icon.test.ts create mode 100644 test/visual/input.test.ts create mode 100644 test/visual/item-card.test.ts create mode 100644 test/visual/key-value-pairs.test.ts create mode 100644 test/visual/line-chart.test.ts create mode 100644 test/visual/link.test.ts create mode 100644 test/visual/list.test.ts create mode 100644 test/visual/mixed-line-bar-chart.test.ts create mode 100644 test/visual/modal.test.ts create mode 100644 test/visual/multiselect.test.ts create mode 100644 test/visual/pagination.test.ts create mode 100644 test/visual/pie-chart.test.ts create mode 100644 test/visual/popover.test.ts create mode 100644 test/visual/progress-bar.test.ts create mode 100644 test/visual/prompt-input.test.ts create mode 100644 test/visual/property-filter.test.ts create mode 100644 test/visual/radio-button.test.ts create mode 100644 test/visual/radio-group.test.ts create mode 100644 test/visual/s3-resource-selector.test.ts create mode 100644 test/visual/segmented-control.test.ts create mode 100644 test/visual/select.test.ts create mode 100644 test/visual/side-navigation.test.ts create mode 100644 test/visual/slider.test.ts create mode 100644 test/visual/space-between.test.ts create mode 100644 test/visual/spinner.test.ts create mode 100644 test/visual/split-panel.test.ts create mode 100644 test/visual/status-indicator.test.ts create mode 100644 test/visual/steps.test.ts create mode 100644 test/visual/table-cells.test.ts create mode 100644 test/visual/table-embedded.test.ts create mode 100644 test/visual/table-inline-editing.test.ts create mode 100644 test/visual/table-resizable-columns.test.ts create mode 100644 test/visual/table-sticky-header.test.ts create mode 100644 test/visual/table-sticky-scrollbar.test.ts create mode 100644 test/visual/table.test.ts create mode 100644 test/visual/tabs.test.ts create mode 100644 test/visual/tag-editor.test.ts create mode 100644 test/visual/text-content.test.ts create mode 100644 test/visual/text-filter.test.ts create mode 100644 test/visual/textarea.test.ts create mode 100644 test/visual/tiles.test.ts create mode 100644 test/visual/time-input.test.ts create mode 100644 test/visual/toggle-button.test.ts create mode 100644 test/visual/toggle.test.ts create mode 100644 test/visual/token-group.test.ts create mode 100644 test/visual/top-navigation.test.ts create mode 100644 test/visual/tree-view.test.ts create mode 100644 test/visual/wizard.test.ts diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index b7eddf67d4..9e6d300d91 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -83,7 +83,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1] + shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40] steps: - uses: actions/checkout@v4 @@ -135,7 +135,7 @@ jobs: run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/1 + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/40 env: TZ: UTC diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 67970b0ab6..f870e6df76 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -1,10 +1,212 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually. import { TestSuite } from './types'; export { TestSuite, TestDefinition, ScreenshotType, ScreenshotTestConfiguration } from './types'; import actionCard from './visual/action-card'; import alert from './visual/alert'; +import appLayout from './visual/app-layout'; +import appLayoutContentPaddings from './visual/app-layout-content-paddings'; +import appLayoutDrawers from './visual/app-layout-drawers'; +import appLayoutFlashbar from './visual/app-layout-flashbar'; +import appLayoutHeader from './visual/app-layout-header'; +import appLayoutMulti from './visual/app-layout-multi'; +import appLayoutResponsive600 from './visual/app-layout-responsive-600'; +import appLayoutResponsive1280 from './visual/app-layout-responsive-1280'; +import appLayoutResponsive1400 from './visual/app-layout-responsive-1400'; +import appLayoutResponsive1920 from './visual/app-layout-responsive-1920'; +import appLayoutResponsive2540 from './visual/app-layout-responsive-2540'; +import appLayoutStickyTableHeaderSplitPanel from './visual/app-layout-sticky-table-header-split-panel'; +import appLayoutToolbar from './visual/app-layout-toolbar'; +import appLayoutZIndex from './visual/app-layout-z-index'; +import areaChart from './visual/area-chart'; +import attributeEditor from './visual/attribute-editor'; +import autosuggest from './visual/autosuggest'; +import badge from './visual/badge'; +import barChart from './visual/bar-chart'; +import box from './visual/box'; +import breadcrumbGroup from './visual/breadcrumb-group'; +import button from './visual/button'; +import buttonDropdown from './visual/button-dropdown'; +import buttonGroup from './visual/button-group'; +import cards from './visual/cards'; +import checkbox from './visual/checkbox'; +import codeEditor from './visual/code-editor'; +import collectionPreferences from './visual/collection-preferences'; +import columnLayout from './visual/column-layout'; +import container from './visual/container'; +import containerSticky from './visual/container-sticky'; +import contentLayout from './visual/content-layout'; +import contentLayoutPermutations from './visual/content-layout-permutations'; +import copyToClipboard from './visual/copy-to-clipboard'; +import dateInput from './visual/date-input'; +import datePicker from './visual/date-picker'; +import dateRangePicker from './visual/date-range-picker'; +import divider from './visual/divider'; +import drawer from './visual/drawer'; +import dropdown from './visual/dropdown'; +import expandableSection from './visual/expandable-section'; +import fileDropzone from './visual/file-dropzone'; +import fileInput from './visual/file-input'; +import fileTokenGroup from './visual/file-token-group'; +import fileUpload from './visual/file-upload'; +import flashbar from './visual/flashbar'; +import flashbarStacked from './visual/flashbar-stacked'; +import form from './visual/form'; +import formField from './visual/form-field'; +import grid from './visual/grid'; +import header from './visual/header'; +import helpPanel from './visual/help-panel'; +import icon from './visual/icon'; +import input from './visual/input'; +import itemCard from './visual/item-card'; +import keyValuePairs from './visual/key-value-pairs'; +import lineChart from './visual/line-chart'; +import link from './visual/link'; +import list from './visual/list'; +import mixedLineBarChart from './visual/mixed-line-bar-chart'; +import modal from './visual/modal'; +import multiselect from './visual/multiselect'; +import pagination from './visual/pagination'; +import pieChart from './visual/pie-chart'; +import popover from './visual/popover'; +import progressBar from './visual/progress-bar'; +import promptInput from './visual/prompt-input'; +import propertyFilter from './visual/property-filter'; +import radioButton from './visual/radio-button'; +import radioGroup from './visual/radio-group'; +import s3ResourceSelector from './visual/s3-resource-selector'; +import segmentedControl from './visual/segmented-control'; +import select from './visual/select'; +import sideNavigation from './visual/side-navigation'; +import slider from './visual/slider'; +import spaceBetween from './visual/space-between'; +import spinner from './visual/spinner'; +import splitPanel from './visual/split-panel'; +import statusIndicator from './visual/status-indicator'; +import steps from './visual/steps'; +import table from './visual/table'; +import tableCells from './visual/table-cells'; +import tableEmbedded from './visual/table-embedded'; +import tableInlineEditing from './visual/table-inline-editing'; +import tableResizableColumns from './visual/table-resizable-columns'; +import tableStickyHeader from './visual/table-sticky-header'; +import tableStickyScrollbar from './visual/table-sticky-scrollbar'; +import tabs from './visual/tabs'; +import tagEditor from './visual/tag-editor'; +import textContent from './visual/text-content'; +import textFilter from './visual/text-filter'; +import textarea from './visual/textarea'; +import tiles from './visual/tiles'; +import timeInput from './visual/time-input'; +import toggle from './visual/toggle'; +import toggleButton from './visual/toggle-button'; +import tokenGroup from './visual/token-group'; +import topNavigation from './visual/top-navigation'; +import treeView from './visual/tree-view'; +import wizard from './visual/wizard'; -export const allSuites: TestSuite[] = [actionCard, alert]; +export const allSuites: TestSuite[] = [ + actionCard, + alert, + appLayout, + appLayoutContentPaddings, + appLayoutDrawers, + appLayoutFlashbar, + appLayoutHeader, + appLayoutMulti, + appLayoutResponsive1280, + appLayoutResponsive1400, + appLayoutResponsive1920, + appLayoutResponsive2540, + appLayoutResponsive600, + appLayoutStickyTableHeaderSplitPanel, + appLayoutToolbar, + appLayoutZIndex, + areaChart, + attributeEditor, + autosuggest, + badge, + barChart, + box, + breadcrumbGroup, + button, + buttonDropdown, + buttonGroup, + cards, + checkbox, + codeEditor, + collectionPreferences, + columnLayout, + container, + containerSticky, + contentLayout, + contentLayoutPermutations, + copyToClipboard, + dateInput, + datePicker, + dateRangePicker, + divider, + drawer, + dropdown, + expandableSection, + fileDropzone, + fileInput, + fileTokenGroup, + fileUpload, + flashbar, + flashbarStacked, + form, + formField, + grid, + header, + helpPanel, + icon, + input, + itemCard, + keyValuePairs, + lineChart, + link, + list, + mixedLineBarChart, + modal, + multiselect, + pagination, + pieChart, + popover, + progressBar, + promptInput, + propertyFilter, + radioButton, + radioGroup, + s3ResourceSelector, + segmentedControl, + select, + sideNavigation, + slider, + spaceBetween, + spinner, + splitPanel, + statusIndicator, + steps, + table, + tableCells, + tableEmbedded, + tableInlineEditing, + tableResizableColumns, + tableStickyHeader, + tableStickyScrollbar, + tabs, + tagEditor, + textContent, + textFilter, + textarea, + tiles, + timeInput, + toggle, + toggleButton, + tokenGroup, + topNavigation, + treeView, + wizard, +]; diff --git a/test/definitions/visual/alert.ts b/test/definitions/visual/alert.ts index a4169741e3..3220c79e7e 100644 --- a/test/definitions/visual/alert.ts +++ b/test/definitions/visual/alert.ts @@ -21,13 +21,11 @@ const suite: TestSuite = { description: `width ${width}px`, tests: [ { - configuration: { width }, description: 'permutations', path: 'alert/permutations', screenshotType: 'permutations' as const, }, { - configuration: { width }, description: 'custom types', path: 'alert/style-custom-types', screenshotType: 'screenshotArea' as const, diff --git a/test/definitions/visual/app-layout-content-paddings.ts b/test/definitions/visual/app-layout-content-paddings.ts new file mode 100644 index 0000000000..e76d055749 --- /dev/null +++ b/test/definitions/visual/app-layout-content-paddings.ts @@ -0,0 +1,30 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Content paddings', + componentName: 'app-layout', + tests: [ + ...(['true', 'false'] as const).flatMap(toolsEnabled => + (['true', 'false'] as const).flatMap(splitPanelEnabled => + (['bottom', 'side'] as const).map(splitPanelPosition => ({ + description: `toolsEnabled=${toolsEnabled} splitPanelEnabled=${splitPanelEnabled} splitPanelPosition=${splitPanelPosition}`, + path: 'app-layout/with-split-panel', + screenshotType: 'viewport' as const, + queryParams: { toolsEnabled, splitPanelEnabled, splitPanelPosition }, + })) + ) + ), + ...[1500, 600].map(width => ({ + description: `with split panel and disabled content paddings - width=${width}`, + path: 'app-layout/disable-paddings-with-split-panel', + screenshotType: 'viewport' as const, + configuration: { width }, + queryParams: { splitPanelOpen: 'true', splitPanelPosition: 'side' }, + })), + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-drawers.ts b/test/definitions/visual/app-layout-drawers.ts new file mode 100644 index 0000000000..5fe2f12b5d --- /dev/null +++ b/test/definitions/visual/app-layout-drawers.ts @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Drawers', + componentName: 'app-layout', + tests: [ + { + description: 'with split panel', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + setup: async (page, wrapper) => { + await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); + }, + }, + { + description: 'with tooltip on hover', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + setup: async (page, wrapper) => { + await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); + }, + }, + { + description: 'with custom scrollable drawer content', + path: 'app-layout/with-drawers-scrollable', + screenshotType: 'viewport', + queryParams: { sideNavFill: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-flashbar.ts b/test/definitions/visual/app-layout-flashbar.ts new file mode 100644 index 0000000000..5849a5cbf5 --- /dev/null +++ b/test/definitions/visual/app-layout-flashbar.ts @@ -0,0 +1,39 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Flashbar', + componentName: 'app-layout', + tests: [true, false].flatMap(disableContentPaddings => + [true, false].flatMap(stickyNotifications => + [true, false].flatMap(stickyTableHeader => + [true, false].map(stackNotifications => ({ + description: `disableContentPaddings: ${disableContentPaddings}, stickyNotifications: ${stickyNotifications}, stickyTableHeader: ${stickyTableHeader}, stackNotifications: ${stackNotifications}`, + path: 'app-layout/with-stacked-notifications-and-table', + screenshotType: 'screenshotArea' as const, + configuration: { width: 1280, height: 900 }, + setup: async (page: import('@cloudscape-design/browser-test-tools/page-objects').ScreenshotPageObject) => { + if (!disableContentPaddings) { + await page.click('[data-id="toggle-content-paddings"]'); + } + if (stickyNotifications) { + await page.click('[data-id="toggle-sticky-notifications"]'); + } + if (!stickyTableHeader) { + await page.click('[data-id="toggle-sticky-table-header"]'); + } + if (!stackNotifications) { + await page.click('[data-id="toggle-stack-items"]'); + } + await page.click('[data-id="add-notification"]'); + await page.click('[data-id="add-notification"]'); + }, + })) + ) + ) + ), +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-header.ts b/test/definitions/visual/app-layout-header.ts new file mode 100644 index 0000000000..3a128a266e --- /dev/null +++ b/test/definitions/visual/app-layout-header.ts @@ -0,0 +1,85 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Headers', + componentName: 'app-layout', + tests: [ + // ── Headers ─────────────────────────────────────────────────────────── + { + description: 'Headers', + tests: [600, 1280].flatMap(width => [ + { + description: `alignment with full-page table (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + { + description: `alignment with full-page table in sticky state (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'viewport' as const, + configuration: { width }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `alignment with full-page table in sticky state with sticky notifications (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'viewport' as const, + configuration: { width }, + queryParams: { stickyNotifications: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `high contrast header variant in landing page (${width}px)`, + path: 'app-layout/landing-page', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + ]), + }, + + // ── High contrast header variant ────────────────────────────────────── + { + description: 'High contrast header variant', + tests: [ + ...[1400, 600].flatMap(width => [ + { + description: `with breadcrumbs and notifications at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', hasContainer: 'true' }, + } as TestDefinition, + { + description: `without overlap at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { disableOverlap: 'true' }, + } as TestDefinition, + { + description: `with content layout at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + hasContainer: 'true', + hasContentLayout: 'true', + }, + } as TestDefinition, + ]), + ], + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-multi.ts b/test/definitions/visual/app-layout-multi.ts new file mode 100644 index 0000000000..babf9733cf --- /dev/null +++ b/test/definitions/visual/app-layout-multi.ts @@ -0,0 +1,25 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Multiple instances', + componentName: 'app-layout', + tests: [600, 1280].flatMap(width => [ + { + description: `simple (${width}px)`, + path: 'app-layout/multi-layout-simple', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + { + description: `iframe (${width}px)`, + path: 'app-layout/multi-layout-iframe', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1280.ts b/test/definitions/visual/app-layout-responsive-1280.ts new file mode 100644 index 0000000000..d5fe823f54 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1280.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1280); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1400.ts b/test/definitions/visual/app-layout-responsive-1400.ts new file mode 100644 index 0000000000..1cb519005c --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1400.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1400); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1920.ts b/test/definitions/visual/app-layout-responsive-1920.ts new file mode 100644 index 0000000000..88b8c6caf3 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1920.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1920); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-2540.ts b/test/definitions/visual/app-layout-responsive-2540.ts new file mode 100644 index 0000000000..9dd62c00db --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-2540.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(2540); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-600.ts b/test/definitions/visual/app-layout-responsive-600.ts new file mode 100644 index 0000000000..f6fd3665cc --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-600.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(600); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-tests.ts b/test/definitions/visual/app-layout-responsive-tests.ts new file mode 100644 index 0000000000..f0ac91d121 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-tests.ts @@ -0,0 +1,159 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +/** + * Shared responsive test scenarios for app-layout. Each width gets its own + * definition file and test runner so that Jest sharding can parallelize them. + */ +export function responsiveTests(width: number): TestSuite { + return { + description: `AppLayout responsive width ${width}px`, + componentName: 'app-layout', + tests: [ + { + description: 'default', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'navigation drawer is open', + path: 'app-layout/with-wizard', + screenshotType: 'viewport', + configuration: { width }, + setup: async page => { + await page.click('[aria-label="Open navigation"]'); + }, + }, + { + description: 'wizard', + path: 'app-layout/with-wizard', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with wizard and table', + path: 'app-layout/with-wizard-and-table', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with wizard, table, and breadcrumbs', + path: 'app-layout/with-wizard-and-table', + screenshotType: 'viewport', + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true' }, + }, + { + description: 'notifications', + path: 'app-layout/with-notifications', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'breadcrumbs', + path: 'app-layout/with-breadcrumbs', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'notifications and breadcrumbs', + path: 'app-layout/with-breadcrumbs-notifications', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'dashboard content type', + path: 'app-layout/dashboard-content-type', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'fixed header and footer', + path: 'app-layout/with-fixed-header-footer', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disableBodyScroll - empty', + path: 'app-layout/legacy-nav-empty', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disableBodyScroll - with content', + path: 'app-layout/legacy-nav-scrollable', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disableBodyScroll - with split panel', + path: 'app-layout/legacy-nav-scrollable-with-split-panel', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disable paddings', + path: 'app-layout/disable-paddings', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disable paddings with breadcrumbs', + path: 'app-layout/disable-paddings-breadcrumbs', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'sticky notifications', + path: 'app-layout/with-sticky-notifications', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'sticky notifications scrolled down', + path: 'app-layout/with-sticky-notifications', + screenshotType: 'viewport', + configuration: { width }, + setup: async page => { + await page.windowScrollTo({ top: 2000 }); + }, + }, + { + description: 'layout without panels', + path: 'app-layout/no-panels', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'layout without panels but with notifications', + path: 'app-layout/no-panels-with-notifications', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with drawers', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with empty drawers', + path: 'app-layout/with-drawers-empty', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with open drawer', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + configuration: { width }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + }, + }, + ], + }; +} diff --git a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts new file mode 100644 index 0000000000..6a0b899686 --- /dev/null +++ b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts @@ -0,0 +1,78 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Sticky header with split panel', + componentName: 'app-layout', + tests: [ + { + description: 'scrolling to bottom with closed split panel (1 table row)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-1"]'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'scrolling to bottom with closed split panel (30 table rows)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky with open split panel (1 table row)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-1"]'); + await page.click('aria/Open panel'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky with open split panel (30 table rows)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.click('aria/Open panel'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky when mounting and unmounting a second table', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.click('aria/Open panel'); + await page.windowScrollTo({ top: 0 }); + await page.click('aria/Close panel'); + await page.scrollToBottom('html'); + }, + }, + // ── Max content width ───────────────────────────────────────────────── + { + description: 'maxContentWidth set to Number.MAX_VALUE', + path: 'app-layout/refresh-content-width', + screenshotType: 'viewport', + configuration: { width: 1280, height: 700 }, + setup: async page => { + await page.click('[data-test-id="button_width-number-max_value"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-toolbar.ts b/test/definitions/visual/app-layout-toolbar.ts new file mode 100644 index 0000000000..fb174b0a71 --- /dev/null +++ b/test/definitions/visual/app-layout-toolbar.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Toolbar', + componentName: 'app-layout', + tests: [ + { + description: 'multiple nested instances (no breadcrumbs dedup)', + path: 'app-layout-toolbar/multi-layout-with-hidden-instances', + screenshotType: 'viewport', + }, + { + description: 'no toolbar', + path: 'app-layout-toolbar/without-toolbar', + screenshotType: 'viewport', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-z-index.ts b/test/definitions/visual/app-layout-z-index.ts new file mode 100644 index 0000000000..b0dacf8bc5 --- /dev/null +++ b/test/definitions/visual/app-layout-z-index.ts @@ -0,0 +1,60 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Z-index', + componentName: 'app-layout', + tests: [ + ...[600, 1280].flatMap(width => [ + { + description: `button dropdown (${width}px)`, + path: 'app-layout/with-absolute-components', + screenshotType: 'viewport' as const, + configuration: { width }, + setup: async page => { + await page.click('button=Button dropdown'); + await page.click('[data-testid="2"]'); + await page.windowScrollTo({ top: 300 }); + }, + } as TestDefinition, + { + description: `select (${width}px)`, + path: 'app-layout/with-absolute-components', + screenshotType: 'viewport' as const, + configuration: { width, height: 800 }, + setup: async page => { + await page.click('[data-testid="select-demo"] button'); + await page.windowScrollTo({ top: 300 }); + }, + } as TestDefinition, + { + description: `split-panel and full-page table (${width}px)`, + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + ]), + { + description: 'split-panel and full-page with open navigation (600px)', + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'viewport' as const, + configuration: { width: 600 }, + setup: async page => { + await page.click('button[aria-label="Open navigation"]'); + }, + }, + { + description: 'split-panel and full-page with open tools (600px)', + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'viewport' as const, + configuration: { width: 600 }, + setup: async page => { + await page.click('button[aria-label="Open tools"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts new file mode 100644 index 0000000000..683efdcc25 --- /dev/null +++ b/test/definitions/visual/app-layout.ts @@ -0,0 +1,104 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'AppLayout', + componentName: 'app-layout', + tests: [ + { + description: 'no scrollbars at 320px', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width: 320 }, + }, + { + description: 'drawer buttons alignment', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('[aria-label="Open tools"]'); + }, + }, + { + description: 'disable paddings - navigation closed', + path: 'app-layout/disable-paddings', + screenshotType: 'viewport', + configuration: { width: 1280 }, + setup: async page => { + await page.click('[aria-label="Close navigation"]'); + }, + }, + { + description: 'panels stacking on mobile', + path: 'app-layout/all-panels-open', + screenshotType: 'viewport', + configuration: { width: 600 }, + }, + { + description: 'wrapping long words', + path: 'app-layout/text-wrap', + screenshotType: 'viewport', + }, + { + description: 'fill content area', + path: 'app-layout/fill-content-area', + screenshotType: 'viewport', + }, + { + description: 'with tools and drawers', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + queryParams: { hasTools: 'true' }, + }, + { + description: 'with open drawer and open side split panel', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { splitPanelPosition: 'side' }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + await page.click('[aria-label="Open panel"]'); + }, + }, + + // regression for https://github.com/cloudscape-design/components/pull/1612 + { + description: 'with open drawer and open side split panel after resize', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + configuration: { width: 1500 }, + queryParams: { splitPanelPosition: 'side' }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + await page.click('[aria-label="Open panel"]'); + await page.setWindowSize({ width: 1400, height: 800 }); + }, + }, + + // ── Transitions ─────────────────────────────────────────────────────── + { + description: 'transition from 400px to 1800px', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width: 400, height: 400 }, + setup: async page => { + await page.setWindowSize({ width: 1800, height: 400 }); + }, + }, + { + description: 'transition from 1800px to 400px', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width: 1800, height: 400 }, + setup: async page => { + await page.setWindowSize({ width: 400, height: 400 }); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/area-chart.ts b/test/definitions/visual/area-chart.ts new file mode 100644 index 0000000000..cfa0b5e47b --- /dev/null +++ b/test/definitions/visual/area-chart.ts @@ -0,0 +1,136 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_FILTER_TRIGGER = '#linear-latency-chart button'; +const TEST_CHART_TOOLTIP_HEADER = '#linear-latency-chart h2'; + +const suite: TestSuite = { + description: 'Area chart', + componentName: 'area-chart', + tests: [ + { + description: 'permutations', + path: 'area-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'fit-height', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit-height no filter, no legend', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFilter: 'true', hideLegend: 'true' }, + }, + { + description: 'fit-height, no legend', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideLegend: 'true' }, + }, + { + description: 'chart plot has a focus outline', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + }, + }, + { + description: 'can navigate along X axis highlighting all series with keyboard', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can navigate a specific series with keyboard', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowDown']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'selects correct series when navigated back from legend', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.keys(['Tab']); + await page.keys(['Tab']); + await page.keys(['ArrowRight']); + await page.keys(['Shift', 'Tab']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover for all data points at a given X coordinate with keyboard', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'can pin popover for a point in a specific series with keyboard', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowDown']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('[aria-label="Linear latency chart"]', 200, 50); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/attribute-editor.ts b/test/definitions/visual/attribute-editor.ts new file mode 100644 index 0000000000..33da3fad86 --- /dev/null +++ b/test/definitions/visual/attribute-editor.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Attribute Editor', + componentName: 'attribute-editor', + tests: [360, 768, 992].flatMap(width => [ + { + description: `permutations at ${width}px`, + path: 'attribute-editor/permutations', + screenshotType: 'permutations' as const, + configuration: { width }, + }, + { + description: `customizable-footer at ${width}px`, + path: 'attribute-editor/customizable-footer', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: `with long select at ${width}px`, + path: 'attribute-editor/select-with-long-value', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/autosuggest.ts b/test/definitions/visual/autosuggest.ts new file mode 100644 index 0000000000..62910a0ed1 --- /dev/null +++ b/test/definitions/visual/autosuggest.ts @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Autosuggest', + componentName: 'autosuggest', + tests: [ + { + description: 'permutations', + path: 'autosuggest/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'permutations for async properties', + path: 'autosuggest/permutations-async', + screenshotType: 'permutations', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Displays options with groups correctly', + path: 'autosuggest/scenarios', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Correctly displays dropdown regions', + path: 'autosuggest/regions-scenarios', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Long virtual list - navigate to last item', + path: 'autosuggest/virtual-scroll', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); + await page.keys(['ArrowUp']); + }, + }, + ...[true, false].map( + virtualScroll => + ({ + description: `with custom renderOption (virtualScroll=${virtualScroll})`, + path: 'autosuggest/custom-render-option', + screenshotType: 'screenshotArea' as const, + queryParams: { virtualScroll: String(virtualScroll) }, + setup: async (page, wrapper) => { + await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); + await page.keys(['ArrowDown']); + }, + }) as TestDefinition + ), + ], +}; + +export default suite; diff --git a/test/definitions/visual/badge.ts b/test/definitions/visual/badge.ts new file mode 100644 index 0000000000..7d405ed843 --- /dev/null +++ b/test/definitions/visual/badge.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Badge', + componentName: 'badge', + tests: [ + { + description: 'permutation page', + path: 'badge/permutations', + screenshotType: 'permutations', + }, + { + description: 'style custom page', + path: 'badge/style-custom-types', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/bar-chart.ts b/test/definitions/visual/bar-chart.ts new file mode 100644 index 0000000000..e61b6b36c1 --- /dev/null +++ b/test/definitions/visual/bar-chart.ts @@ -0,0 +1,97 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_TOOLTIP_HEADER = '#chart'; + +const suite: TestSuite = { + description: 'Bar chart', + componentName: 'bar-chart', + tests: [ + { + description: 'Horizontal bars permutations', + path: 'bar-chart/horizontal-bars-permutations', + screenshotType: 'permutations', + }, + { + description: 'Horizontal stacked bars permutations', + path: 'bar-chart/horizontal-stacked-bars-permutations', + screenshotType: 'permutations', + }, + { + description: 'Other permutations', + path: 'bar-chart/other-permutations', + screenshotType: 'permutations', + }, + { + description: 'Threshold permutations', + path: 'bar-chart/threshold-permutations', + screenshotType: 'permutations', + }, + { + description: 'Vertical bars permutations', + path: 'bar-chart/vertical-bars-permutations', + screenshotType: 'permutations', + }, + { + description: 'Vertical stacked bars permutations', + path: 'bar-chart/vertical-stacked-bars-permutations', + screenshotType: 'permutations', + }, + { + description: 'can navigate series with keyboard', + path: 'bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover with keyboard', + path: 'bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('#chart svg[aria-label="Bar chart"]'); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'wrapping long series title 123', + path: 'bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.scrollToBottom('html'); + await page.click('#focus-target-3'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/box.ts b/test/definitions/visual/box.ts new file mode 100644 index 0000000000..0de10d4318 --- /dev/null +++ b/test/definitions/visual/box.ts @@ -0,0 +1,43 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Box', + componentName: 'box', + tests: [ + { + description: 'variants permutations', + path: 'box/variants', + screenshotType: 'permutations', + }, + { + description: 'margins permutations', + path: 'box/margins', + screenshotType: 'permutations', + }, + { + description: 'paddings permutations', + path: 'box/paddings', + screenshotType: 'permutations', + }, + { + description: 'float and textAlign', + path: 'box/float-align', + screenshotType: 'permutations', + }, + { + description: 'with overrides to layout defaults', + path: 'box/elements-with-extra-defaults', + screenshotType: 'screenshotArea', + }, + { + description: 'with icons in content', + path: 'box/iconography', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/breadcrumb-group.ts b/test/definitions/visual/breadcrumb-group.ts new file mode 100644 index 0000000000..72fbddf96f --- /dev/null +++ b/test/definitions/visual/breadcrumb-group.ts @@ -0,0 +1,46 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'BreadcrumbGroup', + componentName: 'breadcrumb-group', + tests: [ + { + description: 'layout at 300px', + path: 'breadcrumb-group/scenarios', + screenshotType: 'screenshotArea', + configuration: { width: 300 }, + }, + { + description: 'layout at 680px', + path: 'breadcrumb-group/scenarios', + screenshotType: 'screenshotArea', + configuration: { width: 680 }, + }, + { + description: 'layout at 1200px', + path: 'breadcrumb-group/scenarios', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + { + description: 'dropdown', + path: 'breadcrumb-group/scenarios', + screenshotType: 'viewport', + configuration: { width: 300, height: 1000 }, + setup: async (page, wrapper) => { + await page.click(wrapper.findBreadcrumbGroup('[data-testid="breadcrumbs-6"]').findDropdown().toSelector()); + }, + }, + { + description: 'responsive behavior', + path: 'breadcrumb-group/responsive', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/button-dropdown.ts b/test/definitions/visual/button-dropdown.ts new file mode 100644 index 0000000000..3dda4a427c --- /dev/null +++ b/test/definitions/visual/button-dropdown.ts @@ -0,0 +1,395 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ButtonDropdown', + componentName: 'button-dropdown', + tests: [ + { + description: 'ButtonDropdown opening top left at 500', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-top-left'); + }, + }, + { + description: 'ButtonDropdown opening top right at 500', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-top-right'); + }, + }, + { + description: 'ButtonDropdown opening bottom left at 500', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-bottom-left'); + }, + }, + { + description: 'ButtonDropdown opening bottom right at 500', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-bottom-right'); + }, + }, + { + description: 'ButtonDropdown opening top left at 800', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('.bd-top-left'); + }, + }, + { + description: 'ButtonDropdown opening top right at 800', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('.bd-top-right'); + }, + }, + { + description: 'ButtonDropdown opening bottom left at 800', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('.bd-bottom-left'); + }, + }, + { + description: 'ButtonDropdown opening bottom right at 800', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('.bd-bottom-right'); + }, + }, + { + description: 'ButtonDropdown opening top left, width maximum truncated', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 230, height: 400 }, + setup: async page => { + await page.click('.bd-top-left'); + }, + }, + { + description: 'ButtonDropdown opening bottom left, width maximum truncated', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 230, height: 400 }, + setup: async page => { + await page.click('.bd-bottom-left'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening top left at 500', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-top-left'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening top right at 500', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-top-right'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening bottom left at 500', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-bottom-left'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening bottom right at 500', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-bottom-right'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening top left at 1200', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 1200 }, + setup: async page => { + await page.click('.bd-top-left'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening top right at 1200', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 1200 }, + setup: async page => { + await page.click('.bd-top-right'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening bottom left at 1200', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 1200 }, + setup: async page => { + await page.click('.bd-bottom-left'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening bottom right at 1200', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 1200 }, + setup: async page => { + await page.click('.bd-bottom-right'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown in scrollable container', + path: 'button-dropdown/scenarios-container', + screenshotType: 'viewport', + configuration: { width: 600 }, + setup: async page => { + await page.waitForVisible('#scrollable-container'); + const containerBBox = await page.getBoundingBox('#scrollable-container'); + const buttonBBox = await page.getBoundingBox('#ButtonDropdown button'); + await page.elementScrollTo('#scrollable-container', { + top: (containerBBox.height + buttonBBox.width) / 2, + left: (containerBBox.width + buttonBBox.width) / 2, + }); + await page.click('#ButtonDropdown'); + }, + }, + { + description: 'ButtonDropdown with expandToViewport overflowing a scroll container', + path: 'button-dropdown/scenarios-overflow-container', + screenshotType: 'viewport', + configuration: { width: 1000 }, + setup: async page => { + await page.waitForVisible('#scroll-container'); + const containerBBox = await page.getBoundingBox('#scroll-container'); + const buttonBBox = await page.getBoundingBox('#button-dropdown-scroll button'); + await page.elementScrollTo('#scroll-container', { + top: (containerBBox.height + buttonBBox.width) / 2, + left: (containerBBox.width + buttonBBox.width) / 2, + }); + await page.click('#button-dropdown-scroll'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandToViewport overflowing a hidden container', + path: 'button-dropdown/scenarios-overflow-container', + screenshotType: 'viewport', + configuration: { width: 1000 }, + setup: async page => { + await page.waitForVisible('#hidden-container'); + await page.click('#button-dropdown-hidden'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandToViewport overflowing an auto container', + path: 'button-dropdown/scenarios-overflow-container', + screenshotType: 'viewport', + configuration: { width: 1000 }, + setup: async page => { + await page.waitForVisible('#auto-container'); + const containerBBox = await page.getBoundingBox('#auto-container'); + const buttonBBox = await page.getBoundingBox('#button-dropdown-auto button'); + await page.elementScrollTo('#auto-container', { + top: (containerBBox.height + buttonBBox.width) / 2, + left: (containerBBox.width + buttonBBox.width) / 2, + }); + await page.click('#button-dropdown-auto'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown permutations', + path: 'button-dropdown/permutations', + screenshotType: 'permutations', + }, + { + description: 'ButtonDropdown item element permutations', + path: 'button-dropdown/item-element.permutations', + screenshotType: 'permutations', + }, + { + description: 'ButtonDropdown main action permutations', + path: 'button-dropdown/permutations-main-action', + screenshotType: 'permutations', + }, + { + description: 'ButtonDropdown dimmed category group at width 500', + path: 'button-dropdown/simple', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#ButtonDropdown8'); + await page.keys(['ArrowDown', 'ArrowDown', 'Enter']); + }, + }, + { + description: 'ButtonDropdown dimmed category group at width 800', + path: 'button-dropdown/simple', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#ButtonDropdown8'); + await page.keys(['ArrowDown', 'ArrowDown', 'Enter']); + }, + }, + { + description: 'ButtonDropdown with disabled reason at width 500', + path: 'button-dropdown/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="buttonDropdown"]'); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + { + description: 'ButtonDropdown with disabled reason at width 800', + path: 'button-dropdown/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="buttonDropdown"]'); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + { + description: 'ButtonDropdown with disabled reason for selectable item at width 500', + path: 'button-dropdown/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="buttonDropdownSelectableItems"]'); + }, + }, + { + description: 'ButtonDropdown with disabled reason for selectable item at width 800', + path: 'button-dropdown/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="buttonDropdownSelectableItems"]'); + }, + }, + { + description: 'ButtonDropdown expandable groups show icons at width 500', + path: 'button-dropdown/icon-expandable', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + queryParams: { expandableGroups: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown non-expandable groups show icons at width 500', + path: 'button-dropdown/icon-expandable', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + queryParams: { expandableGroups: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + }, + }, + { + description: 'ButtonDropdown expandable groups show icons at width 800', + path: 'button-dropdown/icon-expandable', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { expandableGroups: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown non-expandable groups show icons at width 800', + path: 'button-dropdown/icon-expandable', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { expandableGroups: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + }, + }, + { + description: 'ButtonDropdown expandable groups with custom renderItem width 500', + path: 'button-dropdown/custom-render-item', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + queryParams: { expandableGroups: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + await page.click('[data-testid="group"]'); + }, + }, + { + description: 'ButtonDropdown non-expandable groups with custom renderItem width 500', + path: 'button-dropdown/custom-render-item', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + queryParams: { expandableGroups: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + }, + }, + { + description: 'ButtonDropdown expandable groups with custom renderItem width 1000', + path: 'button-dropdown/custom-render-item', + screenshotType: 'screenshotArea', + configuration: { width: 1000 }, + queryParams: { expandableGroups: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + await page.click('[data-testid="group"]'); + }, + }, + { + description: 'ButtonDropdown non-expandable groups with custom renderItem width 1000', + path: 'button-dropdown/custom-render-item', + screenshotType: 'screenshotArea', + configuration: { width: 1000 }, + queryParams: { expandableGroups: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/button-group.ts b/test/definitions/visual/button-group.ts new file mode 100644 index 0000000000..ecdbc17d06 --- /dev/null +++ b/test/definitions/visual/button-group.ts @@ -0,0 +1,52 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ButtonGroup', + componentName: 'button-group', + tests: [ + { + description: 'item permutations', + path: 'button-group/item-permutations', + screenshotType: 'permutations', + }, + { + description: 'permutations', + path: 'button-group/permutations', + screenshotType: 'permutations', + }, + { + description: 'shows tooltip when hovering item', + path: 'button-group/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="like"]'); + }, + }, + { + description: 'shows tooltip when hovering menu', + path: 'button-group/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="more-actions"]'); + }, + }, + { + description: 'shows feedback when clicking copy', + path: 'button-group/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="copy"]'); + }, + }, + { + description: 'style custom page', + path: 'button-group/style-custom-types', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/button.ts b/test/definitions/visual/button.ts new file mode 100644 index 0000000000..198412c3ec --- /dev/null +++ b/test/definitions/visual/button.ts @@ -0,0 +1,73 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Button', + componentName: 'button', + tests: [ + { + description: 'permutations', + path: 'button/permutations', + screenshotType: 'permutations', + }, + { + description: 'style-permutations', + path: 'button/style-permutations', + screenshotType: 'permutations', + }, + { + description: 'style-custom-types', + path: 'button/style-custom-types', + screenshotType: 'screenshotArea', + }, + { + description: 'external', + path: 'button/external.permutations', + screenshotType: 'permutations', + }, + { + description: 'alignment', + path: 'button/alignment', + screenshotType: 'screenshotArea', + }, + { + description: 'wrapping text', + path: 'button/text-wrap', + screenshotType: 'screenshotArea', + }, + { + description: 'wrapping text with icon', + path: 'button/with-icon-wrap', + screenshotType: 'screenshotArea', + }, + { + description: 'Button is focused', + path: 'button/tab-navigation', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focusButton'); + await page.focusNextElement(); + }, + }, + { + description: 'shows disabled reason tooltip on hover within modal', + path: 'button/disabled-reason-modal', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="button"]'); + }, + }, + { + description: 'shows disabled reason tooltip on hover over a button with an href', + path: 'button/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="normal-button-with-href"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/cards.ts b/test/definitions/visual/cards.ts new file mode 100644 index 0000000000..25227eedb1 --- /dev/null +++ b/test/definitions/visual/cards.ts @@ -0,0 +1,49 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Cards', + componentName: 'cards', + tests: [ + { + description: 'permutations at 2200', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 2200 }, + }, + { + description: 'permutations at 1920', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 1920 }, + }, + { + description: 'permutations at 1400', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 1400 }, + }, + { + description: 'permutations at 1200', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 1200 }, + }, + { + description: 'permutations at 992', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 992 }, + }, + { + description: 'permutations at 768', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 768 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/checkbox.ts b/test/definitions/visual/checkbox.ts new file mode 100644 index 0000000000..34e92eff2b --- /dev/null +++ b/test/definitions/visual/checkbox.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Checkbox', + componentName: 'checkbox', + tests: [ + { + description: 'Permutations', + path: 'checkbox/permutations', + screenshotType: 'permutations', + }, + { + description: 'Checkbox is focused', + path: 'checkbox/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'Checkbox has label with a correct width', + path: 'checkbox/labels-highlight', + screenshotType: 'screenshotArea', + }, + { + description: 'Style custom page', + path: 'checkbox/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts new file mode 100644 index 0000000000..051ae5bd92 --- /dev/null +++ b/test/definitions/visual/code-editor.ts @@ -0,0 +1,68 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const ACE_SELECTOR = '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright'; + +const suite: TestSuite = { + description: 'Code editor', + componentName: 'code-editor', + tests: [ + { + description: 'simple', + path: 'code-editor/simple', + screenshotType: 'screenshotArea', + setup: async page => { + await page.waitForVisible(ACE_SELECTOR); + }, + }, + { + description: 'error', + path: 'code-editor/error', + screenshotType: 'screenshotArea', + }, + { + description: 'loading', + path: 'code-editor/loading', + screenshotType: 'screenshotArea', + }, + { + description: 'theme resolution', + path: 'code-editor/themes', + screenshotType: 'screenshotArea', + setup: async page => { + await page.waitForVisible(ACE_SELECTOR); + }, + }, + { + description: 'permutations', + path: 'code-editor/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.waitForVisible(ACE_SELECTOR + ' .ace_error'); + await page.waitForVisible('.ace_gutter-cell.ace_gutter-active-line.ace_error'); + }, + }, + { + description: 'listens to mode change', + path: 'code-editor/simple', + screenshotType: 'screenshotArea', + setup: async page => { + await page.waitForVisible(ACE_SELECTOR); + await page.click('#mode-toggle'); + }, + }, + { + description: 'compare simple on small screen', + path: 'code-editor/simple', + screenshotType: 'screenshotArea', + configuration: { width: 360 }, + setup: async page => { + await page.waitForVisible(ACE_SELECTOR); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/collection-preferences.ts b/test/definitions/visual/collection-preferences.ts new file mode 100644 index 0000000000..3448633608 --- /dev/null +++ b/test/definitions/visual/collection-preferences.ts @@ -0,0 +1,82 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'CollectionPreferences', + componentName: 'collection-preferences', + tests: [ + { + description: 'complete at 600x1100', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + configuration: { width: 600, height: 1100 }, + setup: async page => { + await page.click('.cp-1 button'); + }, + }, + { + description: 'visible content only at 600x1100', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + setup: async page => { + await page.click('.cp-4 button'); + }, + }, + { + description: 'complete at 1280x700', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + configuration: { width: 1280, height: 700 }, + setup: async page => { + await page.click('.cp-1 button'); + }, + }, + { + description: 'visible content only at 1280x700', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + setup: async page => { + await page.click('.cp-4 button'); + }, + }, + { + description: 'custom', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + setup: async page => { + await page.click('.cp-2 button'); + }, + }, + { + description: 'Content reordering', + componentName: 'collection-preferences', + tests: [ + { + description: 'drag handle focused', + path: 'collection-preferences/reorder-content', + screenshotType: 'viewport', + configuration: { width: 900, height: 650 }, + setup: async page => { + await page.click('.cp-1 button'); + await page.keys(Array(5).fill('Tab')); + }, + }, + { + description: 'reordering active', + path: 'collection-preferences/reorder-content', + screenshotType: 'viewport', + configuration: { width: 900, height: 650 }, + setup: async page => { + await page.click('.cp-1 button'); + await page.keys(Array(5).fill('Tab')); + await page.keys('Space'); + }, + }, + ], + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/column-layout.ts b/test/definitions/visual/column-layout.ts new file mode 100644 index 0000000000..a889050b9b --- /dev/null +++ b/test/definitions/visual/column-layout.ts @@ -0,0 +1,61 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ColumnLayout', + componentName: 'column-layout', + tests: [ + { + description: 'column-layout at "default"', + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'permutations at "default"', + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width: 400 }, + }, + { + description: 'column-layout at "xxs"', + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + }, + { + description: 'permutations at "xxs"', + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width: 500 }, + }, + { + description: 'column-layout at "xs"', + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + }, + { + description: 'permutations at "xs"', + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width: 800 }, + }, + { + description: 'column-layout at "m"', + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + { + description: 'permutations at "m"', + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width: 1200 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/container-sticky.ts b/test/definitions/visual/container-sticky.ts new file mode 100644 index 0000000000..ef67bd3d09 --- /dev/null +++ b/test/definitions/visual/container-sticky.ts @@ -0,0 +1,125 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Container sticky permutations', + componentName: 'container', + tests: [ + { + description: 'simple - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with notifications - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasNotifications: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with breadcrumbs - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with an alert - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with an alert - at 1400px without scroll', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + }, + { + description: 'with high-contrast header - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasNotifications: 'true', highContrast: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'simple - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with notifications - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasNotifications: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with breadcrumbs - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasBreadcrumbs: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with an alert - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with an alert - at 600px without scroll', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + }, + { + description: 'with high-contrast header - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasNotifications: 'true', highContrast: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/container.ts b/test/definitions/visual/container.ts new file mode 100644 index 0000000000..a0ac94e8d0 --- /dev/null +++ b/test/definitions/visual/container.ts @@ -0,0 +1,87 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Container and header', + componentName: 'container', + tests: [ + { + description: 'simple', + path: 'container/simple', + screenshotType: 'screenshotArea', + }, + { + description: 'fit height with footer', + path: 'container/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit height without footer', + path: 'container/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFooters: 'true' }, + }, + { + description: 'correctly displays container with side media', + path: 'container/media', + screenshotType: 'screenshotArea', + queryParams: { position: 'side', width: '33%', content: '16-9' }, + }, + { + description: 'correctly displays container with top media', + path: 'container/media', + screenshotType: 'screenshotArea', + queryParams: { position: 'top', height: '150px', content: '4-3' }, + }, + { + description: 'media with position: side permutations at 465', + path: 'container/media-side-permutations', + screenshotType: 'permutations', + configuration: { width: 465 }, + }, + { + description: 'media with position: side permutations at 688', + path: 'container/media-side-permutations', + screenshotType: 'permutations', + configuration: { width: 688 }, + }, + { + description: 'media with position: side permutations at 1120', + path: 'container/media-side-permutations', + screenshotType: 'permutations', + configuration: { width: 1120 }, + }, + { + description: 'media with position: top permutations at 465', + path: 'container/media-top-permutations', + screenshotType: 'permutations', + configuration: { width: 465 }, + }, + { + description: 'media with position: top permutations at 688', + path: 'container/media-top-permutations', + screenshotType: 'permutations', + configuration: { width: 688 }, + }, + { + description: 'media with position: top permutations at 1120', + path: 'container/media-top-permutations', + screenshotType: 'permutations', + configuration: { width: 1120 }, + }, + { + description: 'stacked', + path: 'container/stacked-components', + screenshotType: 'screenshotArea', + }, + { + description: 'style-custom', + path: 'container/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/content-layout-permutations.ts b/test/definitions/visual/content-layout-permutations.ts new file mode 100644 index 0000000000..4f58d1a70d --- /dev/null +++ b/test/definitions/visual/content-layout-permutations.ts @@ -0,0 +1,661 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ContentLayout permutations', + componentName: 'content-layout', + tests: [ + // default headerVariant + { + description: 'default headerVariant, none headerBackgroundStyle, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'default headerVariant, none headerBackgroundStyle, at 1400, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'default headerVariant, none headerBackgroundStyle, at 600, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'default headerVariant, none headerBackgroundStyle, at 600, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'default headerVariant, none headerBackgroundStyle, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + maxContentWidth: '1000', + }, + }, + // default headerVariant, gradient + { + description: 'default headerVariant, gradient headerBackgroundStyle, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'gradient', + }, + }, + { + description: 'default headerVariant, gradient headerBackgroundStyle, at 1400, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'gradient', + }, + }, + { + description: 'default headerVariant, gradient headerBackgroundStyle, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'gradient', + maxContentWidth: '1000', + }, + }, + // high-contrast headerVariant, none + { + description: 'high-contrast headerVariant, none headerBackgroundStyle, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: + 'high-contrast headerVariant, none headerBackgroundStyle, at 1400, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'high-contrast headerVariant, none headerBackgroundStyle, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + maxContentWidth: '1000', + }, + }, + // high-contrast headerVariant, gradient + { + description: 'high-contrast headerVariant, gradient headerBackgroundStyle, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + }, + }, + { + description: + 'high-contrast headerVariant, gradient headerBackgroundStyle, at 1400, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + }, + }, + { + description: 'high-contrast headerVariant, gradient headerBackgroundStyle, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + maxContentWidth: '1000', + }, + }, + // divider headerVariant + { + description: 'divider headerVariant, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'divider headerVariant, at 1400, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + maxContentWidth: '1000', + }, + }, + { + description: 'divider headerVariant, at 600, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'divider headerVariant, at 600, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + maxContentWidth: '1000', + }, + }, + // with app layout + { + description: 'with app layout, divider headerVariant, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'true', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with app layout, high-contrast gradient, at 1400, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + maxContentWidth: '800', + }, + }, + { + description: 'with open navigation, without maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'true', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with open navigation, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'true', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + maxContentWidth: '700', + }, + }, + // with secondary header + { + description: 'with secondary header, at 1400', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'true', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with secondary header, at 600', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'true', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with secondary header, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'true', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + maxContentWidth: '900', + }, + }, + // without header + { + description: 'without header, at 1400, defaultPadding=true, default headerVariant', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'true', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'without header, at 1400, defaultPadding=true, high-contrast headerVariant', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'true', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'without header, at 1400, defaultPadding=true, divider headerVariant', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'true', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'without header, at 1400, defaultPadding=false, default headerVariant', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'true', + hasContainer: 'true', + defaultPadding: 'false', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + // without default padding + { + description: 'without default padding, at 1400', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'false', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'without default padding, at 600', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'false', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + // with disabled overlap + { + description: 'with disabled overlap, at 1400', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with disabled overlap, at 600', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/content-layout.ts b/test/definitions/visual/content-layout.ts new file mode 100644 index 0000000000..4b387f5d70 --- /dev/null +++ b/test/definitions/visual/content-layout.ts @@ -0,0 +1,98 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ContentLayout', + componentName: 'content-layout', + tests: [ + { + description: 'fill content area', + path: 'content-layout/fill-content-area', + screenshotType: 'screenshotArea', + }, + { + description: 'standalone', + path: 'content-layout/standalone', + screenshotType: 'screenshotArea', + }, + { + description: 'with absolute components', + path: 'content-layout/with-absolute-components', + screenshotType: 'screenshotArea', + }, + { + description: 'form with form header', + path: 'content-layout/with-header-toggles', + screenshotType: 'screenshotArea', + }, + { + description: 'form with content layout header', + path: 'content-layout/with-header-toggles', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="toggle-form-header"] input'); + await page.click('[data-testid="toggle-content-layout"] input'); + }, + }, + { + description: 'without header - at 1400 without breadcrumbs, without notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'false' }, + }, + { + description: 'without header - at 1400 with breadcrumbs, without notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'false', disableOverlap: 'false' }, + }, + { + description: 'without header - at 1400 without breadcrumbs, with notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'true', disableOverlap: 'false' }, + }, + { + description: 'without header - at 1400 with breadcrumbs, with notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'false' }, + }, + { + description: 'without header - at 1400 without breadcrumbs, without notifications, without overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'true' }, + }, + { + description: 'without header - at 1400 with breadcrumbs, with notifications, without overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'true' }, + }, + { + description: 'without header - at 600 without breadcrumbs, without notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'false' }, + }, + { + description: 'without header - at 600 with breadcrumbs, with notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'false' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/copy-to-clipboard.ts b/test/definitions/visual/copy-to-clipboard.ts new file mode 100644 index 0000000000..64f967f0b7 --- /dev/null +++ b/test/definitions/visual/copy-to-clipboard.ts @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'CopyToClipboard', + componentName: 'copy-to-clipboard', + tests: [ + { + description: 'Variants', + path: 'copy-to-clipboard/simple', + screenshotType: 'screenshotArea', + }, + { + description: 'copy-to-clipboard below bottom split panel is not visible', + path: 'copy-to-clipboard/scenario-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[aria-label="Copy dummy text"]'); + await (page as any).scrollIntoView('[data-testid="scroll-me"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/date-input.ts b/test/definitions/visual/date-input.ts new file mode 100644 index 0000000000..431b274b22 --- /dev/null +++ b/test/definitions/visual/date-input.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Date input', + componentName: 'date-input', + tests: [ + { + description: 'Permutations: states', + path: 'date-input/permutations-states', + screenshotType: 'permutations', + }, + { + description: 'Permutations: formats', + path: 'date-input/permutations-formats', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/date-picker.ts b/test/definitions/visual/date-picker.ts new file mode 100644 index 0000000000..901c35b469 --- /dev/null +++ b/test/definitions/visual/date-picker.ts @@ -0,0 +1,61 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Date picker', + componentName: 'date-picker', + tests: [ + { + description: 'Permutations: states', + path: 'date-picker/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.click('[data-testid="date-picker-expanded-example"] button'); + }, + }, + { + description: 'Permutations: formats', + path: 'date-picker/permutations-formats', + screenshotType: 'permutations', + }, + { + description: 'Month picker', + componentName: 'date-picker', + tests: [ + { + description: 'focus ring on selected month', + path: 'date-picker/month-picker', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); + await page.keys(['Tab', 'Tab', 'Tab']); + }, + }, + { + description: 'focus ring on current month', + path: 'date-picker/month-picker', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); + await page.keys(['Tab', 'Tab', 'Tab']); + await page.keys(['ArrowRight']); + }, + }, + { + description: 'focus ring on non selected, non current month', + path: 'date-picker/month-picker', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); + await page.keys(['Tab', 'Tab', 'Tab']); + await page.keys(['ArrowRight', 'ArrowRight']); + }, + }, + ], + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/date-range-picker.ts b/test/definitions/visual/date-range-picker.ts new file mode 100644 index 0000000000..26aa0d2c01 --- /dev/null +++ b/test/definitions/visual/date-range-picker.ts @@ -0,0 +1,121 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Date Range Picker', + componentName: 'date-range-picker', + tests: [ + { + description: 'Absolute range at 450px', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + configuration: { width: 450, height: 950 }, + setup: async page => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.click('[data-date="2018-01-09"]'); + await page.click('[data-date="2018-01-27"]'); + }, + }, + { + description: 'Absolute range at 1200px', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + configuration: { width: 1200, height: 950 }, + setup: async page => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.click('[data-date="2018-01-09"]'); + await page.click('[data-date="2018-02-24"]'); + }, + }, + { + description: 'Absolute range input permutations for day granularity', + path: 'date-range-picker/absolute-format-day-picker.permutations', + screenshotType: 'screenshotArea', + }, + { + description: 'Absolute range input permutations for month granularity', + path: 'date-range-picker/absolute-format-month-picker.permutations', + screenshotType: 'permutations', + }, + { + description: 'Relative range at 450px', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + configuration: { width: 450, height: 950 }, + setup: async page => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.focusNextElement(); + await page.keys(['Space']); + await page.keys(['ArrowUp']); + await page.keys(['Enter']); + await page.focusNextElement(); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + { + description: 'Relative range at 1200px', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + configuration: { width: 1200, height: 950 }, + setup: async page => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.focusNextElement(); + await page.keys(['ArrowLeft']); + await page.keys(['Enter']); + await page.focusNextElement(); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + { + description: 'Calendar permutations for day granularity', + path: 'date-range-picker/month-calendar-permutations', + screenshotType: 'permutations', + }, + { + description: 'Calendar permutations for month granularity', + path: 'date-range-picker/year-calendar-permutations', + screenshotType: 'permutations', + }, + { + description: 'selects text when double-clicking calendar header', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + const firstCalendarHeaderSelector = wrapper + .findDateRangePicker() + .findDropdown() + .findHeader() + .find('h2 span') + .toSelector(); + await (page as any).doubleClick(firstCalendarHeaderSelector); + }, + }, + { + description: 'does not select text when double-clicking next button', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + const nextButtonSelector = wrapper.findDateRangePicker().findDropdown().findNextMonthButton().toSelector(); + await (page as any).doubleClick(nextButtonSelector); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/divider.ts b/test/definitions/visual/divider.ts new file mode 100644 index 0000000000..8dbed83984 --- /dev/null +++ b/test/definitions/visual/divider.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Divider', + componentName: 'divider', + tests: [ + { + description: 'permutations', + path: 'divider/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/drawer.ts b/test/definitions/visual/drawer.ts new file mode 100644 index 0000000000..33fdb8d896 --- /dev/null +++ b/test/definitions/visual/drawer.ts @@ -0,0 +1,105 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Drawer', + componentName: 'drawer', + tests: [ + { + description: 'permutations', + path: 'drawer/permutations', + screenshotType: 'permutations', + }, + { + description: 'short content, with footer, short footer', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + queryParams: { longContent: 'false', longFooter: 'false', hasFooter: 'true' }, + }, + { + description: 'short content, with footer, long footer', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + queryParams: { longContent: 'false', longFooter: 'true', hasFooter: 'true' }, + }, + { + description: 'long content, with footer, short footer', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + queryParams: { longContent: 'true', longFooter: 'false', hasFooter: 'true' }, + }, + { + description: 'long content, with footer, long footer', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + queryParams: { longContent: 'true', longFooter: 'true', hasFooter: 'true' }, + }, + { + description: 'Drawer with small view height', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + configuration: { width: 1280, height: 268 }, + queryParams: { longContent: 'true', hasFooter: 'true', longFooter: 'true' }, + }, + { + description: 'Drawer footer with small view height', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + configuration: { width: 1280, height: 268 }, + queryParams: { longContent: 'true', hasFooter: 'true' }, + setup: async (page, wrapper) => { + await (page as any).scrollIntoView(wrapper.findDrawer().findFooter().toSelector()); + }, + }, + { + description: 'Drawer with absolute position', + path: 'drawer/drawer-position-absolute', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + }, + { + description: 'Drawer with absolute position and backdrops', + path: 'drawer/drawer-position-absolute', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + queryParams: { backdrops: 'start,end' }, + }, + { + description: 'Drawer with sticky position', + path: 'drawer/drawer-position-sticky', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + }, + { + description: 'Drawer with sticky position and offsets', + path: 'drawer/drawer-position-sticky', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + queryParams: { offsets: 'true' }, + }, + { + description: 'Drawer with sticky position and sticky offsets', + path: 'drawer/drawer-position-sticky', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + queryParams: { stickyOffsets: 'true' }, + }, + { + description: 'Drawer with fixed position', + path: 'drawer/drawer-position-fixed', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + }, + { + description: 'Drawer with fixed position, offsets and backdrop', + path: 'drawer/drawer-position-fixed', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + queryParams: { offsets: 'true', backdrop: 'true' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/dropdown.ts b/test/definitions/visual/dropdown.ts new file mode 100644 index 0000000000..8d17b837ca --- /dev/null +++ b/test/definitions/visual/dropdown.ts @@ -0,0 +1,83 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Dropdown', + componentName: 'dropdown', + tests: [ + { + description: 'In fixed container', + path: 'dropdown/fixed-container', + screenshotType: 'viewport', + setup: async page => { + const { height: windowHeight } = await page.getViewportSize(); + await page.windowScrollTo({ top: windowHeight }); + await page.click('button=Open dropdown'); + }, + }, + { + description: 'positions select inside modal, expandToViewport=false', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'false' }, + setup: async page => { + await page.click('#show-modal'); + await page.click('#in-modal'); + }, + }, + { + description: 'positions select inside modal, expandToViewport=true', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'true' }, + setup: async page => { + await page.click('#show-modal'); + await page.click('#in-modal'); + }, + }, + { + description: 'positions select inside popover, expandToViewport=false', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'false' }, + setup: async page => { + await page.click('#show-popover'); + await page.click('#in-popover'); + await page.click('[data-test-index="5"]'); + }, + }, + { + description: 'positions select inside popover, expandToViewport=true', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'true' }, + setup: async page => { + await page.click('#show-popover'); + await page.click('#in-popover'); + await page.click('[data-test-index="5"]'); + }, + }, + { + description: 'select has bottom borders when opened upwards, expandToViewport=false', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'false' }, + setup: async page => { + await page.click('#bottom-left'); + }, + }, + { + description: 'select has bottom borders when opened upwards, expandToViewport=true', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'true' }, + setup: async page => { + await page.click('#bottom-left'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/expandable-section.ts b/test/definitions/visual/expandable-section.ts new file mode 100644 index 0000000000..310a8d1752 --- /dev/null +++ b/test/definitions/visual/expandable-section.ts @@ -0,0 +1,124 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Expandable section', + componentName: 'expandable-section', + tests: [ + { + description: 'permutations', + path: 'expandable-section/permutations', + screenshotType: 'permutations', + }, + { + description: 'container variant', + path: 'expandable-section/container-variant.permutations', + screenshotType: 'permutations', + }, + { + description: 'focus - container variant with only heading', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', variant: 'container' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - default variant with only heading', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', variant: 'default' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - footer variant with only heading', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', variant: 'footer' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - navigation variant with only heading', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', variant: 'navigation' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - container variant with heading and description', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'container' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - default variant with heading and description', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'default' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - footer variant with heading and description', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'footer' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - container variant with interactive elements', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', hasHeaderInfo: 'true', hasHeaderActions: 'true', variant: 'container' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - container variant with interactive elements and description', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { + headerText: 'Header text', + headerDescription: 'Header description', + hasHeaderInfo: 'true', + hasHeaderActions: 'true', + variant: 'container', + }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'stacked variant', + path: 'expandable-section/stacked-variant.permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/file-dropzone.ts b/test/definitions/visual/file-dropzone.ts new file mode 100644 index 0000000000..5c845fa1ac --- /dev/null +++ b/test/definitions/visual/file-dropzone.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'File dropzone', + componentName: 'file-dropzone', + tests: [ + { + description: 'In container', + path: 'file-dropzone/container', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/file-input.ts b/test/definitions/visual/file-input.ts new file mode 100644 index 0000000000..c2d958d9c9 --- /dev/null +++ b/test/definitions/visual/file-input.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'File input', + componentName: 'file-input', + tests: [ + { + description: 'Simple', + path: 'file-input/simple', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/file-token-group.ts b/test/definitions/visual/file-token-group.ts new file mode 100644 index 0000000000..8aa68908d0 --- /dev/null +++ b/test/definitions/visual/file-token-group.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'File token group', + componentName: 'file-token-group', + tests: [ + { + description: 'Permutations', + path: 'file-token-group/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/file-upload.ts b/test/definitions/visual/file-upload.ts new file mode 100644 index 0000000000..7426504608 --- /dev/null +++ b/test/definitions/visual/file-upload.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'FileUpload', + componentName: 'file-upload', + tests: [ + { + description: 'Permutations', + path: 'file-upload/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/flashbar-stacked.ts b/test/definitions/visual/flashbar-stacked.ts new file mode 100644 index 0000000000..7878754974 --- /dev/null +++ b/test/definitions/visual/flashbar-stacked.ts @@ -0,0 +1,125 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Flashbar stacked notifications', + componentName: 'flashbar', + tests: [ + { + description: '380px, collapsed', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 380 }, + }, + { + description: '380px, collapsed, notifications bar button focused', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 380 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: '380px, expanded', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 380 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await new Promise(resolve => setTimeout(resolve, 500)); + }, + }, + { + description: '450px, collapsed', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 450 }, + }, + { + description: '450px, collapsed, notifications bar button focused', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 450 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: '450px, expanded', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 450 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await new Promise(resolve => setTimeout(resolve, 500)); + }, + }, + { + description: '600px, collapsed', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + }, + { + description: '600px, collapsed, notifications bar button focused', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: '600px, expanded', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await new Promise(resolve => setTimeout(resolve, 500)); + }, + }, + { + description: '1200px, collapsed', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + { + description: '1200px, collapsed, notifications bar button focused', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: '1200px, expanded', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await new Promise(resolve => setTimeout(resolve, 500)); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/flashbar.ts b/test/definitions/visual/flashbar.ts new file mode 100644 index 0000000000..61ce21ea16 --- /dev/null +++ b/test/definitions/visual/flashbar.ts @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Flashbar', + componentName: 'flashbar', + tests: [ + { + description: 'permutations at 600', + path: 'flashbar/permutations', + screenshotType: 'permutations', + configuration: { width: 600 }, + }, + { + description: 'permutations at 1280', + path: 'flashbar/permutations', + screenshotType: 'permutations', + configuration: { width: 1280 }, + }, + { + description: 'runtime-action at 600', + path: 'flashbar/runtime-action', + screenshotType: 'permutations', + configuration: { width: 600 }, + }, + { + description: 'runtime-action at 1280', + path: 'flashbar/runtime-action', + screenshotType: 'permutations', + configuration: { width: 1280 }, + }, + { + description: 'content permutations', + path: 'flashbar/content-permutations', + screenshotType: 'permutations', + }, + { + description: 'style-custom', + path: 'flashbar/style-custom', + screenshotType: 'screenshotArea', + }, + { + description: 'small screen button layout', + path: 'flashbar/small-screen', + screenshotType: 'screenshotArea', + configuration: { width: 550 }, + }, + { + description: 'stacking of multiple flashbar items', + path: 'flashbar/stacking', + screenshotType: 'screenshotArea', + }, + { + description: 'focus border color', + path: 'flashbar/dismissal', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/form-field.ts b/test/definitions/visual/form-field.ts new file mode 100644 index 0000000000..59a207a24a --- /dev/null +++ b/test/definitions/visual/form-field.ts @@ -0,0 +1,61 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'FormField', + componentName: 'form-field', + tests: [ + { + description: 'Permutations at 576', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width: 576 }, + }, + { + description: 'Scenarios at 576', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width: 576 }, + }, + { + description: 'Permutations at 768', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width: 768 }, + }, + { + description: 'Scenarios at 768', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width: 768 }, + }, + { + description: 'Permutations at 992', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width: 992 }, + }, + { + description: 'Scenarios at 992', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width: 992 }, + }, + { + description: 'Permutations at 1200', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width: 1200 }, + }, + { + description: 'Scenarios at 1200', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/form.ts b/test/definitions/visual/form.ts new file mode 100644 index 0000000000..3a61c6e5a6 --- /dev/null +++ b/test/definitions/visual/form.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Form', + componentName: 'form', + tests: [ + { + description: 'permutations', + path: 'form/permutations', + screenshotType: 'permutations', + }, + { + description: 'simple', + path: 'form/simple', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/grid.ts b/test/definitions/visual/grid.ts new file mode 100644 index 0000000000..299f2bb6e8 --- /dev/null +++ b/test/definitions/visual/grid.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Grid', + componentName: 'grid', + tests: [ + { + description: 'grid at "default"', + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'grid at "xs"', + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + }, + { + description: 'grid at "m"', + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + { + description: 'grid at "l"', + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/header.ts b/test/definitions/visual/header.ts new file mode 100644 index 0000000000..9643607963 --- /dev/null +++ b/test/definitions/visual/header.ts @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Header', + componentName: 'header', + tests: [ + { + description: 'level-1 at 1500px', + path: 'header/level-1', + screenshotType: 'screenshotArea', + configuration: { width: 1500 }, + }, + { + description: 'level-1 at 850px', + path: 'header/level-1', + screenshotType: 'screenshotArea', + configuration: { width: 850 }, + }, + { + description: 'level-1 at 400px', + path: 'header/level-1', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'level-2 at 1500px', + path: 'header/level-2', + screenshotType: 'screenshotArea', + configuration: { width: 1500 }, + }, + { + description: 'level-2 at 850px', + path: 'header/level-2', + screenshotType: 'screenshotArea', + configuration: { width: 850 }, + }, + { + description: 'level-2 at 400px', + path: 'header/level-2', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'level-3 at 1500px', + path: 'header/level-3', + screenshotType: 'screenshotArea', + configuration: { width: 1500 }, + }, + { + description: 'level-3 at 850px', + path: 'header/level-3', + screenshotType: 'screenshotArea', + configuration: { width: 850 }, + }, + { + description: 'level-3 at 400px', + path: 'header/level-3', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/help-panel.ts b/test/definitions/visual/help-panel.ts new file mode 100644 index 0000000000..69f40a05c5 --- /dev/null +++ b/test/definitions/visual/help-panel.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'HelpPanel', + componentName: 'help-panel', + tests: [ + { + description: 'permutations', + path: 'help-panel/permutations', + screenshotType: 'screenshotArea', + setup: async page => { + await (page as any).focusInputs(); + }, + }, + { + description: 'with AppLayout', + path: 'help-panel/with-app-layout', + screenshotType: 'screenshotArea', + setup: async page => { + await (page as any).focusInputs(); + }, + }, + { + description: 'loading state - with AppLayout', + path: 'help-panel/loading-with-app-layout', + screenshotType: 'screenshotArea', + setup: async page => { + await (page as any).focusInputs(); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/icon.ts b/test/definitions/visual/icon.ts new file mode 100644 index 0000000000..bd8b064b35 --- /dev/null +++ b/test/definitions/visual/icon.ts @@ -0,0 +1,69 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Icon', + componentName: 'icon', + tests: [ + { + description: 'Alignment with text', + path: 'icon/text-align', + screenshotType: 'screenshotArea', + configuration: { width: 300 }, + }, + { + description: 'Icons in normal variant', + path: 'icon/variant-normal', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in disabled variant', + path: 'icon/variant-disabled', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in error variant', + path: 'icon/variant-error', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in inverted variant', + path: 'icon/variant-inverted', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in subtle variant', + path: 'icon/variant-subtle', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in success variant', + path: 'icon/variant-success', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in warning variant', + path: 'icon/variant-warning', + screenshotType: 'screenshotArea', + }, + { + description: 'Custom icon', + path: 'icon/custom-icon', + screenshotType: 'screenshotArea', + }, + { + description: 'Custom svg icon', + path: 'icon/custom-svg', + screenshotType: 'screenshotArea', + }, + { + description: 'Inherit size property', + path: 'icon/size-inherit', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/input.ts b/test/definitions/visual/input.ts new file mode 100644 index 0000000000..81c0145e88 --- /dev/null +++ b/test/definitions/visual/input.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Input', + componentName: 'input', + tests: [ + { + description: 'permutations', + path: 'input/permutations', + screenshotType: 'permutations', + }, + { + description: 'style-permutations', + path: 'input/style-permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/item-card.ts b/test/definitions/visual/item-card.ts new file mode 100644 index 0000000000..bcf0439635 --- /dev/null +++ b/test/definitions/visual/item-card.ts @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Item card', + componentName: 'item-card', + tests: [ + { + description: 'permutations', + path: 'item-card/permutations', + screenshotType: 'permutations', + }, + { + description: 'padding permutations', + path: 'item-card/padding-permutations', + screenshotType: 'permutations', + }, + { + description: 'variant permutations', + path: 'item-card/variant-permutations', + screenshotType: 'permutations', + }, + { + description: 'style-custom', + path: 'item-card/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/key-value-pairs.ts b/test/definitions/visual/key-value-pairs.ts new file mode 100644 index 0000000000..89dc9ec268 --- /dev/null +++ b/test/definitions/visual/key-value-pairs.ts @@ -0,0 +1,29 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Key-value pairs', + componentName: 'key-value-pairs', + tests: [ + { + description: 'permutations', + path: 'key-value-pairs/permutations', + screenshotType: 'permutations', + }, + { + description: 'permutations on mobile (600px)', + path: 'key-value-pairs/permutations', + screenshotType: 'permutations', + configuration: { width: 600 }, + }, + { + description: 'wrapping text', + path: 'key-value-pairs/text-wrap', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/line-chart.ts b/test/definitions/visual/line-chart.ts new file mode 100644 index 0000000000..3b4d56bb09 --- /dev/null +++ b/test/definitions/visual/line-chart.ts @@ -0,0 +1,156 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_FILTER_TRIGGER = '#chart button'; +const TEST_CHART_TOOLTIP_HEADER = '#chart h2'; + +const suite: TestSuite = { + description: 'Line chart', + componentName: 'line-chart', + tests: [ + { + description: 'permutations', + path: 'line-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'can highlight all data points at a given X coordinate with keyboard', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can navigate series with keyboard', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowDown', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover for all data points at a given X coordinate with keyboard', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'can pin popover for a point in a specific series with keyboard', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowDown', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('[aria-label="Line chart"]', 200, 50); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'shows popover with expandable sub-items - no series highlighted, sub-items collapsed', + path: 'line-chart/drilldown', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 1000 }, + queryParams: { expandableSubItems: 'true' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.waitForVisible('[class*="detail-popover"]'); + }, + }, + { + description: 'shows popover with expandable sub-items - no series highlighted, sub-items expanded', + path: 'line-chart/drilldown', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 1000 }, + queryParams: { expandableSubItems: 'true' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.waitForVisible('[class*="detail-popover"]'); + await page.keys(['Tab']); + await page.keys(['Enter']); + }, + }, + { + description: 'shows popover with expandable sub-items - one series highlighted, sub-items collapsed', + path: 'line-chart/drilldown', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 1000 }, + queryParams: { expandableSubItems: 'true' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowUp']); + await page.waitForVisible('[class*="detail-popover"]'); + }, + }, + { + description: 'shows popover with expandable sub-items - one series highlighted, sub-items expanded', + path: 'line-chart/drilldown', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 1000 }, + queryParams: { expandableSubItems: 'true' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowUp']); + await page.waitForVisible('[class*="detail-popover"]'); + await page.keys(['Tab']); + await page.keys(['Enter']); + }, + }, + { + description: 'correctly renders the chart inside an expandable section - X ticks do not overlap nor overflow', + path: 'line-chart/in-expandable-section-test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async (page, wrapper) => { + const expandableSectionWrapper = wrapper.findExpandableSection(); + await page.waitForVisible(expandableSectionWrapper.toSelector()); + await page.click(expandableSectionWrapper.findExpandButton().toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/link.ts b/test/definitions/visual/link.ts new file mode 100644 index 0000000000..c381876e14 --- /dev/null +++ b/test/definitions/visual/link.ts @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Link', + componentName: 'link', + tests: [ + { + description: 'Permutations', + path: 'link/permutations', + screenshotType: 'permutations', + }, + { + description: 'Permutations (long label)', + path: 'link/long-label-permutations', + screenshotType: 'permutations', + }, + { + description: 'Icon overflow permutations', + path: 'link/icon-overflow-permutations', + screenshotType: 'permutations', + }, + { + description: 'Inherit font size permutations', + path: 'link/inherit-permutations', + screenshotType: 'permutations', + }, + { + description: 'Style custom page', + path: 'link/style-custom-types', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/list.ts b/test/definitions/visual/list.ts new file mode 100644 index 0000000000..9dbe349f8c --- /dev/null +++ b/test/definitions/visual/list.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'List', + componentName: 'list', + tests: [ + { + description: 'permutations', + path: 'list/permutations', + screenshotType: 'permutations', + }, + { + description: 'sortable-permutations', + path: 'list/sortable-permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/mixed-line-bar-chart.ts b/test/definitions/visual/mixed-line-bar-chart.ts new file mode 100644 index 0000000000..5c7370a979 --- /dev/null +++ b/test/definitions/visual/mixed-line-bar-chart.ts @@ -0,0 +1,108 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_TOOLTIP_HEADER = '#chart h2'; + +const suite: TestSuite = { + description: 'Mixed line bar chart', + componentName: 'mixed-line-bar-chart', + tests: [ + { + description: 'permutations', + path: 'mixed-line-bar-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'fit-height', + path: 'mixed-line-bar-chart/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit-height no filter, no legend', + path: 'mixed-line-bar-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFilter: 'true', hideLegend: 'true' }, + }, + { + description: 'fit-height, no legend', + path: 'mixed-line-bar-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideLegend: 'true' }, + }, + { + description: 'chart plot has a focus outline', + path: 'mixed-line-bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + }, + }, + { + description: 'can navigate series with keyboard', + path: 'mixed-line-bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover with keyboard', + path: 'mixed-line-bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'mixed-line-bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('#chart svg[aria-label="Mixed chart 1"]', 200, 100); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'handles long left-labels at width 320px', + path: 'mixed-line-bar-chart/with-long-left-labels', + screenshotType: 'screenshotArea', + configuration: { width: 320 }, + }, + { + description: 'handles long left-labels at width 400px', + path: 'mixed-line-bar-chart/with-long-left-labels', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'handles long left-labels at width 600px', + path: 'mixed-line-bar-chart/with-long-left-labels', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/modal.ts b/test/definitions/visual/modal.ts new file mode 100644 index 0000000000..49c4092c33 --- /dev/null +++ b/test/definitions/visual/modal.ts @@ -0,0 +1,163 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Modal', + componentName: 'modal', + tests: [ + { + description: 'simple', + path: 'modal/simple', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'no-paddings', + path: 'modal/no-paddings', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'vertical-scroll', + path: 'modal/vertical-scroll', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'long-header', + path: 'modal/long-header', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'unbreakable-header', + path: 'modal/unbreakable-header', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'size-small', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/small'); + }, + }, + { + description: 'size-medium', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/medium'); + }, + }, + { + description: 'size-large', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/large'); + }, + }, + { + description: 'size-x-large', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/x-large'); + }, + }, + { + description: 'size-xx-large', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/xx-large'); + }, + }, + { + description: 'size-max', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/max'); + }, + }, + { + description: 'position-top', + path: 'modal/position-top', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'custom-dimensions with footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '600', height: '400', footer: 'true' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions without footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '600', height: '400', footer: 'false' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions very small width', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '10' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions very small height with footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { height: '10', footer: 'true' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions very small height & width with footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '10', height: '15', footer: 'true' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions large height & width with footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '10000', height: '10000', footer: 'true' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/multiselect.ts b/test/definitions/visual/multiselect.ts new file mode 100644 index 0000000000..45a022e5e5 --- /dev/null +++ b/test/definitions/visual/multiselect.ts @@ -0,0 +1,132 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Multiselect', + componentName: 'multiselect', + tests: [ + { + description: 'permutations', + path: 'multiselect/permutations', + screenshotType: 'permutations', + }, + { + description: 'inlineLabelText-permutations', + path: 'multiselect/inline-label-text-permutations', + screenshotType: 'permutations', + }, + { + description: 'expandToViewport=true virtualScroll=true filteringType=manual', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'manual' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'expandToViewport=true virtualScroll=true filteringType=auto', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'auto' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'expandToViewport=true virtualScroll=true filteringType=none', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'none' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'expandToViewport=false virtualScroll=false filteringType=manual', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { expandToViewport: 'false', virtualScroll: 'false', filteringType: 'manual' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'error status wrapping - normal list', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { statusType: 'error' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'item selected (dropdown stays open) - normal list', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + await page.click('[data-test-index="4"]'); + }, + }, + { + description: 'item selected (dropdown stays open) - virtual list', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + await page.click('[data-test-index="4"]'); + }, + }, + { + description: 'custom render option - normal list', + path: 'multiselect/custom-render-option', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'custom render option - virtual list', + path: 'multiselect/custom-render-option', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'Long virtual list - navigate to last item', + path: 'multiselect/virtual-scroll', + screenshotType: 'screenshotArea', + queryParams: { type: 'multiselect' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { + top: 99999, + }); + await page.waitForJsTimers(); + }, + }, + { + description: 'Long virtual list (select all) - navigate to last item', + path: 'multiselect/virtual-scroll', + screenshotType: 'screenshotArea', + queryParams: { type: 'multiselect-select-all' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { + top: 99999, + }); + await page.waitForJsTimers(); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/pagination.ts b/test/definitions/visual/pagination.ts new file mode 100644 index 0000000000..ed8ec25aee --- /dev/null +++ b/test/definitions/visual/pagination.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Pagination', + componentName: 'pagination', + tests: [ + { + description: 'permutations', + path: 'pagination/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/pie-chart.ts b/test/definitions/visual/pie-chart.ts new file mode 100644 index 0000000000..25901670cf --- /dev/null +++ b/test/definitions/visual/pie-chart.ts @@ -0,0 +1,66 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Pie chart', + componentName: 'pie-chart', + tests: [ + { + description: 'permutations', + path: 'pie-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'permutations in narrow container', + path: 'pie-chart/permutations', + screenshotType: 'permutations', + configuration: { width: 350 }, + }, + { + description: 'fit-height', + path: 'pie-chart/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit-height no filter, no legend', + path: 'pie-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFilter: 'true', hideLegend: 'true' }, + }, + { + description: 'can focus chart plot', + path: 'pie-chart/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + }, + }, + { + description: 'can navigate segments with keyboard', + path: 'pie-chart/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.keys(['ArrowRight']); + }, + }, + { + description: 'can pin segments with mouse', + path: 'pie-chart/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('svg [aria-label~="Apples"] > path'); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/popover.ts b/test/definitions/visual/popover.ts new file mode 100644 index 0000000000..b15f2589b4 --- /dev/null +++ b/test/definitions/visual/popover.ts @@ -0,0 +1,197 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Popover', + componentName: 'popover', + tests: [ + { + description: 'text wrapping', + path: 'popover/text-wrap', + screenshotType: 'screenshotArea', + }, + { + description: 'alignment inside text', + path: 'popover/text-align', + screenshotType: 'screenshotArea', + }, + { + description: 'inside modal', + path: 'popover/scenario-in-modal', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + await page.click('#popover button'); + }, + }, + { + description: 'positioning with navigation v1.0', + path: 'popover/nav-v1-0-positioning', + screenshotType: 'viewport', + setup: async page => { + await page.click('#popover button'); + }, + }, + { + description: 'close icon positioned inside the popover (no header and fixed width)', + path: 'popover/header-variant', + screenshotType: 'viewport', + setup: async page => { + await page.click('[data-testid="popover-without-title"] button'); + }, + }, + { + description: 'inside table - renderWithPortal=false', + path: 'popover/scenario-in-table', + screenshotType: 'viewport', + setup: async page => { + await page.click('table button'); + }, + }, + { + description: 'inside table - renderWithPortal=true', + path: 'popover/scenario-in-table', + screenshotType: 'viewport', + setup: async page => { + await page.click('#renderWithPortal'); + await page.click('table button'); + }, + }, + { + description: 'scenario - copy - renderWithPortal=false', + path: 'popover/scenarios', + screenshotType: 'viewport', + setup: async page => { + await page.click('#scenario-copy button'); + }, + }, + { + description: 'scenario - copy - renderWithPortal=true', + path: 'popover/scenarios', + screenshotType: 'viewport', + setup: async page => { + await page.click('#renderWithPortal'); + await page.click('#scenario-copy button'); + }, + }, + { + description: 'scenario - medium-key-value - renderWithPortal=false', + path: 'popover/scenarios', + screenshotType: 'viewport', + setup: async page => { + await page.click('#scenario-medium-key-value button'); + }, + }, + { + description: 'scenario - large-key-value - renderWithPortal=false', + path: 'popover/scenarios', + screenshotType: 'viewport', + setup: async page => { + await page.click('#scenario-large-key-value button'); + }, + }, + { + description: 'inline popover - closed - renderWithPortal=false', + path: 'popover/inline', + screenshotType: 'screenshotArea', + }, + { + description: 'inline popover - closed - renderWithPortal=true', + path: 'popover/inline', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#renderWithPortal'); + }, + }, + { + description: 'inline popover - open - renderWithPortal=false', + path: 'popover/inline', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findPopover().findTrigger().toSelector()); + }, + }, + { + description: 'inline popover - open - renderWithPortal=true', + path: 'popover/inline', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click('#renderWithPortal'); + await page.click(wrapper.findPopover().findTrigger().toSelector()); + }, + }, + { + description: 'positioning - opens in the correct position - renderWithPortal=false', + path: 'popover/positioning', + screenshotType: 'viewport', + setup: async page => { + await page.click('#popover-2-2 button'); + }, + }, + { + description: 'positioning - flips to the opposite position - renderWithPortal=false', + path: 'popover/positioning', + screenshotType: 'viewport', + setup: async page => { + await page.click('#popover-1-2 button'); + }, + }, + { + description: 'focus - Permutation 1', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - Permutation 2', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + for (let j = 0; j < 2; j++) { + await page.focusNextElement(); + } + }, + }, + { + description: 'focus - Permutation 3', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + for (let j = 0; j < 3; j++) { + await page.focusNextElement(); + } + }, + }, + { + description: 'focus - Permutation 6', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + for (let j = 0; j < 6; j++) { + await page.focusNextElement(); + } + }, + }, + { + description: 'focus - Permutation 12', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + for (let j = 0; j < 12; j++) { + await page.focusNextElement(); + } + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/progress-bar.ts b/test/definitions/visual/progress-bar.ts new file mode 100644 index 0000000000..1280b00599 --- /dev/null +++ b/test/definitions/visual/progress-bar.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ProgressBar', + componentName: 'progress-bar', + tests: [ + { + description: 'permutations - standalone', + path: 'progress-bar/permutations-standalone', + screenshotType: 'permutations', + configuration: { width: 800 }, + }, + { + description: 'permutations - flash', + path: 'progress-bar/permutations-flash', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + }, + { + description: 'permutations - key-value', + path: 'progress-bar/permutations-key-value', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/prompt-input.ts b/test/definitions/visual/prompt-input.ts new file mode 100644 index 0000000000..7358e18aea --- /dev/null +++ b/test/definitions/visual/prompt-input.ts @@ -0,0 +1,46 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'PromptInput', + componentName: 'prompt-input', + tests: [ + { + description: 'Permutations at 250', + path: 'prompt-input/permutations', + screenshotType: 'permutations', + configuration: { width: 250 }, + }, + { + description: 'Permutations at 400', + path: 'prompt-input/permutations', + screenshotType: 'permutations', + configuration: { width: 400 }, + }, + { + description: 'Permutations at 800', + path: 'prompt-input/permutations', + screenshotType: 'permutations', + configuration: { width: 800 }, + }, + { + description: 'Style Permutations', + path: 'prompt-input/style-permutations', + screenshotType: 'permutations', + }, + { + description: 'focus ring on the last secondary action', + path: 'prompt-input/simple', + screenshotType: 'screenshotArea', + queryParams: { hasSecondaryActions: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findPromptInput('[data-testid="prompt-input"]').findNativeTextarea().toSelector()); + await page.keys(['Tab', 'ArrowRight', 'ArrowRight']); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/property-filter.ts b/test/definitions/visual/property-filter.ts new file mode 100644 index 0000000000..d9529692e4 --- /dev/null +++ b/test/definitions/visual/property-filter.ts @@ -0,0 +1,144 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Property filter', + componentName: 'property-filter', + tests: [ + { + description: 'token editor popover', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-default [aria-haspopup=dialog]'); + }, + }, + { + description: 'filtering token select', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-default [aria-haspopup=listbox]'); + }, + }, + { + description: 'token editor popover overflow', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-overflow [aria-haspopup=dialog]'); + }, + }, + { + description: 'token editor custom property boolean', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-custom-prop-boolean [aria-haspopup=dialog]'); + }, + }, + { + description: 'token editor custom property datetime', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-custom-prop-datetime [aria-haspopup=dialog]'); + }, + }, + { + description: 'token editor group string property', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); + await page.click(propertyFilter.findTokens().get(1).findLabel().toSelector()); + }, + }, + { + description: 'token editor group datetime property', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); + await page.click(propertyFilter.findTokens().get(1).findLabel().toSelector()); + await page.keys(['Escape']); + await page.keys(['Tab', 'Tab', 'Tab', 'Enter']); + }, + }, + { + description: 'token editor enum property', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); + await page.click(propertyFilter.findNativeInput().toSelector()); + await page.keys('state = s'); + }, + }, + { + description: 'token editor enum property no matches', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); + await page.click(propertyFilter.findNativeInput().toSelector()); + await page.keys('state = x'); + }, + }, + { + description: 'permutations', + path: 'property-filter/permutations', + screenshotType: 'permutations', + }, + { + description: 'tokens permutations', + path: 'property-filter/property-filter-tokens-permutations', + screenshotType: 'permutations', + }, + { + description: 'editor permutations at 400', + path: 'property-filter/property-filter-editor-permutations', + screenshotType: 'permutations', + configuration: { width: 400 }, + }, + { + description: 'editor permutations at 1200', + path: 'property-filter/property-filter-editor-permutations', + screenshotType: 'permutations', + configuration: { width: 1200 }, + }, + { + description: 'split panel integration - main content dropdown', + path: 'property-filter/split-panel-app-layout-integration', + screenshotType: 'viewport', + setup: async page => { + await page.click('.main-content input[aria-label="your choice"]'); + }, + }, + { + description: 'split panel integration - main content popover', + path: 'property-filter/split-panel-app-layout-integration', + screenshotType: 'viewport', + setup: async page => { + await page.click('.main-content button[aria-haspopup=dialog]'); + }, + }, + { + description: 'virtual scroll navigate through 100 items', + path: 'property-filter/virtual-scroll', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter(); + await page.click(propertyFilter.findNativeInput().toSelector()); + await page.keys('Property = '); + for (let i = 0; i < 100; i++) { + await page.keys('ArrowDown'); + } + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/radio-button.ts b/test/definitions/visual/radio-button.ts new file mode 100644 index 0000000000..c97bef2e8f --- /dev/null +++ b/test/definitions/visual/radio-button.ts @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'RadioButton', + componentName: 'radio-button', + tests: [ + { + description: 'Permutations', + path: 'radio-button/permutations', + screenshotType: 'permutations', + }, + { + description: 'Focused and not checked', + path: 'radio-button/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'Focused and checked', + path: 'radio-button/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + }, + }, + { + description: 'Custom style permutations', + path: 'radio-button/style-custom', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/radio-group.ts b/test/definitions/visual/radio-group.ts new file mode 100644 index 0000000000..20da0133f1 --- /dev/null +++ b/test/definitions/visual/radio-group.ts @@ -0,0 +1,49 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'RadioGroup', + componentName: 'radio-group', + tests: [ + { + description: 'Permutations', + path: 'radio-group/permutations', + screenshotType: 'permutations', + }, + { + description: 'Radio button is focused', + path: 'radio-group/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'Radio button has label with a correct width', + path: 'radio-group/labels-highlight', + screenshotType: 'screenshotArea', + }, + { + description: 'Horizontal radio group permutations at 600', + path: 'radio-group/horizontal.permutations', + screenshotType: 'permutations', + configuration: { width: 600 }, + }, + { + description: 'Horizontal radio group permutations at 1280', + path: 'radio-group/horizontal.permutations', + screenshotType: 'permutations', + configuration: { width: 1280 }, + }, + { + description: 'Style custom page', + path: 'radio-group/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/s3-resource-selector.ts b/test/definitions/visual/s3-resource-selector.ts new file mode 100644 index 0000000000..0cdaa5bcd9 --- /dev/null +++ b/test/definitions/visual/s3-resource-selector.ts @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'S3 Resource Selector', + componentName: 's3-resource-selector', + tests: [ + { + description: 'Permutations', + path: 's3-resource-selector/permutations', + screenshotType: 'permutations', + }, + { + description: 'Browse dialog - plain', + path: 's3-resource-selector/permutations', + screenshotType: 'viewport', + configuration: { height: 1000 }, + setup: async page => { + await page.click('button=Browse S3'); + await page.waitForVisible('[role="dialog"]'); + }, + }, + { + description: 'Browse dialog - with alert', + path: 's3-resource-selector/with-alert', + screenshotType: 'viewport', + configuration: { height: 1100 }, + setup: async page => { + await page.click('button=Browse S3'); + await page.waitForVisible('[role="dialog"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/segmented-control.ts b/test/definitions/visual/segmented-control.ts new file mode 100644 index 0000000000..5be8493c37 --- /dev/null +++ b/test/definitions/visual/segmented-control.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'SegmentedControl', + componentName: 'segmented-control', + tests: [ + { + description: 'Permutations', + path: 'segmented-control/permutations', + screenshotType: 'permutations', + }, + { + description: 'Style Permutations', + path: 'segmented-control/style-permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/select.ts b/test/definitions/visual/select.ts new file mode 100644 index 0000000000..cfd3abae15 --- /dev/null +++ b/test/definitions/visual/select.ts @@ -0,0 +1,96 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Select', + componentName: 'select', + tests: [ + { + description: 'component - dropdown closed', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + }, + { + description: 'component - dropdown open - plain list', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="select-demo"] button'); + }, + }, + { + description: 'component - dropdown open - virtual list', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#toggle-virtual'); + await page.click('[data-testid="select-demo"] button'); + }, + }, + { + description: 'component - dropdown open limited width - plain list', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="select-demo-with-no-filtering-and-limited-width"] button'); + }, + }, + { + description: 'keyboard interaction - plain list', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="select-demo-no-filtering"] button'); + await page.keys(['ArrowDown', 'Space']); + }, + }, + { + description: 'component - custom render option - plain list', + path: 'select/custom-render-option', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findSelect().findTrigger().toSelector()); + }, + }, + { + description: 'component - custom render option - virtual list', + path: 'select/custom-render-option', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findSelect().findTrigger().toSelector()); + }, + }, + { + description: 'item permutations', + path: 'select/item.permutations', + screenshotType: 'permutations', + }, + { + description: 'selectable-item permutations', + path: 'selectable-item/permutations', + screenshotType: 'permutations', + }, + { + description: 'trigger permutations', + path: 'select/trigger.permutations', + screenshotType: 'permutations', + }, + { + description: 'Long virtual list - navigate to last item', + path: 'select/virtual-scroll', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findSelect().findTrigger().toSelector()); + await page.elementScrollTo(wrapper.findSelect().findDropdown().findOptionsContainer().toSelector(), { + top: 99999, + }); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/side-navigation.ts b/test/definitions/visual/side-navigation.ts new file mode 100644 index 0000000000..fce8be8eb1 --- /dev/null +++ b/test/definitions/visual/side-navigation.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'SideNavigation', + componentName: 'side-navigation', + tests: [ + { + description: 'Permutations', + path: 'side-navigation/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/slider.ts b/test/definitions/visual/slider.ts new file mode 100644 index 0000000000..49f7df5dfb --- /dev/null +++ b/test/definitions/visual/slider.ts @@ -0,0 +1,19 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Slider', + componentName: 'slider', + tests: [ + { + description: 'permutations - standalone', + path: 'slider/permutations', + screenshotType: 'permutations', + configuration: { width: 800 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/space-between.ts b/test/definitions/visual/space-between.ts new file mode 100644 index 0000000000..636d375d42 --- /dev/null +++ b/test/definitions/visual/space-between.ts @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'SpaceBetween', + componentName: 'space-between', + tests: [ + { + description: 'Permutations', + path: 'space-between/permutations', + screenshotType: 'permutations', + }, + { + description: 'in ColumnLayout and Grid', + path: 'space-between/nested-components', + screenshotType: 'screenshotArea', + }, + { + description: 'Alignment permutations', + path: 'space-between/alignment.permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/spinner.ts b/test/definitions/visual/spinner.ts new file mode 100644 index 0000000000..65aa6c6ca1 --- /dev/null +++ b/test/definitions/visual/spinner.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Spinner', + componentName: 'spinner', + tests: [ + { + description: 'Alignment with text', + path: 'spinner/text-align', + screenshotType: 'screenshotArea', + }, + { + description: 'Permutations', + path: 'spinner/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/split-panel.ts b/test/definitions/visual/split-panel.ts new file mode 100644 index 0000000000..ae94690b69 --- /dev/null +++ b/test/definitions/visual/split-panel.ts @@ -0,0 +1,93 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Split panel', + componentName: 'split-panel', + tests: [ + { + description: 'position bottom', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + }, + }, + { + description: 'position bottom - closed', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + }, + { + description: 'position side', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + await page.click('aria/Preferences'); + await page.click('aria/Side'); + await page.click('aria/Confirm'); + }, + }, + { + description: 'position side with tools open', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + await page.click('aria/Preferences'); + await page.click('aria/Side'); + await page.click('aria/Confirm'); + await page.waitForVisible('aria/Open tools'); + await page.click('aria/Open tools'); + }, + }, + { + description: 'position side - closed', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + await page.click('[aria-label="Preferences"]'); + await page.click('aria/Side'); + await page.click('aria/Confirm'); + await page.click('aria/Close panel'); + }, + }, + { + description: 'preferences open', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + await page.click('aria/Preferences'); + }, + }, + { + description: 'popover in bottom panel', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async (page, wrapper) => { + await page.click('aria/Open panel'); + await page.click(wrapper.findSplitPanel().findOpenPanelBottom().findPopover().findTrigger().toSelector()); + await (page as any).scrollIntoView('[data-testid="scroll-me"]'); + }, + }, + { + description: 'headerBefore, info link, actions and description', + path: 'app-layout/split-panel-with-custom-header', + screenshotType: 'screenshotArea', + queryParams: { renderActionsButtonDropdown: 'true', renderBeforeBadge: 'true', renderInfoLink: 'true' }, + }, + { + description: 'Entire layout defined in headerBefore slot', + path: 'app-layout/split-panel-with-custom-header', + screenshotType: 'screenshotArea', + queryParams: { renderActionsButtonDropdown: 'true', renderBeforeButtons: 'true' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/status-indicator.ts b/test/definitions/visual/status-indicator.ts new file mode 100644 index 0000000000..526849f4fa --- /dev/null +++ b/test/definitions/visual/status-indicator.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'StatusIndicator', + componentName: 'status-indicator', + tests: [ + { + description: 'permutations', + path: 'status-indicator/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/steps.ts b/test/definitions/visual/steps.ts new file mode 100644 index 0000000000..3c21b870fb --- /dev/null +++ b/test/definitions/visual/steps.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Steps', + componentName: 'steps', + tests: [ + { + description: 'permutations', + path: 'steps/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-cells.ts b/test/definitions/visual/table-cells.ts new file mode 100644 index 0000000000..38ebde3b8e --- /dev/null +++ b/test/definitions/visual/table-cells.ts @@ -0,0 +1,20 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table cells', + componentName: 'table-cells', + tests: [ + { + description: 'vertical align', + path: 'table/cell-permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + queryParams: { verticalAlignTop: 'true' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-embedded.ts b/test/definitions/visual/table-embedded.ts new file mode 100644 index 0000000000..06da54e843 --- /dev/null +++ b/test/definitions/visual/table-embedded.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table embedded', + componentName: 'table-embedded', + tests: [ + { + description: 'in alert', + path: 'table/embedded-in-alert', + screenshotType: 'viewport', + }, + { + description: 'stacked and container variants', + path: 'table/stacked-and-container-variant', + screenshotType: 'viewport', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-inline-editing.ts b/test/definitions/visual/table-inline-editing.ts new file mode 100644 index 0000000000..52dd8e12ec --- /dev/null +++ b/test/definitions/visual/table-inline-editing.ts @@ -0,0 +1,54 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table inline editing', + componentName: 'table-inline-editing', + tests: [ + { + description: 'permutations', + path: 'table/inline-editor.permutations', + screenshotType: 'permutations', + }, + { + description: 'active select editing', + path: 'table/editable', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[aria-label="Edit EKXAM4L45YPC8 TLS Version"]'); + }, + }, + { + description: 'active select editing with open dropdown', + path: 'table/editable', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click('[aria-label="Edit EKXAM4L45YPC8 TLS Version"]'); + await page.click(wrapper.findSelect().findTrigger().toSelector()); + }, + }, + { + description: 'hovering over cell, resizableColumns=${resizableColumns}', + path: 'table/editable', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[aria-label="Edit EKXAM4L45YPC8 Domain name"]'); + }, + }, + { + description: 'active select editing with keyboard navigation', + path: 'table/editable', + screenshotType: 'screenshotArea', + configuration: { width: 1600 }, + queryParams: { enableKeyboardNavigation: 'true' }, + setup: async page => { + await page.click('[data-testid="focus"]'); + await page.keys(['Tab', 'ArrowDown', 'ArrowRight', 'Enter']); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-resizable-columns.ts b/test/definitions/visual/table-resizable-columns.ts new file mode 100644 index 0000000000..293dc98319 --- /dev/null +++ b/test/definitions/visual/table-resizable-columns.ts @@ -0,0 +1,69 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table', + componentName: 'table-resizable-columns', + tests: [ + { + description: 'resizable columns permutations', + path: 'table/resizable-columns-permutations', + screenshotType: 'permutations', + }, + { + description: 'focused column resizer', + path: 'table/resizable-columns', + screenshotType: 'screenshotArea', + setup: async page => { + await page.buttonDownOnElement('#reset-state'); + await page.keys(['Tab']); + }, + }, + { + description: 'active column resizer on sticky header', + path: 'table/resizable-columns', + screenshotType: 'viewport', + setup: async (page, wrapper) => { + const table = wrapper.findTable(); + await page.click('#sticky-header-toggle input'); + await page.windowScrollTo({ top: 600 }); + await page.buttonDownOnElement(table.findColumnResizer(3).toSelector()); + }, + }, + { + description: 'active column resizer', + path: 'table/resizable-columns', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const table = wrapper.findTable(); + await page.buttonDownOnElement(table.findColumnResizer(3).toSelector()); + }, + }, + { + description: 'pressed column resizer', + path: 'table/resizable-columns', + screenshotType: 'screenshotArea', + queryParams: { enableKeyboardNavigation: 'true' }, + setup: async (page, wrapper) => { + const table = wrapper.findTable(); + await page.click(table.findHeaderSlot().toSelector()); + await page.keys(['Tab', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'Enter']); + }, + }, + { + description: 'pressed last column resizer', + path: 'table/resizable-columns', + screenshotType: 'screenshotArea', + queryParams: { enableKeyboardNavigation: 'true' }, + setup: async (page, wrapper) => { + const table = wrapper.findTable(); + await page.click(table.findHeaderSlot().toSelector()); + await page.keys(['Tab', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'Enter']); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-sticky-header.ts b/test/definitions/visual/table-sticky-header.ts new file mode 100644 index 0000000000..ed508f17a7 --- /dev/null +++ b/test/definitions/visual/table-sticky-header.ts @@ -0,0 +1,44 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table sticky header', + componentName: 'table-sticky-header', + tests: [ + { + description: 'initial state', + path: 'table/sticky-header', + screenshotType: 'viewport', + }, + { + description: 'mid-scroll sticky state - container variant', + path: 'table/sticky-header', + screenshotType: 'viewport', + setup: async page => { + await page.click('#container'); + await page.windowScrollTo({ top: 400 }); + }, + }, + { + description: 'mid-scroll sticky state - embedded variant', + path: 'table/sticky-header', + screenshotType: 'viewport', + setup: async page => { + await page.click('#embedded'); + await page.windowScrollTo({ top: 400 }); + }, + }, + { + description: 'bottom sticky state', + path: 'table/sticky-header', + screenshotType: 'viewport', + setup: async page => { + await page.windowScrollTo({ top: 925 }); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-sticky-scrollbar.ts b/test/definitions/visual/table-sticky-scrollbar.ts new file mode 100644 index 0000000000..1f661f5a48 --- /dev/null +++ b/test/definitions/visual/table-sticky-scrollbar.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table sticky scrollbar', + componentName: 'table-sticky-scrollbar', + tests: [ + { + description: 'on a plain page is positioned at the bottom', + path: 'table/sticky-scrollbar', + screenshotType: 'screenshotArea', + configuration: { width: 600, height: 800 }, + }, + { + description: 'on a page with app layout is positioned over the footer', + path: 'app-layout/with-table', + screenshotType: 'screenshotArea', + configuration: { width: 600, height: 800 }, + }, + { + description: 'on a page with app layout in a container is positioned over the bottom of the container', + path: 'app-layout/with-table-in-container', + screenshotType: 'screenshotArea', + configuration: { width: 600, height: 800 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table.ts b/test/definitions/visual/table.ts new file mode 100644 index 0000000000..9ed8cceced --- /dev/null +++ b/test/definitions/visual/table.ts @@ -0,0 +1,130 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table', + componentName: 'table', + tests: [ + { + description: 'simple permutations at ${width}', + path: 'table/simple-permutations', + screenshotType: 'permutations', + }, + { + description: 'permutations at ${width}', + path: 'table/permutations', + screenshotType: 'permutations', + }, + { + description: 'expandable rows permutations', + path: 'table/expandable-rows.permutations', + screenshotType: 'permutations', + }, + { + description: 'empty state – default', + path: 'table/empty-state', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + }, + { + description: 'empty state – scrolled horizontally', + path: 'table/empty-state', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + setup: async page => { + await page.click('#scroll-content'); + }, + }, + { + description: 'with features', + path: 'table/hooks', + screenshotType: 'screenshotArea', + }, + { + description: 'with striped rows', + path: 'table/striped-rows', + screenshotType: 'screenshotArea', + }, + { + description: 'inside stacked container', + path: 'table/sticky-header-in-stacked-container', + screenshotType: 'screenshotArea', + setup: async page => { + await page.windowScrollTo({ top: 10 }); + await page.click('button=Actions'); + }, + }, + { + description: 'sticky header with open action button dropdown', + path: 'table/sticky-header-with-actions', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-test-id="actions-button"]'); + }, + }, + { + description: 'variants', + path: 'table/variants', + screenshotType: 'screenshotArea', + }, + { + description: 'focus on last cell inline action', + path: 'table/inline-actions', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const tableWrapper = wrapper.findTable('[data-testid="table-with-dropdown-actions"]'); + await page.click(tableWrapper.findRows().get(1).findButtonDropdown().findNativeButton().toSelector()); + await page.keys(['Escape']); + }, + }, + { + description: 'focus on first cell link', + path: 'table/inline-actions', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const tableWrapper = wrapper.findTable('[data-testid="table-with-dropdown-actions"]'); + await page.click(tableWrapper.findRowSelectionArea(1).toSelector()); + await page.keys(['Tab']); + }, + }, + { + description: 'first column sticky state', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsFirst: '1' }, + }, + { + description: 'first column sticky state and selection', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsFirst: '1', selectionType: 'single' }, + }, + { + description: 'first column sticky state and striped rows', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsFirst: '1', stripedRows: 'true' }, + }, + { + description: 'first two columns sticky state', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsFirst: '2' }, + }, + { + description: 'last column sticky state', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsLast: '1' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/tabs.ts b/test/definitions/visual/tabs.ts new file mode 100644 index 0000000000..90b9c61f0f --- /dev/null +++ b/test/definitions/visual/tabs.ts @@ -0,0 +1,80 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Tabs', + componentName: 'tabs', + tests: [ + { + description: 'Permutations', + path: 'tabs/permutations', + screenshotType: 'permutations', + }, + { + description: 'Responsive permutations', + path: 'tabs/responsive-permutations', + screenshotType: 'screenshotArea', + }, + { + description: 'focuses next tab header after clicking on tab header without an href', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#first-tabs li:nth-child(3) button'); + await page.keys('ArrowRight'); + }, + }, + { + description: 'layout at ${width}px', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + }, + { + description: 'focus active tab - default variant', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#click-this'); + await page.focusNextElement(); + }, + }, + { + description: 'focus active tab - container variant', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#click-this-2'); + await page.focusNextElement(); + }, + }, + { + description: 'focus content - default variant', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#click-this'); + await page.focusNextElement(); + await page.focusNextElement(); + }, + }, + { + description: 'focus content - container variant', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#click-this-2'); + await page.focusNextElement(); + await page.focusNextElement(); + }, + }, + { + description: 'Style Permutations', + path: 'tabs/style-permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/tag-editor.ts b/test/definitions/visual/tag-editor.ts new file mode 100644 index 0000000000..041fdbef6a --- /dev/null +++ b/test/definitions/visual/tag-editor.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'TagEditor', + componentName: 'tag-editor', + tests: [ + { + description: 'Permutations', + path: 'tag-editor/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/text-content.ts b/test/definitions/visual/text-content.ts new file mode 100644 index 0000000000..29b72be343 --- /dev/null +++ b/test/definitions/visual/text-content.ts @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'TextContainer', + componentName: 'text-content', + tests: [ + { + description: 'color property', + path: 'text-content/permutations', + screenshotType: 'screenshotArea', + }, + { + description: 'with nested Link components', + path: 'text-content/link-nesting', + screenshotType: 'screenshotArea', + }, + { + description: 'with nested Box components', + path: 'text-content/box-nesting', + screenshotType: 'screenshotArea', + }, + { + description: 'with icons in content', + path: 'text-content/iconography', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/text-filter.ts b/test/definitions/visual/text-filter.ts new file mode 100644 index 0000000000..f291e70574 --- /dev/null +++ b/test/definitions/visual/text-filter.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'TextFilter', + componentName: 'text-filter', + tests: [ + { + description: 'permutations', + path: 'text-filter/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/textarea.ts b/test/definitions/visual/textarea.ts new file mode 100644 index 0000000000..2047d7559d --- /dev/null +++ b/test/definitions/visual/textarea.ts @@ -0,0 +1,77 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const testStates = [ + { toggle: 'toggle-invalid', label: 'invalid', isInteractive: true }, + { toggle: 'toggle-disabled', label: 'disabled', isInteractive: false }, + { toggle: 'toggle-readonly', label: 'readonly', isInteractive: true }, + { toggle: 'toggle-warning', label: 'warning', isInteractive: true }, +]; + +function pseudoSelectorTests(withStyling: boolean): TestSuite { + return { + description: withStyling ? 'Pseudo Selectors with Custom Styling' : 'Pseudo Selectors', + tests: testStates.flatMap(state => [ + { + description: `${state.label} - focus`, + path: 'textarea/pseudo-selectors', + screenshotType: 'screenshotArea' as const, + setup: async (page: any, wrapper: any) => { + const textareaSelector = wrapper + .findTextarea('[data-testid="test-textarea"]') + .findNativeTextarea() + .toSelector(); + if (withStyling) { + await page.click('#toggle-styling'); + } + await page.click(`#${state.toggle}`); + if (state.isInteractive) { + await page.click(textareaSelector); + } + }, + }, + { + description: `${state.label} - focus + hover`, + path: 'textarea/pseudo-selectors', + screenshotType: 'screenshotArea' as const, + setup: async (page: any, wrapper: any) => { + const textareaSelector = wrapper + .findTextarea('[data-testid="test-textarea"]') + .findNativeTextarea() + .toSelector(); + if (withStyling) { + await page.click('#toggle-styling'); + } + await page.click(`#${state.toggle}`); + if (state.isInteractive) { + await page.click(textareaSelector); + } + await page.hoverElement(textareaSelector); + }, + }, + ]), + }; +} + +const suite: TestSuite = { + description: 'Textarea', + componentName: 'textarea', + tests: [ + { + description: 'Permutations', + path: 'textarea/permutations', + screenshotType: 'permutations', + }, + { + description: 'Style Permutations', + path: 'textarea/style-permutations', + screenshotType: 'permutations', + }, + pseudoSelectorTests(false), + pseudoSelectorTests(true), + ], +}; + +export default suite; diff --git a/test/definitions/visual/tiles.ts b/test/definitions/visual/tiles.ts new file mode 100644 index 0000000000..51c290fc80 --- /dev/null +++ b/test/definitions/visual/tiles.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Tiles', + componentName: 'tiles', + tests: [ + { + description: 'tiles at "${breakpoint}"', + path: 'tiles/simple', + screenshotType: 'screenshotArea', + }, + { + description: 'permutations at "${breakpoint}"', + path: 'tiles/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/time-input.ts b/test/definitions/visual/time-input.ts new file mode 100644 index 0000000000..b2e6fc2dcc --- /dev/null +++ b/test/definitions/visual/time-input.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Time Input', + componentName: 'time-input', + tests: [ + { + description: 'Permutations', + path: 'time-input/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/toggle-button.ts b/test/definitions/visual/toggle-button.ts new file mode 100644 index 0000000000..e91340dfdb --- /dev/null +++ b/test/definitions/visual/toggle-button.ts @@ -0,0 +1,26 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Toggle button', + componentName: 'toggle-button', + tests: [ + { + description: 'permutations', + path: 'toggle-button/permutations', + screenshotType: 'permutations', + }, + { + description: 'hovering over normal variant', + path: 'toggle-button/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.hoverElement('[aria-label="Favorite"][aria-pressed="false"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/toggle.ts b/test/definitions/visual/toggle.ts new file mode 100644 index 0000000000..b99818470d --- /dev/null +++ b/test/definitions/visual/toggle.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Toggle', + componentName: 'toggle', + tests: [ + { + description: 'Permutations', + path: 'toggle/permutations', + screenshotType: 'permutations', + }, + { + description: 'Toggle is focused', + path: 'toggle/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'Toggle has label with a correct width', + path: 'toggle/labels-highlight', + screenshotType: 'screenshotArea', + }, + { + description: 'Style custom page', + path: 'toggle/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/token-group.ts b/test/definitions/visual/token-group.ts new file mode 100644 index 0000000000..99ac6b49e6 --- /dev/null +++ b/test/definitions/visual/token-group.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'TokenGroup', + componentName: 'token-group', + tests: [ + { + description: 'Permutations', + path: 'token-group/permutations', + screenshotType: 'permutations', + }, + { + description: 'Simple', + path: 'token-group/index', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/top-navigation.ts b/test/definitions/visual/top-navigation.ts new file mode 100644 index 0000000000..d58b5afa4a --- /dev/null +++ b/test/definitions/visual/top-navigation.ts @@ -0,0 +1,54 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Top navigation', + componentName: 'top-navigation', + tests: [ + { + description: 'Responsiveness', + path: 'top-navigation/screenshot', + screenshotType: 'screenshotArea', + configuration: { width: 1300 }, + }, + { + description: 'Dropdown menu utility', + path: 'top-navigation/scenario-full-page', + screenshotType: 'viewport', + configuration: { width: 1300, height: 800 }, + setup: async (page, wrapper) => { + await page.click(wrapper.findTopNavigation().findUtility(4).toSelector()); + }, + }, + { + description: 'Utility permutations', + path: 'top-navigation/utility.permutations', + screenshotType: 'permutations', + }, + { + description: 'Overflow menu - outer', + path: 'top-navigation/scenario-full-page', + screenshotType: 'viewport', + configuration: { width: 500, height: 800 }, + setup: async (page, wrapper) => { + await page.click(wrapper.findTopNavigation().findOverflowMenuButton().toSelector()); + await page.keys(['Tab']); + }, + }, + { + description: 'Overflow menu - dropdown', + path: 'top-navigation/scenario-full-page', + screenshotType: 'viewport', + configuration: { width: 500, height: 800 }, + setup: async (page, wrapper) => { + await page.click(wrapper.findTopNavigation().findOverflowMenuButton().toSelector()); + await page.click(wrapper.findTopNavigation().findOverflowMenu().findUtility(3).toSelector()); + await page.keys(['Tab']); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/tree-view.ts b/test/definitions/visual/tree-view.ts new file mode 100644 index 0000000000..baab73ad51 --- /dev/null +++ b/test/definitions/visual/tree-view.ts @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Tree View', + componentName: 'tree-view', + tests: [ + { + description: 'basic', + path: 'tree-view/basic', + screenshotType: 'screenshotArea', + }, + { + description: 'with different toggle icon', + path: 'tree-view/basic', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const select = wrapper.findSelect(); + await page.click(select.findTrigger().toSelector()); + await page.click(select.findDropdown().findOptionByValue('custom').toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/wizard.ts b/test/definitions/visual/wizard.ts new file mode 100644 index 0000000000..ac8a3fe795 --- /dev/null +++ b/test/definitions/visual/wizard.ts @@ -0,0 +1,34 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Wizard', + componentName: 'wizard', + tests: [ + { + description: 'first step', + path: 'wizard/wizard-screenshot', + screenshotType: 'screenshotArea', + }, + { + description: 'second step', + path: 'wizard/wizard-screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#next'); + }, + }, + { + description: 'steps menu expanded in mobile view', + path: 'wizard/wizard-screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[role="button"][aria-expanded]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/visual/action-card.test.ts b/test/visual/action-card.test.ts index 9e62b89ebf..24908af18c 100644 --- a/test/visual/action-card.test.ts +++ b/test/visual/action-card.test.ts @@ -1,6 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually. import { runTestSuites } from '../definitions/utils'; import suite from '../definitions/visual/action-card'; diff --git a/test/visual/alert.test.ts b/test/visual/alert.test.ts index 95af4acc78..868c55ff1d 100644 --- a/test/visual/alert.test.ts +++ b/test/visual/alert.test.ts @@ -1,6 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually. import { runTestSuites } from '../definitions/utils'; import suite from '../definitions/visual/alert'; diff --git a/test/visual/app-layout-content-paddings.test.ts b/test/visual/app-layout-content-paddings.test.ts new file mode 100644 index 0000000000..e566d79817 --- /dev/null +++ b/test/visual/app-layout-content-paddings.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-content-paddings'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-drawers.test.ts b/test/visual/app-layout-drawers.test.ts new file mode 100644 index 0000000000..c66454010d --- /dev/null +++ b/test/visual/app-layout-drawers.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-drawers'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-flashbar.test.ts b/test/visual/app-layout-flashbar.test.ts new file mode 100644 index 0000000000..333642e5f3 --- /dev/null +++ b/test/visual/app-layout-flashbar.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-flashbar'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-header.test.ts b/test/visual/app-layout-header.test.ts new file mode 100644 index 0000000000..682f71ffe2 --- /dev/null +++ b/test/visual/app-layout-header.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-header'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-multi.test.ts b/test/visual/app-layout-multi.test.ts new file mode 100644 index 0000000000..244019c8fb --- /dev/null +++ b/test/visual/app-layout-multi.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-multi'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1280.test.ts b/test/visual/app-layout-responsive-1280.test.ts new file mode 100644 index 0000000000..0324b2bf39 --- /dev/null +++ b/test/visual/app-layout-responsive-1280.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-1280'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1400.test.ts b/test/visual/app-layout-responsive-1400.test.ts new file mode 100644 index 0000000000..745372c34e --- /dev/null +++ b/test/visual/app-layout-responsive-1400.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-1400'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1920.test.ts b/test/visual/app-layout-responsive-1920.test.ts new file mode 100644 index 0000000000..bee5fc8763 --- /dev/null +++ b/test/visual/app-layout-responsive-1920.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-1920'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-2540.test.ts b/test/visual/app-layout-responsive-2540.test.ts new file mode 100644 index 0000000000..48bc8580da --- /dev/null +++ b/test/visual/app-layout-responsive-2540.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-2540'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-600.test.ts b/test/visual/app-layout-responsive-600.test.ts new file mode 100644 index 0000000000..cd9243cb32 --- /dev/null +++ b/test/visual/app-layout-responsive-600.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-responsive-600'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-sticky-table-header-split-panel.test.ts b/test/visual/app-layout-sticky-table-header-split-panel.test.ts new file mode 100644 index 0000000000..c1ad3016a1 --- /dev/null +++ b/test/visual/app-layout-sticky-table-header-split-panel.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-sticky-table-header-split-panel'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-toolbar.test.ts b/test/visual/app-layout-toolbar.test.ts new file mode 100644 index 0000000000..398d6386f8 --- /dev/null +++ b/test/visual/app-layout-toolbar.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-toolbar'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout-z-index.test.ts b/test/visual/app-layout-z-index.test.ts new file mode 100644 index 0000000000..5f69f77b71 --- /dev/null +++ b/test/visual/app-layout-z-index.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout-z-index'; + +runTestSuites([suite]); diff --git a/test/visual/app-layout.test.ts b/test/visual/app-layout.test.ts new file mode 100644 index 0000000000..21c3a6ce25 --- /dev/null +++ b/test/visual/app-layout.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/app-layout'; + +runTestSuites([suite]); diff --git a/test/visual/area-chart.test.ts b/test/visual/area-chart.test.ts new file mode 100644 index 0000000000..4ffc4e042e --- /dev/null +++ b/test/visual/area-chart.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/area-chart'; + +runTestSuites([suite]); diff --git a/test/visual/attribute-editor.test.ts b/test/visual/attribute-editor.test.ts new file mode 100644 index 0000000000..6a57853125 --- /dev/null +++ b/test/visual/attribute-editor.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/attribute-editor'; + +runTestSuites([suite]); diff --git a/test/visual/autosuggest.test.ts b/test/visual/autosuggest.test.ts new file mode 100644 index 0000000000..d88cad7117 --- /dev/null +++ b/test/visual/autosuggest.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/autosuggest'; + +runTestSuites([suite]); diff --git a/test/visual/badge.test.ts b/test/visual/badge.test.ts new file mode 100644 index 0000000000..1163052443 --- /dev/null +++ b/test/visual/badge.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/badge'; + +runTestSuites([suite]); diff --git a/test/visual/bar-chart.test.ts b/test/visual/bar-chart.test.ts new file mode 100644 index 0000000000..d54245d935 --- /dev/null +++ b/test/visual/bar-chart.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/bar-chart'; + +runTestSuites([suite]); diff --git a/test/visual/box.test.ts b/test/visual/box.test.ts new file mode 100644 index 0000000000..9422b04f64 --- /dev/null +++ b/test/visual/box.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/box'; + +runTestSuites([suite]); diff --git a/test/visual/breadcrumb-group.test.ts b/test/visual/breadcrumb-group.test.ts new file mode 100644 index 0000000000..77450f3700 --- /dev/null +++ b/test/visual/breadcrumb-group.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/breadcrumb-group'; + +runTestSuites([suite]); diff --git a/test/visual/button-dropdown.test.ts b/test/visual/button-dropdown.test.ts new file mode 100644 index 0000000000..16b90ff4da --- /dev/null +++ b/test/visual/button-dropdown.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/button-dropdown'; + +runTestSuites([suite]); diff --git a/test/visual/button-group.test.ts b/test/visual/button-group.test.ts new file mode 100644 index 0000000000..100c7f9508 --- /dev/null +++ b/test/visual/button-group.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/button-group'; + +runTestSuites([suite]); diff --git a/test/visual/button.test.ts b/test/visual/button.test.ts new file mode 100644 index 0000000000..d4b642af53 --- /dev/null +++ b/test/visual/button.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/button'; + +runTestSuites([suite]); diff --git a/test/visual/cards.test.ts b/test/visual/cards.test.ts new file mode 100644 index 0000000000..ecb282b266 --- /dev/null +++ b/test/visual/cards.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/cards'; + +runTestSuites([suite]); diff --git a/test/visual/checkbox.test.ts b/test/visual/checkbox.test.ts new file mode 100644 index 0000000000..f569e2e7ab --- /dev/null +++ b/test/visual/checkbox.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/checkbox'; + +runTestSuites([suite]); diff --git a/test/visual/code-editor.test.ts b/test/visual/code-editor.test.ts new file mode 100644 index 0000000000..735638ea5d --- /dev/null +++ b/test/visual/code-editor.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/code-editor'; + +runTestSuites([suite]); diff --git a/test/visual/collection-preferences.test.ts b/test/visual/collection-preferences.test.ts new file mode 100644 index 0000000000..ac54029622 --- /dev/null +++ b/test/visual/collection-preferences.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/collection-preferences'; + +runTestSuites([suite]); diff --git a/test/visual/column-layout.test.ts b/test/visual/column-layout.test.ts new file mode 100644 index 0000000000..55749a8e8b --- /dev/null +++ b/test/visual/column-layout.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/column-layout'; + +runTestSuites([suite]); diff --git a/test/visual/container-sticky.test.ts b/test/visual/container-sticky.test.ts new file mode 100644 index 0000000000..ce014d9805 --- /dev/null +++ b/test/visual/container-sticky.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/container-sticky'; + +runTestSuites([suite]); diff --git a/test/visual/container.test.ts b/test/visual/container.test.ts new file mode 100644 index 0000000000..d738bd8429 --- /dev/null +++ b/test/visual/container.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/container'; + +runTestSuites([suite]); diff --git a/test/visual/content-layout-permutations.test.ts b/test/visual/content-layout-permutations.test.ts new file mode 100644 index 0000000000..20de7543dc --- /dev/null +++ b/test/visual/content-layout-permutations.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/content-layout-permutations'; + +runTestSuites([suite]); diff --git a/test/visual/content-layout.test.ts b/test/visual/content-layout.test.ts new file mode 100644 index 0000000000..cc62ebf2a4 --- /dev/null +++ b/test/visual/content-layout.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/content-layout'; + +runTestSuites([suite]); diff --git a/test/visual/copy-to-clipboard.test.ts b/test/visual/copy-to-clipboard.test.ts new file mode 100644 index 0000000000..8f6f3afa5a --- /dev/null +++ b/test/visual/copy-to-clipboard.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/copy-to-clipboard'; + +runTestSuites([suite]); diff --git a/test/visual/date-input.test.ts b/test/visual/date-input.test.ts new file mode 100644 index 0000000000..d711364611 --- /dev/null +++ b/test/visual/date-input.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/date-input'; + +runTestSuites([suite]); diff --git a/test/visual/date-picker.test.ts b/test/visual/date-picker.test.ts new file mode 100644 index 0000000000..fd7087585b --- /dev/null +++ b/test/visual/date-picker.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/date-picker'; + +runTestSuites([suite]); diff --git a/test/visual/date-range-picker.test.ts b/test/visual/date-range-picker.test.ts new file mode 100644 index 0000000000..df5ed2d355 --- /dev/null +++ b/test/visual/date-range-picker.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/date-range-picker'; + +runTestSuites([suite]); diff --git a/test/visual/divider.test.ts b/test/visual/divider.test.ts new file mode 100644 index 0000000000..30305f9eff --- /dev/null +++ b/test/visual/divider.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/divider'; + +runTestSuites([suite]); diff --git a/test/visual/drawer.test.ts b/test/visual/drawer.test.ts new file mode 100644 index 0000000000..0a16626101 --- /dev/null +++ b/test/visual/drawer.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/drawer'; + +runTestSuites([suite]); diff --git a/test/visual/dropdown.test.ts b/test/visual/dropdown.test.ts new file mode 100644 index 0000000000..1dbb5ca9b2 --- /dev/null +++ b/test/visual/dropdown.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/dropdown'; + +runTestSuites([suite]); diff --git a/test/visual/expandable-section.test.ts b/test/visual/expandable-section.test.ts new file mode 100644 index 0000000000..b052d5cf5f --- /dev/null +++ b/test/visual/expandable-section.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/expandable-section'; + +runTestSuites([suite]); diff --git a/test/visual/file-dropzone.test.ts b/test/visual/file-dropzone.test.ts new file mode 100644 index 0000000000..ad937625a3 --- /dev/null +++ b/test/visual/file-dropzone.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/file-dropzone'; + +runTestSuites([suite]); diff --git a/test/visual/file-input.test.ts b/test/visual/file-input.test.ts new file mode 100644 index 0000000000..918feeb9ff --- /dev/null +++ b/test/visual/file-input.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/file-input'; + +runTestSuites([suite]); diff --git a/test/visual/file-token-group.test.ts b/test/visual/file-token-group.test.ts new file mode 100644 index 0000000000..96b0e7396b --- /dev/null +++ b/test/visual/file-token-group.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/file-token-group'; + +runTestSuites([suite]); diff --git a/test/visual/file-upload.test.ts b/test/visual/file-upload.test.ts new file mode 100644 index 0000000000..06dafb87ca --- /dev/null +++ b/test/visual/file-upload.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/file-upload'; + +runTestSuites([suite]); diff --git a/test/visual/flashbar-stacked.test.ts b/test/visual/flashbar-stacked.test.ts new file mode 100644 index 0000000000..5e37f288af --- /dev/null +++ b/test/visual/flashbar-stacked.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/flashbar-stacked'; + +runTestSuites([suite]); diff --git a/test/visual/flashbar.test.ts b/test/visual/flashbar.test.ts new file mode 100644 index 0000000000..d02aaae84c --- /dev/null +++ b/test/visual/flashbar.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/flashbar'; + +runTestSuites([suite]); diff --git a/test/visual/form-field.test.ts b/test/visual/form-field.test.ts new file mode 100644 index 0000000000..8406e9351b --- /dev/null +++ b/test/visual/form-field.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/form-field'; + +runTestSuites([suite]); diff --git a/test/visual/form.test.ts b/test/visual/form.test.ts new file mode 100644 index 0000000000..89a44f2ec8 --- /dev/null +++ b/test/visual/form.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/form'; + +runTestSuites([suite]); diff --git a/test/visual/grid.test.ts b/test/visual/grid.test.ts new file mode 100644 index 0000000000..28313ba4f2 --- /dev/null +++ b/test/visual/grid.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/grid'; + +runTestSuites([suite]); diff --git a/test/visual/header.test.ts b/test/visual/header.test.ts new file mode 100644 index 0000000000..c08e211517 --- /dev/null +++ b/test/visual/header.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/header'; + +runTestSuites([suite]); diff --git a/test/visual/help-panel.test.ts b/test/visual/help-panel.test.ts new file mode 100644 index 0000000000..10fe7ed091 --- /dev/null +++ b/test/visual/help-panel.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/help-panel'; + +runTestSuites([suite]); diff --git a/test/visual/icon.test.ts b/test/visual/icon.test.ts new file mode 100644 index 0000000000..ecd9f9656a --- /dev/null +++ b/test/visual/icon.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/icon'; + +runTestSuites([suite]); diff --git a/test/visual/input.test.ts b/test/visual/input.test.ts new file mode 100644 index 0000000000..361de80dd5 --- /dev/null +++ b/test/visual/input.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/input'; + +runTestSuites([suite]); diff --git a/test/visual/item-card.test.ts b/test/visual/item-card.test.ts new file mode 100644 index 0000000000..1c06f6493b --- /dev/null +++ b/test/visual/item-card.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/item-card'; + +runTestSuites([suite]); diff --git a/test/visual/key-value-pairs.test.ts b/test/visual/key-value-pairs.test.ts new file mode 100644 index 0000000000..7702a5645b --- /dev/null +++ b/test/visual/key-value-pairs.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/key-value-pairs'; + +runTestSuites([suite]); diff --git a/test/visual/line-chart.test.ts b/test/visual/line-chart.test.ts new file mode 100644 index 0000000000..d6d088adf9 --- /dev/null +++ b/test/visual/line-chart.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/line-chart'; + +runTestSuites([suite]); diff --git a/test/visual/link.test.ts b/test/visual/link.test.ts new file mode 100644 index 0000000000..bd34346c9f --- /dev/null +++ b/test/visual/link.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/link'; + +runTestSuites([suite]); diff --git a/test/visual/list.test.ts b/test/visual/list.test.ts new file mode 100644 index 0000000000..aae3228105 --- /dev/null +++ b/test/visual/list.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/list'; + +runTestSuites([suite]); diff --git a/test/visual/mixed-line-bar-chart.test.ts b/test/visual/mixed-line-bar-chart.test.ts new file mode 100644 index 0000000000..0409acf8a6 --- /dev/null +++ b/test/visual/mixed-line-bar-chart.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/mixed-line-bar-chart'; + +runTestSuites([suite]); diff --git a/test/visual/modal.test.ts b/test/visual/modal.test.ts new file mode 100644 index 0000000000..ad2c71ea48 --- /dev/null +++ b/test/visual/modal.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/modal'; + +runTestSuites([suite]); diff --git a/test/visual/multiselect.test.ts b/test/visual/multiselect.test.ts new file mode 100644 index 0000000000..632ad5afaa --- /dev/null +++ b/test/visual/multiselect.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/multiselect'; + +runTestSuites([suite]); diff --git a/test/visual/pagination.test.ts b/test/visual/pagination.test.ts new file mode 100644 index 0000000000..0a868faa5d --- /dev/null +++ b/test/visual/pagination.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/pagination'; + +runTestSuites([suite]); diff --git a/test/visual/pie-chart.test.ts b/test/visual/pie-chart.test.ts new file mode 100644 index 0000000000..31d111a8db --- /dev/null +++ b/test/visual/pie-chart.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/pie-chart'; + +runTestSuites([suite]); diff --git a/test/visual/popover.test.ts b/test/visual/popover.test.ts new file mode 100644 index 0000000000..1573aa33d1 --- /dev/null +++ b/test/visual/popover.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/popover'; + +runTestSuites([suite]); diff --git a/test/visual/progress-bar.test.ts b/test/visual/progress-bar.test.ts new file mode 100644 index 0000000000..8bbd40ad30 --- /dev/null +++ b/test/visual/progress-bar.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/progress-bar'; + +runTestSuites([suite]); diff --git a/test/visual/prompt-input.test.ts b/test/visual/prompt-input.test.ts new file mode 100644 index 0000000000..c8dbcac0b4 --- /dev/null +++ b/test/visual/prompt-input.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/prompt-input'; + +runTestSuites([suite]); diff --git a/test/visual/property-filter.test.ts b/test/visual/property-filter.test.ts new file mode 100644 index 0000000000..d7290f1142 --- /dev/null +++ b/test/visual/property-filter.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/property-filter'; + +runTestSuites([suite]); diff --git a/test/visual/radio-button.test.ts b/test/visual/radio-button.test.ts new file mode 100644 index 0000000000..527390dcf0 --- /dev/null +++ b/test/visual/radio-button.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/radio-button'; + +runTestSuites([suite]); diff --git a/test/visual/radio-group.test.ts b/test/visual/radio-group.test.ts new file mode 100644 index 0000000000..b267dc05e3 --- /dev/null +++ b/test/visual/radio-group.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/radio-group'; + +runTestSuites([suite]); diff --git a/test/visual/s3-resource-selector.test.ts b/test/visual/s3-resource-selector.test.ts new file mode 100644 index 0000000000..f38b1750cc --- /dev/null +++ b/test/visual/s3-resource-selector.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/s3-resource-selector'; + +runTestSuites([suite]); diff --git a/test/visual/segmented-control.test.ts b/test/visual/segmented-control.test.ts new file mode 100644 index 0000000000..d96906c91d --- /dev/null +++ b/test/visual/segmented-control.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/segmented-control'; + +runTestSuites([suite]); diff --git a/test/visual/select.test.ts b/test/visual/select.test.ts new file mode 100644 index 0000000000..d3adc1dc91 --- /dev/null +++ b/test/visual/select.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/select'; + +runTestSuites([suite]); diff --git a/test/visual/side-navigation.test.ts b/test/visual/side-navigation.test.ts new file mode 100644 index 0000000000..91aa6d4895 --- /dev/null +++ b/test/visual/side-navigation.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/side-navigation'; + +runTestSuites([suite]); diff --git a/test/visual/slider.test.ts b/test/visual/slider.test.ts new file mode 100644 index 0000000000..ea85f9c860 --- /dev/null +++ b/test/visual/slider.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/slider'; + +runTestSuites([suite]); diff --git a/test/visual/space-between.test.ts b/test/visual/space-between.test.ts new file mode 100644 index 0000000000..4afe1219bc --- /dev/null +++ b/test/visual/space-between.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/space-between'; + +runTestSuites([suite]); diff --git a/test/visual/spinner.test.ts b/test/visual/spinner.test.ts new file mode 100644 index 0000000000..38e1ede756 --- /dev/null +++ b/test/visual/spinner.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/spinner'; + +runTestSuites([suite]); diff --git a/test/visual/split-panel.test.ts b/test/visual/split-panel.test.ts new file mode 100644 index 0000000000..c9268d3c3a --- /dev/null +++ b/test/visual/split-panel.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/split-panel'; + +runTestSuites([suite]); diff --git a/test/visual/status-indicator.test.ts b/test/visual/status-indicator.test.ts new file mode 100644 index 0000000000..88e5d5942d --- /dev/null +++ b/test/visual/status-indicator.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/status-indicator'; + +runTestSuites([suite]); diff --git a/test/visual/steps.test.ts b/test/visual/steps.test.ts new file mode 100644 index 0000000000..c82db29656 --- /dev/null +++ b/test/visual/steps.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/steps'; + +runTestSuites([suite]); diff --git a/test/visual/table-cells.test.ts b/test/visual/table-cells.test.ts new file mode 100644 index 0000000000..31e495ea22 --- /dev/null +++ b/test/visual/table-cells.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/table-cells'; + +runTestSuites([suite]); diff --git a/test/visual/table-embedded.test.ts b/test/visual/table-embedded.test.ts new file mode 100644 index 0000000000..e158981079 --- /dev/null +++ b/test/visual/table-embedded.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/table-embedded'; + +runTestSuites([suite]); diff --git a/test/visual/table-inline-editing.test.ts b/test/visual/table-inline-editing.test.ts new file mode 100644 index 0000000000..d6321b5b38 --- /dev/null +++ b/test/visual/table-inline-editing.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/table-inline-editing'; + +runTestSuites([suite]); diff --git a/test/visual/table-resizable-columns.test.ts b/test/visual/table-resizable-columns.test.ts new file mode 100644 index 0000000000..bb0a535139 --- /dev/null +++ b/test/visual/table-resizable-columns.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/table-resizable-columns'; + +runTestSuites([suite]); diff --git a/test/visual/table-sticky-header.test.ts b/test/visual/table-sticky-header.test.ts new file mode 100644 index 0000000000..68a994750e --- /dev/null +++ b/test/visual/table-sticky-header.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/table-sticky-header'; + +runTestSuites([suite]); diff --git a/test/visual/table-sticky-scrollbar.test.ts b/test/visual/table-sticky-scrollbar.test.ts new file mode 100644 index 0000000000..2ac31ef9b9 --- /dev/null +++ b/test/visual/table-sticky-scrollbar.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/table-sticky-scrollbar'; + +runTestSuites([suite]); diff --git a/test/visual/table.test.ts b/test/visual/table.test.ts new file mode 100644 index 0000000000..2b2bc6a6d9 --- /dev/null +++ b/test/visual/table.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/table'; + +runTestSuites([suite]); diff --git a/test/visual/tabs.test.ts b/test/visual/tabs.test.ts new file mode 100644 index 0000000000..1829134d38 --- /dev/null +++ b/test/visual/tabs.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/tabs'; + +runTestSuites([suite]); diff --git a/test/visual/tag-editor.test.ts b/test/visual/tag-editor.test.ts new file mode 100644 index 0000000000..8edbf8e05d --- /dev/null +++ b/test/visual/tag-editor.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/tag-editor'; + +runTestSuites([suite]); diff --git a/test/visual/text-content.test.ts b/test/visual/text-content.test.ts new file mode 100644 index 0000000000..9bc8a0e784 --- /dev/null +++ b/test/visual/text-content.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/text-content'; + +runTestSuites([suite]); diff --git a/test/visual/text-filter.test.ts b/test/visual/text-filter.test.ts new file mode 100644 index 0000000000..218fd62481 --- /dev/null +++ b/test/visual/text-filter.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/text-filter'; + +runTestSuites([suite]); diff --git a/test/visual/textarea.test.ts b/test/visual/textarea.test.ts new file mode 100644 index 0000000000..3282ff813b --- /dev/null +++ b/test/visual/textarea.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/textarea'; + +runTestSuites([suite]); diff --git a/test/visual/tiles.test.ts b/test/visual/tiles.test.ts new file mode 100644 index 0000000000..3d7d1e5329 --- /dev/null +++ b/test/visual/tiles.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/tiles'; + +runTestSuites([suite]); diff --git a/test/visual/time-input.test.ts b/test/visual/time-input.test.ts new file mode 100644 index 0000000000..0d96ace144 --- /dev/null +++ b/test/visual/time-input.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/time-input'; + +runTestSuites([suite]); diff --git a/test/visual/toggle-button.test.ts b/test/visual/toggle-button.test.ts new file mode 100644 index 0000000000..be636ceb49 --- /dev/null +++ b/test/visual/toggle-button.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/toggle-button'; + +runTestSuites([suite]); diff --git a/test/visual/toggle.test.ts b/test/visual/toggle.test.ts new file mode 100644 index 0000000000..2ea4312468 --- /dev/null +++ b/test/visual/toggle.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/toggle'; + +runTestSuites([suite]); diff --git a/test/visual/token-group.test.ts b/test/visual/token-group.test.ts new file mode 100644 index 0000000000..7e20f831e4 --- /dev/null +++ b/test/visual/token-group.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/token-group'; + +runTestSuites([suite]); diff --git a/test/visual/top-navigation.test.ts b/test/visual/top-navigation.test.ts new file mode 100644 index 0000000000..12d9ed80d8 --- /dev/null +++ b/test/visual/top-navigation.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/top-navigation'; + +runTestSuites([suite]); diff --git a/test/visual/tree-view.test.ts b/test/visual/tree-view.test.ts new file mode 100644 index 0000000000..e9cb8d76c7 --- /dev/null +++ b/test/visual/tree-view.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/tree-view'; + +runTestSuites([suite]); diff --git a/test/visual/wizard.test.ts b/test/visual/wizard.test.ts new file mode 100644 index 0000000000..45ee321225 --- /dev/null +++ b/test/visual/wizard.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/wizard'; + +runTestSuites([suite]); From b1ae9c6f9e8776b068d11de1008a64be02ecaaf6 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 00:25:45 +0200 Subject: [PATCH 097/135] Refactor --- test/definitions/visual/app-layout-drawers.ts | 6 +- .../definitions/visual/app-layout-flashbar.ts | 2 +- test/definitions/visual/app-layout-header.ts | 4 +- .../visual/app-layout-responsive-tests.ts | 6 +- ...-layout-sticky-table-header-split-panel.ts | 12 ++-- test/definitions/visual/app-layout-z-index.ts | 8 +-- test/definitions/visual/app-layout.ts | 12 ++-- test/definitions/visual/area-chart.ts | 14 ++-- test/definitions/visual/autosuggest.ts | 12 ++-- test/definitions/visual/bar-chart.ts | 8 +-- test/definitions/visual/breadcrumb-group.ts | 2 +- test/definitions/visual/button-dropdown.ts | 72 +++++++++---------- test/definitions/visual/button-group.ts | 6 +- test/definitions/visual/button.ts | 6 +- test/definitions/visual/checkbox.ts | 2 +- test/definitions/visual/code-editor.ts | 10 +-- .../visual/collection-preferences.ts | 14 ++-- test/definitions/visual/container-sticky.ts | 20 +++--- test/definitions/visual/content-layout.ts | 2 +- test/definitions/visual/copy-to-clipboard.ts | 2 +- test/definitions/visual/date-picker.ts | 8 +-- test/definitions/visual/date-range-picker.ts | 12 ++-- test/definitions/visual/drawer.ts | 2 +- test/definitions/visual/dropdown.ts | 14 ++-- test/definitions/visual/expandable-section.ts | 18 ++--- test/definitions/visual/flashbar-stacked.ts | 16 ++--- test/definitions/visual/flashbar.ts | 2 +- test/definitions/visual/help-panel.ts | 6 +- test/definitions/visual/line-chart.ts | 20 +++--- .../visual/mixed-line-bar-chart.ts | 8 +-- test/definitions/visual/modal.ts | 36 +++++----- test/definitions/visual/multiselect.ts | 22 +++--- test/definitions/visual/pie-chart.ts | 6 +- test/definitions/visual/popover.ts | 38 +++++----- test/definitions/visual/prompt-input.ts | 2 +- test/definitions/visual/property-filter.ts | 24 +++---- test/definitions/visual/radio-button.ts | 4 +- test/definitions/visual/radio-group.ts | 2 +- .../visual/s3-resource-selector.ts | 4 +- test/definitions/visual/select.ts | 14 ++-- test/definitions/visual/split-panel.ts | 12 ++-- .../visual/table-inline-editing.ts | 8 +-- .../visual/table-resizable-columns.ts | 10 +-- .../definitions/visual/table-sticky-header.ts | 6 +- test/definitions/visual/table.ts | 10 +-- test/definitions/visual/tabs.ts | 10 +-- test/definitions/visual/textarea.ts | 4 +- test/definitions/visual/toggle-button.ts | 2 +- test/definitions/visual/toggle.ts | 2 +- test/definitions/visual/top-navigation.ts | 6 +- test/definitions/visual/tree-view.ts | 2 +- test/definitions/visual/wizard.ts | 4 +- 52 files changed, 277 insertions(+), 277 deletions(-) diff --git a/test/definitions/visual/app-layout-drawers.ts b/test/definitions/visual/app-layout-drawers.ts index 5fe2f12b5d..b9c97388a5 100644 --- a/test/definitions/visual/app-layout-drawers.ts +++ b/test/definitions/visual/app-layout-drawers.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { description: 'with split panel', path: 'app-layout/with-drawers', screenshotType: 'viewport', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); }, }, @@ -19,7 +19,7 @@ const suite: TestSuite = { description: 'with tooltip on hover', path: 'app-layout/with-drawers', screenshotType: 'viewport', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); }, }, @@ -28,7 +28,7 @@ const suite: TestSuite = { path: 'app-layout/with-drawers-scrollable', screenshotType: 'viewport', queryParams: { sideNavFill: 'false' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); }, }, diff --git a/test/definitions/visual/app-layout-flashbar.ts b/test/definitions/visual/app-layout-flashbar.ts index 5849a5cbf5..bf402ebbed 100644 --- a/test/definitions/visual/app-layout-flashbar.ts +++ b/test/definitions/visual/app-layout-flashbar.ts @@ -14,7 +14,7 @@ const suite: TestSuite = { path: 'app-layout/with-stacked-notifications-and-table', screenshotType: 'screenshotArea' as const, configuration: { width: 1280, height: 900 }, - setup: async (page: import('@cloudscape-design/browser-test-tools/page-objects').ScreenshotPageObject) => { + setup: async ({ page }) => { if (!disableContentPaddings) { await page.click('[data-id="toggle-content-paddings"]'); } diff --git a/test/definitions/visual/app-layout-header.ts b/test/definitions/visual/app-layout-header.ts index 3a128a266e..e9d5fbac98 100644 --- a/test/definitions/visual/app-layout-header.ts +++ b/test/definitions/visual/app-layout-header.ts @@ -22,7 +22,7 @@ const suite: TestSuite = { path: 'app-layout/with-table', screenshotType: 'viewport' as const, configuration: { width }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -32,7 +32,7 @@ const suite: TestSuite = { screenshotType: 'viewport' as const, configuration: { width }, queryParams: { stickyNotifications: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, diff --git a/test/definitions/visual/app-layout-responsive-tests.ts b/test/definitions/visual/app-layout-responsive-tests.ts index f0ac91d121..2243f87a2a 100644 --- a/test/definitions/visual/app-layout-responsive-tests.ts +++ b/test/definitions/visual/app-layout-responsive-tests.ts @@ -23,7 +23,7 @@ export function responsiveTests(width: number): TestSuite { path: 'app-layout/with-wizard', screenshotType: 'viewport', configuration: { width }, - setup: async page => { + setup: async ({ page }) => { await page.click('[aria-label="Open navigation"]'); }, }, @@ -117,7 +117,7 @@ export function responsiveTests(width: number): TestSuite { path: 'app-layout/with-sticky-notifications', screenshotType: 'viewport', configuration: { width }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 2000 }); }, }, @@ -150,7 +150,7 @@ export function responsiveTests(width: number): TestSuite { path: 'app-layout/with-drawers', screenshotType: 'viewport', configuration: { width }, - setup: async page => { + setup: async ({ page }) => { await page.click('[aria-label="Security trigger button"]'); }, }, diff --git a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts index 6a0b899686..3bc920d22e 100644 --- a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts +++ b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts @@ -12,7 +12,7 @@ const suite: TestSuite = { path: 'app-layout/with-sticky-table-and-split-panel', screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="set-item-count-to-1"]'); await page.scrollToBottom('html'); }, @@ -22,7 +22,7 @@ const suite: TestSuite = { path: 'app-layout/with-sticky-table-and-split-panel', screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="set-item-count-to-30"]'); await page.scrollToBottom('html'); }, @@ -32,7 +32,7 @@ const suite: TestSuite = { path: 'app-layout/with-sticky-table-and-split-panel', screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="set-item-count-to-1"]'); await page.click('aria/Open panel'); await page.scrollToBottom('html'); @@ -43,7 +43,7 @@ const suite: TestSuite = { path: 'app-layout/with-sticky-table-and-split-panel', screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="set-item-count-to-30"]'); await page.click('aria/Open panel'); await page.scrollToBottom('html'); @@ -54,7 +54,7 @@ const suite: TestSuite = { path: 'app-layout/with-sticky-table-and-split-panel', screenshotType: 'viewport', configuration: { width: 1280, height: 900 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="set-item-count-to-30"]'); await page.click('aria/Open panel'); await page.windowScrollTo({ top: 0 }); @@ -68,7 +68,7 @@ const suite: TestSuite = { path: 'app-layout/refresh-content-width', screenshotType: 'viewport', configuration: { width: 1280, height: 700 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-test-id="button_width-number-max_value"]'); }, }, diff --git a/test/definitions/visual/app-layout-z-index.ts b/test/definitions/visual/app-layout-z-index.ts index b0dacf8bc5..149b2b8f36 100644 --- a/test/definitions/visual/app-layout-z-index.ts +++ b/test/definitions/visual/app-layout-z-index.ts @@ -13,7 +13,7 @@ const suite: TestSuite = { path: 'app-layout/with-absolute-components', screenshotType: 'viewport' as const, configuration: { width }, - setup: async page => { + setup: async ({ page }) => { await page.click('button=Button dropdown'); await page.click('[data-testid="2"]'); await page.windowScrollTo({ top: 300 }); @@ -24,7 +24,7 @@ const suite: TestSuite = { path: 'app-layout/with-absolute-components', screenshotType: 'viewport' as const, configuration: { width, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="select-demo"] button'); await page.windowScrollTo({ top: 300 }); }, @@ -41,7 +41,7 @@ const suite: TestSuite = { path: 'app-layout/with-full-page-table-and-split-panel', screenshotType: 'viewport' as const, configuration: { width: 600 }, - setup: async page => { + setup: async ({ page }) => { await page.click('button[aria-label="Open navigation"]'); }, }, @@ -50,7 +50,7 @@ const suite: TestSuite = { path: 'app-layout/with-full-page-table-and-split-panel', screenshotType: 'viewport' as const, configuration: { width: 600 }, - setup: async page => { + setup: async ({ page }) => { await page.click('button[aria-label="Open tools"]'); }, }, diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts index 683efdcc25..3a8453c47a 100644 --- a/test/definitions/visual/app-layout.ts +++ b/test/definitions/visual/app-layout.ts @@ -18,7 +18,7 @@ const suite: TestSuite = { path: 'app-layout/default', screenshotType: 'viewport', configuration: { width: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[aria-label="Open tools"]'); }, }, @@ -27,7 +27,7 @@ const suite: TestSuite = { path: 'app-layout/disable-paddings', screenshotType: 'viewport', configuration: { width: 1280 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[aria-label="Close navigation"]'); }, }, @@ -59,7 +59,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 1400 }, queryParams: { splitPanelPosition: 'side' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[aria-label="Security trigger button"]'); await page.click('[aria-label="Open panel"]'); }, @@ -72,7 +72,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 1500 }, queryParams: { splitPanelPosition: 'side' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[aria-label="Security trigger button"]'); await page.click('[aria-label="Open panel"]'); await page.setWindowSize({ width: 1400, height: 800 }); @@ -85,7 +85,7 @@ const suite: TestSuite = { path: 'app-layout/default', screenshotType: 'viewport', configuration: { width: 400, height: 400 }, - setup: async page => { + setup: async ({ page }) => { await page.setWindowSize({ width: 1800, height: 400 }); }, }, @@ -94,7 +94,7 @@ const suite: TestSuite = { path: 'app-layout/default', screenshotType: 'viewport', configuration: { width: 1800, height: 400 }, - setup: async page => { + setup: async ({ page }) => { await page.setWindowSize({ width: 400, height: 400 }); }, }, diff --git a/test/definitions/visual/area-chart.ts b/test/definitions/visual/area-chart.ts index cfa0b5e47b..2d11756416 100644 --- a/test/definitions/visual/area-chart.ts +++ b/test/definitions/visual/area-chart.ts @@ -37,7 +37,7 @@ const suite: TestSuite = { path: 'area-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -48,7 +48,7 @@ const suite: TestSuite = { path: 'area-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -61,7 +61,7 @@ const suite: TestSuite = { path: 'area-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -76,7 +76,7 @@ const suite: TestSuite = { path: 'area-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.keys(['Tab']); @@ -92,7 +92,7 @@ const suite: TestSuite = { path: 'area-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -108,7 +108,7 @@ const suite: TestSuite = { path: 'area-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -125,7 +125,7 @@ const suite: TestSuite = { path: 'area-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[aria-label="Linear latency chart"]', 200, 50); await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); }, diff --git a/test/definitions/visual/autosuggest.ts b/test/definitions/visual/autosuggest.ts index 62910a0ed1..4f528d0801 100644 --- a/test/definitions/visual/autosuggest.ts +++ b/test/definitions/visual/autosuggest.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { description: 'permutations', path: 'autosuggest/permutations', screenshotType: 'permutations', - setup: async page => { + setup: async ({ page }) => { await page.click('input'); }, }, @@ -19,7 +19,7 @@ const suite: TestSuite = { description: 'permutations for async properties', path: 'autosuggest/permutations-async', screenshotType: 'permutations', - setup: async page => { + setup: async ({ page }) => { await page.click('input'); }, }, @@ -27,7 +27,7 @@ const suite: TestSuite = { description: 'Displays options with groups correctly', path: 'autosuggest/scenarios', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('input'); }, }, @@ -35,7 +35,7 @@ const suite: TestSuite = { description: 'Correctly displays dropdown regions', path: 'autosuggest/regions-scenarios', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('input'); }, }, @@ -43,7 +43,7 @@ const suite: TestSuite = { description: 'Long virtual list - navigate to last item', path: 'autosuggest/virtual-scroll', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); await page.keys(['ArrowUp']); }, @@ -55,7 +55,7 @@ const suite: TestSuite = { path: 'autosuggest/custom-render-option', screenshotType: 'screenshotArea' as const, queryParams: { virtualScroll: String(virtualScroll) }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); await page.keys(['ArrowDown']); }, diff --git a/test/definitions/visual/bar-chart.ts b/test/definitions/visual/bar-chart.ts index e61b6b36c1..68508d40b6 100644 --- a/test/definitions/visual/bar-chart.ts +++ b/test/definitions/visual/bar-chart.ts @@ -44,7 +44,7 @@ const suite: TestSuite = { path: 'bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.focusNextElement(); @@ -57,7 +57,7 @@ const suite: TestSuite = { path: 'bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.focusNextElement(); @@ -72,7 +72,7 @@ const suite: TestSuite = { path: 'bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('#chart svg[aria-label="Bar chart"]'); await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); }, @@ -82,7 +82,7 @@ const suite: TestSuite = { path: 'bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.scrollToBottom('html'); await page.click('#focus-target-3'); await page.focusNextElement(); diff --git a/test/definitions/visual/breadcrumb-group.ts b/test/definitions/visual/breadcrumb-group.ts index 72fbddf96f..cf5a3013d7 100644 --- a/test/definitions/visual/breadcrumb-group.ts +++ b/test/definitions/visual/breadcrumb-group.ts @@ -30,7 +30,7 @@ const suite: TestSuite = { path: 'breadcrumb-group/scenarios', screenshotType: 'viewport', configuration: { width: 300, height: 1000 }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findBreadcrumbGroup('[data-testid="breadcrumbs-6"]').findDropdown().toSelector()); }, }, diff --git a/test/definitions/visual/button-dropdown.ts b/test/definitions/visual/button-dropdown.ts index 3dda4a427c..04c8c4c5c7 100644 --- a/test/definitions/visual/button-dropdown.ts +++ b/test/definitions/visual/button-dropdown.ts @@ -12,7 +12,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 500 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-left'); }, }, @@ -21,7 +21,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 500 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-right'); }, }, @@ -30,7 +30,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 500 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-left'); }, }, @@ -39,7 +39,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 500 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-right'); }, }, @@ -48,7 +48,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-left'); }, }, @@ -57,7 +57,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-right'); }, }, @@ -66,7 +66,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-left'); }, }, @@ -75,7 +75,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-right'); }, }, @@ -84,7 +84,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 230, height: 400 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-left'); }, }, @@ -93,7 +93,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-positioning', screenshotType: 'viewport', configuration: { width: 230, height: 400 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-left'); }, }, @@ -102,7 +102,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-expandable', screenshotType: 'viewport', configuration: { width: 500 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-left'); await page.click('[data-testid="category1"]'); }, @@ -112,7 +112,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-expandable', screenshotType: 'viewport', configuration: { width: 500 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-right'); await page.click('[data-testid="category1"]'); }, @@ -122,7 +122,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-expandable', screenshotType: 'viewport', configuration: { width: 500 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-left'); await page.click('[data-testid="category1"]'); }, @@ -132,7 +132,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-expandable', screenshotType: 'viewport', configuration: { width: 500 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-right'); await page.click('[data-testid="category1"]'); }, @@ -142,7 +142,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-expandable', screenshotType: 'viewport', configuration: { width: 1200 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-left'); await page.click('[data-testid="category1"]'); }, @@ -152,7 +152,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-expandable', screenshotType: 'viewport', configuration: { width: 1200 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-top-right'); await page.click('[data-testid="category1"]'); }, @@ -162,7 +162,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-expandable', screenshotType: 'viewport', configuration: { width: 1200 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-left'); await page.click('[data-testid="category1"]'); }, @@ -172,7 +172,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-expandable', screenshotType: 'viewport', configuration: { width: 1200 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.bd-bottom-right'); await page.click('[data-testid="category1"]'); }, @@ -182,7 +182,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-container', screenshotType: 'viewport', configuration: { width: 600 }, - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible('#scrollable-container'); const containerBBox = await page.getBoundingBox('#scrollable-container'); const buttonBBox = await page.getBoundingBox('#ButtonDropdown button'); @@ -198,7 +198,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-overflow-container', screenshotType: 'viewport', configuration: { width: 1000 }, - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible('#scroll-container'); const containerBBox = await page.getBoundingBox('#scroll-container'); const buttonBBox = await page.getBoundingBox('#button-dropdown-scroll button'); @@ -215,7 +215,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-overflow-container', screenshotType: 'viewport', configuration: { width: 1000 }, - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible('#hidden-container'); await page.click('#button-dropdown-hidden'); await page.click('[data-testid="category1"]'); @@ -226,7 +226,7 @@ const suite: TestSuite = { path: 'button-dropdown/scenarios-overflow-container', screenshotType: 'viewport', configuration: { width: 1000 }, - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible('#auto-container'); const containerBBox = await page.getBoundingBox('#auto-container'); const buttonBBox = await page.getBoundingBox('#button-dropdown-auto button'); @@ -257,7 +257,7 @@ const suite: TestSuite = { description: 'ButtonDropdown dimmed category group at width 500', path: 'button-dropdown/simple', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#ButtonDropdown8'); await page.keys(['ArrowDown', 'ArrowDown', 'Enter']); }, @@ -266,7 +266,7 @@ const suite: TestSuite = { description: 'ButtonDropdown dimmed category group at width 800', path: 'button-dropdown/simple', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#ButtonDropdown8'); await page.keys(['ArrowDown', 'ArrowDown', 'Enter']); }, @@ -275,7 +275,7 @@ const suite: TestSuite = { description: 'ButtonDropdown with disabled reason at width 500', path: 'button-dropdown/disabled-reason', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="buttonDropdown"]'); await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown']); }, @@ -284,7 +284,7 @@ const suite: TestSuite = { description: 'ButtonDropdown with disabled reason at width 800', path: 'button-dropdown/disabled-reason', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="buttonDropdown"]'); await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown']); }, @@ -293,7 +293,7 @@ const suite: TestSuite = { description: 'ButtonDropdown with disabled reason for selectable item at width 500', path: 'button-dropdown/disabled-reason', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="buttonDropdownSelectableItems"]'); }, }, @@ -301,7 +301,7 @@ const suite: TestSuite = { description: 'ButtonDropdown with disabled reason for selectable item at width 800', path: 'button-dropdown/disabled-reason', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="buttonDropdownSelectableItems"]'); }, }, @@ -311,7 +311,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 500 }, queryParams: { expandableGroups: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findButtonDropdown().toSelector()); await page.click('[data-testid="category1"]'); }, @@ -322,7 +322,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 500 }, queryParams: { expandableGroups: 'false' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findButtonDropdown().toSelector()); }, }, @@ -332,7 +332,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800 }, queryParams: { expandableGroups: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findButtonDropdown().toSelector()); await page.click('[data-testid="category1"]'); }, @@ -343,7 +343,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800 }, queryParams: { expandableGroups: 'false' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findButtonDropdown().toSelector()); }, }, @@ -353,7 +353,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 500 }, queryParams: { expandableGroups: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findButtonDropdown().toSelector()); await page.click('[data-testid="group"]'); }, @@ -364,7 +364,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 500 }, queryParams: { expandableGroups: 'false' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findButtonDropdown().toSelector()); }, }, @@ -374,7 +374,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 1000 }, queryParams: { expandableGroups: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findButtonDropdown().toSelector()); await page.click('[data-testid="group"]'); }, @@ -385,7 +385,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 1000 }, queryParams: { expandableGroups: 'false' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findButtonDropdown().toSelector()); }, }, diff --git a/test/definitions/visual/button-group.ts b/test/definitions/visual/button-group.ts index ecdbc17d06..839f19c19e 100644 --- a/test/definitions/visual/button-group.ts +++ b/test/definitions/visual/button-group.ts @@ -21,7 +21,7 @@ const suite: TestSuite = { description: 'shows tooltip when hovering item', path: 'button-group/test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[data-testid="like"]'); }, }, @@ -29,7 +29,7 @@ const suite: TestSuite = { description: 'shows tooltip when hovering menu', path: 'button-group/test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[data-testid="more-actions"]'); }, }, @@ -37,7 +37,7 @@ const suite: TestSuite = { description: 'shows feedback when clicking copy', path: 'button-group/test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[data-testid="copy"]'); }, }, diff --git a/test/definitions/visual/button.ts b/test/definitions/visual/button.ts index 198412c3ec..b6b8645764 100644 --- a/test/definitions/visual/button.ts +++ b/test/definitions/visual/button.ts @@ -46,7 +46,7 @@ const suite: TestSuite = { description: 'Button is focused', path: 'button/tab-navigation', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focusButton'); await page.focusNextElement(); }, @@ -55,7 +55,7 @@ const suite: TestSuite = { description: 'shows disabled reason tooltip on hover within modal', path: 'button/disabled-reason-modal', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[data-testid="button"]'); }, }, @@ -63,7 +63,7 @@ const suite: TestSuite = { description: 'shows disabled reason tooltip on hover over a button with an href', path: 'button/disabled-reason', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[data-testid="normal-button-with-href"]'); }, }, diff --git a/test/definitions/visual/checkbox.ts b/test/definitions/visual/checkbox.ts index 34e92eff2b..9f87723cf8 100644 --- a/test/definitions/visual/checkbox.ts +++ b/test/definitions/visual/checkbox.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'Checkbox is focused', path: 'checkbox/focus-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts index 051ae5bd92..615544b402 100644 --- a/test/definitions/visual/code-editor.ts +++ b/test/definitions/visual/code-editor.ts @@ -13,7 +13,7 @@ const suite: TestSuite = { description: 'simple', path: 'code-editor/simple', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible(ACE_SELECTOR); }, }, @@ -31,7 +31,7 @@ const suite: TestSuite = { description: 'theme resolution', path: 'code-editor/themes', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible(ACE_SELECTOR); }, }, @@ -39,7 +39,7 @@ const suite: TestSuite = { description: 'permutations', path: 'code-editor/permutations', screenshotType: 'permutations', - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible(ACE_SELECTOR + ' .ace_error'); await page.waitForVisible('.ace_gutter-cell.ace_gutter-active-line.ace_error'); }, @@ -48,7 +48,7 @@ const suite: TestSuite = { description: 'listens to mode change', path: 'code-editor/simple', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible(ACE_SELECTOR); await page.click('#mode-toggle'); }, @@ -58,7 +58,7 @@ const suite: TestSuite = { path: 'code-editor/simple', screenshotType: 'screenshotArea', configuration: { width: 360 }, - setup: async page => { + setup: async ({ page }) => { await page.waitForVisible(ACE_SELECTOR); }, }, diff --git a/test/definitions/visual/collection-preferences.ts b/test/definitions/visual/collection-preferences.ts index 3448633608..a12ad8b545 100644 --- a/test/definitions/visual/collection-preferences.ts +++ b/test/definitions/visual/collection-preferences.ts @@ -12,7 +12,7 @@ const suite: TestSuite = { path: 'collection-preferences/simple', screenshotType: 'viewport', configuration: { width: 600, height: 1100 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.cp-1 button'); }, }, @@ -20,7 +20,7 @@ const suite: TestSuite = { description: 'visible content only at 600x1100', path: 'collection-preferences/simple', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('.cp-4 button'); }, }, @@ -29,7 +29,7 @@ const suite: TestSuite = { path: 'collection-preferences/simple', screenshotType: 'viewport', configuration: { width: 1280, height: 700 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.cp-1 button'); }, }, @@ -37,7 +37,7 @@ const suite: TestSuite = { description: 'visible content only at 1280x700', path: 'collection-preferences/simple', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('.cp-4 button'); }, }, @@ -45,7 +45,7 @@ const suite: TestSuite = { description: 'custom', path: 'collection-preferences/simple', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('.cp-2 button'); }, }, @@ -58,7 +58,7 @@ const suite: TestSuite = { path: 'collection-preferences/reorder-content', screenshotType: 'viewport', configuration: { width: 900, height: 650 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.cp-1 button'); await page.keys(Array(5).fill('Tab')); }, @@ -68,7 +68,7 @@ const suite: TestSuite = { path: 'collection-preferences/reorder-content', screenshotType: 'viewport', configuration: { width: 900, height: 650 }, - setup: async page => { + setup: async ({ page }) => { await page.click('.cp-1 button'); await page.keys(Array(5).fill('Tab')); await page.keys('Space'); diff --git a/test/definitions/visual/container-sticky.ts b/test/definitions/visual/container-sticky.ts index ef67bd3d09..a322883a50 100644 --- a/test/definitions/visual/container-sticky.ts +++ b/test/definitions/visual/container-sticky.ts @@ -12,7 +12,7 @@ const suite: TestSuite = { path: 'container/sticky-permutations', screenshotType: 'viewport', configuration: { width: 1400 }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -22,7 +22,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 1400 }, queryParams: { hasNotifications: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -32,7 +32,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 1400 }, queryParams: { hasBreadcrumbs: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -42,7 +42,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 1400 }, queryParams: { hasNotifications: 'true', hasAlert: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -59,7 +59,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 1400 }, queryParams: { hasNotifications: 'true', highContrast: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -68,7 +68,7 @@ const suite: TestSuite = { path: 'container/sticky-permutations', screenshotType: 'viewport', configuration: { width: 600 }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -78,7 +78,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 600 }, queryParams: { hasNotifications: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -88,7 +88,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 600 }, queryParams: { hasBreadcrumbs: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -98,7 +98,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 600 }, queryParams: { hasNotifications: 'true', hasAlert: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, @@ -115,7 +115,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 600 }, queryParams: { hasNotifications: 'true', highContrast: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 200 }); }, }, diff --git a/test/definitions/visual/content-layout.ts b/test/definitions/visual/content-layout.ts index 4b387f5d70..ea88b5de4c 100644 --- a/test/definitions/visual/content-layout.ts +++ b/test/definitions/visual/content-layout.ts @@ -31,7 +31,7 @@ const suite: TestSuite = { description: 'form with content layout header', path: 'content-layout/with-header-toggles', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="toggle-form-header"] input'); await page.click('[data-testid="toggle-content-layout"] input'); }, diff --git a/test/definitions/visual/copy-to-clipboard.ts b/test/definitions/visual/copy-to-clipboard.ts index 64f967f0b7..2655bd0f26 100644 --- a/test/definitions/visual/copy-to-clipboard.ts +++ b/test/definitions/visual/copy-to-clipboard.ts @@ -17,7 +17,7 @@ const suite: TestSuite = { path: 'copy-to-clipboard/scenario-split-panel', screenshotType: 'screenshotArea', configuration: { width: 1280, height: 900 }, - setup: async page => { + setup: async ({ page }) => { await page.click('[aria-label="Copy dummy text"]'); await (page as any).scrollIntoView('[data-testid="scroll-me"]'); }, diff --git a/test/definitions/visual/date-picker.ts b/test/definitions/visual/date-picker.ts index 901c35b469..c694cc816b 100644 --- a/test/definitions/visual/date-picker.ts +++ b/test/definitions/visual/date-picker.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { description: 'Permutations: states', path: 'date-picker/permutations', screenshotType: 'permutations', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="date-picker-expanded-example"] button'); }, }, @@ -28,7 +28,7 @@ const suite: TestSuite = { description: 'focus ring on selected month', path: 'date-picker/month-picker', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); await page.keys(['Tab', 'Tab', 'Tab']); }, @@ -37,7 +37,7 @@ const suite: TestSuite = { description: 'focus ring on current month', path: 'date-picker/month-picker', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); await page.keys(['Tab', 'Tab', 'Tab']); await page.keys(['ArrowRight']); @@ -47,7 +47,7 @@ const suite: TestSuite = { description: 'focus ring on non selected, non current month', path: 'date-picker/month-picker', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); await page.keys(['Tab', 'Tab', 'Tab']); await page.keys(['ArrowRight', 'ArrowRight']); diff --git a/test/definitions/visual/date-range-picker.ts b/test/definitions/visual/date-range-picker.ts index 26aa0d2c01..3a851f1993 100644 --- a/test/definitions/visual/date-range-picker.ts +++ b/test/definitions/visual/date-range-picker.ts @@ -12,7 +12,7 @@ const suite: TestSuite = { path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', configuration: { width: 450, height: 950 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focusable-before'); await page.focusNextElement(); await page.keys(['Enter']); @@ -25,7 +25,7 @@ const suite: TestSuite = { path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', configuration: { width: 1200, height: 950 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focusable-before'); await page.focusNextElement(); await page.keys(['Enter']); @@ -48,7 +48,7 @@ const suite: TestSuite = { path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', configuration: { width: 450, height: 950 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focusable-before'); await page.focusNextElement(); await page.keys(['Enter']); @@ -65,7 +65,7 @@ const suite: TestSuite = { path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', configuration: { width: 1200, height: 950 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focusable-before'); await page.focusNextElement(); await page.keys(['Enter']); @@ -90,7 +90,7 @@ const suite: TestSuite = { description: 'selects text when double-clicking calendar header', path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click('#focusable-before'); await page.focusNextElement(); await page.keys(['Enter']); @@ -107,7 +107,7 @@ const suite: TestSuite = { description: 'does not select text when double-clicking next button', path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click('#focusable-before'); await page.focusNextElement(); await page.keys(['Enter']); diff --git a/test/definitions/visual/drawer.ts b/test/definitions/visual/drawer.ts index 33fdb8d896..8ea09d8343 100644 --- a/test/definitions/visual/drawer.ts +++ b/test/definitions/visual/drawer.ts @@ -49,7 +49,7 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 1280, height: 268 }, queryParams: { longContent: 'true', hasFooter: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await (page as any).scrollIntoView(wrapper.findDrawer().findFooter().toSelector()); }, }, diff --git a/test/definitions/visual/dropdown.ts b/test/definitions/visual/dropdown.ts index 8d17b837ca..4bbb716e5d 100644 --- a/test/definitions/visual/dropdown.ts +++ b/test/definitions/visual/dropdown.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { description: 'In fixed container', path: 'dropdown/fixed-container', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { const { height: windowHeight } = await page.getViewportSize(); await page.windowScrollTo({ top: windowHeight }); await page.click('button=Open dropdown'); @@ -22,7 +22,7 @@ const suite: TestSuite = { path: 'dropdown/expandable', screenshotType: 'viewport', queryParams: { componentType: 'Select', expandToViewport: 'false' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#show-modal'); await page.click('#in-modal'); }, @@ -32,7 +32,7 @@ const suite: TestSuite = { path: 'dropdown/expandable', screenshotType: 'viewport', queryParams: { componentType: 'Select', expandToViewport: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#show-modal'); await page.click('#in-modal'); }, @@ -42,7 +42,7 @@ const suite: TestSuite = { path: 'dropdown/expandable', screenshotType: 'viewport', queryParams: { componentType: 'Select', expandToViewport: 'false' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#show-popover'); await page.click('#in-popover'); await page.click('[data-test-index="5"]'); @@ -53,7 +53,7 @@ const suite: TestSuite = { path: 'dropdown/expandable', screenshotType: 'viewport', queryParams: { componentType: 'Select', expandToViewport: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#show-popover'); await page.click('#in-popover'); await page.click('[data-test-index="5"]'); @@ -64,7 +64,7 @@ const suite: TestSuite = { path: 'dropdown/expandable', screenshotType: 'viewport', queryParams: { componentType: 'Select', expandToViewport: 'false' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#bottom-left'); }, }, @@ -73,7 +73,7 @@ const suite: TestSuite = { path: 'dropdown/expandable', screenshotType: 'viewport', queryParams: { componentType: 'Select', expandToViewport: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#bottom-left'); }, }, diff --git a/test/definitions/visual/expandable-section.ts b/test/definitions/visual/expandable-section.ts index 310a8d1752..9564685028 100644 --- a/test/definitions/visual/expandable-section.ts +++ b/test/definitions/visual/expandable-section.ts @@ -22,7 +22,7 @@ const suite: TestSuite = { path: 'expandable-section/focus', screenshotType: 'screenshotArea', queryParams: { headerText: 'Header text', variant: 'container' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -32,7 +32,7 @@ const suite: TestSuite = { path: 'expandable-section/focus', screenshotType: 'screenshotArea', queryParams: { headerText: 'Header text', variant: 'default' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -42,7 +42,7 @@ const suite: TestSuite = { path: 'expandable-section/focus', screenshotType: 'screenshotArea', queryParams: { headerText: 'Header text', variant: 'footer' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -52,7 +52,7 @@ const suite: TestSuite = { path: 'expandable-section/focus', screenshotType: 'screenshotArea', queryParams: { headerText: 'Header text', variant: 'navigation' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -62,7 +62,7 @@ const suite: TestSuite = { path: 'expandable-section/focus', screenshotType: 'screenshotArea', queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'container' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -72,7 +72,7 @@ const suite: TestSuite = { path: 'expandable-section/focus', screenshotType: 'screenshotArea', queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'default' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -82,7 +82,7 @@ const suite: TestSuite = { path: 'expandable-section/focus', screenshotType: 'screenshotArea', queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'footer' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -92,7 +92,7 @@ const suite: TestSuite = { path: 'expandable-section/focus', screenshotType: 'screenshotArea', queryParams: { headerText: 'Header text', hasHeaderInfo: 'true', hasHeaderActions: 'true', variant: 'container' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -108,7 +108,7 @@ const suite: TestSuite = { hasHeaderActions: 'true', variant: 'container', }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, diff --git a/test/definitions/visual/flashbar-stacked.ts b/test/definitions/visual/flashbar-stacked.ts index 7878754974..9b795791a6 100644 --- a/test/definitions/visual/flashbar-stacked.ts +++ b/test/definitions/visual/flashbar-stacked.ts @@ -18,7 +18,7 @@ const suite: TestSuite = { path: 'flashbar/collapsible.visual-tests', screenshotType: 'screenshotArea', configuration: { width: 380 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -28,7 +28,7 @@ const suite: TestSuite = { path: 'flashbar/collapsible.visual-tests', screenshotType: 'screenshotArea', configuration: { width: 380 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['Space']); @@ -46,7 +46,7 @@ const suite: TestSuite = { path: 'flashbar/collapsible.visual-tests', screenshotType: 'screenshotArea', configuration: { width: 450 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -56,7 +56,7 @@ const suite: TestSuite = { path: 'flashbar/collapsible.visual-tests', screenshotType: 'screenshotArea', configuration: { width: 450 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['Space']); @@ -74,7 +74,7 @@ const suite: TestSuite = { path: 'flashbar/collapsible.visual-tests', screenshotType: 'screenshotArea', configuration: { width: 600 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -84,7 +84,7 @@ const suite: TestSuite = { path: 'flashbar/collapsible.visual-tests', screenshotType: 'screenshotArea', configuration: { width: 600 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['Space']); @@ -102,7 +102,7 @@ const suite: TestSuite = { path: 'flashbar/collapsible.visual-tests', screenshotType: 'screenshotArea', configuration: { width: 1200 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -112,7 +112,7 @@ const suite: TestSuite = { path: 'flashbar/collapsible.visual-tests', screenshotType: 'screenshotArea', configuration: { width: 1200 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['Space']); diff --git a/test/definitions/visual/flashbar.ts b/test/definitions/visual/flashbar.ts index 61ce21ea16..b620a4e031 100644 --- a/test/definitions/visual/flashbar.ts +++ b/test/definitions/visual/flashbar.ts @@ -56,7 +56,7 @@ const suite: TestSuite = { description: 'focus border color', path: 'flashbar/dismissal', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, diff --git a/test/definitions/visual/help-panel.ts b/test/definitions/visual/help-panel.ts index 69f40a05c5..8b994fe71d 100644 --- a/test/definitions/visual/help-panel.ts +++ b/test/definitions/visual/help-panel.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { description: 'permutations', path: 'help-panel/permutations', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await (page as any).focusInputs(); }, }, @@ -19,7 +19,7 @@ const suite: TestSuite = { description: 'with AppLayout', path: 'help-panel/with-app-layout', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await (page as any).focusInputs(); }, }, @@ -27,7 +27,7 @@ const suite: TestSuite = { description: 'loading state - with AppLayout', path: 'help-panel/loading-with-app-layout', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await (page as any).focusInputs(); }, }, diff --git a/test/definitions/visual/line-chart.ts b/test/definitions/visual/line-chart.ts index 3b4d56bb09..1031af89db 100644 --- a/test/definitions/visual/line-chart.ts +++ b/test/definitions/visual/line-chart.ts @@ -20,7 +20,7 @@ const suite: TestSuite = { path: 'line-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -33,7 +33,7 @@ const suite: TestSuite = { path: 'line-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -46,7 +46,7 @@ const suite: TestSuite = { path: 'line-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -61,7 +61,7 @@ const suite: TestSuite = { path: 'line-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); await page.focusNextElement(); @@ -76,7 +76,7 @@ const suite: TestSuite = { path: 'line-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[aria-label="Line chart"]', 200, 50); await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); }, @@ -87,7 +87,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800, height: 1000 }, queryParams: { expandableSubItems: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['ArrowRight']); @@ -100,7 +100,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800, height: 1000 }, queryParams: { expandableSubItems: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['ArrowRight']); @@ -115,7 +115,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800, height: 1000 }, queryParams: { expandableSubItems: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['ArrowRight']); @@ -129,7 +129,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800, height: 1000 }, queryParams: { expandableSubItems: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['ArrowRight']); @@ -144,7 +144,7 @@ const suite: TestSuite = { path: 'line-chart/in-expandable-section-test', screenshotType: 'screenshotArea', configuration: { width: 800, height: 800 }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const expandableSectionWrapper = wrapper.findExpandableSection(); await page.waitForVisible(expandableSectionWrapper.toSelector()); await page.click(expandableSectionWrapper.findExpandButton().toSelector()); diff --git a/test/definitions/visual/mixed-line-bar-chart.ts b/test/definitions/visual/mixed-line-bar-chart.ts index 5c7370a979..d34ad90105 100644 --- a/test/definitions/visual/mixed-line-bar-chart.ts +++ b/test/definitions/visual/mixed-line-bar-chart.ts @@ -36,7 +36,7 @@ const suite: TestSuite = { path: 'mixed-line-bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.focusNextElement(); @@ -47,7 +47,7 @@ const suite: TestSuite = { path: 'mixed-line-bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.focusNextElement(); @@ -62,7 +62,7 @@ const suite: TestSuite = { path: 'mixed-line-bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.focusNextElement(); @@ -79,7 +79,7 @@ const suite: TestSuite = { path: 'mixed-line-bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('#chart svg[aria-label="Mixed chart 1"]', 200, 100); await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); }, diff --git a/test/definitions/visual/modal.ts b/test/definitions/visual/modal.ts index 49c4092c33..28fb043c6d 100644 --- a/test/definitions/visual/modal.ts +++ b/test/definitions/visual/modal.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { description: 'simple', path: 'modal/simple', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Show modal'); }, }, @@ -19,7 +19,7 @@ const suite: TestSuite = { description: 'no-paddings', path: 'modal/no-paddings', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Show modal'); }, }, @@ -27,7 +27,7 @@ const suite: TestSuite = { description: 'vertical-scroll', path: 'modal/vertical-scroll', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Show modal'); }, }, @@ -35,7 +35,7 @@ const suite: TestSuite = { description: 'long-header', path: 'modal/long-header', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Show modal'); }, }, @@ -43,7 +43,7 @@ const suite: TestSuite = { description: 'unbreakable-header', path: 'modal/unbreakable-header', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Show modal'); }, }, @@ -51,7 +51,7 @@ const suite: TestSuite = { description: 'size-small', path: 'modal/sizes', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/small'); }, }, @@ -59,7 +59,7 @@ const suite: TestSuite = { description: 'size-medium', path: 'modal/sizes', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/medium'); }, }, @@ -67,7 +67,7 @@ const suite: TestSuite = { description: 'size-large', path: 'modal/sizes', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/large'); }, }, @@ -75,7 +75,7 @@ const suite: TestSuite = { description: 'size-x-large', path: 'modal/sizes', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/x-large'); }, }, @@ -83,7 +83,7 @@ const suite: TestSuite = { description: 'size-xx-large', path: 'modal/sizes', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/xx-large'); }, }, @@ -91,7 +91,7 @@ const suite: TestSuite = { description: 'size-max', path: 'modal/sizes', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/max'); }, }, @@ -99,7 +99,7 @@ const suite: TestSuite = { description: 'position-top', path: 'modal/position-top', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Show modal'); }, }, @@ -108,7 +108,7 @@ const suite: TestSuite = { path: 'modal/custom-dimensions', screenshotType: 'viewport', queryParams: { width: '600', height: '400', footer: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="modal-trigger"]'); }, }, @@ -117,7 +117,7 @@ const suite: TestSuite = { path: 'modal/custom-dimensions', screenshotType: 'viewport', queryParams: { width: '600', height: '400', footer: 'false' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="modal-trigger"]'); }, }, @@ -126,7 +126,7 @@ const suite: TestSuite = { path: 'modal/custom-dimensions', screenshotType: 'viewport', queryParams: { width: '10' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="modal-trigger"]'); }, }, @@ -135,7 +135,7 @@ const suite: TestSuite = { path: 'modal/custom-dimensions', screenshotType: 'viewport', queryParams: { height: '10', footer: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="modal-trigger"]'); }, }, @@ -144,7 +144,7 @@ const suite: TestSuite = { path: 'modal/custom-dimensions', screenshotType: 'viewport', queryParams: { width: '10', height: '15', footer: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="modal-trigger"]'); }, }, @@ -153,7 +153,7 @@ const suite: TestSuite = { path: 'modal/custom-dimensions', screenshotType: 'viewport', queryParams: { width: '10000', height: '10000', footer: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="modal-trigger"]'); }, }, diff --git a/test/definitions/visual/multiselect.ts b/test/definitions/visual/multiselect.ts index 45a022e5e5..3de5c1691b 100644 --- a/test/definitions/visual/multiselect.ts +++ b/test/definitions/visual/multiselect.ts @@ -22,7 +22,7 @@ const suite: TestSuite = { path: 'multiselect/screenshot', screenshotType: 'screenshotArea', queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'manual' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); }, }, @@ -31,7 +31,7 @@ const suite: TestSuite = { path: 'multiselect/screenshot', screenshotType: 'screenshotArea', queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'auto' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); }, }, @@ -40,7 +40,7 @@ const suite: TestSuite = { path: 'multiselect/screenshot', screenshotType: 'screenshotArea', queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'none' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); }, }, @@ -49,7 +49,7 @@ const suite: TestSuite = { path: 'multiselect/screenshot', screenshotType: 'screenshotArea', queryParams: { expandToViewport: 'false', virtualScroll: 'false', filteringType: 'manual' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); }, }, @@ -58,7 +58,7 @@ const suite: TestSuite = { path: 'multiselect/screenshot', screenshotType: 'screenshotArea', queryParams: { statusType: 'error' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); }, }, @@ -67,7 +67,7 @@ const suite: TestSuite = { path: 'multiselect/screenshot', screenshotType: 'screenshotArea', queryParams: { virtualScroll: 'false' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); await page.click('[data-test-index="4"]'); }, @@ -77,7 +77,7 @@ const suite: TestSuite = { path: 'multiselect/screenshot', screenshotType: 'screenshotArea', queryParams: { virtualScroll: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); await page.click('[data-test-index="4"]'); }, @@ -87,7 +87,7 @@ const suite: TestSuite = { path: 'multiselect/custom-render-option', screenshotType: 'screenshotArea', queryParams: { virtualScroll: 'false' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); }, }, @@ -96,7 +96,7 @@ const suite: TestSuite = { path: 'multiselect/custom-render-option', screenshotType: 'screenshotArea', queryParams: { virtualScroll: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); }, }, @@ -105,7 +105,7 @@ const suite: TestSuite = { path: 'multiselect/virtual-scroll', screenshotType: 'screenshotArea', queryParams: { type: 'multiselect' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { top: 99999, @@ -118,7 +118,7 @@ const suite: TestSuite = { path: 'multiselect/virtual-scroll', screenshotType: 'screenshotArea', queryParams: { type: 'multiselect-select-all' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findMultiselect().findTrigger().toSelector()); await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { top: 99999, diff --git a/test/definitions/visual/pie-chart.ts b/test/definitions/visual/pie-chart.ts index 25901670cf..ee92299a71 100644 --- a/test/definitions/visual/pie-chart.ts +++ b/test/definitions/visual/pie-chart.ts @@ -33,7 +33,7 @@ const suite: TestSuite = { description: 'can focus chart plot', path: 'pie-chart/test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.focusNextElement(); @@ -43,7 +43,7 @@ const suite: TestSuite = { description: 'can navigate segments with keyboard', path: 'pie-chart/test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.focusNextElement(); @@ -55,7 +55,7 @@ const suite: TestSuite = { description: 'can pin segments with mouse', path: 'pie-chart/test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('svg [aria-label~="Apples"] > path'); await page.waitForVisible('[aria-label="Dismiss"]'); }, diff --git a/test/definitions/visual/popover.ts b/test/definitions/visual/popover.ts index b15f2589b4..1acd509b76 100644 --- a/test/definitions/visual/popover.ts +++ b/test/definitions/visual/popover.ts @@ -21,7 +21,7 @@ const suite: TestSuite = { description: 'inside modal', path: 'popover/scenario-in-modal', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Show modal'); await page.click('#popover button'); }, @@ -30,7 +30,7 @@ const suite: TestSuite = { description: 'positioning with navigation v1.0', path: 'popover/nav-v1-0-positioning', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#popover button'); }, }, @@ -38,7 +38,7 @@ const suite: TestSuite = { description: 'close icon positioned inside the popover (no header and fixed width)', path: 'popover/header-variant', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="popover-without-title"] button'); }, }, @@ -46,7 +46,7 @@ const suite: TestSuite = { description: 'inside table - renderWithPortal=false', path: 'popover/scenario-in-table', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('table button'); }, }, @@ -54,7 +54,7 @@ const suite: TestSuite = { description: 'inside table - renderWithPortal=true', path: 'popover/scenario-in-table', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#renderWithPortal'); await page.click('table button'); }, @@ -63,7 +63,7 @@ const suite: TestSuite = { description: 'scenario - copy - renderWithPortal=false', path: 'popover/scenarios', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#scenario-copy button'); }, }, @@ -71,7 +71,7 @@ const suite: TestSuite = { description: 'scenario - copy - renderWithPortal=true', path: 'popover/scenarios', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#renderWithPortal'); await page.click('#scenario-copy button'); }, @@ -80,7 +80,7 @@ const suite: TestSuite = { description: 'scenario - medium-key-value - renderWithPortal=false', path: 'popover/scenarios', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#scenario-medium-key-value button'); }, }, @@ -88,7 +88,7 @@ const suite: TestSuite = { description: 'scenario - large-key-value - renderWithPortal=false', path: 'popover/scenarios', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#scenario-large-key-value button'); }, }, @@ -101,7 +101,7 @@ const suite: TestSuite = { description: 'inline popover - closed - renderWithPortal=true', path: 'popover/inline', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#renderWithPortal'); }, }, @@ -109,7 +109,7 @@ const suite: TestSuite = { description: 'inline popover - open - renderWithPortal=false', path: 'popover/inline', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findPopover().findTrigger().toSelector()); }, }, @@ -117,7 +117,7 @@ const suite: TestSuite = { description: 'inline popover - open - renderWithPortal=true', path: 'popover/inline', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click('#renderWithPortal'); await page.click(wrapper.findPopover().findTrigger().toSelector()); }, @@ -126,7 +126,7 @@ const suite: TestSuite = { description: 'positioning - opens in the correct position - renderWithPortal=false', path: 'popover/positioning', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#popover-2-2 button'); }, }, @@ -134,7 +134,7 @@ const suite: TestSuite = { description: 'positioning - flips to the opposite position - renderWithPortal=false', path: 'popover/positioning', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#popover-1-2 button'); }, }, @@ -142,7 +142,7 @@ const suite: TestSuite = { description: 'focus - Permutation 1', path: 'popover/focus-ring', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -151,7 +151,7 @@ const suite: TestSuite = { description: 'focus - Permutation 2', path: 'popover/focus-ring', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); for (let j = 0; j < 2; j++) { await page.focusNextElement(); @@ -162,7 +162,7 @@ const suite: TestSuite = { description: 'focus - Permutation 3', path: 'popover/focus-ring', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); for (let j = 0; j < 3; j++) { await page.focusNextElement(); @@ -173,7 +173,7 @@ const suite: TestSuite = { description: 'focus - Permutation 6', path: 'popover/focus-ring', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); for (let j = 0; j < 6; j++) { await page.focusNextElement(); @@ -184,7 +184,7 @@ const suite: TestSuite = { description: 'focus - Permutation 12', path: 'popover/focus-ring', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); for (let j = 0; j < 12; j++) { await page.focusNextElement(); diff --git a/test/definitions/visual/prompt-input.ts b/test/definitions/visual/prompt-input.ts index 7358e18aea..536f1a10d9 100644 --- a/test/definitions/visual/prompt-input.ts +++ b/test/definitions/visual/prompt-input.ts @@ -35,7 +35,7 @@ const suite: TestSuite = { path: 'prompt-input/simple', screenshotType: 'screenshotArea', queryParams: { hasSecondaryActions: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findPromptInput('[data-testid="prompt-input"]').findNativeTextarea().toSelector()); await page.keys(['Tab', 'ArrowRight', 'ArrowRight']); }, diff --git a/test/definitions/visual/property-filter.ts b/test/definitions/visual/property-filter.ts index d9529692e4..ef96c61725 100644 --- a/test/definitions/visual/property-filter.ts +++ b/test/definitions/visual/property-filter.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { description: 'token editor popover', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('.property-filter-default [aria-haspopup=dialog]'); }, }, @@ -19,7 +19,7 @@ const suite: TestSuite = { description: 'filtering token select', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('.property-filter-default [aria-haspopup=listbox]'); }, }, @@ -27,7 +27,7 @@ const suite: TestSuite = { description: 'token editor popover overflow', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('.property-filter-overflow [aria-haspopup=dialog]'); }, }, @@ -35,7 +35,7 @@ const suite: TestSuite = { description: 'token editor custom property boolean', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('.property-filter-custom-prop-boolean [aria-haspopup=dialog]'); }, }, @@ -43,7 +43,7 @@ const suite: TestSuite = { description: 'token editor custom property datetime', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('.property-filter-custom-prop-datetime [aria-haspopup=dialog]'); }, }, @@ -51,7 +51,7 @@ const suite: TestSuite = { description: 'token editor group string property', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); await page.click(propertyFilter.findTokens().get(1).findLabel().toSelector()); }, @@ -60,7 +60,7 @@ const suite: TestSuite = { description: 'token editor group datetime property', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); await page.click(propertyFilter.findTokens().get(1).findLabel().toSelector()); await page.keys(['Escape']); @@ -71,7 +71,7 @@ const suite: TestSuite = { description: 'token editor enum property', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); await page.click(propertyFilter.findNativeInput().toSelector()); await page.keys('state = s'); @@ -81,7 +81,7 @@ const suite: TestSuite = { description: 'token editor enum property no matches', path: 'property-filter/token-editor', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); await page.click(propertyFilter.findNativeInput().toSelector()); await page.keys('state = x'); @@ -113,7 +113,7 @@ const suite: TestSuite = { description: 'split panel integration - main content dropdown', path: 'property-filter/split-panel-app-layout-integration', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('.main-content input[aria-label="your choice"]'); }, }, @@ -121,7 +121,7 @@ const suite: TestSuite = { description: 'split panel integration - main content popover', path: 'property-filter/split-panel-app-layout-integration', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('.main-content button[aria-haspopup=dialog]'); }, }, @@ -129,7 +129,7 @@ const suite: TestSuite = { description: 'virtual scroll navigate through 100 items', path: 'property-filter/virtual-scroll', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const propertyFilter = wrapper.findPropertyFilter(); await page.click(propertyFilter.findNativeInput().toSelector()); await page.keys('Property = '); diff --git a/test/definitions/visual/radio-button.ts b/test/definitions/visual/radio-button.ts index c97bef2e8f..5554d51057 100644 --- a/test/definitions/visual/radio-button.ts +++ b/test/definitions/visual/radio-button.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'Focused and not checked', path: 'radio-button/focus-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, @@ -25,7 +25,7 @@ const suite: TestSuite = { description: 'Focused and checked', path: 'radio-button/focus-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['Space']); diff --git a/test/definitions/visual/radio-group.ts b/test/definitions/visual/radio-group.ts index 20da0133f1..49bfbcdc32 100644 --- a/test/definitions/visual/radio-group.ts +++ b/test/definitions/visual/radio-group.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'Radio button is focused', path: 'radio-group/focus-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, diff --git a/test/definitions/visual/s3-resource-selector.ts b/test/definitions/visual/s3-resource-selector.ts index 0cdaa5bcd9..54c39daada 100644 --- a/test/definitions/visual/s3-resource-selector.ts +++ b/test/definitions/visual/s3-resource-selector.ts @@ -17,7 +17,7 @@ const suite: TestSuite = { path: 's3-resource-selector/permutations', screenshotType: 'viewport', configuration: { height: 1000 }, - setup: async page => { + setup: async ({ page }) => { await page.click('button=Browse S3'); await page.waitForVisible('[role="dialog"]'); }, @@ -27,7 +27,7 @@ const suite: TestSuite = { path: 's3-resource-selector/with-alert', screenshotType: 'viewport', configuration: { height: 1100 }, - setup: async page => { + setup: async ({ page }) => { await page.click('button=Browse S3'); await page.waitForVisible('[role="dialog"]'); }, diff --git a/test/definitions/visual/select.ts b/test/definitions/visual/select.ts index cfd3abae15..84d6b3ac04 100644 --- a/test/definitions/visual/select.ts +++ b/test/definitions/visual/select.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'component - dropdown open - plain list', path: 'select/screenshot', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="select-demo"] button'); }, }, @@ -24,7 +24,7 @@ const suite: TestSuite = { description: 'component - dropdown open - virtual list', path: 'select/screenshot', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#toggle-virtual'); await page.click('[data-testid="select-demo"] button'); }, @@ -33,7 +33,7 @@ const suite: TestSuite = { description: 'component - dropdown open limited width - plain list', path: 'select/screenshot', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="select-demo-with-no-filtering-and-limited-width"] button'); }, }, @@ -41,7 +41,7 @@ const suite: TestSuite = { description: 'keyboard interaction - plain list', path: 'select/screenshot', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="select-demo-no-filtering"] button'); await page.keys(['ArrowDown', 'Space']); }, @@ -51,7 +51,7 @@ const suite: TestSuite = { path: 'select/custom-render-option', screenshotType: 'screenshotArea', queryParams: { virtualScroll: 'false' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findSelect().findTrigger().toSelector()); }, }, @@ -60,7 +60,7 @@ const suite: TestSuite = { path: 'select/custom-render-option', screenshotType: 'screenshotArea', queryParams: { virtualScroll: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findSelect().findTrigger().toSelector()); }, }, @@ -83,7 +83,7 @@ const suite: TestSuite = { description: 'Long virtual list - navigate to last item', path: 'select/virtual-scroll', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findSelect().findTrigger().toSelector()); await page.elementScrollTo(wrapper.findSelect().findDropdown().findOptionsContainer().toSelector(), { top: 99999, diff --git a/test/definitions/visual/split-panel.ts b/test/definitions/visual/split-panel.ts index ae94690b69..eab3cafc3d 100644 --- a/test/definitions/visual/split-panel.ts +++ b/test/definitions/visual/split-panel.ts @@ -11,7 +11,7 @@ const suite: TestSuite = { description: 'position bottom', path: 'app-layout/with-split-panel', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Open panel'); }, }, @@ -24,7 +24,7 @@ const suite: TestSuite = { description: 'position side', path: 'app-layout/with-split-panel', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Open panel'); await page.click('aria/Preferences'); await page.click('aria/Side'); @@ -35,7 +35,7 @@ const suite: TestSuite = { description: 'position side with tools open', path: 'app-layout/with-split-panel', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Open panel'); await page.click('aria/Preferences'); await page.click('aria/Side'); @@ -48,7 +48,7 @@ const suite: TestSuite = { description: 'position side - closed', path: 'app-layout/with-split-panel', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Open panel'); await page.click('[aria-label="Preferences"]'); await page.click('aria/Side'); @@ -60,7 +60,7 @@ const suite: TestSuite = { description: 'preferences open', path: 'app-layout/with-split-panel', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('aria/Open panel'); await page.click('aria/Preferences'); }, @@ -69,7 +69,7 @@ const suite: TestSuite = { description: 'popover in bottom panel', path: 'app-layout/with-split-panel', screenshotType: 'viewport', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click('aria/Open panel'); await page.click(wrapper.findSplitPanel().findOpenPanelBottom().findPopover().findTrigger().toSelector()); await (page as any).scrollIntoView('[data-testid="scroll-me"]'); diff --git a/test/definitions/visual/table-inline-editing.ts b/test/definitions/visual/table-inline-editing.ts index 52dd8e12ec..123df5b2f1 100644 --- a/test/definitions/visual/table-inline-editing.ts +++ b/test/definitions/visual/table-inline-editing.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'active select editing', path: 'table/editable', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[aria-label="Edit EKXAM4L45YPC8 TLS Version"]'); }, }, @@ -24,7 +24,7 @@ const suite: TestSuite = { description: 'active select editing with open dropdown', path: 'table/editable', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click('[aria-label="Edit EKXAM4L45YPC8 TLS Version"]'); await page.click(wrapper.findSelect().findTrigger().toSelector()); }, @@ -33,7 +33,7 @@ const suite: TestSuite = { description: 'hovering over cell, resizableColumns=${resizableColumns}', path: 'table/editable', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[aria-label="Edit EKXAM4L45YPC8 Domain name"]'); }, }, @@ -43,7 +43,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 1600 }, queryParams: { enableKeyboardNavigation: 'true' }, - setup: async page => { + setup: async ({ page }) => { await page.click('[data-testid="focus"]'); await page.keys(['Tab', 'ArrowDown', 'ArrowRight', 'Enter']); }, diff --git a/test/definitions/visual/table-resizable-columns.ts b/test/definitions/visual/table-resizable-columns.ts index 293dc98319..7f373760d8 100644 --- a/test/definitions/visual/table-resizable-columns.ts +++ b/test/definitions/visual/table-resizable-columns.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'focused column resizer', path: 'table/resizable-columns', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.buttonDownOnElement('#reset-state'); await page.keys(['Tab']); }, @@ -25,7 +25,7 @@ const suite: TestSuite = { description: 'active column resizer on sticky header', path: 'table/resizable-columns', screenshotType: 'viewport', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const table = wrapper.findTable(); await page.click('#sticky-header-toggle input'); await page.windowScrollTo({ top: 600 }); @@ -36,7 +36,7 @@ const suite: TestSuite = { description: 'active column resizer', path: 'table/resizable-columns', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const table = wrapper.findTable(); await page.buttonDownOnElement(table.findColumnResizer(3).toSelector()); }, @@ -46,7 +46,7 @@ const suite: TestSuite = { path: 'table/resizable-columns', screenshotType: 'screenshotArea', queryParams: { enableKeyboardNavigation: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const table = wrapper.findTable(); await page.click(table.findHeaderSlot().toSelector()); await page.keys(['Tab', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'Enter']); @@ -57,7 +57,7 @@ const suite: TestSuite = { path: 'table/resizable-columns', screenshotType: 'screenshotArea', queryParams: { enableKeyboardNavigation: 'true' }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const table = wrapper.findTable(); await page.click(table.findHeaderSlot().toSelector()); await page.keys(['Tab', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'Enter']); diff --git a/test/definitions/visual/table-sticky-header.ts b/test/definitions/visual/table-sticky-header.ts index ed508f17a7..1f30619287 100644 --- a/test/definitions/visual/table-sticky-header.ts +++ b/test/definitions/visual/table-sticky-header.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'mid-scroll sticky state - container variant', path: 'table/sticky-header', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#container'); await page.windowScrollTo({ top: 400 }); }, @@ -25,7 +25,7 @@ const suite: TestSuite = { description: 'mid-scroll sticky state - embedded variant', path: 'table/sticky-header', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.click('#embedded'); await page.windowScrollTo({ top: 400 }); }, @@ -34,7 +34,7 @@ const suite: TestSuite = { description: 'bottom sticky state', path: 'table/sticky-header', screenshotType: 'viewport', - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 925 }); }, }, diff --git a/test/definitions/visual/table.ts b/test/definitions/visual/table.ts index 9ed8cceced..15ed94bab1 100644 --- a/test/definitions/visual/table.ts +++ b/test/definitions/visual/table.ts @@ -33,7 +33,7 @@ const suite: TestSuite = { path: 'table/empty-state', screenshotType: 'screenshotArea', configuration: { width: 600 }, - setup: async page => { + setup: async ({ page }) => { await page.click('#scroll-content'); }, }, @@ -51,7 +51,7 @@ const suite: TestSuite = { description: 'inside stacked container', path: 'table/sticky-header-in-stacked-container', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.windowScrollTo({ top: 10 }); await page.click('button=Actions'); }, @@ -60,7 +60,7 @@ const suite: TestSuite = { description: 'sticky header with open action button dropdown', path: 'table/sticky-header-with-actions', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[data-test-id="actions-button"]'); }, }, @@ -73,7 +73,7 @@ const suite: TestSuite = { description: 'focus on last cell inline action', path: 'table/inline-actions', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const tableWrapper = wrapper.findTable('[data-testid="table-with-dropdown-actions"]'); await page.click(tableWrapper.findRows().get(1).findButtonDropdown().findNativeButton().toSelector()); await page.keys(['Escape']); @@ -83,7 +83,7 @@ const suite: TestSuite = { description: 'focus on first cell link', path: 'table/inline-actions', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const tableWrapper = wrapper.findTable('[data-testid="table-with-dropdown-actions"]'); await page.click(tableWrapper.findRowSelectionArea(1).toSelector()); await page.keys(['Tab']); diff --git a/test/definitions/visual/tabs.ts b/test/definitions/visual/tabs.ts index 90b9c61f0f..5f7dceb628 100644 --- a/test/definitions/visual/tabs.ts +++ b/test/definitions/visual/tabs.ts @@ -21,7 +21,7 @@ const suite: TestSuite = { description: 'focuses next tab header after clicking on tab header without an href', path: 'tabs/integration-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#first-tabs li:nth-child(3) button'); await page.keys('ArrowRight'); }, @@ -35,7 +35,7 @@ const suite: TestSuite = { description: 'focus active tab - default variant', path: 'tabs/integration-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#click-this'); await page.focusNextElement(); }, @@ -44,7 +44,7 @@ const suite: TestSuite = { description: 'focus active tab - container variant', path: 'tabs/integration-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#click-this-2'); await page.focusNextElement(); }, @@ -53,7 +53,7 @@ const suite: TestSuite = { description: 'focus content - default variant', path: 'tabs/integration-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#click-this'); await page.focusNextElement(); await page.focusNextElement(); @@ -63,7 +63,7 @@ const suite: TestSuite = { description: 'focus content - container variant', path: 'tabs/integration-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#click-this-2'); await page.focusNextElement(); await page.focusNextElement(); diff --git a/test/definitions/visual/textarea.ts b/test/definitions/visual/textarea.ts index 2047d7559d..c497230b61 100644 --- a/test/definitions/visual/textarea.ts +++ b/test/definitions/visual/textarea.ts @@ -18,7 +18,7 @@ function pseudoSelectorTests(withStyling: boolean): TestSuite { description: `${state.label} - focus`, path: 'textarea/pseudo-selectors', screenshotType: 'screenshotArea' as const, - setup: async (page: any, wrapper: any) => { + setup: async ({ page, wrapper }) => { const textareaSelector = wrapper .findTextarea('[data-testid="test-textarea"]') .findNativeTextarea() @@ -36,7 +36,7 @@ function pseudoSelectorTests(withStyling: boolean): TestSuite { description: `${state.label} - focus + hover`, path: 'textarea/pseudo-selectors', screenshotType: 'screenshotArea' as const, - setup: async (page: any, wrapper: any) => { + setup: async ({ page, wrapper }) => { const textareaSelector = wrapper .findTextarea('[data-testid="test-textarea"]') .findNativeTextarea() diff --git a/test/definitions/visual/toggle-button.ts b/test/definitions/visual/toggle-button.ts index e91340dfdb..c7b55e8079 100644 --- a/test/definitions/visual/toggle-button.ts +++ b/test/definitions/visual/toggle-button.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'hovering over normal variant', path: 'toggle-button/permutations', screenshotType: 'permutations', - setup: async page => { + setup: async ({ page }) => { await page.hoverElement('[aria-label="Favorite"][aria-pressed="false"]'); }, }, diff --git a/test/definitions/visual/toggle.ts b/test/definitions/visual/toggle.ts index b99818470d..f2e3bd0cd1 100644 --- a/test/definitions/visual/toggle.ts +++ b/test/definitions/visual/toggle.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'Toggle is focused', path: 'toggle/focus-test', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); }, diff --git a/test/definitions/visual/top-navigation.ts b/test/definitions/visual/top-navigation.ts index d58b5afa4a..d21c71c1fb 100644 --- a/test/definitions/visual/top-navigation.ts +++ b/test/definitions/visual/top-navigation.ts @@ -18,7 +18,7 @@ const suite: TestSuite = { path: 'top-navigation/scenario-full-page', screenshotType: 'viewport', configuration: { width: 1300, height: 800 }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findTopNavigation().findUtility(4).toSelector()); }, }, @@ -32,7 +32,7 @@ const suite: TestSuite = { path: 'top-navigation/scenario-full-page', screenshotType: 'viewport', configuration: { width: 500, height: 800 }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findTopNavigation().findOverflowMenuButton().toSelector()); await page.keys(['Tab']); }, @@ -42,7 +42,7 @@ const suite: TestSuite = { path: 'top-navigation/scenario-full-page', screenshotType: 'viewport', configuration: { width: 500, height: 800 }, - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { await page.click(wrapper.findTopNavigation().findOverflowMenuButton().toSelector()); await page.click(wrapper.findTopNavigation().findOverflowMenu().findUtility(3).toSelector()); await page.keys(['Tab']); diff --git a/test/definitions/visual/tree-view.ts b/test/definitions/visual/tree-view.ts index baab73ad51..4809c79fa2 100644 --- a/test/definitions/visual/tree-view.ts +++ b/test/definitions/visual/tree-view.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'with different toggle icon', path: 'tree-view/basic', screenshotType: 'screenshotArea', - setup: async (page, wrapper) => { + setup: async ({ page, wrapper }) => { const select = wrapper.findSelect(); await page.click(select.findTrigger().toSelector()); await page.click(select.findDropdown().findOptionByValue('custom').toSelector()); diff --git a/test/definitions/visual/wizard.ts b/test/definitions/visual/wizard.ts index ac8a3fe795..212cdc0a5b 100644 --- a/test/definitions/visual/wizard.ts +++ b/test/definitions/visual/wizard.ts @@ -16,7 +16,7 @@ const suite: TestSuite = { description: 'second step', path: 'wizard/wizard-screenshot', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('#next'); }, }, @@ -24,7 +24,7 @@ const suite: TestSuite = { description: 'steps menu expanded in mobile view', path: 'wizard/wizard-screenshot', screenshotType: 'screenshotArea', - setup: async page => { + setup: async ({ page }) => { await page.click('[role="button"][aria-expanded]'); }, }, From f9ddc27f961a931e418e200fdb8efae856f3071a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 00:32:58 +0200 Subject: [PATCH 098/135] Fixes --- .../__integ__/page-objects/select-page.ts | 2 +- test/definitions/types.ts | 10 +--------- test/definitions/utils.ts | 2 +- test/definitions/visual/copy-to-clipboard.ts | 4 ++-- test/definitions/visual/drawer.ts | 4 ++-- test/definitions/visual/line-chart.ts | 17 ++++++++--------- test/definitions/visual/split-panel.ts | 4 ++-- 7 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/select/__integ__/page-objects/select-page.ts b/src/select/__integ__/page-objects/select-page.ts index 38682eb564..26587fe536 100644 --- a/src/select/__integ__/page-objects/select-page.ts +++ b/src/select/__integ__/page-objects/select-page.ts @@ -36,7 +36,7 @@ export default class SelectPageObject< async selectOptionUsingDrag(optionNumber: number) { const triggerSelector = this.wrapper.findTrigger().toSelector(); // actions API does not work when element is not in the viewport - await (await this.browser.$(triggerSelector)).scrollIntoView(); + await this.browser.$(triggerSelector).scrollIntoView(); await this.buttonDownOnElement(triggerSelector); const { left, width, top, height } = await this.getBoundingBox( diff --git a/test/definitions/types.ts b/test/definitions/types.ts index 2d12d9c1fa..4cae18c113 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -23,15 +23,7 @@ export interface TestDefinition { screenshotType: ScreenshotType; queryParams?: Record; configuration?: ScreenshotTestConfiguration; - setup?: ({ - page, - wrapper, - browser, - }: { - page: ScreenshotPageObject; - wrapper: Wrapper; - browser: Browser; - }) => Promise; + setup?: ({ page, wrapper, browser }: { page: ScreenshotPageObject; wrapper: Wrapper; browser: Browser }) => void; } export interface TestSuite { diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 71173a4690..fdc48caa72 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -98,7 +98,7 @@ async function preparePage( await browser.url(`${url}?${params.toString()}`); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { - await testDef.setup({ page, wrapper, browser }); + return testDef.setup({ page, wrapper, browser }); } } diff --git a/test/definitions/visual/copy-to-clipboard.ts b/test/definitions/visual/copy-to-clipboard.ts index 2655bd0f26..250ecbb6f4 100644 --- a/test/definitions/visual/copy-to-clipboard.ts +++ b/test/definitions/visual/copy-to-clipboard.ts @@ -17,9 +17,9 @@ const suite: TestSuite = { path: 'copy-to-clipboard/scenario-split-panel', screenshotType: 'screenshotArea', configuration: { width: 1280, height: 900 }, - setup: async ({ page }) => { + setup: async ({ page, browser }) => { await page.click('[aria-label="Copy dummy text"]'); - await (page as any).scrollIntoView('[data-testid="scroll-me"]'); + browser.$('[data-testid="scroll-me"]').scrollIntoView(); }, }, ], diff --git a/test/definitions/visual/drawer.ts b/test/definitions/visual/drawer.ts index 8ea09d8343..e81a979769 100644 --- a/test/definitions/visual/drawer.ts +++ b/test/definitions/visual/drawer.ts @@ -49,8 +49,8 @@ const suite: TestSuite = { screenshotType: 'viewport', configuration: { width: 1280, height: 268 }, queryParams: { longContent: 'true', hasFooter: 'true' }, - setup: async ({ page, wrapper }) => { - await (page as any).scrollIntoView(wrapper.findDrawer().findFooter().toSelector()); + setup: ({ browser, wrapper }) => { + browser.$(wrapper.findDrawer().findFooter().toSelector()).scrollIntoView(); }, }, { diff --git a/test/definitions/visual/line-chart.ts b/test/definitions/visual/line-chart.ts index 1031af89db..ce4c9ede00 100644 --- a/test/definitions/visual/line-chart.ts +++ b/test/definitions/visual/line-chart.ts @@ -1,6 +1,5 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 - import { TestSuite } from '../types'; const TEST_CHART_FILTER_TRIGGER = '#chart button'; @@ -87,11 +86,11 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800, height: 1000 }, queryParams: { expandableSubItems: 'true' }, - setup: async ({ page }) => { + setup: async ({ page, wrapper }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['ArrowRight']); - await page.waitForVisible('[class*="detail-popover"]'); + await page.waitForVisible(wrapper.findLineChart().findDetailPopover().toSelector()); }, }, { @@ -100,11 +99,11 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800, height: 1000 }, queryParams: { expandableSubItems: 'true' }, - setup: async ({ page }) => { + setup: async ({ page, wrapper }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['ArrowRight']); - await page.waitForVisible('[class*="detail-popover"]'); + await page.waitForVisible(wrapper.findLineChart().findDetailPopover().toSelector()); await page.keys(['Tab']); await page.keys(['Enter']); }, @@ -115,12 +114,12 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800, height: 1000 }, queryParams: { expandableSubItems: 'true' }, - setup: async ({ page }) => { + setup: async ({ page, wrapper }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['ArrowRight']); await page.keys(['ArrowUp']); - await page.waitForVisible('[class*="detail-popover"]'); + await page.waitForVisible(wrapper.findLineChart().findDetailPopover().toSelector()); }, }, { @@ -129,12 +128,12 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 800, height: 1000 }, queryParams: { expandableSubItems: 'true' }, - setup: async ({ page }) => { + setup: async ({ page, wrapper }) => { await page.click('#focus-target'); await page.focusNextElement(); await page.keys(['ArrowRight']); await page.keys(['ArrowUp']); - await page.waitForVisible('[class*="detail-popover"]'); + await page.waitForVisible(wrapper.findLineChart().findDetailPopover().toSelector()); await page.keys(['Tab']); await page.keys(['Enter']); }, diff --git a/test/definitions/visual/split-panel.ts b/test/definitions/visual/split-panel.ts index eab3cafc3d..cfbca3b7a5 100644 --- a/test/definitions/visual/split-panel.ts +++ b/test/definitions/visual/split-panel.ts @@ -69,10 +69,10 @@ const suite: TestSuite = { description: 'popover in bottom panel', path: 'app-layout/with-split-panel', screenshotType: 'viewport', - setup: async ({ page, wrapper }) => { + setup: async ({ page, wrapper, browser }) => { await page.click('aria/Open panel'); await page.click(wrapper.findSplitPanel().findOpenPanelBottom().findPopover().findTrigger().toSelector()); - await (page as any).scrollIntoView('[data-testid="scroll-me"]'); + browser.$('[data-testid="scroll-me"]').scrollIntoView(); }, }, { From 50f107a1837f6629469d4244b8cc51e1d8071de3 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 00:57:16 +0200 Subject: [PATCH 099/135] Fixes --- test/definitions/visual/date-range-picker.ts | 8 ++++---- test/definitions/visual/help-panel.ts | 9 --------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/test/definitions/visual/date-range-picker.ts b/test/definitions/visual/date-range-picker.ts index 3a851f1993..eb5ce32e32 100644 --- a/test/definitions/visual/date-range-picker.ts +++ b/test/definitions/visual/date-range-picker.ts @@ -90,7 +90,7 @@ const suite: TestSuite = { description: 'selects text when double-clicking calendar header', path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { + setup: async ({ page, wrapper, browser }) => { await page.click('#focusable-before'); await page.focusNextElement(); await page.keys(['Enter']); @@ -100,19 +100,19 @@ const suite: TestSuite = { .findHeader() .find('h2 span') .toSelector(); - await (page as any).doubleClick(firstCalendarHeaderSelector); + browser.$(firstCalendarHeaderSelector).doubleClick(); }, }, { description: 'does not select text when double-clicking next button', path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { + setup: async ({ page, wrapper, browser }) => { await page.click('#focusable-before'); await page.focusNextElement(); await page.keys(['Enter']); const nextButtonSelector = wrapper.findDateRangePicker().findDropdown().findNextMonthButton().toSelector(); - await (page as any).doubleClick(nextButtonSelector); + browser.$(nextButtonSelector).doubleClick(); }, }, ], diff --git a/test/definitions/visual/help-panel.ts b/test/definitions/visual/help-panel.ts index 8b994fe71d..fabc497026 100644 --- a/test/definitions/visual/help-panel.ts +++ b/test/definitions/visual/help-panel.ts @@ -11,25 +11,16 @@ const suite: TestSuite = { description: 'permutations', path: 'help-panel/permutations', screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await (page as any).focusInputs(); - }, }, { description: 'with AppLayout', path: 'help-panel/with-app-layout', screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await (page as any).focusInputs(); - }, }, { description: 'loading state - with AppLayout', path: 'help-panel/loading-with-app-layout', screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await (page as any).focusInputs(); - }, }, ], }; From 7946d5101c7d90f57cc5a7d0a32c0a1092792853 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 01:08:24 +0200 Subject: [PATCH 100/135] Fixes --- test/definitions/visual/wizard.ts | 33 +++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/test/definitions/visual/wizard.ts b/test/definitions/visual/wizard.ts index 212cdc0a5b..de7931f957 100644 --- a/test/definitions/visual/wizard.ts +++ b/test/definitions/visual/wizard.ts @@ -7,19 +7,26 @@ const suite: TestSuite = { description: 'Wizard', componentName: 'wizard', tests: [ - { - description: 'first step', - path: 'wizard/wizard-screenshot', - screenshotType: 'screenshotArea', - }, - { - description: 'second step', - path: 'wizard/wizard-screenshot', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#next'); - }, - }, + ...[600, 1280].map(width => ({ + description: `width ${width}px`, + tests: [ + { + description: 'first step', + path: 'wizard/wizard-screenshot', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: 'second step', + path: 'wizard/wizard-screenshot', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + setup: async ({ page }: { page: any }) => { + await page.click('#next'); + }, + }, + ], + })), { description: 'steps menu expanded in mobile view', path: 'wizard/wizard-screenshot', From 6ba70dde830b6dd5643cbf94eadcdad84ce4cab0 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 01:15:06 +0200 Subject: [PATCH 101/135] Increase timeout --- test/definitions/visual/code-editor.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts index 615544b402..8c61bdcfbd 100644 --- a/test/definitions/visual/code-editor.ts +++ b/test/definitions/visual/code-editor.ts @@ -4,6 +4,7 @@ import { TestSuite } from '../types'; const ACE_SELECTOR = '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright'; +const timeout = 10_000; const suite: TestSuite = { description: 'Code editor', @@ -14,7 +15,7 @@ const suite: TestSuite = { path: 'code-editor/simple', screenshotType: 'screenshotArea', setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR); + await page.waitForVisible(ACE_SELECTOR, true, timeout); }, }, { @@ -32,7 +33,7 @@ const suite: TestSuite = { path: 'code-editor/themes', screenshotType: 'screenshotArea', setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR); + await page.waitForVisible(ACE_SELECTOR, true, timeout); }, }, { @@ -41,7 +42,7 @@ const suite: TestSuite = { screenshotType: 'permutations', setup: async ({ page }) => { await page.waitForVisible(ACE_SELECTOR + ' .ace_error'); - await page.waitForVisible('.ace_gutter-cell.ace_gutter-active-line.ace_error'); + await page.waitForVisible('.ace_gutter-cell.ace_gutter-active-line.ace_error', true, timeout); }, }, { @@ -49,7 +50,7 @@ const suite: TestSuite = { path: 'code-editor/simple', screenshotType: 'screenshotArea', setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR); + await page.waitForVisible(ACE_SELECTOR, true, timeout); await page.click('#mode-toggle'); }, }, @@ -59,7 +60,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 360 }, setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR); + await page.waitForVisible(ACE_SELECTOR, true, timeout); }, }, ], From 4d072409d240904d90ffe3e6b7263a4d9ab09145 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 01:19:43 +0200 Subject: [PATCH 102/135] Fixes --- test/definitions/visual/autosuggest.ts | 2 +- test/definitions/visual/multiselect.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/test/definitions/visual/autosuggest.ts b/test/definitions/visual/autosuggest.ts index 4f528d0801..b93cdf3b8d 100644 --- a/test/definitions/visual/autosuggest.ts +++ b/test/definitions/visual/autosuggest.ts @@ -53,7 +53,7 @@ const suite: TestSuite = { ({ description: `with custom renderOption (virtualScroll=${virtualScroll})`, path: 'autosuggest/custom-render-option', - screenshotType: 'screenshotArea' as const, + screenshotType: 'screenshotArea', queryParams: { virtualScroll: String(virtualScroll) }, setup: async ({ page, wrapper }) => { await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); diff --git a/test/definitions/visual/multiselect.ts b/test/definitions/visual/multiselect.ts index 3de5c1691b..d08b3b06b8 100644 --- a/test/definitions/visual/multiselect.ts +++ b/test/definitions/visual/multiselect.ts @@ -106,7 +106,9 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', queryParams: { type: 'multiselect' }, setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + const triggerSelector = wrapper.findMultiselect().findTrigger().toSelector(); + page.waitForVisible(triggerSelector); + await page.click(triggerSelector); await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { top: 99999, }); @@ -119,7 +121,9 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', queryParams: { type: 'multiselect-select-all' }, setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + const triggerSelector = wrapper.findMultiselect().findTrigger().toSelector(); + page.waitForVisible(triggerSelector); + await page.click(triggerSelector); await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { top: 99999, }); From 9fc1ec9f0ea6a34d39637a4e1c91c5316d875c09 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 01:34:44 +0200 Subject: [PATCH 103/135] Add missing width --- test/definitions/visual/wizard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/definitions/visual/wizard.ts b/test/definitions/visual/wizard.ts index de7931f957..51864a6396 100644 --- a/test/definitions/visual/wizard.ts +++ b/test/definitions/visual/wizard.ts @@ -31,6 +31,7 @@ const suite: TestSuite = { description: 'steps menu expanded in mobile view', path: 'wizard/wizard-screenshot', screenshotType: 'screenshotArea', + configuration: { width: 600 }, setup: async ({ page }) => { await page.click('[role="button"][aria-expanded]'); }, From 69c7efa3430ffe4cb70c81c90a0cd5b51e209f31 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 01:47:11 +0200 Subject: [PATCH 104/135] Fix query parameters --- test/definitions/utils.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index fdc48caa72..149d667980 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -18,7 +18,7 @@ const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; const wrapper = createWrapper(); function buildUrl(host: string, path: string, queryParams?: Record): string { - const params = new URLSearchParams(queryParams); + const params = new URLSearchParams({ motionDisabled: 'true', ...queryParams }); const qs = params.toString(); return `${host}/#/${path}${qs ? `?${qs}` : ''}`; } @@ -94,8 +94,7 @@ async function preparePage( windowSize?.width ?? defaultWindowSize.width, windowSize?.height ?? defaultWindowSize.height ); - const params = new URLSearchParams({ motionDisabled: 'true' }); - await browser.url(`${url}?${params.toString()}`); + await browser.url(url); await page.waitForVisible(screenshotAreaSelector); if (testDef.setup) { return testDef.setup({ page, wrapper, browser }); From 470069b713154bf5d27fbfa6c388246675cdac45 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 01:49:09 +0200 Subject: [PATCH 105/135] Increase timeout --- test/definitions/visual/code-editor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts index 8c61bdcfbd..0494b68bd8 100644 --- a/test/definitions/visual/code-editor.ts +++ b/test/definitions/visual/code-editor.ts @@ -4,7 +4,7 @@ import { TestSuite } from '../types'; const ACE_SELECTOR = '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright'; -const timeout = 10_000; +const timeout = 20_000; const suite: TestSuite = { description: 'Code editor', From 0db3567a4ba68d0a11383dd685155a93e0f612d8 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 11:42:59 +0200 Subject: [PATCH 106/135] instrument and log --- test/definitions/instrumented-page-object.ts | 307 +++++++++++++++++++ test/definitions/utils.ts | 47 ++- 2 files changed, 350 insertions(+), 4 deletions(-) create mode 100644 test/definitions/instrumented-page-object.ts diff --git a/test/definitions/instrumented-page-object.ts b/test/definitions/instrumented-page-object.ts new file mode 100644 index 0000000000..f6eaa083ad --- /dev/null +++ b/test/definitions/instrumented-page-object.ts @@ -0,0 +1,307 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Instrumented ScreenshotPageObject that logs performance timings. + * Re-implements key methods to show which strategy is used and where time is spent. + */ +import pixelmatch from 'pixelmatch'; +import { PNG } from 'pngjs'; + +import { parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; +import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; + +type PermutationScreenshot = ScreenshotWithOffset & { id: string }; + +function ms(start: number): string { + return `${(performance.now() - start).toFixed(0)}ms`; +} + +export class InstrumentedPageObject extends ScreenshotPageObject { + private label = ''; + + setLabel(value: string) { + this.label = value; + } + + private log(msg: string) { + console.log(` ⏱ [${this.label}] ${msg}`); + } + + async captureViewport(): Promise { + const t0 = performance.now(); + + let t = performance.now(); + const { height, width } = await this.getViewportSize(); + this.log(` getViewportSize: ${ms(t)}`); + + t = performance.now(); + const rawBase64 = await this.browser.takeScreenshot(); + this.log(` takeScreenshot: ${ms(t)} (${(rawBase64.length / 1024).toFixed(0)}KB base64)`); + + t = performance.now(); + const image = await parsePng(rawBase64); + this.log(` parsePng: ${ms(t)}`); + + this.log(`captureViewport total: ${ms(t0)}`); + return { image, offset: { top: 0, left: 0 }, height, width } as ScreenshotWithOffset; + } + + async captureBySelector(selector: string, options: { viewportOnly?: boolean } = {}): Promise { + const t0 = performance.now(); + + let t = performance.now(); + await this.waitForVisible(selector); + this.log(` waitForVisible: ${ms(t)}`); + + t = performance.now(); + const { pixelRatio, top, left } = await this.getViewportSize(); + this.log(` getViewportSize: ${ms(t)}`); + + t = performance.now(); + const box = await this.getBoundingBox(selector); + this.log(` getBoundingBox: ${ms(t)}`); + + let rawBase64: string; + if (options.viewportOnly) { + t = performance.now(); + rawBase64 = await this.browser.takeScreenshot(); + this.log(` takeScreenshot (viewportOnly): ${ms(t)} (${(rawBase64.length / 1024).toFixed(0)}KB)`); + } else { + t = performance.now(); + rawBase64 = await this.fullPageScreenshotInstrumented(); + this.log(` fullPageScreenshot total: ${ms(t)} (${(rawBase64.length / 1024).toFixed(0)}KB)`); + } + + t = performance.now(); + const image = await parsePng(rawBase64); + this.log(` parsePng: ${ms(t)}`); + + const offset = { top: box.top, left: box.left }; + if (!options.viewportOnly) { + offset.top += top; + offset.left += left; + } + + this.log(`captureBySelector total: ${ms(t0)}`); + return { image, offset, pixelRatio, height: box.height, width: box.width } as ScreenshotWithOffset; + } + + async capturePermutations(): Promise { + const t0 = performance.now(); + + let t = performance.now(); + await this.windowScrollTo({ top: 0, left: 0 }); + this.log(` windowScrollTo(0): ${ms(t)}`); + + // fitWindowHeightToContent + t = performance.now(); + const originalWindowSize = await this.browser.getWindowSize(); + const { viewportHeight, pageHeight } = await (this.browser as any).execute(function () { + return { + viewportHeight: window.innerHeight, + pageHeight: document.documentElement.scrollHeight, + }; + }); + const windowUIHeight = originalWindowSize.height - viewportHeight; + const targetHeight = pageHeight + windowUIHeight; + await this.browser.setWindowSize(originalWindowSize.width, targetHeight); + this.log(` fitWindowHeightToContent: ${ms(t)} (pageHeight=${pageHeight}, targetWindowHeight=${targetHeight})`); + + // getPermutationSizes + t = performance.now(); + const permutations: Array<{ id: string; width: number; height: number; offset: { top: number; left: number } }> = + await (this.browser as any).execute(function () { + const elements = document.querySelectorAll('[data-testid="permutation"]'); + return Array.from(elements).map(function (el: Element) { + const rect = el.getBoundingClientRect(); + return { + id: el.getAttribute('data-permutation-id') || rect.top + '-' + rect.left, + width: rect.width, + height: rect.height, + offset: { top: rect.top, left: rect.left }, + }; + }); + }); + this.log(` getPermutationSizes: ${ms(t)} (${permutations.length} found)`); + + if (permutations.length === 0) { + throw new Error('No permutations found on current page.'); + } + + // fullPageScreenshot + t = performance.now(); + const rawBase64 = await this.fullPageScreenshotInstrumented(); + this.log(` fullPageScreenshot: ${ms(t)} (${(rawBase64.length / 1024).toFixed(0)}KB)`); + + // parsePng + t = performance.now(); + const image = await parsePng(rawBase64); + this.log(` parsePng: ${ms(t)}`); + + // restore window size + t = performance.now(); + await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); + this.log(` restoreWindowSize: ${ms(t)}`); + + this.log(`capturePermutations total: ${ms(t0)}`); + return permutations.map(permutation => ({ ...permutation, image }) as PermutationScreenshot); + } + + /** + * Instrumented fullPageScreenshot that logs which strategy is selected. + */ + private async fullPageScreenshotInstrumented(): Promise { + const scrollPosition = await this.getWindowScroll(); + await this.waitForJsTimers(); + + const browserName = (this.browser.capabilities as any)?.browserName || ''; + if (browserName.toLowerCase().includes('firefox')) { + this.log(` strategy: scroll-and-merge (Firefox detected)`); + const result = await super.fullPageScreenshot(); + return result; + } + + // Try Puppeteer + let t = performance.now(); + let puppeteer: any = null; + try { + puppeteer = await (this.browser as any).getPuppeteer(); + } catch (e: any) { + this.log(` getPuppeteer failed: ${e.message?.substring(0, 80)}`); + } + const getPuppeteerTime = performance.now() - t; + + if (puppeteer && !this.forceScrollAndMerge) { + this.log(` strategy: PUPPETEER (getPuppeteer: ${getPuppeteerTime.toFixed(0)}ms)`); + t = performance.now(); + const image = await (this.browser as any).call(async () => { + const [current] = await puppeteer.pages(); + return current.screenshot({ fullPage: true, encoding: 'base64' }); + }); + this.log(` puppeteer.screenshot: ${ms(t)}`); + await this.windowScrollTo(scrollPosition); + return image as string; + } + + this.log( + ` strategy: SCROLL-AND-MERGE (puppeteer=${!!puppeteer}, forceScrollAndMerge=${this.forceScrollAndMerge}, getPuppeteer: ${getPuppeteerTime.toFixed(0)}ms)` + ); + // Delegate to parent which implements the scroll-and-merge properly + const result = await super.fullPageScreenshot(); + return result; + } +} + +// ─── Instrumented cropAndCompare ────────────────────────────────────────────── + +interface CropAndCompareResult { + firstImage: Buffer; + secondImage: Buffer; + diffImage: Buffer | null; + isEqual: boolean; + diffPixels: number; +} + +function cropImage(inImage: PNG, rect: { top: number; left: number; width: number; height: number }, pixelRatio = 1) { + const imageWidth = Math.ceil(rect.width * pixelRatio || inImage.width); + const imageHeight = Math.ceil(rect.height * pixelRatio || inImage.height); + const outImage = new PNG({ width: imageWidth, height: imageHeight }); + const safeLeft = Math.max(Math.round(rect.left), 0) * pixelRatio; + const safeTop = Math.max(Math.round(rect.top), 0) * pixelRatio; + const safeWidth = Math.min(imageWidth, inImage.width - safeLeft); + const safeHeight = Math.min(imageHeight, inImage.height - safeTop); + inImage.bitblt(outImage, safeLeft, safeTop, safeWidth, safeHeight, 0, 0); + return outImage; +} + +function packPng(png: PNG): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + const stream = png.pack(); + stream.on('data', (chunk: Buffer) => chunks.push(chunk)); + stream.on('end', () => resolve(Buffer.concat(chunks))); + stream.on('error', reject); + }); +} + +/** + * Instrumented cropAndCompare with per-step timing logs. + */ +export async function instrumentedCropAndCompare( + firstScreenshot: ScreenshotWithOffset, + secondScreenshot: ScreenshotWithOffset, + label = '' +): Promise { + const t0 = performance.now(); + const prefix = label ? `[${label}] ` : ''; + + // Fast path: raw base64 equality + const rawFirst = (firstScreenshot as any).rawBase64; + const rawSecond = (secondScreenshot as any).rawBase64; + if (rawFirst && rawSecond && rawFirst === rawSecond) { + const t = performance.now(); + const imageBuffer = Buffer.from(rawFirst, 'base64'); + console.log(` ⏱ ${prefix}cropAndCompare: ${ms(t0)} (FAST PATH: rawBase64 identical, decode: ${ms(t)})`); + return { firstImage: imageBuffer, secondImage: imageBuffer, diffImage: null, isEqual: true, diffPixels: 0 }; + } + + // Crop + const pixelRatio = firstScreenshot.pixelRatio || 1; + const size = { + height: Math.round(Math.max(firstScreenshot.height, secondScreenshot.height)), + width: Math.round(Math.max(firstScreenshot.width, secondScreenshot.width)), + }; + const scaledSize = { + width: Math.ceil(size.width * pixelRatio), + height: Math.ceil(size.height * pixelRatio), + }; + + let t = performance.now(); + const firstImage = cropImage( + firstScreenshot.image, + { top: firstScreenshot.offset.top, left: firstScreenshot.offset.left, width: size.width, height: size.height }, + pixelRatio + ); + const secondImage = cropImage( + secondScreenshot.image, + { top: secondScreenshot.offset.top, left: secondScreenshot.offset.left, width: size.width, height: size.height }, + pixelRatio + ); + console.log(` ⏱ ${prefix} crop (2 images): ${ms(t)} (${scaledSize.width}x${scaledSize.height}px)`); + + // Pixel comparison + t = performance.now(); + let diffPixels: number; + let diffImage: PNG | null = null; + if (scaledSize.width === 0 || scaledSize.height === 0) { + diffPixels = -1; + } else if (firstImage.data.equals(secondImage.data)) { + diffPixels = 0; + console.log(` ⏱ ${prefix} compareImages: ${ms(t)} (FAST: pixel buffers identical)`); + } else { + diffImage = new PNG({ width: scaledSize.width, height: scaledSize.height }); + diffPixels = pixelmatch(firstImage.data, secondImage.data, diffImage.data, scaledSize.width, scaledSize.height, { + threshold: 0.01, + }); + console.log(` ⏱ ${prefix} pixelmatch: ${ms(t)} (diffPixels=${diffPixels})`); + } + + // Pack PNGs + t = performance.now(); + const [firstPacked, secondPacked, diffPacked] = await Promise.all([ + packPng(firstImage), + packPng(secondImage), + diffImage ? packPng(diffImage) : Promise.resolve(null), + ]); + console.log(` ⏱ ${prefix} packPng (${diffImage ? 3 : 2} images): ${ms(t)}`); + + console.log(` ⏱ ${prefix}cropAndCompare total: ${ms(t0)} (diffPixels=${diffPixels})`); + return { + firstImage: firstPacked, + secondImage: secondPacked, + diffImage: diffPacked, + isEqual: diffPixels >= 0 && diffPixels <= 1, + diffPixels, + }; +} diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 149d667980..2050cf4b54 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { attachment } from 'allure-js-commons'; -import { cropAndCompare } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; import createWrapper from '../../lib/components/test-utils/selectors'; +import { instrumentedCropAndCompare, InstrumentedPageObject } from './instrumented-page-object'; import { TestDefinition, TestSuite } from './types'; const screenshotAreaSelector = '.screenshot-area'; @@ -113,48 +113,87 @@ function capture(page: ScreenshotPageObject, testDef: TestDefinition): Promise WebdriverIO.Browser) { test(testDef.description, async () => { + const testStart = performance.now(); const browser = getBrowser(); - const page = new ScreenshotPageObject(browser); + const page = new InstrumentedPageObject(browser); + page.setLabel(testDef.description); const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); + let t = performance.now(); await preparePage(browser, page, newUrl, testDef, testDef.configuration); + console.log(` ⏱ [${testDef.description}] preparePage(new): ${(performance.now() - t).toFixed(0)}ms`); + + t = performance.now(); const newScreenshot = await capture(page, testDef); + console.log(` ⏱ [${testDef.description}] capture(new): ${(performance.now() - t).toFixed(0)}ms`); + t = performance.now(); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); + console.log(` ⏱ [${testDef.description}] preparePage(old): ${(performance.now() - t).toFixed(0)}ms`); + + t = performance.now(); const oldScreenshot = await capture(page, testDef); + console.log(` ⏱ [${testDef.description}] capture(old): ${(performance.now() - t).toFixed(0)}ms`); - const result = await cropAndCompare(newScreenshot, oldScreenshot); + t = performance.now(); + const result = await instrumentedCropAndCompare(newScreenshot, oldScreenshot); + console.log( + ` ⏱ [${testDef.description}] cropAndCompare: ${(performance.now() - t).toFixed(0)}ms (diffPixels=${result.diffPixels})` + ); if (testDef.screenshotType === 'permutations') { if (result.diffPixels === 0) { + console.log( + ` ⏱ [${testDef.description}] TOTAL: ${(performance.now() - testStart).toFixed(0)}ms (pass, no permutation re-capture needed)` + ); return; } + + t = performance.now(); await preparePage(browser, page, newUrl, testDef, testDef.configuration); const newPermutations = await page.capturePermutations(); + console.log(` ⏱ [${testDef.description}] capturePermutations(new): ${(performance.now() - t).toFixed(0)}ms`); + t = performance.now(); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); const oldPermutations = await page.capturePermutations(); + console.log(` ⏱ [${testDef.description}] capturePermutations(old): ${(performance.now() - t).toFixed(0)}ms`); expect(newPermutations.length).toBe(oldPermutations.length); const permFailures: number[] = []; const attachmentPromises: Promise[] = []; + + t = performance.now(); for (let i = 0; i < newPermutations.length; i++) { - const permResult = await cropAndCompare(newPermutations[i], oldPermutations[i]); + const permResult = await instrumentedCropAndCompare(newPermutations[i], oldPermutations[i]); attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i + 1}`)); if (permResult.diffPixels !== 0) { permFailures.push(i); } } + console.log( + ` ⏱ [${testDef.description}] comparePermutations(${newPermutations.length}): ${(performance.now() - t).toFixed(0)}ms` + ); + + t = performance.now(); await Promise.all(attachmentPromises); + console.log( + ` ⏱ [${testDef.description}] attachPermutations(${newPermutations.length}): ${(performance.now() - t).toFixed(0)}ms` + ); + + console.log(` ⏱ [${testDef.description}] TOTAL: ${(performance.now() - testStart).toFixed(0)}ms`); expect(permFailures).toEqual([]); return; } // Always attach for visibility in the Allure report. + t = performance.now(); await attachDiffImages(result, testDef.description); + console.log(` ⏱ [${testDef.description}] attachDiffImages: ${(performance.now() - t).toFixed(0)}ms`); + console.log(` ⏱ [${testDef.description}] TOTAL: ${(performance.now() - testStart).toFixed(0)}ms`); expect(result.diffPixels).toBe(0); }); } From a950516bf2314ad802c2e3af8314cdc5ad067aec Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 20:08:52 +0200 Subject: [PATCH 107/135] Extend logging --- test/definitions/instrumented-page-object.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/definitions/instrumented-page-object.ts b/test/definitions/instrumented-page-object.ts index f6eaa083ad..3ad73f8fe6 100644 --- a/test/definitions/instrumented-page-object.ts +++ b/test/definitions/instrumented-page-object.ts @@ -75,7 +75,9 @@ export class InstrumentedPageObject extends ScreenshotPageObject { t = performance.now(); const image = await parsePng(rawBase64); - this.log(` parsePng: ${ms(t)}`); + this.log( + ` parsePng: ${ms(t)} (${image.width}x${image.height}px, ${(image.data.length / 1024 / 1024).toFixed(1)}MB raw)` + ); const offset = { top: box.top, left: box.left }; if (!options.viewportOnly) { @@ -137,7 +139,9 @@ export class InstrumentedPageObject extends ScreenshotPageObject { // parsePng t = performance.now(); const image = await parsePng(rawBase64); - this.log(` parsePng: ${ms(t)}`); + this.log( + ` parsePng: ${ms(t)} (${image.width}x${image.height}px, ${(image.data.length / 1024 / 1024).toFixed(1)}MB raw pixels)` + ); // restore window size t = performance.now(); @@ -268,7 +272,9 @@ export async function instrumentedCropAndCompare( { top: secondScreenshot.offset.top, left: secondScreenshot.offset.left, width: size.width, height: size.height }, pixelRatio ); - console.log(` ⏱ ${prefix} crop (2 images): ${ms(t)} (${scaledSize.width}x${scaledSize.height}px)`); + console.log( + ` ⏱ ${prefix} crop (2 images): ${ms(t)} (${scaledSize.width}x${scaledSize.height}px, src: ${firstScreenshot.image.width}x${firstScreenshot.image.height}px)` + ); // Pixel comparison t = performance.now(); From 0ee95da1b56b0819ca08cbf873fbd5de0fb17711 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 20:37:09 +0200 Subject: [PATCH 108/135] Avoid calling parsePng if possible --- test/definitions/instrumented-page-object.ts | 33 ++- test/definitions/utils.ts | 278 ++++++++++++------- 2 files changed, 203 insertions(+), 108 deletions(-) diff --git a/test/definitions/instrumented-page-object.ts b/test/definitions/instrumented-page-object.ts index 3ad73f8fe6..1851658e01 100644 --- a/test/definitions/instrumented-page-object.ts +++ b/test/definitions/instrumented-page-object.ts @@ -293,14 +293,33 @@ export async function instrumentedCropAndCompare( console.log(` ⏱ ${prefix} pixelmatch: ${ms(t)} (diffPixels=${diffPixels})`); } - // Pack PNGs + // Pack PNGs — skip entirely when images are identical since we can use rawBase64 directly t = performance.now(); - const [firstPacked, secondPacked, diffPacked] = await Promise.all([ - packPng(firstImage), - packPng(secondImage), - diffImage ? packPng(diffImage) : Promise.resolve(null), - ]); - console.log(` ⏱ ${prefix} packPng (${diffImage ? 3 : 2} images): ${ms(t)}`); + let firstPacked: Buffer; + let secondPacked: Buffer; + let diffPacked: Buffer | null = null; + + if (diffPixels === 0 && !diffImage) { + // Images are identical — use the raw screenshot PNG directly (it's already a valid PNG). + // This avoids the extremely expensive packPng re-encoding. + const rawFirst = (firstScreenshot as any).rawBase64; + if (rawFirst) { + firstPacked = Buffer.from(rawFirst, 'base64'); + secondPacked = firstPacked; + console.log(` ⏱ ${prefix} packPng SKIPPED (using rawBase64: ${(firstPacked.length / 1024).toFixed(0)}KB)`); + } else { + firstPacked = await packPng(firstImage); + secondPacked = firstPacked; + console.log(` ⏱ ${prefix} packPng (1 image, reused): ${ms(t)}`); + } + } else { + [firstPacked, secondPacked, diffPacked] = await Promise.all([ + packPng(firstImage), + packPng(secondImage), + diffImage ? packPng(diffImage) : Promise.resolve(null), + ]); + console.log(` ⏱ ${prefix} packPng (${diffImage ? 3 : 2} images): ${ms(t)}`); + } console.log(` ⏱ ${prefix}cropAndCompare total: ${ms(t0)} (diffPixels=${diffPixels})`); return { diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 2050cf4b54..43f70fab77 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { attachment } from 'allure-js-commons'; +import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; import createWrapper from '../../lib/components/test-utils/selectors'; -import { instrumentedCropAndCompare, InstrumentedPageObject } from './instrumented-page-object'; import { TestDefinition, TestSuite } from './types'; const screenshotAreaSelector = '.screenshot-area'; @@ -17,6 +17,30 @@ const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; const wrapper = createWrapper(); +// ─── Types ─────────────────────────────────────────────────────────────────── + +interface CropAndCompareResult { + firstImage: Buffer; + secondImage: Buffer; + diffImage: Buffer | null; + isEqual: boolean; + diffPixels: number; +} + +/** + * A raw screenshot that defers PNG decoding until needed. + * Allows fast byte-equality comparison on the compressed data. + */ +interface RawScreenshot { + rawBase64: string; + offset: { top: number; left: number }; + width: number; + height: number; + pixelRatio?: number; +} + +// ─── Helpers ───────────────────────────────────────────────────────────────── + function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams({ motionDisabled: 'true', ...queryParams }); const qs = params.toString(); @@ -28,28 +52,146 @@ function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinit } /** - * Attaches a visual comparison to the Allure report using the built-in image diff viewer. + * Attaches a visual comparison to the Allure report. */ -async function attachDiffImages( - result: { firstImage: Buffer; secondImage: Buffer; diffImage: Buffer | null }, - testName: string -): Promise { +async function attachDiffImages(result: CropAndCompareResult, testName: string): Promise { const diffPayload = JSON.stringify({ expected: `data:image/png;base64,${result.secondImage.toString('base64')}`, actual: `data:image/png;base64,${result.firstImage.toString('base64')}`, diff: result.diffImage ? `data:image/png;base64,${result.diffImage.toString('base64')}` : undefined, }); - await attachment(testName, diffPayload, { - contentType: 'application/vnd.allure.image.diff', - fileExtension: 'imagediff', - } as any); + await attachment(testName, diffPayload, 'application/vnd.allure.image.diff'); } +// ─── Capture functions ─────────────────────────────────────────────────────── + /** - * Registers all test suites with a single shared browser session per worker. - * This avoids the per-test session creation overhead. + * Navigates to a URL, waits for the screenshot area, and runs setup. */ +async function preparePage( + browser: WebdriverIO.Browser, + page: ScreenshotPageObject, + url: string, + testDef: TestDefinition, + windowSize?: { width?: number; height?: number } +): Promise { + await browser.setWindowSize( + windowSize?.width ?? defaultWindowSize.width, + windowSize?.height ?? defaultWindowSize.height + ); + await browser.url(url); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup({ page, wrapper, browser }); + } +} + +/** + * Captures a raw screenshot (base64 + metadata) WITHOUT decoding the PNG. + * This enables fast comparison on the compressed bytes. + */ +async function captureRaw( + browser: WebdriverIO.Browser, + page: ScreenshotPageObject, + testDef: TestDefinition +): Promise { + if (testDef.screenshotType === 'viewport') { + const { height, width } = await page.getViewportSize(); + const rawBase64 = await browser.takeScreenshot(); + return { rawBase64, offset: { top: 0, left: 0 }, width, height }; + } + + // screenshotArea or permutations: full-page screenshot with offset + const { pixelRatio } = await page.getViewportSize(); + const box = await page.getBoundingBox(screenshotAreaSelector); + const rawBase64 = await page.fullPageScreenshot(); + return { rawBase64, offset: { top: box.top, left: box.left }, width: box.width, height: box.height, pixelRatio }; +} + +/** + * Decodes a RawScreenshot into a ScreenshotWithOffset for cropAndCompare. + */ +async function decodeRaw(raw: RawScreenshot): Promise { + const image = await parsePng(raw.rawBase64); + return { image, offset: raw.offset, width: raw.width, height: raw.height, pixelRatio: raw.pixelRatio }; +} + +/** + * Compares two raw screenshots. If the compressed bytes are identical, + * skips the expensive parsePng + cropAndCompare pipeline entirely. + */ +async function compareScreenshots(newRaw: RawScreenshot, oldRaw: RawScreenshot): Promise { + // Fast path: identical compressed PNG bytes → images are the same. + // Skips parsePng (~300-1200ms) and packPng (~6000-45000ms). + if (newRaw.rawBase64 === oldRaw.rawBase64) { + const imageBuffer = Buffer.from(newRaw.rawBase64, 'base64'); + return { firstImage: imageBuffer, secondImage: imageBuffer, diffImage: null, isEqual: true, diffPixels: 0 }; + } + + // Slow path: decode and do full pixel comparison. + const [newScreenshot, oldScreenshot] = await Promise.all([decodeRaw(newRaw), decodeRaw(oldRaw)]); + return cropAndCompare(newScreenshot, oldScreenshot); +} + +/** + * Similar to page.capturePermutations() but returns raw base64 per permutation + * without decoding the shared full-page PNG upfront. Decoding is deferred until + * an actual difference is detected. + */ +async function capturePermutationsRaw( + browser: WebdriverIO.Browser, + page: ScreenshotPageObject +): Promise { + // Replicates the logic from ScreenshotPageObject.capturePermutations() + // but keeps the screenshot as raw base64 instead of decoding it. + await page.windowScrollTo({ top: 0, left: 0 }); + + // Fit window height to page content + const originalWindowSize = await browser.getWindowSize(); + const dims: { viewportHeight: number; pageHeight: number } = await browser.execute(function () { + return { viewportHeight: window.innerHeight, pageHeight: document.documentElement.scrollHeight }; + }); + const windowUIHeight = originalWindowSize.height - dims.viewportHeight; + await browser.setWindowSize(originalWindowSize.width, dims.pageHeight + windowUIHeight); + + // Get permutation sizes + const permutations: Array<{ id: string; width: number; height: number; offset: { top: number; left: number } }> = + await browser.execute(function () { + const elements = document.querySelectorAll('[data-testid="permutation"]'); + return Array.from(elements).map(function (el) { + const rect = el.getBoundingClientRect(); + return { + id: el.getAttribute('data-permutation-id') || `${rect.top}-${rect.left}`, + width: rect.width, + height: rect.height, + offset: { top: rect.top, left: rect.left }, + }; + }); + }); + + if (permutations.length === 0) { + throw new Error('No permutations found on current page.'); + } + + // Take full page screenshot (raw base64, NOT decoded) + const rawBase64 = await page.fullPageScreenshot(); + + // Restore window size + await browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); + + // Return one RawScreenshot per permutation — they all share the same rawBase64 + // but have different offsets/sizes for cropping later if needed. + return permutations.map(p => ({ + rawBase64, + offset: p.offset, + width: p.width, + height: p.height, + })); +} + +// ─── Test runner ───────────────────────────────────────────────────────────── + export function runTestSuites(suites: Array) { let browser: WebdriverIO.Browser; @@ -80,120 +222,54 @@ function registerSuites(suites: Array, getBrowser: ( } } -/** - * Navigates to a URL, waits for the screenshot area, and runs any setup interactions. - */ -async function preparePage( - browser: WebdriverIO.Browser, - page: ScreenshotPageObject, - url: string, - testDef: TestDefinition, - windowSize?: { width?: number; height?: number } -): Promise { - await browser.setWindowSize( - windowSize?.width ?? defaultWindowSize.width, - windowSize?.height ?? defaultWindowSize.height - ); - await browser.url(url); - await page.waitForVisible(screenshotAreaSelector); - if (testDef.setup) { - return testDef.setup({ page, wrapper, browser }); - } -} - -/** - * Captures a screenshot based on the test's screenshotType. - */ -function capture(page: ScreenshotPageObject, testDef: TestDefinition): Promise { - if (testDef.screenshotType === 'viewport') { - return page.captureViewport(); - } - return page.captureBySelector(screenshotAreaSelector); -} - function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { test(testDef.description, async () => { - const testStart = performance.now(); const browser = getBrowser(); - const page = new InstrumentedPageObject(browser); - page.setLabel(testDef.description); + const page = new ScreenshotPageObject(browser); const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - let t = performance.now(); - await preparePage(browser, page, newUrl, testDef, testDef.configuration); - console.log(` ⏱ [${testDef.description}] preparePage(new): ${(performance.now() - t).toFixed(0)}ms`); - - t = performance.now(); - const newScreenshot = await capture(page, testDef); - console.log(` ⏱ [${testDef.description}] capture(new): ${(performance.now() - t).toFixed(0)}ms`); - - t = performance.now(); - await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - console.log(` ⏱ [${testDef.description}] preparePage(old): ${(performance.now() - t).toFixed(0)}ms`); + if (testDef.screenshotType === 'permutations') { + // Capture permutations as raw (no PNG decode) + await preparePage(browser, page, newUrl, testDef, testDef.configuration); + const newPerms = await capturePermutationsRaw(browser, page); - t = performance.now(); - const oldScreenshot = await capture(page, testDef); - console.log(` ⏱ [${testDef.description}] capture(old): ${(performance.now() - t).toFixed(0)}ms`); + await preparePage(browser, page, oldUrl, testDef, testDef.configuration); + const oldPerms = await capturePermutationsRaw(browser, page); - t = performance.now(); - const result = await instrumentedCropAndCompare(newScreenshot, oldScreenshot); - console.log( - ` ⏱ [${testDef.description}] cropAndCompare: ${(performance.now() - t).toFixed(0)}ms (diffPixels=${result.diffPixels})` - ); + expect(newPerms.length).toBe(oldPerms.length); - if (testDef.screenshotType === 'permutations') { - if (result.diffPixels === 0) { - console.log( - ` ⏱ [${testDef.description}] TOTAL: ${(performance.now() - testStart).toFixed(0)}ms (pass, no permutation re-capture needed)` - ); + // All permutations share the same full-page screenshot raw base64. + // If the full-page screenshots are byte-identical, ALL permutations pass. + if (newPerms.length > 0 && newPerms[0].rawBase64 === oldPerms[0].rawBase64) { return; } - t = performance.now(); - await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newPermutations = await page.capturePermutations(); - console.log(` ⏱ [${testDef.description}] capturePermutations(new): ${(performance.now() - t).toFixed(0)}ms`); - - t = performance.now(); - await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldPermutations = await page.capturePermutations(); - console.log(` ⏱ [${testDef.description}] capturePermutations(old): ${(performance.now() - t).toFixed(0)}ms`); - - expect(newPermutations.length).toBe(oldPermutations.length); + // Full-page differs — decode and compare individual permutations. const permFailures: number[] = []; const attachmentPromises: Promise[] = []; - - t = performance.now(); - for (let i = 0; i < newPermutations.length; i++) { - const permResult = await instrumentedCropAndCompare(newPermutations[i], oldPermutations[i]); - attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i + 1}`)); + for (let i = 0; i < newPerms.length; i++) { + const permResult = await compareScreenshots(newPerms[i], oldPerms[i]); if (permResult.diffPixels !== 0) { + attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i + 1}`)); permFailures.push(i); } } - console.log( - ` ⏱ [${testDef.description}] comparePermutations(${newPermutations.length}): ${(performance.now() - t).toFixed(0)}ms` - ); - - t = performance.now(); await Promise.all(attachmentPromises); - console.log( - ` ⏱ [${testDef.description}] attachPermutations(${newPermutations.length}): ${(performance.now() - t).toFixed(0)}ms` - ); - - console.log(` ⏱ [${testDef.description}] TOTAL: ${(performance.now() - testStart).toFixed(0)}ms`); expect(permFailures).toEqual([]); return; } - // Always attach for visibility in the Allure report. - t = performance.now(); - await attachDiffImages(result, testDef.description); - console.log(` ⏱ [${testDef.description}] attachDiffImages: ${(performance.now() - t).toFixed(0)}ms`); + // Non-permutation: single screenshot comparison + await preparePage(browser, page, newUrl, testDef, testDef.configuration); + const newRaw = await captureRaw(browser, page, testDef); - console.log(` ⏱ [${testDef.description}] TOTAL: ${(performance.now() - testStart).toFixed(0)}ms`); + await preparePage(browser, page, oldUrl, testDef, testDef.configuration); + const oldRaw = await captureRaw(browser, page, testDef); + + const result = await compareScreenshots(newRaw, oldRaw); + await attachDiffImages(result, testDef.description); expect(result.diffPixels).toBe(0); }); } From 289686344db6f1f14f5f074fc885c95ba9bfad08 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sat, 13 Jun 2026 21:36:15 +0200 Subject: [PATCH 109/135] Fix permutation selector --- test/definitions/utils.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 43f70fab77..2640af18c9 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -149,26 +149,26 @@ async function capturePermutationsRaw( // Fit window height to page content const originalWindowSize = await browser.getWindowSize(); - const dims: { viewportHeight: number; pageHeight: number } = await browser.execute(function () { - return { viewportHeight: window.innerHeight, pageHeight: document.documentElement.scrollHeight }; - }); + const dims = (await browser.execute(() => ({ + viewportHeight: window.innerHeight, + pageHeight: document.documentElement.scrollHeight, + }))) as { viewportHeight: number; pageHeight: number }; const windowUIHeight = originalWindowSize.height - dims.viewportHeight; await browser.setWindowSize(originalWindowSize.width, dims.pageHeight + windowUIHeight); - // Get permutation sizes - const permutations: Array<{ id: string; width: number; height: number; offset: { top: number; left: number } }> = - await browser.execute(function () { - const elements = document.querySelectorAll('[data-testid="permutation"]'); - return Array.from(elements).map(function (el) { - const rect = el.getBoundingClientRect(); - return { - id: el.getAttribute('data-permutation-id') || `${rect.top}-${rect.left}`, - width: rect.width, - height: rect.height, - offset: { top: rect.top, left: rect.left }, - }; - }); + // Get permutation sizes (same logic as browser-test-tools getPermutationSizes) + const permutations = (await browser.execute(() => { + const pixelRatio = window.devicePixelRatio || 1; + return Array.from(document.querySelectorAll('[data-permutation]')).map(element => { + const rect = element.getBoundingClientRect(); + return { + id: element.getAttribute('data-permutation') || '', + width: rect.width * pixelRatio, + height: rect.height * pixelRatio, + offset: { top: rect.top * pixelRatio, left: rect.left * pixelRatio }, + }; }); + })) as Array<{ id: string; width: number; height: number; offset: { top: number; left: number } }>; if (permutations.length === 0) { throw new Error('No permutations found on current page.'); From 210f8019ea8ac9c28ec395f503d9ddee1da7e012 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sun, 14 Jun 2026 00:57:45 +0200 Subject: [PATCH 110/135] Add delay --- test/definitions/utils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 2640af18c9..962cf0d5b0 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -82,6 +82,7 @@ async function preparePage( ); await browser.url(url); await page.waitForVisible(screenshotAreaSelector); + await page.waitForJsTimers(100); if (testDef.setup) { await testDef.setup({ page, wrapper, browser }); } From e189bd274cb98b9b23b09ecd5694ad047619a119 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sun, 14 Jun 2026 01:02:11 +0200 Subject: [PATCH 111/135] Refine attachment name --- test/definitions/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 962cf0d5b0..98cc8636f0 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -253,7 +253,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro for (let i = 0; i < newPerms.length; i++) { const permResult = await compareScreenshots(newPerms[i], oldPerms[i]); if (permResult.diffPixels !== 0) { - attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i + 1}`)); + attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i.toString().padStart(3, '0')}`)); permFailures.push(i); } } From 8119dd844da632aed21f865a1a005e44dd599d01 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Sun, 14 Jun 2026 01:17:39 +0200 Subject: [PATCH 112/135] Apply tolerance --- test/definitions/types.ts | 1 + test/definitions/utils.ts | 5 +++-- test/definitions/visual/bar-chart.ts | 2 ++ test/definitions/visual/line-chart.ts | 1 + test/definitions/visual/mixed-line-bar-chart.ts | 2 ++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/definitions/types.ts b/test/definitions/types.ts index 4cae18c113..087fe2c52c 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -23,6 +23,7 @@ export interface TestDefinition { screenshotType: ScreenshotType; queryParams?: Record; configuration?: ScreenshotTestConfiguration; + pixelDiffTolerance?: number; setup?: ({ page, wrapper, browser }: { page: ScreenshotPageObject; wrapper: Wrapper; browser: Browser }) => void; } diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 98cc8636f0..9a4d5def5b 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -225,6 +225,7 @@ function registerSuites(suites: Array, getBrowser: ( function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { test(testDef.description, async () => { + const tolerance = testDef.pixelDiffTolerance ?? 0; const browser = getBrowser(); const page = new ScreenshotPageObject(browser); @@ -252,7 +253,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const attachmentPromises: Promise[] = []; for (let i = 0; i < newPerms.length; i++) { const permResult = await compareScreenshots(newPerms[i], oldPerms[i]); - if (permResult.diffPixels !== 0) { + if (permResult.diffPixels > tolerance) { attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i.toString().padStart(3, '0')}`)); permFailures.push(i); } @@ -271,6 +272,6 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const result = await compareScreenshots(newRaw, oldRaw); await attachDiffImages(result, testDef.description); - expect(result.diffPixels).toBe(0); + expect(result.diffPixels).toBeLessThanOrEqual(tolerance); }); } diff --git a/test/definitions/visual/bar-chart.ts b/test/definitions/visual/bar-chart.ts index 68508d40b6..dde6ef5969 100644 --- a/test/definitions/visual/bar-chart.ts +++ b/test/definitions/visual/bar-chart.ts @@ -44,6 +44,7 @@ const suite: TestSuite = { path: 'bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, + pixelDiffTolerance: 12, setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); @@ -57,6 +58,7 @@ const suite: TestSuite = { path: 'bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, + pixelDiffTolerance: 10, setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); diff --git a/test/definitions/visual/line-chart.ts b/test/definitions/visual/line-chart.ts index ce4c9ede00..560a355039 100644 --- a/test/definitions/visual/line-chart.ts +++ b/test/definitions/visual/line-chart.ts @@ -45,6 +45,7 @@ const suite: TestSuite = { path: 'line-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, + pixelDiffTolerance: 6, setup: async ({ page }) => { await page.click(TEST_CHART_FILTER_TRIGGER); await page.keys(['Escape']); diff --git a/test/definitions/visual/mixed-line-bar-chart.ts b/test/definitions/visual/mixed-line-bar-chart.ts index d34ad90105..d97a37a9d2 100644 --- a/test/definitions/visual/mixed-line-bar-chart.ts +++ b/test/definitions/visual/mixed-line-bar-chart.ts @@ -47,6 +47,7 @@ const suite: TestSuite = { path: 'mixed-line-bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, + pixelDiffTolerance: 12, setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); @@ -62,6 +63,7 @@ const suite: TestSuite = { path: 'mixed-line-bar-chart/test', screenshotType: 'viewport', configuration: { width: 800, height: 800 }, + pixelDiffTolerance: 12, setup: async ({ page }) => { await page.click('#focus-target'); await page.focusNextElement(); From b3b74b0758282bb967e90d964ff5f1c982db86fc Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 15 Jun 2026 07:17:42 +0200 Subject: [PATCH 113/135] Use takeElementScreenshot --- test/definitions/utils.ts | 66 ++++++++++++++------------------------- 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 9a4d5def5b..eab28d7340 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -37,6 +37,7 @@ interface RawScreenshot { width: number; height: number; pixelRatio?: number; + id?: string; } // ─── Helpers ───────────────────────────────────────────────────────────────── @@ -124,71 +125,52 @@ async function decodeRaw(raw: RawScreenshot): Promise { */ async function compareScreenshots(newRaw: RawScreenshot, oldRaw: RawScreenshot): Promise { // Fast path: identical compressed PNG bytes → images are the same. - // Skips parsePng (~300-1200ms) and packPng (~6000-45000ms). if (newRaw.rawBase64 === oldRaw.rawBase64) { const imageBuffer = Buffer.from(newRaw.rawBase64, 'base64'); return { firstImage: imageBuffer, secondImage: imageBuffer, diffImage: null, isEqual: true, diffPixels: 0 }; } + // For element screenshots (taken via takeElementScreenshot with offset 0,0 and width 0), + // the PNG is already cropped — no decode/crop/re-encode needed. Just report the diff. + if (newRaw.width === 0 && newRaw.offset.top === 0 && newRaw.offset.left === 0) { + const firstImage = Buffer.from(newRaw.rawBase64, 'base64'); + const secondImage = Buffer.from(oldRaw.rawBase64, 'base64'); + return { firstImage, secondImage, diffImage: null, isEqual: false, diffPixels: 1 }; + } + // Slow path: decode and do full pixel comparison. const [newScreenshot, oldScreenshot] = await Promise.all([decodeRaw(newRaw), decodeRaw(oldRaw)]); return cropAndCompare(newScreenshot, oldScreenshot); } /** - * Similar to page.capturePermutations() but returns raw base64 per permutation - * without decoding the shared full-page PNG upfront. Decoding is deferred until - * an actual difference is detected. + * Captures each permutation element individually using takeElementScreenshot. + * Returns one raw base64 PNG per permutation — no decode, no crop, no re-encode. */ async function capturePermutationsRaw( browser: WebdriverIO.Browser, page: ScreenshotPageObject ): Promise { - // Replicates the logic from ScreenshotPageObject.capturePermutations() - // but keeps the screenshot as raw base64 instead of decoding it. await page.windowScrollTo({ top: 0, left: 0 }); - // Fit window height to page content - const originalWindowSize = await browser.getWindowSize(); - const dims = (await browser.execute(() => ({ - viewportHeight: window.innerHeight, - pageHeight: document.documentElement.scrollHeight, - }))) as { viewportHeight: number; pageHeight: number }; - const windowUIHeight = originalWindowSize.height - dims.viewportHeight; - await browser.setWindowSize(originalWindowSize.width, dims.pageHeight + windowUIHeight); - - // Get permutation sizes (same logic as browser-test-tools getPermutationSizes) - const permutations = (await browser.execute(() => { - const pixelRatio = window.devicePixelRatio || 1; - return Array.from(document.querySelectorAll('[data-permutation]')).map(element => { - const rect = element.getBoundingClientRect(); - return { - id: element.getAttribute('data-permutation') || '', - width: rect.width * pixelRatio, - height: rect.height * pixelRatio, - offset: { top: rect.top * pixelRatio, left: rect.left * pixelRatio }, - }; - }); - })) as Array<{ id: string; width: number; height: number; offset: { top: number; left: number } }>; + // Find all permutation elements + const elements = await browser.$$('[data-permutation]'); - if (permutations.length === 0) { + if ((await elements.length) === 0) { throw new Error('No permutations found on current page.'); } - // Take full page screenshot (raw base64, NOT decoded) - const rawBase64 = await page.fullPageScreenshot(); + // Take a screenshot of each permutation element directly via WebDriver. + // This returns a pre-cropped PNG for each element — no parsePng/packPng needed. + const results: RawScreenshot[] = []; + for (const element of elements) { + const id = (await element.getAttribute('data-permutation')) || ''; + const rawBase64 = await browser.takeElementScreenshot(element.elementId); + // offset/width/height are irrelevant since the screenshot is already cropped + results.push({ rawBase64, offset: { top: 0, left: 0 }, width: 0, height: 0, id }); + } - // Restore window size - await browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); - - // Return one RawScreenshot per permutation — they all share the same rawBase64 - // but have different offsets/sizes for cropping later if needed. - return permutations.map(p => ({ - rawBase64, - offset: p.offset, - width: p.width, - height: p.height, - })); + return results; } // ─── Test runner ───────────────────────────────────────────────────────────── From c5da1f601f06c1f19f01071854588c2cd40cbe9e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 15 Jun 2026 10:49:05 +0200 Subject: [PATCH 114/135] Attach screenshots in all cases --- test/definitions/utils.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index eab28d7340..9be7f95075 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -224,19 +224,13 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro expect(newPerms.length).toBe(oldPerms.length); - // All permutations share the same full-page screenshot raw base64. - // If the full-page screenshots are byte-identical, ALL permutations pass. - if (newPerms.length > 0 && newPerms[0].rawBase64 === oldPerms[0].rawBase64) { - return; - } - - // Full-page differs — decode and compare individual permutations. + // Compare each permutation individually and attach results. const permFailures: number[] = []; const attachmentPromises: Promise[] = []; for (let i = 0; i < newPerms.length; i++) { const permResult = await compareScreenshots(newPerms[i], oldPerms[i]); + attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i.toString().padStart(3, '0')}`)); if (permResult.diffPixels > tolerance) { - attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i.toString().padStart(3, '0')}`)); permFailures.push(i); } } From 8e05873e353e375af8e055df10248b593fb4382e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 23 Jun 2026 15:36:26 +0200 Subject: [PATCH 115/135] Use PR deployment as new host --- .github/workflows/visual-regression.yml | 38 +++++++++++++++++++++---- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 9e6d300d91..a44c592d27 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -125,19 +125,47 @@ jobs: # ── Run tests ───────────────────────────────────────────────────────── - - name: Start test server (port 8080) - run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-default & + - name: Resolve PR deployment URL + id: hosts + run: | + DEPLOY_ENV="dev-pages-react18" + echo "Looking for deployment with environment '${DEPLOY_ENV}'..." + DEPLOY_ID=$(gh api \ + "repos/${REPO}/deployments?environment=${DEPLOY_ENV}&per_page=5" \ + --jq '.[0].id // empty' 2>/dev/null || true) + NEW_URL="" + if [ -n "$DEPLOY_ID" ]; then + NEW_URL=$(gh api \ + "repos/${REPO}/deployments/${DEPLOY_ID}/statuses" \ + --jq '[.[] | select(.state == "success")] | first | .environment_url // empty' 2>/dev/null || true) + NEW_URL="${NEW_URL%index.html}" + echo "Found PR deployment: ID=${DEPLOY_ID}, URL=${NEW_URL}" + fi + if [ -z "$NEW_URL" ]; then + echo "::error::Could not resolve PR deployment URL" + exit 1 + fi + echo "new=${NEW_URL}" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} - name: Start baseline server (port 8081) run: npx --yes serve --no-clipboard --listen 8081 pages/lib/static-visual-baseline & - - name: Wait for servers to be ready - run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 + - name: Wait for baseline server + run: node_modules/.bin/wait-on http://localhost:8081 - name: Run visual regression tests - run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/40 + run: | + echo "NEW_HOST=${NEW_HOST}" + echo "OLD_HOST=${OLD_HOST}" + NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard="${SHARD}/40" env: TZ: UTC + SHARD: ${{ matrix.shard }} + NEW_HOST: ${{ steps.hosts.outputs.new }} + OLD_HOST: http://localhost:8081 - name: Upload diff artifacts if: failure() From 1435fefaf7d2a471502786f4af859629b181e753 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 23 Jun 2026 17:15:22 +0200 Subject: [PATCH 116/135] Add optimizations --- test/definitions/optimized-page-object.ts | 122 ++++++++++++++++++++++ test/definitions/utils.ts | 114 ++++++-------------- 2 files changed, 153 insertions(+), 83 deletions(-) create mode 100644 test/definitions/optimized-page-object.ts diff --git a/test/definitions/optimized-page-object.ts b/test/definitions/optimized-page-object.ts new file mode 100644 index 0000000000..f8299de495 --- /dev/null +++ b/test/definitions/optimized-page-object.ts @@ -0,0 +1,122 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Extends ScreenshotPageObject with optimized capture methods that use + * takeElementScreenshot instead of full-page screenshot + crop + re-encode. + * + * This avoids the expensive parsePng/packPng pipeline for identical images + * by returning raw base64 PNGs that can be compared as strings. + */ +import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; + +interface RawScreenshot { + rawBase64: string; + width: number; + height: number; + pixelRatio?: number; + id?: string; +} + +export type { RawScreenshot }; + +export class OptimizedPageObject extends ScreenshotPageObject { + /** + * Captures the screenshot area using takeElementScreenshot. + * Returns the raw base64 PNG without decoding — suitable for fast string comparison. + * Falls back to the parent's captureBySelector if takeElementScreenshot fails. + */ + async captureSelectorRaw(selector: string): Promise { + await this.waitForVisible(selector); + const { pixelRatio } = await this.getViewportSize(); + const box = await this.getBoundingBox(selector); + + try { + const originalWindowSize = await this.browser.getWindowSize(); + // Fit window height to content so the element is fully visible + const dims: { viewportHeight: number; pageHeight: number } = await this.browser.execute(() => ({ + viewportHeight: window.innerHeight, + pageHeight: document.documentElement.scrollHeight, + })); + const windowUIHeight = originalWindowSize.height - dims.viewportHeight; + await this.browser.setWindowSize(originalWindowSize.width, dims.pageHeight + windowUIHeight); + + const element = this.browser.$(selector); + const rawBase64 = await this.browser.takeElementScreenshot(await element.elementId); + + // Restore window size + await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); + + return { rawBase64, pixelRatio, width: box.width, height: box.height }; + } catch { + // Fallback: use full-page screenshot (will need parsePng later if comparison fails) + console.warn('takeElementScreenshot failed, falling back to fullPageScreenshot'); + const rawBase64 = await this.fullPageScreenshot(); + return { rawBase64, pixelRatio, width: box.width, height: box.height }; + } + } + + /** + * Captures a viewport screenshot as raw base64. + */ + async captureViewportRaw(): Promise { + const { height, width } = await this.getViewportSize(); + const rawBase64 = await this.browser.takeScreenshot(); + return { rawBase64, width, height }; + } + + /** + * Captures each permutation element individually using takeElementScreenshot. + * Returns raw base64 PNGs — no decode, no crop, no re-encode. + * Falls back to the parent's capturePermutations if takeElementScreenshot fails. + */ + async capturePermutationsRaw(): Promise { + await this.windowScrollTo({ top: 0, left: 0 }); + + // Fit window height to content + const originalWindowSize = await this.browser.getWindowSize(); + const dims: { viewportHeight: number; pageHeight: number } = await this.browser.execute(() => ({ + viewportHeight: window.innerHeight, + pageHeight: document.documentElement.scrollHeight, + })); + const windowUIHeight = originalWindowSize.height - dims.viewportHeight; + await this.browser.setWindowSize(originalWindowSize.width, dims.pageHeight + windowUIHeight); + + const elements = await this.browser.$$('[data-permutation]'); + if ((await elements.length) === 0) { + await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); + throw new Error('No permutations found on current page.'); + } + + try { + const pixelRatio: number = await this.browser.execute(() => window.devicePixelRatio || 1); + const results: RawScreenshot[] = []; + + for (const element of elements) { + const id = (await element.getAttribute('data-permutation')) || ''; + const rawBase64 = await this.browser.takeElementScreenshot(element.elementId); + const size = await element.getSize(); + results.push({ + id, + rawBase64, + width: size.width * pixelRatio, + height: size.height * pixelRatio, + }); + } + + await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); + return results; + } catch { + console.warn('takeElementScreenshot failed for permutations, falling back to full-page strategy'); + await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); + // Fall back to the parent's approach + const permutations = await super.capturePermutations(); + return permutations.map(p => ({ + id: (p as any).id || '', + rawBase64: '', // Will force slow-path comparison + width: p.width, + height: p.height, + })); + } + } +} diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 9be7f95075..bec3c5d4d8 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -3,9 +3,10 @@ import { attachment } from 'allure-js-commons'; import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; -import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; +import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; import createWrapper from '../../lib/components/test-utils/selectors'; +import { OptimizedPageObject, RawScreenshot } from './optimized-page-object'; import { TestDefinition, TestSuite } from './types'; const screenshotAreaSelector = '.screenshot-area'; @@ -27,19 +28,6 @@ interface CropAndCompareResult { diffPixels: number; } -/** - * A raw screenshot that defers PNG decoding until needed. - * Allows fast byte-equality comparison on the compressed data. - */ -interface RawScreenshot { - rawBase64: string; - offset: { top: number; left: number }; - width: number; - height: number; - pixelRatio?: number; - id?: string; -} - // ─── Helpers ───────────────────────────────────────────────────────────────── function buildUrl(host: string, path: string, queryParams?: Record): string { @@ -91,32 +79,13 @@ async function preparePage( /** * Captures a raw screenshot (base64 + metadata) WITHOUT decoding the PNG. - * This enables fast comparison on the compressed bytes. + * Uses takeElementScreenshot for the screenshot area to avoid full-page capture. */ -async function captureRaw( - browser: WebdriverIO.Browser, - page: ScreenshotPageObject, - testDef: TestDefinition -): Promise { +function captureRaw(page: OptimizedPageObject, testDef: TestDefinition): Promise { if (testDef.screenshotType === 'viewport') { - const { height, width } = await page.getViewportSize(); - const rawBase64 = await browser.takeScreenshot(); - return { rawBase64, offset: { top: 0, left: 0 }, width, height }; + return page.captureViewportRaw(); } - - // screenshotArea or permutations: full-page screenshot with offset - const { pixelRatio } = await page.getViewportSize(); - const box = await page.getBoundingBox(screenshotAreaSelector); - const rawBase64 = await page.fullPageScreenshot(); - return { rawBase64, offset: { top: box.top, left: box.left }, width: box.width, height: box.height, pixelRatio }; -} - -/** - * Decodes a RawScreenshot into a ScreenshotWithOffset for cropAndCompare. - */ -async function decodeRaw(raw: RawScreenshot): Promise { - const image = await parsePng(raw.rawBase64); - return { image, offset: raw.offset, width: raw.width, height: raw.height, pixelRatio: raw.pixelRatio }; + return page.captureSelectorRaw(screenshotAreaSelector); } /** @@ -130,47 +99,26 @@ async function compareScreenshots(newRaw: RawScreenshot, oldRaw: RawScreenshot): return { firstImage: imageBuffer, secondImage: imageBuffer, diffImage: null, isEqual: true, diffPixels: 0 }; } - // For element screenshots (taken via takeElementScreenshot with offset 0,0 and width 0), - // the PNG is already cropped — no decode/crop/re-encode needed. Just report the diff. - if (newRaw.width === 0 && newRaw.offset.top === 0 && newRaw.offset.left === 0) { - const firstImage = Buffer.from(newRaw.rawBase64, 'base64'); - const secondImage = Buffer.from(oldRaw.rawBase64, 'base64'); - return { firstImage, secondImage, diffImage: null, isEqual: false, diffPixels: 1 }; - } - - // Slow path: decode and do full pixel comparison. - const [newScreenshot, oldScreenshot] = await Promise.all([decodeRaw(newRaw), decodeRaw(oldRaw)]); - return cropAndCompare(newScreenshot, oldScreenshot); -} - -/** - * Captures each permutation element individually using takeElementScreenshot. - * Returns one raw base64 PNG per permutation — no decode, no crop, no re-encode. - */ -async function capturePermutationsRaw( - browser: WebdriverIO.Browser, - page: ScreenshotPageObject -): Promise { - await page.windowScrollTo({ top: 0, left: 0 }); - - // Find all permutation elements - const elements = await browser.$$('[data-permutation]'); - - if ((await elements.length) === 0) { - throw new Error('No permutations found on current page.'); - } - - // Take a screenshot of each permutation element directly via WebDriver. - // This returns a pre-cropped PNG for each element — no parsePng/packPng needed. - const results: RawScreenshot[] = []; - for (const element of elements) { - const id = (await element.getAttribute('data-permutation')) || ''; - const rawBase64 = await browser.takeElementScreenshot(element.elementId); - // offset/width/height are irrelevant since the screenshot is already cropped - results.push({ rawBase64, offset: { top: 0, left: 0 }, width: 0, height: 0, id }); - } - - return results; + // Images differ — decode and run pixelmatch to produce a diff image for the Allure report. + // This only runs for failing tests, so the cost is acceptable. + const firstPng = await parsePng(newRaw.rawBase64); + const secondPng = await parsePng(oldRaw.rawBase64); + + // No cropping needed since takeElementScreenshot already produces cropped images. + const firstScreenshot = { + image: firstPng, + offset: { top: 0, left: 0 }, + width: firstPng.width, + height: firstPng.height, + }; + + const secondScreenshot = { + image: secondPng, + offset: { top: 0, left: 0 }, + width: secondPng.width, + height: secondPng.height, + }; + return cropAndCompare(firstScreenshot, secondScreenshot); } // ─── Test runner ───────────────────────────────────────────────────────────── @@ -209,7 +157,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro test(testDef.description, async () => { const tolerance = testDef.pixelDiffTolerance ?? 0; const browser = getBrowser(); - const page = new ScreenshotPageObject(browser); + const page = new OptimizedPageObject(browser); const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); @@ -217,10 +165,10 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro if (testDef.screenshotType === 'permutations') { // Capture permutations as raw (no PNG decode) await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newPerms = await capturePermutationsRaw(browser, page); + const newPerms = await page.capturePermutationsRaw(); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldPerms = await capturePermutationsRaw(browser, page); + const oldPerms = await page.capturePermutationsRaw(); expect(newPerms.length).toBe(oldPerms.length); @@ -241,10 +189,10 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro // Non-permutation: single screenshot comparison await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newRaw = await captureRaw(browser, page, testDef); + const newRaw = await captureRaw(page, testDef); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldRaw = await captureRaw(browser, page, testDef); + const oldRaw = await captureRaw(page, testDef); const result = await compareScreenshots(newRaw, oldRaw); await attachDiffImages(result, testDef.description); From 5674948902937ab485e358e3beac8f0c20d540c6 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 23 Jun 2026 18:29:50 +0200 Subject: [PATCH 117/135] Fix buildUrl --- test/definitions/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index bec3c5d4d8..876bb0196c 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -33,7 +33,7 @@ interface CropAndCompareResult { function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams({ motionDisabled: 'true', ...queryParams }); const qs = params.toString(); - return `${host}/#/${path}${qs ? `?${qs}` : ''}`; + return `${host}#/${path}${qs ? `?${qs}` : ''}`; } function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinition { From 63f0490bc0365cc2f01de71b31cb7ae1e08f0c10 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 23 Jun 2026 18:53:29 +0200 Subject: [PATCH 118/135] Add logging --- test/definitions/utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 876bb0196c..0878ab5e9b 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -162,6 +162,8 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); + console.log({ newUrl, oldUrl }); + if (testDef.screenshotType === 'permutations') { // Capture permutations as raw (no PNG decode) await preparePage(browser, page, newUrl, testDef, testDef.configuration); From dd64d04fe0ecdc65135b5f233fdc4a3bc5d0f470 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 23 Jun 2026 19:17:57 +0200 Subject: [PATCH 119/135] Fixes --- .github/workflows/visual-regression.yml | 9 ++++----- .gitignore | 2 -- docs/RUNNING_TESTS.md | 8 ++++---- test/definitions/utils.ts | 2 +- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index a44c592d27..2c186dcdeb 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -138,7 +138,6 @@ jobs: NEW_URL=$(gh api \ "repos/${REPO}/deployments/${DEPLOY_ID}/statuses" \ --jq '[.[] | select(.state == "success")] | first | .environment_url // empty' 2>/dev/null || true) - NEW_URL="${NEW_URL%index.html}" echo "Found PR deployment: ID=${DEPLOY_ID}, URL=${NEW_URL}" fi if [ -z "$NEW_URL" ]; then @@ -150,11 +149,11 @@ jobs: GH_TOKEN: ${{ github.token }} REPO: ${{ github.repository }} - - name: Start baseline server (port 8081) - run: npx --yes serve --no-clipboard --listen 8081 pages/lib/static-visual-baseline & + - name: Start baseline server (port 8080) + run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-visual-baseline & - name: Wait for baseline server - run: node_modules/.bin/wait-on http://localhost:8081 + run: node_modules/.bin/wait-on http://localhost:8080 - name: Run visual regression tests run: | @@ -165,7 +164,7 @@ jobs: TZ: UTC SHARD: ${{ matrix.shard }} NEW_HOST: ${{ steps.hosts.outputs.new }} - OLD_HOST: http://localhost:8081 + OLD_HOST: http://localhost:8080 - name: Upload diff artifacts if: failure() diff --git a/.gitignore b/.gitignore index 9b14cf8ced..db3d62b944 100644 --- a/.gitignore +++ b/.gitignore @@ -6,8 +6,6 @@ coverage lib # generated sources src/index.ts -allure-results -allure-report src/test-utils/dom/index.ts src/test-utils/selectors src/icon/generated diff --git a/docs/RUNNING_TESTS.md b/docs/RUNNING_TESTS.md index 4db82e361b..b7793be83e 100644 --- a/docs/RUNNING_TESTS.md +++ b/docs/RUNNING_TESTS.md @@ -66,9 +66,9 @@ They compare permutation pages between the PR build and a baseline build of `mai ### How it works -1. The PR pages are built and served on port 8080. -2. A git worktree of `origin/main` is created, its dependencies installed, and its pages built and served on port 8081. -3. The single test runner (`test/visual.test.ts`) iterates over all test definitions, captures the `.screenshot-area` element from both servers for each test, and fails if any pixels differ. +1. The visual regression job waits for the deployment job —the deployment URL is used as the host containing the changes in the PR. +2. A git worktree of `origin/main` is created, its dependencies installed, and its pages built and served on port 8080. +3. Each of the test runners under `test/visual` imports their corresponding test definitions, captures the `.screenshot-area` element from both servers for each test, and fails if any pixels differ. ### Running locally @@ -82,7 +82,7 @@ This handles the full build and comparison in one command. If both outputs are a NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js ``` -(Requires both servers to be running — start the PR build with `npm run start:integ` on port 8080 and the baseline build on port 8081, or set `NEW_HOST` / `OLD_HOST` env vars to point at different hosts.) +(Requires both servers to be running — start the PR build with `npm run start:integ` on port 8080 and the baseline build on port 8080, or set `NEW_HOST` / `OLD_HOST` env vars to point at different hosts.) ### Adding tests for a new component diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 0878ab5e9b..235d5851a6 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -14,7 +14,7 @@ const defaultWindowSize = { width: 1200, height: 800 }; // NEW_HOST serves the PR's pages, OLD_HOST serves the baseline (main) pages. const newHost = process.env.NEW_HOST || 'http://localhost:8080'; -const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; +const oldHost = process.env.OLD_HOST || 'http://localhost:8080'; const wrapper = createWrapper(); From 6069f694c4b1ab4f3e53bb2392f09e6966a13687 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 23 Jun 2026 19:43:08 +0200 Subject: [PATCH 120/135] Various adjustments --- .github/workflows/deploy.yml | 47 +++++++++++++++-- .github/workflows/visual-regression.yml | 70 +++---------------------- test/definitions/utils.ts | 2 - 3 files changed, 51 insertions(+), 68 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 36964ddc1f..c6ff9ee628 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Deploy +name: Deploy and visual regression on: pull_request: @@ -73,13 +73,54 @@ jobs: artifact-name: dev-pages-react${{ matrix.react }} deployment-path: pages/lib/static-default + build-baseline: + name: Build baseline pages + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Create baseline worktree from origin/main + run: git worktree add /tmp/baseline origin/main + + - name: Install baseline dependencies + run: npm i + working-directory: /tmp/baseline + + - name: Build baseline pages + run: npx gulp quick-build + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Bundle baseline pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path "${GITHUB_WORKSPACE}/pages/lib/static-visual-baseline" + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Upload baseline artifact + uses: actions/upload-artifact@v4 + with: + name: visual-baseline-pages + path: pages/lib/static-visual-baseline + retention-days: 1 + visual: name: Visual regression - needs: quick-build + needs: [deploy, build-baseline] if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} uses: ./.github/workflows/visual-regression.yml secrets: inherit with: - pr-artifact-name: dev-pages-react18 test-utils-artifact-name: test-utils-selectors + baseline-artifact-name: visual-baseline-pages caller-run-id: ${{ github.run_id }} diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 2c186dcdeb..a462d2f549 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -3,10 +3,6 @@ name: Visual Regression Tests on: workflow_call: inputs: - pr-artifact-name: - description: 'Name of the artifact containing PR pages (built by the caller workflow).' - required: true - type: string test-utils-artifact-name: description: 'Name of the artifact containing test-utils selectors.' required: true @@ -15,6 +11,10 @@ on: description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' required: true type: string + baseline-artifact-name: + description: 'Name of the artifact containing baseline pages (built by the caller workflow).' + required: true + type: string defaults: run: @@ -27,58 +27,8 @@ permissions: deployments: write jobs: - # Build the baseline (main branch) pages once and share them across all browser jobs. - build-baseline: - name: Build baseline pages - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm - - - name: Install dependencies - run: npm i - - # Use a git worktree so the baseline has its own directory and its own - # node_modules. This means a PR that changes package-lock.json will still - # produce a correct baseline: the baseline installs from main's lockfile - # and the PR build installs from the PR's lockfile, so both sides use the - # dependency versions that are correct for their respective source trees. - - name: Create baseline worktree from origin/main - run: git worktree add /tmp/baseline origin/main - - - name: Install baseline dependencies - run: npm i - working-directory: /tmp/baseline - - - name: Build baseline pages - run: npx gulp quick-build - working-directory: /tmp/baseline - env: - NODE_ENV: production - - - name: Bundle baseline pages - run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline - working-directory: /tmp/baseline - env: - NODE_ENV: production - - - name: Upload baseline artifact - uses: actions/upload-artifact@v4 - with: - name: visual-baseline-pages - path: pages/lib/static-visual-baseline - retention-days: 1 - visual: name: Visual regression (shard ${{ matrix.shard }}) - needs: [build-baseline] runs-on: ubuntu-latest strategy: fail-fast: false @@ -101,19 +51,13 @@ jobs: - name: Install dependencies run: npm i - - name: Download PR pages artifact - uses: actions/download-artifact@v4 - with: - name: ${{ inputs.pr-artifact-name }} - path: pages/lib/static-default - github-token: ${{ github.token }} - run-id: ${{ inputs.caller-run-id }} - - name: Download baseline artifact uses: actions/download-artifact@v4 with: - name: visual-baseline-pages + name: ${{ inputs.baseline-artifact-name }} path: pages/lib/static-visual-baseline + github-token: ${{ github.token }} + run-id: ${{ inputs.caller-run-id }} - name: Download test utils artifact uses: actions/download-artifact@v4 diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 235d5851a6..6d337c8cd4 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -162,8 +162,6 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - console.log({ newUrl, oldUrl }); - if (testDef.screenshotType === 'permutations') { // Capture permutations as raw (no PNG decode) await preparePage(browser, page, newUrl, testDef, testDef.configuration); From 1edddb2bb39ffcd98b3d407e7c10fbe29abb0c1a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 23 Jun 2026 22:01:27 +0200 Subject: [PATCH 121/135] Refinements --- test/definitions/instrumented-page-object.ts | 332 ------------------- test/definitions/utils.ts | 25 +- 2 files changed, 14 insertions(+), 343 deletions(-) delete mode 100644 test/definitions/instrumented-page-object.ts diff --git a/test/definitions/instrumented-page-object.ts b/test/definitions/instrumented-page-object.ts deleted file mode 100644 index 1851658e01..0000000000 --- a/test/definitions/instrumented-page-object.ts +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/** - * Instrumented ScreenshotPageObject that logs performance timings. - * Re-implements key methods to show which strategy is used and where time is spent. - */ -import pixelmatch from 'pixelmatch'; -import { PNG } from 'pngjs'; - -import { parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; -import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; - -type PermutationScreenshot = ScreenshotWithOffset & { id: string }; - -function ms(start: number): string { - return `${(performance.now() - start).toFixed(0)}ms`; -} - -export class InstrumentedPageObject extends ScreenshotPageObject { - private label = ''; - - setLabel(value: string) { - this.label = value; - } - - private log(msg: string) { - console.log(` ⏱ [${this.label}] ${msg}`); - } - - async captureViewport(): Promise { - const t0 = performance.now(); - - let t = performance.now(); - const { height, width } = await this.getViewportSize(); - this.log(` getViewportSize: ${ms(t)}`); - - t = performance.now(); - const rawBase64 = await this.browser.takeScreenshot(); - this.log(` takeScreenshot: ${ms(t)} (${(rawBase64.length / 1024).toFixed(0)}KB base64)`); - - t = performance.now(); - const image = await parsePng(rawBase64); - this.log(` parsePng: ${ms(t)}`); - - this.log(`captureViewport total: ${ms(t0)}`); - return { image, offset: { top: 0, left: 0 }, height, width } as ScreenshotWithOffset; - } - - async captureBySelector(selector: string, options: { viewportOnly?: boolean } = {}): Promise { - const t0 = performance.now(); - - let t = performance.now(); - await this.waitForVisible(selector); - this.log(` waitForVisible: ${ms(t)}`); - - t = performance.now(); - const { pixelRatio, top, left } = await this.getViewportSize(); - this.log(` getViewportSize: ${ms(t)}`); - - t = performance.now(); - const box = await this.getBoundingBox(selector); - this.log(` getBoundingBox: ${ms(t)}`); - - let rawBase64: string; - if (options.viewportOnly) { - t = performance.now(); - rawBase64 = await this.browser.takeScreenshot(); - this.log(` takeScreenshot (viewportOnly): ${ms(t)} (${(rawBase64.length / 1024).toFixed(0)}KB)`); - } else { - t = performance.now(); - rawBase64 = await this.fullPageScreenshotInstrumented(); - this.log(` fullPageScreenshot total: ${ms(t)} (${(rawBase64.length / 1024).toFixed(0)}KB)`); - } - - t = performance.now(); - const image = await parsePng(rawBase64); - this.log( - ` parsePng: ${ms(t)} (${image.width}x${image.height}px, ${(image.data.length / 1024 / 1024).toFixed(1)}MB raw)` - ); - - const offset = { top: box.top, left: box.left }; - if (!options.viewportOnly) { - offset.top += top; - offset.left += left; - } - - this.log(`captureBySelector total: ${ms(t0)}`); - return { image, offset, pixelRatio, height: box.height, width: box.width } as ScreenshotWithOffset; - } - - async capturePermutations(): Promise { - const t0 = performance.now(); - - let t = performance.now(); - await this.windowScrollTo({ top: 0, left: 0 }); - this.log(` windowScrollTo(0): ${ms(t)}`); - - // fitWindowHeightToContent - t = performance.now(); - const originalWindowSize = await this.browser.getWindowSize(); - const { viewportHeight, pageHeight } = await (this.browser as any).execute(function () { - return { - viewportHeight: window.innerHeight, - pageHeight: document.documentElement.scrollHeight, - }; - }); - const windowUIHeight = originalWindowSize.height - viewportHeight; - const targetHeight = pageHeight + windowUIHeight; - await this.browser.setWindowSize(originalWindowSize.width, targetHeight); - this.log(` fitWindowHeightToContent: ${ms(t)} (pageHeight=${pageHeight}, targetWindowHeight=${targetHeight})`); - - // getPermutationSizes - t = performance.now(); - const permutations: Array<{ id: string; width: number; height: number; offset: { top: number; left: number } }> = - await (this.browser as any).execute(function () { - const elements = document.querySelectorAll('[data-testid="permutation"]'); - return Array.from(elements).map(function (el: Element) { - const rect = el.getBoundingClientRect(); - return { - id: el.getAttribute('data-permutation-id') || rect.top + '-' + rect.left, - width: rect.width, - height: rect.height, - offset: { top: rect.top, left: rect.left }, - }; - }); - }); - this.log(` getPermutationSizes: ${ms(t)} (${permutations.length} found)`); - - if (permutations.length === 0) { - throw new Error('No permutations found on current page.'); - } - - // fullPageScreenshot - t = performance.now(); - const rawBase64 = await this.fullPageScreenshotInstrumented(); - this.log(` fullPageScreenshot: ${ms(t)} (${(rawBase64.length / 1024).toFixed(0)}KB)`); - - // parsePng - t = performance.now(); - const image = await parsePng(rawBase64); - this.log( - ` parsePng: ${ms(t)} (${image.width}x${image.height}px, ${(image.data.length / 1024 / 1024).toFixed(1)}MB raw pixels)` - ); - - // restore window size - t = performance.now(); - await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); - this.log(` restoreWindowSize: ${ms(t)}`); - - this.log(`capturePermutations total: ${ms(t0)}`); - return permutations.map(permutation => ({ ...permutation, image }) as PermutationScreenshot); - } - - /** - * Instrumented fullPageScreenshot that logs which strategy is selected. - */ - private async fullPageScreenshotInstrumented(): Promise { - const scrollPosition = await this.getWindowScroll(); - await this.waitForJsTimers(); - - const browserName = (this.browser.capabilities as any)?.browserName || ''; - if (browserName.toLowerCase().includes('firefox')) { - this.log(` strategy: scroll-and-merge (Firefox detected)`); - const result = await super.fullPageScreenshot(); - return result; - } - - // Try Puppeteer - let t = performance.now(); - let puppeteer: any = null; - try { - puppeteer = await (this.browser as any).getPuppeteer(); - } catch (e: any) { - this.log(` getPuppeteer failed: ${e.message?.substring(0, 80)}`); - } - const getPuppeteerTime = performance.now() - t; - - if (puppeteer && !this.forceScrollAndMerge) { - this.log(` strategy: PUPPETEER (getPuppeteer: ${getPuppeteerTime.toFixed(0)}ms)`); - t = performance.now(); - const image = await (this.browser as any).call(async () => { - const [current] = await puppeteer.pages(); - return current.screenshot({ fullPage: true, encoding: 'base64' }); - }); - this.log(` puppeteer.screenshot: ${ms(t)}`); - await this.windowScrollTo(scrollPosition); - return image as string; - } - - this.log( - ` strategy: SCROLL-AND-MERGE (puppeteer=${!!puppeteer}, forceScrollAndMerge=${this.forceScrollAndMerge}, getPuppeteer: ${getPuppeteerTime.toFixed(0)}ms)` - ); - // Delegate to parent which implements the scroll-and-merge properly - const result = await super.fullPageScreenshot(); - return result; - } -} - -// ─── Instrumented cropAndCompare ────────────────────────────────────────────── - -interface CropAndCompareResult { - firstImage: Buffer; - secondImage: Buffer; - diffImage: Buffer | null; - isEqual: boolean; - diffPixels: number; -} - -function cropImage(inImage: PNG, rect: { top: number; left: number; width: number; height: number }, pixelRatio = 1) { - const imageWidth = Math.ceil(rect.width * pixelRatio || inImage.width); - const imageHeight = Math.ceil(rect.height * pixelRatio || inImage.height); - const outImage = new PNG({ width: imageWidth, height: imageHeight }); - const safeLeft = Math.max(Math.round(rect.left), 0) * pixelRatio; - const safeTop = Math.max(Math.round(rect.top), 0) * pixelRatio; - const safeWidth = Math.min(imageWidth, inImage.width - safeLeft); - const safeHeight = Math.min(imageHeight, inImage.height - safeTop); - inImage.bitblt(outImage, safeLeft, safeTop, safeWidth, safeHeight, 0, 0); - return outImage; -} - -function packPng(png: PNG): Promise { - return new Promise((resolve, reject) => { - const chunks: Buffer[] = []; - const stream = png.pack(); - stream.on('data', (chunk: Buffer) => chunks.push(chunk)); - stream.on('end', () => resolve(Buffer.concat(chunks))); - stream.on('error', reject); - }); -} - -/** - * Instrumented cropAndCompare with per-step timing logs. - */ -export async function instrumentedCropAndCompare( - firstScreenshot: ScreenshotWithOffset, - secondScreenshot: ScreenshotWithOffset, - label = '' -): Promise { - const t0 = performance.now(); - const prefix = label ? `[${label}] ` : ''; - - // Fast path: raw base64 equality - const rawFirst = (firstScreenshot as any).rawBase64; - const rawSecond = (secondScreenshot as any).rawBase64; - if (rawFirst && rawSecond && rawFirst === rawSecond) { - const t = performance.now(); - const imageBuffer = Buffer.from(rawFirst, 'base64'); - console.log(` ⏱ ${prefix}cropAndCompare: ${ms(t0)} (FAST PATH: rawBase64 identical, decode: ${ms(t)})`); - return { firstImage: imageBuffer, secondImage: imageBuffer, diffImage: null, isEqual: true, diffPixels: 0 }; - } - - // Crop - const pixelRatio = firstScreenshot.pixelRatio || 1; - const size = { - height: Math.round(Math.max(firstScreenshot.height, secondScreenshot.height)), - width: Math.round(Math.max(firstScreenshot.width, secondScreenshot.width)), - }; - const scaledSize = { - width: Math.ceil(size.width * pixelRatio), - height: Math.ceil(size.height * pixelRatio), - }; - - let t = performance.now(); - const firstImage = cropImage( - firstScreenshot.image, - { top: firstScreenshot.offset.top, left: firstScreenshot.offset.left, width: size.width, height: size.height }, - pixelRatio - ); - const secondImage = cropImage( - secondScreenshot.image, - { top: secondScreenshot.offset.top, left: secondScreenshot.offset.left, width: size.width, height: size.height }, - pixelRatio - ); - console.log( - ` ⏱ ${prefix} crop (2 images): ${ms(t)} (${scaledSize.width}x${scaledSize.height}px, src: ${firstScreenshot.image.width}x${firstScreenshot.image.height}px)` - ); - - // Pixel comparison - t = performance.now(); - let diffPixels: number; - let diffImage: PNG | null = null; - if (scaledSize.width === 0 || scaledSize.height === 0) { - diffPixels = -1; - } else if (firstImage.data.equals(secondImage.data)) { - diffPixels = 0; - console.log(` ⏱ ${prefix} compareImages: ${ms(t)} (FAST: pixel buffers identical)`); - } else { - diffImage = new PNG({ width: scaledSize.width, height: scaledSize.height }); - diffPixels = pixelmatch(firstImage.data, secondImage.data, diffImage.data, scaledSize.width, scaledSize.height, { - threshold: 0.01, - }); - console.log(` ⏱ ${prefix} pixelmatch: ${ms(t)} (diffPixels=${diffPixels})`); - } - - // Pack PNGs — skip entirely when images are identical since we can use rawBase64 directly - t = performance.now(); - let firstPacked: Buffer; - let secondPacked: Buffer; - let diffPacked: Buffer | null = null; - - if (diffPixels === 0 && !diffImage) { - // Images are identical — use the raw screenshot PNG directly (it's already a valid PNG). - // This avoids the extremely expensive packPng re-encoding. - const rawFirst = (firstScreenshot as any).rawBase64; - if (rawFirst) { - firstPacked = Buffer.from(rawFirst, 'base64'); - secondPacked = firstPacked; - console.log(` ⏱ ${prefix} packPng SKIPPED (using rawBase64: ${(firstPacked.length / 1024).toFixed(0)}KB)`); - } else { - firstPacked = await packPng(firstImage); - secondPacked = firstPacked; - console.log(` ⏱ ${prefix} packPng (1 image, reused): ${ms(t)}`); - } - } else { - [firstPacked, secondPacked, diffPacked] = await Promise.all([ - packPng(firstImage), - packPng(secondImage), - diffImage ? packPng(diffImage) : Promise.resolve(null), - ]); - console.log(` ⏱ ${prefix} packPng (${diffImage ? 3 : 2} images): ${ms(t)}`); - } - - console.log(` ⏱ ${prefix}cropAndCompare total: ${ms(t0)} (diffPixels=${diffPixels})`); - return { - firstImage: firstPacked, - secondImage: secondPacked, - diffImage: diffPacked, - isEqual: diffPixels >= 0 && diffPixels <= 1, - diffPixels, - }; -} diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 6d337c8cd4..b6823c704e 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { attachment } from 'allure-js-commons'; +import { attachment, step } from 'allure-js-commons'; import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; @@ -172,18 +172,21 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro expect(newPerms.length).toBe(oldPerms.length); - // Compare each permutation individually and attach results. - const permFailures: number[] = []; - const attachmentPromises: Promise[] = []; + // Compare each permutation individually, wrapping each in an Allure step. + let failures = 0; for (let i = 0; i < newPerms.length; i++) { - const permResult = await compareScreenshots(newPerms[i], oldPerms[i]); - attachmentPromises.push(attachDiffImages(permResult, `Permutation #${i.toString().padStart(3, '0')}`)); - if (permResult.diffPixels > tolerance) { - permFailures.push(i); - } + const id = newPerms[i].id || ''; + const index = `#${(i + 1).toString().padStart(3, '0')}`; + await step(`Permutation ${index}`, async () => { + const permResult = await compareScreenshots(newPerms[i], oldPerms[i]); + await attachDiffImages(permResult, index); + if (permResult.diffPixels > tolerance) { + failures++; + throw new Error(`Permutation ${index} differs by ${permResult.diffPixels} pixels\n${id}`); + } + }); } - await Promise.all(attachmentPromises); - expect(permFailures).toEqual([]); + expect(failures).toBe(0); return; } From 6ecd15a0d1603621f5ccd2c9a19106aa0659b474 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Tue, 23 Jun 2026 23:59:38 +0200 Subject: [PATCH 122/135] Refinements --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c6ff9ee628..daaf5e57b0 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,7 +16,7 @@ jobs: # Produces the artifact consumed by deploy, allowing it to start # before the full build+test job finishes. quick-build: - name: quick-build${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} + name: quick-build$ {{format('(React {0})', matrix.react)}} # skip this job for external contributions if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} strategy: @@ -62,7 +62,7 @@ jobs: deploy: needs: quick-build - name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} + name: deploy$ {{format('(React {0})', matrix.react)}} if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} strategy: matrix: From ef0a5d693465e5e92c6b487fb70d17ce6b4c5579 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 25 Jun 2026 17:52:32 +0200 Subject: [PATCH 123/135] Refactor --- test/definitions/optimized-page-object.ts | 122 ------------------ .../definitions/raw-screenshot-page-object.ts | 107 +++++++++++++++ test/definitions/types.ts | 4 +- test/definitions/utils.ts | 18 ++- tsconfig.visual.json | 2 +- 5 files changed, 118 insertions(+), 135 deletions(-) delete mode 100644 test/definitions/optimized-page-object.ts create mode 100644 test/definitions/raw-screenshot-page-object.ts diff --git a/test/definitions/optimized-page-object.ts b/test/definitions/optimized-page-object.ts deleted file mode 100644 index f8299de495..0000000000 --- a/test/definitions/optimized-page-object.ts +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -/** - * Extends ScreenshotPageObject with optimized capture methods that use - * takeElementScreenshot instead of full-page screenshot + crop + re-encode. - * - * This avoids the expensive parsePng/packPng pipeline for identical images - * by returning raw base64 PNGs that can be compared as strings. - */ -import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; - -interface RawScreenshot { - rawBase64: string; - width: number; - height: number; - pixelRatio?: number; - id?: string; -} - -export type { RawScreenshot }; - -export class OptimizedPageObject extends ScreenshotPageObject { - /** - * Captures the screenshot area using takeElementScreenshot. - * Returns the raw base64 PNG without decoding — suitable for fast string comparison. - * Falls back to the parent's captureBySelector if takeElementScreenshot fails. - */ - async captureSelectorRaw(selector: string): Promise { - await this.waitForVisible(selector); - const { pixelRatio } = await this.getViewportSize(); - const box = await this.getBoundingBox(selector); - - try { - const originalWindowSize = await this.browser.getWindowSize(); - // Fit window height to content so the element is fully visible - const dims: { viewportHeight: number; pageHeight: number } = await this.browser.execute(() => ({ - viewportHeight: window.innerHeight, - pageHeight: document.documentElement.scrollHeight, - })); - const windowUIHeight = originalWindowSize.height - dims.viewportHeight; - await this.browser.setWindowSize(originalWindowSize.width, dims.pageHeight + windowUIHeight); - - const element = this.browser.$(selector); - const rawBase64 = await this.browser.takeElementScreenshot(await element.elementId); - - // Restore window size - await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); - - return { rawBase64, pixelRatio, width: box.width, height: box.height }; - } catch { - // Fallback: use full-page screenshot (will need parsePng later if comparison fails) - console.warn('takeElementScreenshot failed, falling back to fullPageScreenshot'); - const rawBase64 = await this.fullPageScreenshot(); - return { rawBase64, pixelRatio, width: box.width, height: box.height }; - } - } - - /** - * Captures a viewport screenshot as raw base64. - */ - async captureViewportRaw(): Promise { - const { height, width } = await this.getViewportSize(); - const rawBase64 = await this.browser.takeScreenshot(); - return { rawBase64, width, height }; - } - - /** - * Captures each permutation element individually using takeElementScreenshot. - * Returns raw base64 PNGs — no decode, no crop, no re-encode. - * Falls back to the parent's capturePermutations if takeElementScreenshot fails. - */ - async capturePermutationsRaw(): Promise { - await this.windowScrollTo({ top: 0, left: 0 }); - - // Fit window height to content - const originalWindowSize = await this.browser.getWindowSize(); - const dims: { viewportHeight: number; pageHeight: number } = await this.browser.execute(() => ({ - viewportHeight: window.innerHeight, - pageHeight: document.documentElement.scrollHeight, - })); - const windowUIHeight = originalWindowSize.height - dims.viewportHeight; - await this.browser.setWindowSize(originalWindowSize.width, dims.pageHeight + windowUIHeight); - - const elements = await this.browser.$$('[data-permutation]'); - if ((await elements.length) === 0) { - await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); - throw new Error('No permutations found on current page.'); - } - - try { - const pixelRatio: number = await this.browser.execute(() => window.devicePixelRatio || 1); - const results: RawScreenshot[] = []; - - for (const element of elements) { - const id = (await element.getAttribute('data-permutation')) || ''; - const rawBase64 = await this.browser.takeElementScreenshot(element.elementId); - const size = await element.getSize(); - results.push({ - id, - rawBase64, - width: size.width * pixelRatio, - height: size.height * pixelRatio, - }); - } - - await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); - return results; - } catch { - console.warn('takeElementScreenshot failed for permutations, falling back to full-page strategy'); - await this.browser.setWindowSize(originalWindowSize.width, originalWindowSize.height); - // Fall back to the parent's approach - const permutations = await super.capturePermutations(); - return permutations.map(p => ({ - id: (p as any).id || '', - rawBase64: '', // Will force slow-path comparison - width: p.width, - height: p.height, - })); - } - } -} diff --git a/test/definitions/raw-screenshot-page-object.ts b/test/definitions/raw-screenshot-page-object.ts new file mode 100644 index 0000000000..b95a5d1705 --- /dev/null +++ b/test/definitions/raw-screenshot-page-object.ts @@ -0,0 +1,107 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// Local copy of RawScreenshotPageObject from browser-test-tools +// (dev-v3-jotresse-optimized-screenshots branch). Once that branch is merged, +// this file can be replaced with a direct import from browser-test-tools. + +import { BasePageObject } from '@cloudscape-design/browser-test-tools/page-objects'; + +/** + * A raw screenshot with base64 data and dimensions. No decoded image, no offset. + */ +export interface RawScreenshot { + rawBase64: string; + width: number; + height: number; + pixelRatio?: number; +} + +/** + * Raw permutation screenshot captured via takeElementScreenshot. + * No decoded image, no offset — just the raw base64 PNG per element. + */ +export interface RawPermutationScreenshot extends RawScreenshot { + id: string; +} + +/** + * A page object that captures screenshots using takeElementScreenshot. + * Returns raw base64 PNGs without decoding, cropping, or re-encoding — + * significantly faster when pixel-level comparison can be done on raw bytes. + */ +export class RawScreenshotPageObject extends BasePageObject { + async captureBySelector(selector: string): Promise { + await this.waitForVisible(selector); + const { pixelRatio } = await this.getViewportSize(); + const box = await this.getBoundingBox(selector); + + const originalWindowSize = await this.fitWindowHeightToContent(); + const element = this.browser.$(selector); + const elementId = await element.elementId; + const rawBase64 = await this.browser.takeElementScreenshot(elementId); + await this.safeSetWindowSize(originalWindowSize.width, originalWindowSize.height); + + return { rawBase64, pixelRatio, height: box.height, width: box.width }; + } + + async captureViewport(): Promise { + const { height, width } = await this.getViewportSize(); + const rawBase64 = await this.browser.takeScreenshot(); + return { rawBase64, height, width }; + } + + async capturePermutations(): Promise { + await this.windowScrollTo({ top: 0, left: 0 }); + + // Adapt viewport height to fit all elements before taking screenshots + const originalWindowSize = await this.fitWindowHeightToContent(); + + const elements = await this.browser.$$('[data-permutation]').map(el => el); + if (elements.length === 0) { + await this.safeSetWindowSize(originalWindowSize.width, originalWindowSize.height); + throw new Error('No permutations found on current page.'); + } + + const { pixelRatio } = await this.getViewportSize(); + const results: RawPermutationScreenshot[] = []; + for (const element of elements) { + const id = (await element.getAttribute('data-permutation')) || ''; + const elementId = await element.elementId; + const rawBase64 = await this.browser.takeElementScreenshot(elementId); + const size = await element.getSize(); + results.push({ + id, + rawBase64, + width: size.width * pixelRatio, + height: size.height * pixelRatio, + }); + } + + await this.safeSetWindowSize(originalWindowSize.width, originalWindowSize.height); + return results; + } + + private async fitWindowHeightToContent(): Promise<{ width: number; height: number }> { + const originalWindowSize = await this.browser.getWindowSize(); + const dims: { viewportHeight: number; pageHeight: number } = await this.browser.execute(() => ({ + viewportHeight: window.innerHeight, + pageHeight: document.documentElement.scrollHeight, + })); + const windowUIHeight = originalWindowSize.height - dims.viewportHeight; + await this.safeSetWindowSize(originalWindowSize.width, dims.pageHeight + windowUIHeight); + return originalWindowSize; + } + + private async safeSetWindowSize(width: number, height: number): Promise { + try { + await this.browser.setWindowSize(width, height); + } catch (error) { + if (error instanceof Error && error.message.includes('Method has not yet been implemented')) { + console.log('setWindowSize is not supported on this device'); + } else { + throw error; + } + } + } +} diff --git a/test/definitions/types.ts b/test/definitions/types.ts index 087fe2c52c..43884f3345 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { Browser } from 'webdriverio'; -import type { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; +import type { BasePageObject } from '@cloudscape-design/browser-test-tools/page-objects'; import type createWrapper from '../../lib/components/test-utils/selectors'; @@ -24,7 +24,7 @@ export interface TestDefinition { queryParams?: Record; configuration?: ScreenshotTestConfiguration; pixelDiffTolerance?: number; - setup?: ({ page, wrapper, browser }: { page: ScreenshotPageObject; wrapper: Wrapper; browser: Browser }) => void; + setup?: ({ page, wrapper, browser }: { page: BasePageObject; wrapper: Wrapper; browser: Browser }) => void; } export interface TestSuite { diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index b6823c704e..72133bc7a2 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -3,12 +3,10 @@ import { attachment, step } from 'allure-js-commons'; import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; -import { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; import createWrapper from '../../lib/components/test-utils/selectors'; -import { OptimizedPageObject, RawScreenshot } from './optimized-page-object'; +import { RawScreenshot, RawScreenshotPageObject } from './raw-screenshot-page-object'; import { TestDefinition, TestSuite } from './types'; - const screenshotAreaSelector = '.screenshot-area'; const defaultWindowSize = { width: 1200, height: 800 }; @@ -60,7 +58,7 @@ async function attachDiffImages(result: CropAndCompareResult, testName: string): */ async function preparePage( browser: WebdriverIO.Browser, - page: ScreenshotPageObject, + page: RawScreenshotPageObject, url: string, testDef: TestDefinition, windowSize?: { width?: number; height?: number } @@ -81,11 +79,11 @@ async function preparePage( * Captures a raw screenshot (base64 + metadata) WITHOUT decoding the PNG. * Uses takeElementScreenshot for the screenshot area to avoid full-page capture. */ -function captureRaw(page: OptimizedPageObject, testDef: TestDefinition): Promise { +function captureRaw(page: RawScreenshotPageObject, testDef: TestDefinition): Promise { if (testDef.screenshotType === 'viewport') { - return page.captureViewportRaw(); + return page.captureViewport(); } - return page.captureSelectorRaw(screenshotAreaSelector); + return page.captureBySelector(screenshotAreaSelector); } /** @@ -157,7 +155,7 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro test(testDef.description, async () => { const tolerance = testDef.pixelDiffTolerance ?? 0; const browser = getBrowser(); - const page = new OptimizedPageObject(browser); + const page = new RawScreenshotPageObject(browser); const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); @@ -165,10 +163,10 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro if (testDef.screenshotType === 'permutations') { // Capture permutations as raw (no PNG decode) await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newPerms = await page.capturePermutationsRaw(); + const newPerms = await page.capturePermutations(); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldPerms = await page.capturePermutationsRaw(); + const oldPerms = await page.capturePermutations(); expect(newPerms.length).toBe(oldPerms.length); diff --git a/tsconfig.visual.json b/tsconfig.visual.json index be61d962ef..2cc46a29b5 100644 --- a/tsconfig.visual.json +++ b/tsconfig.visual.json @@ -1,4 +1,4 @@ { "extends": "./tsconfig.integ.json", - "include": ["test/definitions/utils.ts", "test/visual/**/*.test.ts", "types"] + "include": ["test/definitions/**/*.ts", "test/visual/**/*.test.ts", "types"] } From 2085a99764dafc2b098a0999d0968374a81fa996 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 25 Jun 2026 18:05:35 +0200 Subject: [PATCH 124/135] Syntax fixes --- .github/workflows/deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index daaf5e57b0..833c6ef980 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,7 +16,7 @@ jobs: # Produces the artifact consumed by deploy, allowing it to start # before the full build+test job finishes. quick-build: - name: quick-build$ {{format('(React {0})', matrix.react)}} + name: quick-build $({{format('React {0}', matrix.react)}}) # skip this job for external contributions if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} strategy: @@ -62,7 +62,7 @@ jobs: deploy: needs: quick-build - name: deploy$ {{format('(React {0})', matrix.react)}} + name: deploy $({{format('React {0}', matrix.react)}}) if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} strategy: matrix: From d007cbdab6e30737bdd41a0aa19130ae7f1d70db Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 25 Jun 2026 18:05:57 +0200 Subject: [PATCH 125/135] Remove test definitions out of scope --- .../visual/app-layout-content-paddings.ts | 30 - test/definitions/visual/app-layout-drawers.ts | 38 - .../definitions/visual/app-layout-flashbar.ts | 39 -- test/definitions/visual/app-layout-header.ts | 85 --- test/definitions/visual/app-layout-multi.ts | 25 - .../visual/app-layout-responsive-1280.ts | 7 - .../visual/app-layout-responsive-1400.ts | 7 - .../visual/app-layout-responsive-1920.ts | 7 - .../visual/app-layout-responsive-2540.ts | 7 - .../visual/app-layout-responsive-600.ts | 7 - .../visual/app-layout-responsive-tests.ts | 159 ----- ...-layout-sticky-table-header-split-panel.ts | 78 --- test/definitions/visual/app-layout-toolbar.ts | 23 - test/definitions/visual/app-layout-z-index.ts | 60 -- test/definitions/visual/app-layout.ts | 104 --- test/definitions/visual/area-chart.ts | 136 ---- test/definitions/visual/attribute-editor.ts | 31 - test/definitions/visual/autosuggest.ts | 67 -- test/definitions/visual/badge.ts | 23 - test/definitions/visual/bar-chart.ts | 99 --- test/definitions/visual/box.ts | 43 -- test/definitions/visual/breadcrumb-group.ts | 46 -- test/definitions/visual/button-dropdown.ts | 395 ----------- test/definitions/visual/button-group.ts | 52 -- test/definitions/visual/button.ts | 73 -- test/definitions/visual/cards.ts | 49 -- test/definitions/visual/checkbox.ts | 37 - test/definitions/visual/code-editor.ts | 69 -- .../visual/collection-preferences.ts | 82 --- test/definitions/visual/column-layout.ts | 61 -- test/definitions/visual/container-sticky.ts | 125 ---- test/definitions/visual/container.ts | 87 --- .../visual/content-layout-permutations.ts | 661 ------------------ test/definitions/visual/content-layout.ts | 98 --- test/definitions/visual/copy-to-clipboard.ts | 28 - test/definitions/visual/date-input.ts | 23 - test/definitions/visual/date-picker.ts | 61 -- test/definitions/visual/date-range-picker.ts | 121 ---- test/definitions/visual/divider.ts | 18 - test/definitions/visual/drawer.ts | 105 --- test/definitions/visual/dropdown.ts | 83 --- test/definitions/visual/expandable-section.ts | 124 ---- test/definitions/visual/file-dropzone.ts | 18 - test/definitions/visual/file-input.ts | 18 - test/definitions/visual/file-token-group.ts | 18 - test/definitions/visual/file-upload.ts | 18 - test/definitions/visual/flashbar-stacked.ts | 125 ---- test/definitions/visual/flashbar.ts | 67 -- test/definitions/visual/form-field.ts | 61 -- test/definitions/visual/form.ts | 23 - test/definitions/visual/grid.ts | 37 - test/definitions/visual/header.ts | 67 -- test/definitions/visual/help-panel.ts | 28 - test/definitions/visual/icon.ts | 69 -- test/definitions/visual/input.ts | 23 - test/definitions/visual/item-card.ts | 33 - test/definitions/visual/key-value-pairs.ts | 29 - test/definitions/visual/line-chart.ts | 156 ----- test/definitions/visual/link.ts | 38 - test/definitions/visual/list.ts | 23 - .../visual/mixed-line-bar-chart.ts | 110 --- test/definitions/visual/modal.ts | 163 ----- test/definitions/visual/multiselect.ts | 136 ---- test/definitions/visual/pagination.ts | 18 - test/definitions/visual/pie-chart.ts | 66 -- test/definitions/visual/popover.ts | 197 ------ test/definitions/visual/progress-bar.ts | 31 - test/definitions/visual/prompt-input.ts | 46 -- test/definitions/visual/property-filter.ts | 144 ---- test/definitions/visual/radio-button.ts | 42 -- test/definitions/visual/radio-group.ts | 49 -- .../visual/s3-resource-selector.ts | 38 - test/definitions/visual/segmented-control.ts | 23 - test/definitions/visual/select.ts | 96 --- test/definitions/visual/side-navigation.ts | 18 - test/definitions/visual/slider.ts | 19 - test/definitions/visual/space-between.ts | 28 - test/definitions/visual/spinner.ts | 23 - test/definitions/visual/split-panel.ts | 93 --- test/definitions/visual/status-indicator.ts | 18 - test/definitions/visual/steps.ts | 18 - test/definitions/visual/table-cells.ts | 20 - test/definitions/visual/table-embedded.ts | 23 - .../visual/table-inline-editing.ts | 54 -- .../visual/table-resizable-columns.ts | 69 -- .../definitions/visual/table-sticky-header.ts | 44 -- .../visual/table-sticky-scrollbar.ts | 31 - test/definitions/visual/table.ts | 130 ---- test/definitions/visual/tabs.ts | 80 --- test/definitions/visual/tag-editor.ts | 18 - test/definitions/visual/text-content.ts | 33 - test/definitions/visual/text-filter.ts | 18 - test/definitions/visual/textarea.ts | 77 -- test/definitions/visual/tiles.ts | 23 - test/definitions/visual/time-input.ts | 18 - test/definitions/visual/toggle-button.ts | 26 - test/definitions/visual/toggle.ts | 37 - test/definitions/visual/token-group.ts | 23 - test/definitions/visual/top-navigation.ts | 54 -- test/definitions/visual/tree-view.ts | 28 - test/definitions/visual/wizard.ts | 42 -- .../app-layout-content-paddings.test.ts | 6 - test/visual/app-layout-drawers.test.ts | 6 - test/visual/app-layout-flashbar.test.ts | 6 - test/visual/app-layout-header.test.ts | 6 - test/visual/app-layout-multi.test.ts | 6 - .../visual/app-layout-responsive-1280.test.ts | 6 - .../visual/app-layout-responsive-1400.test.ts | 6 - .../visual/app-layout-responsive-1920.test.ts | 6 - .../visual/app-layout-responsive-2540.test.ts | 6 - test/visual/app-layout-responsive-600.test.ts | 6 - ...ut-sticky-table-header-split-panel.test.ts | 6 - test/visual/app-layout-toolbar.test.ts | 6 - test/visual/app-layout-z-index.test.ts | 6 - test/visual/app-layout.test.ts | 6 - test/visual/area-chart.test.ts | 6 - test/visual/attribute-editor.test.ts | 6 - test/visual/autosuggest.test.ts | 6 - test/visual/badge.test.ts | 6 - test/visual/bar-chart.test.ts | 6 - test/visual/box.test.ts | 6 - test/visual/breadcrumb-group.test.ts | 6 - test/visual/button-dropdown.test.ts | 6 - test/visual/button-group.test.ts | 6 - test/visual/button.test.ts | 6 - test/visual/cards.test.ts | 6 - test/visual/checkbox.test.ts | 6 - test/visual/code-editor.test.ts | 6 - test/visual/collection-preferences.test.ts | 6 - test/visual/column-layout.test.ts | 6 - test/visual/container-sticky.test.ts | 6 - test/visual/container.test.ts | 6 - .../content-layout-permutations.test.ts | 6 - test/visual/content-layout.test.ts | 6 - test/visual/copy-to-clipboard.test.ts | 6 - test/visual/date-input.test.ts | 6 - test/visual/date-picker.test.ts | 6 - test/visual/date-range-picker.test.ts | 6 - test/visual/divider.test.ts | 6 - test/visual/drawer.test.ts | 6 - test/visual/dropdown.test.ts | 6 - test/visual/expandable-section.test.ts | 6 - test/visual/file-dropzone.test.ts | 6 - test/visual/file-input.test.ts | 6 - test/visual/file-token-group.test.ts | 6 - test/visual/file-upload.test.ts | 6 - test/visual/flashbar-stacked.test.ts | 6 - test/visual/flashbar.test.ts | 6 - test/visual/form-field.test.ts | 6 - test/visual/form.test.ts | 6 - test/visual/grid.test.ts | 6 - test/visual/header.test.ts | 6 - test/visual/help-panel.test.ts | 6 - test/visual/icon.test.ts | 6 - test/visual/input.test.ts | 6 - test/visual/item-card.test.ts | 6 - test/visual/key-value-pairs.test.ts | 6 - test/visual/line-chart.test.ts | 6 - test/visual/link.test.ts | 6 - test/visual/list.test.ts | 6 - test/visual/mixed-line-bar-chart.test.ts | 6 - test/visual/modal.test.ts | 6 - test/visual/multiselect.test.ts | 6 - test/visual/pagination.test.ts | 6 - test/visual/pie-chart.test.ts | 6 - test/visual/popover.test.ts | 6 - test/visual/progress-bar.test.ts | 6 - test/visual/prompt-input.test.ts | 6 - test/visual/property-filter.test.ts | 6 - test/visual/radio-button.test.ts | 6 - test/visual/radio-group.test.ts | 6 - test/visual/s3-resource-selector.test.ts | 6 - test/visual/segmented-control.test.ts | 6 - test/visual/select.test.ts | 6 - test/visual/side-navigation.test.ts | 6 - test/visual/slider.test.ts | 6 - test/visual/space-between.test.ts | 6 - test/visual/spinner.test.ts | 6 - test/visual/split-panel.test.ts | 6 - test/visual/status-indicator.test.ts | 6 - test/visual/steps.test.ts | 6 - test/visual/table-cells.test.ts | 6 - test/visual/table-embedded.test.ts | 6 - test/visual/table-inline-editing.test.ts | 6 - test/visual/table-resizable-columns.test.ts | 6 - test/visual/table-sticky-header.test.ts | 6 - test/visual/table-sticky-scrollbar.test.ts | 6 - test/visual/table.test.ts | 6 - test/visual/tabs.test.ts | 6 - test/visual/tag-editor.test.ts | 6 - test/visual/text-content.test.ts | 6 - test/visual/text-filter.test.ts | 6 - test/visual/textarea.test.ts | 6 - test/visual/tiles.test.ts | 6 - test/visual/time-input.test.ts | 6 - test/visual/toggle-button.test.ts | 6 - test/visual/toggle.test.ts | 6 - test/visual/token-group.test.ts | 6 - test/visual/top-navigation.test.ts | 6 - test/visual/tree-view.test.ts | 6 - test/visual/wizard.test.ts | 6 - 201 files changed, 7148 deletions(-) delete mode 100644 test/definitions/visual/app-layout-content-paddings.ts delete mode 100644 test/definitions/visual/app-layout-drawers.ts delete mode 100644 test/definitions/visual/app-layout-flashbar.ts delete mode 100644 test/definitions/visual/app-layout-header.ts delete mode 100644 test/definitions/visual/app-layout-multi.ts delete mode 100644 test/definitions/visual/app-layout-responsive-1280.ts delete mode 100644 test/definitions/visual/app-layout-responsive-1400.ts delete mode 100644 test/definitions/visual/app-layout-responsive-1920.ts delete mode 100644 test/definitions/visual/app-layout-responsive-2540.ts delete mode 100644 test/definitions/visual/app-layout-responsive-600.ts delete mode 100644 test/definitions/visual/app-layout-responsive-tests.ts delete mode 100644 test/definitions/visual/app-layout-sticky-table-header-split-panel.ts delete mode 100644 test/definitions/visual/app-layout-toolbar.ts delete mode 100644 test/definitions/visual/app-layout-z-index.ts delete mode 100644 test/definitions/visual/app-layout.ts delete mode 100644 test/definitions/visual/area-chart.ts delete mode 100644 test/definitions/visual/attribute-editor.ts delete mode 100644 test/definitions/visual/autosuggest.ts delete mode 100644 test/definitions/visual/badge.ts delete mode 100644 test/definitions/visual/bar-chart.ts delete mode 100644 test/definitions/visual/box.ts delete mode 100644 test/definitions/visual/breadcrumb-group.ts delete mode 100644 test/definitions/visual/button-dropdown.ts delete mode 100644 test/definitions/visual/button-group.ts delete mode 100644 test/definitions/visual/button.ts delete mode 100644 test/definitions/visual/cards.ts delete mode 100644 test/definitions/visual/checkbox.ts delete mode 100644 test/definitions/visual/code-editor.ts delete mode 100644 test/definitions/visual/collection-preferences.ts delete mode 100644 test/definitions/visual/column-layout.ts delete mode 100644 test/definitions/visual/container-sticky.ts delete mode 100644 test/definitions/visual/container.ts delete mode 100644 test/definitions/visual/content-layout-permutations.ts delete mode 100644 test/definitions/visual/content-layout.ts delete mode 100644 test/definitions/visual/copy-to-clipboard.ts delete mode 100644 test/definitions/visual/date-input.ts delete mode 100644 test/definitions/visual/date-picker.ts delete mode 100644 test/definitions/visual/date-range-picker.ts delete mode 100644 test/definitions/visual/divider.ts delete mode 100644 test/definitions/visual/drawer.ts delete mode 100644 test/definitions/visual/dropdown.ts delete mode 100644 test/definitions/visual/expandable-section.ts delete mode 100644 test/definitions/visual/file-dropzone.ts delete mode 100644 test/definitions/visual/file-input.ts delete mode 100644 test/definitions/visual/file-token-group.ts delete mode 100644 test/definitions/visual/file-upload.ts delete mode 100644 test/definitions/visual/flashbar-stacked.ts delete mode 100644 test/definitions/visual/flashbar.ts delete mode 100644 test/definitions/visual/form-field.ts delete mode 100644 test/definitions/visual/form.ts delete mode 100644 test/definitions/visual/grid.ts delete mode 100644 test/definitions/visual/header.ts delete mode 100644 test/definitions/visual/help-panel.ts delete mode 100644 test/definitions/visual/icon.ts delete mode 100644 test/definitions/visual/input.ts delete mode 100644 test/definitions/visual/item-card.ts delete mode 100644 test/definitions/visual/key-value-pairs.ts delete mode 100644 test/definitions/visual/line-chart.ts delete mode 100644 test/definitions/visual/link.ts delete mode 100644 test/definitions/visual/list.ts delete mode 100644 test/definitions/visual/mixed-line-bar-chart.ts delete mode 100644 test/definitions/visual/modal.ts delete mode 100644 test/definitions/visual/multiselect.ts delete mode 100644 test/definitions/visual/pagination.ts delete mode 100644 test/definitions/visual/pie-chart.ts delete mode 100644 test/definitions/visual/popover.ts delete mode 100644 test/definitions/visual/progress-bar.ts delete mode 100644 test/definitions/visual/prompt-input.ts delete mode 100644 test/definitions/visual/property-filter.ts delete mode 100644 test/definitions/visual/radio-button.ts delete mode 100644 test/definitions/visual/radio-group.ts delete mode 100644 test/definitions/visual/s3-resource-selector.ts delete mode 100644 test/definitions/visual/segmented-control.ts delete mode 100644 test/definitions/visual/select.ts delete mode 100644 test/definitions/visual/side-navigation.ts delete mode 100644 test/definitions/visual/slider.ts delete mode 100644 test/definitions/visual/space-between.ts delete mode 100644 test/definitions/visual/spinner.ts delete mode 100644 test/definitions/visual/split-panel.ts delete mode 100644 test/definitions/visual/status-indicator.ts delete mode 100644 test/definitions/visual/steps.ts delete mode 100644 test/definitions/visual/table-cells.ts delete mode 100644 test/definitions/visual/table-embedded.ts delete mode 100644 test/definitions/visual/table-inline-editing.ts delete mode 100644 test/definitions/visual/table-resizable-columns.ts delete mode 100644 test/definitions/visual/table-sticky-header.ts delete mode 100644 test/definitions/visual/table-sticky-scrollbar.ts delete mode 100644 test/definitions/visual/table.ts delete mode 100644 test/definitions/visual/tabs.ts delete mode 100644 test/definitions/visual/tag-editor.ts delete mode 100644 test/definitions/visual/text-content.ts delete mode 100644 test/definitions/visual/text-filter.ts delete mode 100644 test/definitions/visual/textarea.ts delete mode 100644 test/definitions/visual/tiles.ts delete mode 100644 test/definitions/visual/time-input.ts delete mode 100644 test/definitions/visual/toggle-button.ts delete mode 100644 test/definitions/visual/toggle.ts delete mode 100644 test/definitions/visual/token-group.ts delete mode 100644 test/definitions/visual/top-navigation.ts delete mode 100644 test/definitions/visual/tree-view.ts delete mode 100644 test/definitions/visual/wizard.ts delete mode 100644 test/visual/app-layout-content-paddings.test.ts delete mode 100644 test/visual/app-layout-drawers.test.ts delete mode 100644 test/visual/app-layout-flashbar.test.ts delete mode 100644 test/visual/app-layout-header.test.ts delete mode 100644 test/visual/app-layout-multi.test.ts delete mode 100644 test/visual/app-layout-responsive-1280.test.ts delete mode 100644 test/visual/app-layout-responsive-1400.test.ts delete mode 100644 test/visual/app-layout-responsive-1920.test.ts delete mode 100644 test/visual/app-layout-responsive-2540.test.ts delete mode 100644 test/visual/app-layout-responsive-600.test.ts delete mode 100644 test/visual/app-layout-sticky-table-header-split-panel.test.ts delete mode 100644 test/visual/app-layout-toolbar.test.ts delete mode 100644 test/visual/app-layout-z-index.test.ts delete mode 100644 test/visual/app-layout.test.ts delete mode 100644 test/visual/area-chart.test.ts delete mode 100644 test/visual/attribute-editor.test.ts delete mode 100644 test/visual/autosuggest.test.ts delete mode 100644 test/visual/badge.test.ts delete mode 100644 test/visual/bar-chart.test.ts delete mode 100644 test/visual/box.test.ts delete mode 100644 test/visual/breadcrumb-group.test.ts delete mode 100644 test/visual/button-dropdown.test.ts delete mode 100644 test/visual/button-group.test.ts delete mode 100644 test/visual/button.test.ts delete mode 100644 test/visual/cards.test.ts delete mode 100644 test/visual/checkbox.test.ts delete mode 100644 test/visual/code-editor.test.ts delete mode 100644 test/visual/collection-preferences.test.ts delete mode 100644 test/visual/column-layout.test.ts delete mode 100644 test/visual/container-sticky.test.ts delete mode 100644 test/visual/container.test.ts delete mode 100644 test/visual/content-layout-permutations.test.ts delete mode 100644 test/visual/content-layout.test.ts delete mode 100644 test/visual/copy-to-clipboard.test.ts delete mode 100644 test/visual/date-input.test.ts delete mode 100644 test/visual/date-picker.test.ts delete mode 100644 test/visual/date-range-picker.test.ts delete mode 100644 test/visual/divider.test.ts delete mode 100644 test/visual/drawer.test.ts delete mode 100644 test/visual/dropdown.test.ts delete mode 100644 test/visual/expandable-section.test.ts delete mode 100644 test/visual/file-dropzone.test.ts delete mode 100644 test/visual/file-input.test.ts delete mode 100644 test/visual/file-token-group.test.ts delete mode 100644 test/visual/file-upload.test.ts delete mode 100644 test/visual/flashbar-stacked.test.ts delete mode 100644 test/visual/flashbar.test.ts delete mode 100644 test/visual/form-field.test.ts delete mode 100644 test/visual/form.test.ts delete mode 100644 test/visual/grid.test.ts delete mode 100644 test/visual/header.test.ts delete mode 100644 test/visual/help-panel.test.ts delete mode 100644 test/visual/icon.test.ts delete mode 100644 test/visual/input.test.ts delete mode 100644 test/visual/item-card.test.ts delete mode 100644 test/visual/key-value-pairs.test.ts delete mode 100644 test/visual/line-chart.test.ts delete mode 100644 test/visual/link.test.ts delete mode 100644 test/visual/list.test.ts delete mode 100644 test/visual/mixed-line-bar-chart.test.ts delete mode 100644 test/visual/modal.test.ts delete mode 100644 test/visual/multiselect.test.ts delete mode 100644 test/visual/pagination.test.ts delete mode 100644 test/visual/pie-chart.test.ts delete mode 100644 test/visual/popover.test.ts delete mode 100644 test/visual/progress-bar.test.ts delete mode 100644 test/visual/prompt-input.test.ts delete mode 100644 test/visual/property-filter.test.ts delete mode 100644 test/visual/radio-button.test.ts delete mode 100644 test/visual/radio-group.test.ts delete mode 100644 test/visual/s3-resource-selector.test.ts delete mode 100644 test/visual/segmented-control.test.ts delete mode 100644 test/visual/select.test.ts delete mode 100644 test/visual/side-navigation.test.ts delete mode 100644 test/visual/slider.test.ts delete mode 100644 test/visual/space-between.test.ts delete mode 100644 test/visual/spinner.test.ts delete mode 100644 test/visual/split-panel.test.ts delete mode 100644 test/visual/status-indicator.test.ts delete mode 100644 test/visual/steps.test.ts delete mode 100644 test/visual/table-cells.test.ts delete mode 100644 test/visual/table-embedded.test.ts delete mode 100644 test/visual/table-inline-editing.test.ts delete mode 100644 test/visual/table-resizable-columns.test.ts delete mode 100644 test/visual/table-sticky-header.test.ts delete mode 100644 test/visual/table-sticky-scrollbar.test.ts delete mode 100644 test/visual/table.test.ts delete mode 100644 test/visual/tabs.test.ts delete mode 100644 test/visual/tag-editor.test.ts delete mode 100644 test/visual/text-content.test.ts delete mode 100644 test/visual/text-filter.test.ts delete mode 100644 test/visual/textarea.test.ts delete mode 100644 test/visual/tiles.test.ts delete mode 100644 test/visual/time-input.test.ts delete mode 100644 test/visual/toggle-button.test.ts delete mode 100644 test/visual/toggle.test.ts delete mode 100644 test/visual/token-group.test.ts delete mode 100644 test/visual/top-navigation.test.ts delete mode 100644 test/visual/tree-view.test.ts delete mode 100644 test/visual/wizard.test.ts diff --git a/test/definitions/visual/app-layout-content-paddings.ts b/test/definitions/visual/app-layout-content-paddings.ts deleted file mode 100644 index e76d055749..0000000000 --- a/test/definitions/visual/app-layout-content-paddings.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Content paddings', - componentName: 'app-layout', - tests: [ - ...(['true', 'false'] as const).flatMap(toolsEnabled => - (['true', 'false'] as const).flatMap(splitPanelEnabled => - (['bottom', 'side'] as const).map(splitPanelPosition => ({ - description: `toolsEnabled=${toolsEnabled} splitPanelEnabled=${splitPanelEnabled} splitPanelPosition=${splitPanelPosition}`, - path: 'app-layout/with-split-panel', - screenshotType: 'viewport' as const, - queryParams: { toolsEnabled, splitPanelEnabled, splitPanelPosition }, - })) - ) - ), - ...[1500, 600].map(width => ({ - description: `with split panel and disabled content paddings - width=${width}`, - path: 'app-layout/disable-paddings-with-split-panel', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { splitPanelOpen: 'true', splitPanelPosition: 'side' }, - })), - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-drawers.ts b/test/definitions/visual/app-layout-drawers.ts deleted file mode 100644 index b9c97388a5..0000000000 --- a/test/definitions/visual/app-layout-drawers.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Drawers', - componentName: 'app-layout', - tests: [ - { - description: 'with split panel', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); - }, - }, - { - description: 'with tooltip on hover', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - setup: async ({ page, wrapper }) => { - await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); - }, - }, - { - description: 'with custom scrollable drawer content', - path: 'app-layout/with-drawers-scrollable', - screenshotType: 'viewport', - queryParams: { sideNavFill: 'false' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-flashbar.ts b/test/definitions/visual/app-layout-flashbar.ts deleted file mode 100644 index bf402ebbed..0000000000 --- a/test/definitions/visual/app-layout-flashbar.ts +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Flashbar', - componentName: 'app-layout', - tests: [true, false].flatMap(disableContentPaddings => - [true, false].flatMap(stickyNotifications => - [true, false].flatMap(stickyTableHeader => - [true, false].map(stackNotifications => ({ - description: `disableContentPaddings: ${disableContentPaddings}, stickyNotifications: ${stickyNotifications}, stickyTableHeader: ${stickyTableHeader}, stackNotifications: ${stackNotifications}`, - path: 'app-layout/with-stacked-notifications-and-table', - screenshotType: 'screenshotArea' as const, - configuration: { width: 1280, height: 900 }, - setup: async ({ page }) => { - if (!disableContentPaddings) { - await page.click('[data-id="toggle-content-paddings"]'); - } - if (stickyNotifications) { - await page.click('[data-id="toggle-sticky-notifications"]'); - } - if (!stickyTableHeader) { - await page.click('[data-id="toggle-sticky-table-header"]'); - } - if (!stackNotifications) { - await page.click('[data-id="toggle-stack-items"]'); - } - await page.click('[data-id="add-notification"]'); - await page.click('[data-id="add-notification"]'); - }, - })) - ) - ) - ), -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-header.ts b/test/definitions/visual/app-layout-header.ts deleted file mode 100644 index e9d5fbac98..0000000000 --- a/test/definitions/visual/app-layout-header.ts +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestDefinition, TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Headers', - componentName: 'app-layout', - tests: [ - // ── Headers ─────────────────────────────────────────────────────────── - { - description: 'Headers', - tests: [600, 1280].flatMap(width => [ - { - description: `alignment with full-page table (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - { - description: `alignment with full-page table in sticky state (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'viewport' as const, - configuration: { width }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `alignment with full-page table in sticky state with sticky notifications (${width}px)`, - path: 'app-layout/with-table', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { stickyNotifications: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `high contrast header variant in landing page (${width}px)`, - path: 'app-layout/landing-page', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - ]), - }, - - // ── High contrast header variant ────────────────────────────────────── - { - description: 'High contrast header variant', - tests: [ - ...[1400, 600].flatMap(width => [ - { - description: `with breadcrumbs and notifications at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', hasContainer: 'true' }, - } as TestDefinition, - { - description: `without overlap at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { disableOverlap: 'true' }, - } as TestDefinition, - { - description: `with content layout at ${width}px`, - path: 'app-layout/high-contrast-header-variant', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - hasContainer: 'true', - hasContentLayout: 'true', - }, - } as TestDefinition, - ]), - ], - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-multi.ts b/test/definitions/visual/app-layout-multi.ts deleted file mode 100644 index babf9733cf..0000000000 --- a/test/definitions/visual/app-layout-multi.ts +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Multiple instances', - componentName: 'app-layout', - tests: [600, 1280].flatMap(width => [ - { - description: `simple (${width}px)`, - path: 'app-layout/multi-layout-simple', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - { - description: `iframe (${width}px)`, - path: 'app-layout/multi-layout-iframe', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - ]), -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1280.ts b/test/definitions/visual/app-layout-responsive-1280.ts deleted file mode 100644 index d5fe823f54..0000000000 --- a/test/definitions/visual/app-layout-responsive-1280.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(1280); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1400.ts b/test/definitions/visual/app-layout-responsive-1400.ts deleted file mode 100644 index 1cb519005c..0000000000 --- a/test/definitions/visual/app-layout-responsive-1400.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(1400); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1920.ts b/test/definitions/visual/app-layout-responsive-1920.ts deleted file mode 100644 index 88b8c6caf3..0000000000 --- a/test/definitions/visual/app-layout-responsive-1920.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(1920); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-2540.ts b/test/definitions/visual/app-layout-responsive-2540.ts deleted file mode 100644 index 9dd62c00db..0000000000 --- a/test/definitions/visual/app-layout-responsive-2540.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(2540); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-600.ts b/test/definitions/visual/app-layout-responsive-600.ts deleted file mode 100644 index f6fd3665cc..0000000000 --- a/test/definitions/visual/app-layout-responsive-600.ts +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { responsiveTests } from './app-layout-responsive-tests'; - -const suite = responsiveTests(600); -export default suite; diff --git a/test/definitions/visual/app-layout-responsive-tests.ts b/test/definitions/visual/app-layout-responsive-tests.ts deleted file mode 100644 index 2243f87a2a..0000000000 --- a/test/definitions/visual/app-layout-responsive-tests.ts +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -/** - * Shared responsive test scenarios for app-layout. Each width gets its own - * definition file and test runner so that Jest sharding can parallelize them. - */ -export function responsiveTests(width: number): TestSuite { - return { - description: `AppLayout responsive width ${width}px`, - componentName: 'app-layout', - tests: [ - { - description: 'default', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'navigation drawer is open', - path: 'app-layout/with-wizard', - screenshotType: 'viewport', - configuration: { width }, - setup: async ({ page }) => { - await page.click('[aria-label="Open navigation"]'); - }, - }, - { - description: 'wizard', - path: 'app-layout/with-wizard', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with wizard and table', - path: 'app-layout/with-wizard-and-table', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with wizard, table, and breadcrumbs', - path: 'app-layout/with-wizard-and-table', - screenshotType: 'viewport', - configuration: { width }, - queryParams: { hasBreadcrumbs: 'true' }, - }, - { - description: 'notifications', - path: 'app-layout/with-notifications', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'breadcrumbs', - path: 'app-layout/with-breadcrumbs', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'notifications and breadcrumbs', - path: 'app-layout/with-breadcrumbs-notifications', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'dashboard content type', - path: 'app-layout/dashboard-content-type', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'fixed header and footer', - path: 'app-layout/with-fixed-header-footer', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disableBodyScroll - empty', - path: 'app-layout/legacy-nav-empty', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disableBodyScroll - with content', - path: 'app-layout/legacy-nav-scrollable', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disableBodyScroll - with split panel', - path: 'app-layout/legacy-nav-scrollable-with-split-panel', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disable paddings', - path: 'app-layout/disable-paddings', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'disable paddings with breadcrumbs', - path: 'app-layout/disable-paddings-breadcrumbs', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'sticky notifications', - path: 'app-layout/with-sticky-notifications', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'sticky notifications scrolled down', - path: 'app-layout/with-sticky-notifications', - screenshotType: 'viewport', - configuration: { width }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 2000 }); - }, - }, - { - description: 'layout without panels', - path: 'app-layout/no-panels', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'layout without panels but with notifications', - path: 'app-layout/no-panels-with-notifications', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with drawers', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with empty drawers', - path: 'app-layout/with-drawers-empty', - screenshotType: 'viewport', - configuration: { width }, - }, - { - description: 'with open drawer', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - configuration: { width }, - setup: async ({ page }) => { - await page.click('[aria-label="Security trigger button"]'); - }, - }, - ], - }; -} diff --git a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts deleted file mode 100644 index 3bc920d22e..0000000000 --- a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Sticky header with split panel', - componentName: 'app-layout', - tests: [ - { - description: 'scrolling to bottom with closed split panel (1 table row)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async ({ page }) => { - await page.click('[data-testid="set-item-count-to-1"]'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'scrolling to bottom with closed split panel (30 table rows)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async ({ page }) => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky with open split panel (1 table row)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async ({ page }) => { - await page.click('[data-testid="set-item-count-to-1"]'); - await page.click('aria/Open panel'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky with open split panel (30 table rows)', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async ({ page }) => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.click('aria/Open panel'); - await page.scrollToBottom('html'); - }, - }, - { - description: 'header stays sticky when mounting and unmounting a second table', - path: 'app-layout/with-sticky-table-and-split-panel', - screenshotType: 'viewport', - configuration: { width: 1280, height: 900 }, - setup: async ({ page }) => { - await page.click('[data-testid="set-item-count-to-30"]'); - await page.click('aria/Open panel'); - await page.windowScrollTo({ top: 0 }); - await page.click('aria/Close panel'); - await page.scrollToBottom('html'); - }, - }, - // ── Max content width ───────────────────────────────────────────────── - { - description: 'maxContentWidth set to Number.MAX_VALUE', - path: 'app-layout/refresh-content-width', - screenshotType: 'viewport', - configuration: { width: 1280, height: 700 }, - setup: async ({ page }) => { - await page.click('[data-test-id="button_width-number-max_value"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-toolbar.ts b/test/definitions/visual/app-layout-toolbar.ts deleted file mode 100644 index fb174b0a71..0000000000 --- a/test/definitions/visual/app-layout-toolbar.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Toolbar', - componentName: 'app-layout', - tests: [ - { - description: 'multiple nested instances (no breadcrumbs dedup)', - path: 'app-layout-toolbar/multi-layout-with-hidden-instances', - screenshotType: 'viewport', - }, - { - description: 'no toolbar', - path: 'app-layout-toolbar/without-toolbar', - screenshotType: 'viewport', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout-z-index.ts b/test/definitions/visual/app-layout-z-index.ts deleted file mode 100644 index 149b2b8f36..0000000000 --- a/test/definitions/visual/app-layout-z-index.ts +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestDefinition, TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Z-index', - componentName: 'app-layout', - tests: [ - ...[600, 1280].flatMap(width => [ - { - description: `button dropdown (${width}px)`, - path: 'app-layout/with-absolute-components', - screenshotType: 'viewport' as const, - configuration: { width }, - setup: async ({ page }) => { - await page.click('button=Button dropdown'); - await page.click('[data-testid="2"]'); - await page.windowScrollTo({ top: 300 }); - }, - } as TestDefinition, - { - description: `select (${width}px)`, - path: 'app-layout/with-absolute-components', - screenshotType: 'viewport' as const, - configuration: { width, height: 800 }, - setup: async ({ page }) => { - await page.click('[data-testid="select-demo"] button'); - await page.windowScrollTo({ top: 300 }); - }, - } as TestDefinition, - { - description: `split-panel and full-page table (${width}px)`, - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, - configuration: { width }, - }, - ]), - { - description: 'split-panel and full-page with open navigation (600px)', - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, - configuration: { width: 600 }, - setup: async ({ page }) => { - await page.click('button[aria-label="Open navigation"]'); - }, - }, - { - description: 'split-panel and full-page with open tools (600px)', - path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, - configuration: { width: 600 }, - setup: async ({ page }) => { - await page.click('button[aria-label="Open tools"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts deleted file mode 100644 index 3a8453c47a..0000000000 --- a/test/definitions/visual/app-layout.ts +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'AppLayout', - componentName: 'app-layout', - tests: [ - { - description: 'no scrollbars at 320px', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width: 320 }, - }, - { - description: 'drawer buttons alignment', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width: 800 }, - setup: async ({ page }) => { - await page.click('[aria-label="Open tools"]'); - }, - }, - { - description: 'disable paddings - navigation closed', - path: 'app-layout/disable-paddings', - screenshotType: 'viewport', - configuration: { width: 1280 }, - setup: async ({ page }) => { - await page.click('[aria-label="Close navigation"]'); - }, - }, - { - description: 'panels stacking on mobile', - path: 'app-layout/all-panels-open', - screenshotType: 'viewport', - configuration: { width: 600 }, - }, - { - description: 'wrapping long words', - path: 'app-layout/text-wrap', - screenshotType: 'viewport', - }, - { - description: 'fill content area', - path: 'app-layout/fill-content-area', - screenshotType: 'viewport', - }, - { - description: 'with tools and drawers', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - queryParams: { hasTools: 'true' }, - }, - { - description: 'with open drawer and open side split panel', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - configuration: { width: 1400 }, - queryParams: { splitPanelPosition: 'side' }, - setup: async ({ page }) => { - await page.click('[aria-label="Security trigger button"]'); - await page.click('[aria-label="Open panel"]'); - }, - }, - - // regression for https://github.com/cloudscape-design/components/pull/1612 - { - description: 'with open drawer and open side split panel after resize', - path: 'app-layout/with-drawers', - screenshotType: 'viewport', - configuration: { width: 1500 }, - queryParams: { splitPanelPosition: 'side' }, - setup: async ({ page }) => { - await page.click('[aria-label="Security trigger button"]'); - await page.click('[aria-label="Open panel"]'); - await page.setWindowSize({ width: 1400, height: 800 }); - }, - }, - - // ── Transitions ─────────────────────────────────────────────────────── - { - description: 'transition from 400px to 1800px', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width: 400, height: 400 }, - setup: async ({ page }) => { - await page.setWindowSize({ width: 1800, height: 400 }); - }, - }, - { - description: 'transition from 1800px to 400px', - path: 'app-layout/default', - screenshotType: 'viewport', - configuration: { width: 1800, height: 400 }, - setup: async ({ page }) => { - await page.setWindowSize({ width: 400, height: 400 }); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/area-chart.ts b/test/definitions/visual/area-chart.ts deleted file mode 100644 index 2d11756416..0000000000 --- a/test/definitions/visual/area-chart.ts +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const TEST_CHART_FILTER_TRIGGER = '#linear-latency-chart button'; -const TEST_CHART_TOOLTIP_HEADER = '#linear-latency-chart h2'; - -const suite: TestSuite = { - description: 'Area chart', - componentName: 'area-chart', - tests: [ - { - description: 'permutations', - path: 'area-chart/permutations', - screenshotType: 'permutations', - }, - { - description: 'fit-height', - path: 'area-chart/fit-height', - screenshotType: 'screenshotArea', - }, - { - description: 'fit-height no filter, no legend', - path: 'area-chart/fit-height', - screenshotType: 'screenshotArea', - queryParams: { hideFilter: 'true', hideLegend: 'true' }, - }, - { - description: 'fit-height, no legend', - path: 'area-chart/fit-height', - screenshotType: 'screenshotArea', - queryParams: { hideLegend: 'true' }, - }, - { - description: 'chart plot has a focus outline', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - }, - }, - { - description: 'can navigate along X axis highlighting all series with keyboard', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'can navigate a specific series with keyboard', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowDown']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'selects correct series when navigated back from legend', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.keys(['Tab']); - await page.keys(['Tab']); - await page.keys(['ArrowRight']); - await page.keys(['Shift', 'Tab']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'can pin popover for all data points at a given X coordinate with keyboard', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - await page.keys(['Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - { - description: 'can pin popover for a point in a specific series with keyboard', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowDown']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - await page.keys(['Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - { - description: 'shows popover on hover', - path: 'area-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.hoverElement('[aria-label="Linear latency chart"]', 200, 50); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/attribute-editor.ts b/test/definitions/visual/attribute-editor.ts deleted file mode 100644 index 33da3fad86..0000000000 --- a/test/definitions/visual/attribute-editor.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Attribute Editor', - componentName: 'attribute-editor', - tests: [360, 768, 992].flatMap(width => [ - { - description: `permutations at ${width}px`, - path: 'attribute-editor/permutations', - screenshotType: 'permutations' as const, - configuration: { width }, - }, - { - description: `customizable-footer at ${width}px`, - path: 'attribute-editor/customizable-footer', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - { - description: `with long select at ${width}px`, - path: 'attribute-editor/select-with-long-value', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - ]), -}; - -export default suite; diff --git a/test/definitions/visual/autosuggest.ts b/test/definitions/visual/autosuggest.ts deleted file mode 100644 index b93cdf3b8d..0000000000 --- a/test/definitions/visual/autosuggest.ts +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestDefinition, TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Autosuggest', - componentName: 'autosuggest', - tests: [ - { - description: 'permutations', - path: 'autosuggest/permutations', - screenshotType: 'permutations', - setup: async ({ page }) => { - await page.click('input'); - }, - }, - { - description: 'permutations for async properties', - path: 'autosuggest/permutations-async', - screenshotType: 'permutations', - setup: async ({ page }) => { - await page.click('input'); - }, - }, - { - description: 'Displays options with groups correctly', - path: 'autosuggest/scenarios', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('input'); - }, - }, - { - description: 'Correctly displays dropdown regions', - path: 'autosuggest/regions-scenarios', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('input'); - }, - }, - { - description: 'Long virtual list - navigate to last item', - path: 'autosuggest/virtual-scroll', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); - await page.keys(['ArrowUp']); - }, - }, - ...[true, false].map( - virtualScroll => - ({ - description: `with custom renderOption (virtualScroll=${virtualScroll})`, - path: 'autosuggest/custom-render-option', - screenshotType: 'screenshotArea', - queryParams: { virtualScroll: String(virtualScroll) }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); - await page.keys(['ArrowDown']); - }, - }) as TestDefinition - ), - ], -}; - -export default suite; diff --git a/test/definitions/visual/badge.ts b/test/definitions/visual/badge.ts deleted file mode 100644 index 7d405ed843..0000000000 --- a/test/definitions/visual/badge.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Badge', - componentName: 'badge', - tests: [ - { - description: 'permutation page', - path: 'badge/permutations', - screenshotType: 'permutations', - }, - { - description: 'style custom page', - path: 'badge/style-custom-types', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/bar-chart.ts b/test/definitions/visual/bar-chart.ts deleted file mode 100644 index dde6ef5969..0000000000 --- a/test/definitions/visual/bar-chart.ts +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const TEST_CHART_TOOLTIP_HEADER = '#chart'; - -const suite: TestSuite = { - description: 'Bar chart', - componentName: 'bar-chart', - tests: [ - { - description: 'Horizontal bars permutations', - path: 'bar-chart/horizontal-bars-permutations', - screenshotType: 'permutations', - }, - { - description: 'Horizontal stacked bars permutations', - path: 'bar-chart/horizontal-stacked-bars-permutations', - screenshotType: 'permutations', - }, - { - description: 'Other permutations', - path: 'bar-chart/other-permutations', - screenshotType: 'permutations', - }, - { - description: 'Threshold permutations', - path: 'bar-chart/threshold-permutations', - screenshotType: 'permutations', - }, - { - description: 'Vertical bars permutations', - path: 'bar-chart/vertical-bars-permutations', - screenshotType: 'permutations', - }, - { - description: 'Vertical stacked bars permutations', - path: 'bar-chart/vertical-stacked-bars-permutations', - screenshotType: 'permutations', - }, - { - description: 'can navigate series with keyboard', - path: 'bar-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - pixelDiffTolerance: 12, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'can pin popover with keyboard', - path: 'bar-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - pixelDiffTolerance: 10, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - await page.keys(['Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - { - description: 'shows popover on hover', - path: 'bar-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.hoverElement('#chart svg[aria-label="Bar chart"]'); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'wrapping long series title 123', - path: 'bar-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.scrollToBottom('html'); - await page.click('#focus-target-3'); - await page.focusNextElement(); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/box.ts b/test/definitions/visual/box.ts deleted file mode 100644 index 0de10d4318..0000000000 --- a/test/definitions/visual/box.ts +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Box', - componentName: 'box', - tests: [ - { - description: 'variants permutations', - path: 'box/variants', - screenshotType: 'permutations', - }, - { - description: 'margins permutations', - path: 'box/margins', - screenshotType: 'permutations', - }, - { - description: 'paddings permutations', - path: 'box/paddings', - screenshotType: 'permutations', - }, - { - description: 'float and textAlign', - path: 'box/float-align', - screenshotType: 'permutations', - }, - { - description: 'with overrides to layout defaults', - path: 'box/elements-with-extra-defaults', - screenshotType: 'screenshotArea', - }, - { - description: 'with icons in content', - path: 'box/iconography', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/breadcrumb-group.ts b/test/definitions/visual/breadcrumb-group.ts deleted file mode 100644 index cf5a3013d7..0000000000 --- a/test/definitions/visual/breadcrumb-group.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'BreadcrumbGroup', - componentName: 'breadcrumb-group', - tests: [ - { - description: 'layout at 300px', - path: 'breadcrumb-group/scenarios', - screenshotType: 'screenshotArea', - configuration: { width: 300 }, - }, - { - description: 'layout at 680px', - path: 'breadcrumb-group/scenarios', - screenshotType: 'screenshotArea', - configuration: { width: 680 }, - }, - { - description: 'layout at 1200px', - path: 'breadcrumb-group/scenarios', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - }, - { - description: 'dropdown', - path: 'breadcrumb-group/scenarios', - screenshotType: 'viewport', - configuration: { width: 300, height: 1000 }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findBreadcrumbGroup('[data-testid="breadcrumbs-6"]').findDropdown().toSelector()); - }, - }, - { - description: 'responsive behavior', - path: 'breadcrumb-group/responsive', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/button-dropdown.ts b/test/definitions/visual/button-dropdown.ts deleted file mode 100644 index 04c8c4c5c7..0000000000 --- a/test/definitions/visual/button-dropdown.ts +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'ButtonDropdown', - componentName: 'button-dropdown', - tests: [ - { - description: 'ButtonDropdown opening top left at 500', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 500 }, - setup: async ({ page }) => { - await page.click('.bd-top-left'); - }, - }, - { - description: 'ButtonDropdown opening top right at 500', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 500 }, - setup: async ({ page }) => { - await page.click('.bd-top-right'); - }, - }, - { - description: 'ButtonDropdown opening bottom left at 500', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 500 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-left'); - }, - }, - { - description: 'ButtonDropdown opening bottom right at 500', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 500 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-right'); - }, - }, - { - description: 'ButtonDropdown opening top left at 800', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 800 }, - setup: async ({ page }) => { - await page.click('.bd-top-left'); - }, - }, - { - description: 'ButtonDropdown opening top right at 800', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 800 }, - setup: async ({ page }) => { - await page.click('.bd-top-right'); - }, - }, - { - description: 'ButtonDropdown opening bottom left at 800', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 800 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-left'); - }, - }, - { - description: 'ButtonDropdown opening bottom right at 800', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 800 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-right'); - }, - }, - { - description: 'ButtonDropdown opening top left, width maximum truncated', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 230, height: 400 }, - setup: async ({ page }) => { - await page.click('.bd-top-left'); - }, - }, - { - description: 'ButtonDropdown opening bottom left, width maximum truncated', - path: 'button-dropdown/scenarios-positioning', - screenshotType: 'viewport', - configuration: { width: 230, height: 400 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-left'); - }, - }, - { - description: 'ButtonDropdown with expandable groups opening top left at 500', - path: 'button-dropdown/scenarios-expandable', - screenshotType: 'viewport', - configuration: { width: 500 }, - setup: async ({ page }) => { - await page.click('.bd-top-left'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandable groups opening top right at 500', - path: 'button-dropdown/scenarios-expandable', - screenshotType: 'viewport', - configuration: { width: 500 }, - setup: async ({ page }) => { - await page.click('.bd-top-right'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandable groups opening bottom left at 500', - path: 'button-dropdown/scenarios-expandable', - screenshotType: 'viewport', - configuration: { width: 500 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-left'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandable groups opening bottom right at 500', - path: 'button-dropdown/scenarios-expandable', - screenshotType: 'viewport', - configuration: { width: 500 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-right'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandable groups opening top left at 1200', - path: 'button-dropdown/scenarios-expandable', - screenshotType: 'viewport', - configuration: { width: 1200 }, - setup: async ({ page }) => { - await page.click('.bd-top-left'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandable groups opening top right at 1200', - path: 'button-dropdown/scenarios-expandable', - screenshotType: 'viewport', - configuration: { width: 1200 }, - setup: async ({ page }) => { - await page.click('.bd-top-right'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandable groups opening bottom left at 1200', - path: 'button-dropdown/scenarios-expandable', - screenshotType: 'viewport', - configuration: { width: 1200 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-left'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandable groups opening bottom right at 1200', - path: 'button-dropdown/scenarios-expandable', - screenshotType: 'viewport', - configuration: { width: 1200 }, - setup: async ({ page }) => { - await page.click('.bd-bottom-right'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown in scrollable container', - path: 'button-dropdown/scenarios-container', - screenshotType: 'viewport', - configuration: { width: 600 }, - setup: async ({ page }) => { - await page.waitForVisible('#scrollable-container'); - const containerBBox = await page.getBoundingBox('#scrollable-container'); - const buttonBBox = await page.getBoundingBox('#ButtonDropdown button'); - await page.elementScrollTo('#scrollable-container', { - top: (containerBBox.height + buttonBBox.width) / 2, - left: (containerBBox.width + buttonBBox.width) / 2, - }); - await page.click('#ButtonDropdown'); - }, - }, - { - description: 'ButtonDropdown with expandToViewport overflowing a scroll container', - path: 'button-dropdown/scenarios-overflow-container', - screenshotType: 'viewport', - configuration: { width: 1000 }, - setup: async ({ page }) => { - await page.waitForVisible('#scroll-container'); - const containerBBox = await page.getBoundingBox('#scroll-container'); - const buttonBBox = await page.getBoundingBox('#button-dropdown-scroll button'); - await page.elementScrollTo('#scroll-container', { - top: (containerBBox.height + buttonBBox.width) / 2, - left: (containerBBox.width + buttonBBox.width) / 2, - }); - await page.click('#button-dropdown-scroll'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandToViewport overflowing a hidden container', - path: 'button-dropdown/scenarios-overflow-container', - screenshotType: 'viewport', - configuration: { width: 1000 }, - setup: async ({ page }) => { - await page.waitForVisible('#hidden-container'); - await page.click('#button-dropdown-hidden'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown with expandToViewport overflowing an auto container', - path: 'button-dropdown/scenarios-overflow-container', - screenshotType: 'viewport', - configuration: { width: 1000 }, - setup: async ({ page }) => { - await page.waitForVisible('#auto-container'); - const containerBBox = await page.getBoundingBox('#auto-container'); - const buttonBBox = await page.getBoundingBox('#button-dropdown-auto button'); - await page.elementScrollTo('#auto-container', { - top: (containerBBox.height + buttonBBox.width) / 2, - left: (containerBBox.width + buttonBBox.width) / 2, - }); - await page.click('#button-dropdown-auto'); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown permutations', - path: 'button-dropdown/permutations', - screenshotType: 'permutations', - }, - { - description: 'ButtonDropdown item element permutations', - path: 'button-dropdown/item-element.permutations', - screenshotType: 'permutations', - }, - { - description: 'ButtonDropdown main action permutations', - path: 'button-dropdown/permutations-main-action', - screenshotType: 'permutations', - }, - { - description: 'ButtonDropdown dimmed category group at width 500', - path: 'button-dropdown/simple', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#ButtonDropdown8'); - await page.keys(['ArrowDown', 'ArrowDown', 'Enter']); - }, - }, - { - description: 'ButtonDropdown dimmed category group at width 800', - path: 'button-dropdown/simple', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#ButtonDropdown8'); - await page.keys(['ArrowDown', 'ArrowDown', 'Enter']); - }, - }, - { - description: 'ButtonDropdown with disabled reason at width 500', - path: 'button-dropdown/disabled-reason', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-testid="buttonDropdown"]'); - await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown']); - }, - }, - { - description: 'ButtonDropdown with disabled reason at width 800', - path: 'button-dropdown/disabled-reason', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-testid="buttonDropdown"]'); - await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown']); - }, - }, - { - description: 'ButtonDropdown with disabled reason for selectable item at width 500', - path: 'button-dropdown/disabled-reason', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-testid="buttonDropdownSelectableItems"]'); - }, - }, - { - description: 'ButtonDropdown with disabled reason for selectable item at width 800', - path: 'button-dropdown/disabled-reason', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-testid="buttonDropdownSelectableItems"]'); - }, - }, - { - description: 'ButtonDropdown expandable groups show icons at width 500', - path: 'button-dropdown/icon-expandable', - screenshotType: 'screenshotArea', - configuration: { width: 500 }, - queryParams: { expandableGroups: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findButtonDropdown().toSelector()); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown non-expandable groups show icons at width 500', - path: 'button-dropdown/icon-expandable', - screenshotType: 'screenshotArea', - configuration: { width: 500 }, - queryParams: { expandableGroups: 'false' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findButtonDropdown().toSelector()); - }, - }, - { - description: 'ButtonDropdown expandable groups show icons at width 800', - path: 'button-dropdown/icon-expandable', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - queryParams: { expandableGroups: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findButtonDropdown().toSelector()); - await page.click('[data-testid="category1"]'); - }, - }, - { - description: 'ButtonDropdown non-expandable groups show icons at width 800', - path: 'button-dropdown/icon-expandable', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - queryParams: { expandableGroups: 'false' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findButtonDropdown().toSelector()); - }, - }, - { - description: 'ButtonDropdown expandable groups with custom renderItem width 500', - path: 'button-dropdown/custom-render-item', - screenshotType: 'screenshotArea', - configuration: { width: 500 }, - queryParams: { expandableGroups: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findButtonDropdown().toSelector()); - await page.click('[data-testid="group"]'); - }, - }, - { - description: 'ButtonDropdown non-expandable groups with custom renderItem width 500', - path: 'button-dropdown/custom-render-item', - screenshotType: 'screenshotArea', - configuration: { width: 500 }, - queryParams: { expandableGroups: 'false' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findButtonDropdown().toSelector()); - }, - }, - { - description: 'ButtonDropdown expandable groups with custom renderItem width 1000', - path: 'button-dropdown/custom-render-item', - screenshotType: 'screenshotArea', - configuration: { width: 1000 }, - queryParams: { expandableGroups: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findButtonDropdown().toSelector()); - await page.click('[data-testid="group"]'); - }, - }, - { - description: 'ButtonDropdown non-expandable groups with custom renderItem width 1000', - path: 'button-dropdown/custom-render-item', - screenshotType: 'screenshotArea', - configuration: { width: 1000 }, - queryParams: { expandableGroups: 'false' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findButtonDropdown().toSelector()); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/button-group.ts b/test/definitions/visual/button-group.ts deleted file mode 100644 index 839f19c19e..0000000000 --- a/test/definitions/visual/button-group.ts +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'ButtonGroup', - componentName: 'button-group', - tests: [ - { - description: 'item permutations', - path: 'button-group/item-permutations', - screenshotType: 'permutations', - }, - { - description: 'permutations', - path: 'button-group/permutations', - screenshotType: 'permutations', - }, - { - description: 'shows tooltip when hovering item', - path: 'button-group/test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.hoverElement('[data-testid="like"]'); - }, - }, - { - description: 'shows tooltip when hovering menu', - path: 'button-group/test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.hoverElement('[data-testid="more-actions"]'); - }, - }, - { - description: 'shows feedback when clicking copy', - path: 'button-group/test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.hoverElement('[data-testid="copy"]'); - }, - }, - { - description: 'style custom page', - path: 'button-group/style-custom-types', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/button.ts b/test/definitions/visual/button.ts deleted file mode 100644 index b6b8645764..0000000000 --- a/test/definitions/visual/button.ts +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Button', - componentName: 'button', - tests: [ - { - description: 'permutations', - path: 'button/permutations', - screenshotType: 'permutations', - }, - { - description: 'style-permutations', - path: 'button/style-permutations', - screenshotType: 'permutations', - }, - { - description: 'style-custom-types', - path: 'button/style-custom-types', - screenshotType: 'screenshotArea', - }, - { - description: 'external', - path: 'button/external.permutations', - screenshotType: 'permutations', - }, - { - description: 'alignment', - path: 'button/alignment', - screenshotType: 'screenshotArea', - }, - { - description: 'wrapping text', - path: 'button/text-wrap', - screenshotType: 'screenshotArea', - }, - { - description: 'wrapping text with icon', - path: 'button/with-icon-wrap', - screenshotType: 'screenshotArea', - }, - { - description: 'Button is focused', - path: 'button/tab-navigation', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focusButton'); - await page.focusNextElement(); - }, - }, - { - description: 'shows disabled reason tooltip on hover within modal', - path: 'button/disabled-reason-modal', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.hoverElement('[data-testid="button"]'); - }, - }, - { - description: 'shows disabled reason tooltip on hover over a button with an href', - path: 'button/disabled-reason', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.hoverElement('[data-testid="normal-button-with-href"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/cards.ts b/test/definitions/visual/cards.ts deleted file mode 100644 index 25227eedb1..0000000000 --- a/test/definitions/visual/cards.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Cards', - componentName: 'cards', - tests: [ - { - description: 'permutations at 2200', - path: 'cards/permutations', - screenshotType: 'permutations', - configuration: { width: 2200 }, - }, - { - description: 'permutations at 1920', - path: 'cards/permutations', - screenshotType: 'permutations', - configuration: { width: 1920 }, - }, - { - description: 'permutations at 1400', - path: 'cards/permutations', - screenshotType: 'permutations', - configuration: { width: 1400 }, - }, - { - description: 'permutations at 1200', - path: 'cards/permutations', - screenshotType: 'permutations', - configuration: { width: 1200 }, - }, - { - description: 'permutations at 992', - path: 'cards/permutations', - screenshotType: 'permutations', - configuration: { width: 992 }, - }, - { - description: 'permutations at 768', - path: 'cards/permutations', - screenshotType: 'permutations', - configuration: { width: 768 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/checkbox.ts b/test/definitions/visual/checkbox.ts deleted file mode 100644 index 9f87723cf8..0000000000 --- a/test/definitions/visual/checkbox.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Checkbox', - componentName: 'checkbox', - tests: [ - { - description: 'Permutations', - path: 'checkbox/permutations', - screenshotType: 'permutations', - }, - { - description: 'Checkbox is focused', - path: 'checkbox/focus-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'Checkbox has label with a correct width', - path: 'checkbox/labels-highlight', - screenshotType: 'screenshotArea', - }, - { - description: 'Style custom page', - path: 'checkbox/style-custom', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts deleted file mode 100644 index 0494b68bd8..0000000000 --- a/test/definitions/visual/code-editor.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const ACE_SELECTOR = '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright'; -const timeout = 20_000; - -const suite: TestSuite = { - description: 'Code editor', - componentName: 'code-editor', - tests: [ - { - description: 'simple', - path: 'code-editor/simple', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR, true, timeout); - }, - }, - { - description: 'error', - path: 'code-editor/error', - screenshotType: 'screenshotArea', - }, - { - description: 'loading', - path: 'code-editor/loading', - screenshotType: 'screenshotArea', - }, - { - description: 'theme resolution', - path: 'code-editor/themes', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR, true, timeout); - }, - }, - { - description: 'permutations', - path: 'code-editor/permutations', - screenshotType: 'permutations', - setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR + ' .ace_error'); - await page.waitForVisible('.ace_gutter-cell.ace_gutter-active-line.ace_error', true, timeout); - }, - }, - { - description: 'listens to mode change', - path: 'code-editor/simple', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR, true, timeout); - await page.click('#mode-toggle'); - }, - }, - { - description: 'compare simple on small screen', - path: 'code-editor/simple', - screenshotType: 'screenshotArea', - configuration: { width: 360 }, - setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR, true, timeout); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/collection-preferences.ts b/test/definitions/visual/collection-preferences.ts deleted file mode 100644 index a12ad8b545..0000000000 --- a/test/definitions/visual/collection-preferences.ts +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'CollectionPreferences', - componentName: 'collection-preferences', - tests: [ - { - description: 'complete at 600x1100', - path: 'collection-preferences/simple', - screenshotType: 'viewport', - configuration: { width: 600, height: 1100 }, - setup: async ({ page }) => { - await page.click('.cp-1 button'); - }, - }, - { - description: 'visible content only at 600x1100', - path: 'collection-preferences/simple', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('.cp-4 button'); - }, - }, - { - description: 'complete at 1280x700', - path: 'collection-preferences/simple', - screenshotType: 'viewport', - configuration: { width: 1280, height: 700 }, - setup: async ({ page }) => { - await page.click('.cp-1 button'); - }, - }, - { - description: 'visible content only at 1280x700', - path: 'collection-preferences/simple', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('.cp-4 button'); - }, - }, - { - description: 'custom', - path: 'collection-preferences/simple', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('.cp-2 button'); - }, - }, - { - description: 'Content reordering', - componentName: 'collection-preferences', - tests: [ - { - description: 'drag handle focused', - path: 'collection-preferences/reorder-content', - screenshotType: 'viewport', - configuration: { width: 900, height: 650 }, - setup: async ({ page }) => { - await page.click('.cp-1 button'); - await page.keys(Array(5).fill('Tab')); - }, - }, - { - description: 'reordering active', - path: 'collection-preferences/reorder-content', - screenshotType: 'viewport', - configuration: { width: 900, height: 650 }, - setup: async ({ page }) => { - await page.click('.cp-1 button'); - await page.keys(Array(5).fill('Tab')); - await page.keys('Space'); - }, - }, - ], - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/column-layout.ts b/test/definitions/visual/column-layout.ts deleted file mode 100644 index a889050b9b..0000000000 --- a/test/definitions/visual/column-layout.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'ColumnLayout', - componentName: 'column-layout', - tests: [ - { - description: 'column-layout at "default"', - path: 'column-layout/simple', - screenshotType: 'screenshotArea', - configuration: { width: 400 }, - }, - { - description: 'permutations at "default"', - path: 'column-layout/permutations', - screenshotType: 'permutations', - configuration: { width: 400 }, - }, - { - description: 'column-layout at "xxs"', - path: 'column-layout/simple', - screenshotType: 'screenshotArea', - configuration: { width: 500 }, - }, - { - description: 'permutations at "xxs"', - path: 'column-layout/permutations', - screenshotType: 'permutations', - configuration: { width: 500 }, - }, - { - description: 'column-layout at "xs"', - path: 'column-layout/simple', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - }, - { - description: 'permutations at "xs"', - path: 'column-layout/permutations', - screenshotType: 'permutations', - configuration: { width: 800 }, - }, - { - description: 'column-layout at "m"', - path: 'column-layout/simple', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - }, - { - description: 'permutations at "m"', - path: 'column-layout/permutations', - screenshotType: 'permutations', - configuration: { width: 1200 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/container-sticky.ts b/test/definitions/visual/container-sticky.ts deleted file mode 100644 index a322883a50..0000000000 --- a/test/definitions/visual/container-sticky.ts +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Container sticky permutations', - componentName: 'container', - tests: [ - { - description: 'simple - at 1400px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 1400 }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'with notifications - at 1400px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 1400 }, - queryParams: { hasNotifications: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'with breadcrumbs - at 1400px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 1400 }, - queryParams: { hasBreadcrumbs: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'with an alert - at 1400px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 1400 }, - queryParams: { hasNotifications: 'true', hasAlert: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'with an alert - at 1400px without scroll', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 1400 }, - queryParams: { hasNotifications: 'true', hasAlert: 'true' }, - }, - { - description: 'with high-contrast header - at 1400px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 1400 }, - queryParams: { hasNotifications: 'true', highContrast: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'simple - at 600px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 600 }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'with notifications - at 600px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 600 }, - queryParams: { hasNotifications: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'with breadcrumbs - at 600px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 600 }, - queryParams: { hasBreadcrumbs: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'with an alert - at 600px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 600 }, - queryParams: { hasNotifications: 'true', hasAlert: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: 'with an alert - at 600px without scroll', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 600 }, - queryParams: { hasNotifications: 'true', hasAlert: 'true' }, - }, - { - description: 'with high-contrast header - at 600px', - path: 'container/sticky-permutations', - screenshotType: 'viewport', - configuration: { width: 600 }, - queryParams: { hasNotifications: 'true', highContrast: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/container.ts b/test/definitions/visual/container.ts deleted file mode 100644 index a0ac94e8d0..0000000000 --- a/test/definitions/visual/container.ts +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Container and header', - componentName: 'container', - tests: [ - { - description: 'simple', - path: 'container/simple', - screenshotType: 'screenshotArea', - }, - { - description: 'fit height with footer', - path: 'container/fit-height', - screenshotType: 'screenshotArea', - }, - { - description: 'fit height without footer', - path: 'container/fit-height', - screenshotType: 'screenshotArea', - queryParams: { hideFooters: 'true' }, - }, - { - description: 'correctly displays container with side media', - path: 'container/media', - screenshotType: 'screenshotArea', - queryParams: { position: 'side', width: '33%', content: '16-9' }, - }, - { - description: 'correctly displays container with top media', - path: 'container/media', - screenshotType: 'screenshotArea', - queryParams: { position: 'top', height: '150px', content: '4-3' }, - }, - { - description: 'media with position: side permutations at 465', - path: 'container/media-side-permutations', - screenshotType: 'permutations', - configuration: { width: 465 }, - }, - { - description: 'media with position: side permutations at 688', - path: 'container/media-side-permutations', - screenshotType: 'permutations', - configuration: { width: 688 }, - }, - { - description: 'media with position: side permutations at 1120', - path: 'container/media-side-permutations', - screenshotType: 'permutations', - configuration: { width: 1120 }, - }, - { - description: 'media with position: top permutations at 465', - path: 'container/media-top-permutations', - screenshotType: 'permutations', - configuration: { width: 465 }, - }, - { - description: 'media with position: top permutations at 688', - path: 'container/media-top-permutations', - screenshotType: 'permutations', - configuration: { width: 688 }, - }, - { - description: 'media with position: top permutations at 1120', - path: 'container/media-top-permutations', - screenshotType: 'permutations', - configuration: { width: 1120 }, - }, - { - description: 'stacked', - path: 'container/stacked-components', - screenshotType: 'screenshotArea', - }, - { - description: 'style-custom', - path: 'container/style-custom', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/content-layout-permutations.ts b/test/definitions/visual/content-layout-permutations.ts deleted file mode 100644 index 4f58d1a70d..0000000000 --- a/test/definitions/visual/content-layout-permutations.ts +++ /dev/null @@ -1,661 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'ContentLayout permutations', - componentName: 'content-layout', - tests: [ - // default headerVariant - { - description: 'default headerVariant, none headerBackgroundStyle, at 1400, simple', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'default headerVariant, none headerBackgroundStyle, at 1400, with notifications and breadcrumbs', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'default headerVariant, none headerBackgroundStyle, at 600, simple', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'default headerVariant, none headerBackgroundStyle, at 600, with notifications and breadcrumbs', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'default headerVariant, none headerBackgroundStyle, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'none', - maxContentWidth: '1000', - }, - }, - // default headerVariant, gradient - { - description: 'default headerVariant, gradient headerBackgroundStyle, at 1400, simple', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'gradient', - }, - }, - { - description: 'default headerVariant, gradient headerBackgroundStyle, at 1400, with notifications and breadcrumbs', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'gradient', - }, - }, - { - description: 'default headerVariant, gradient headerBackgroundStyle, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'gradient', - maxContentWidth: '1000', - }, - }, - // high-contrast headerVariant, none - { - description: 'high-contrast headerVariant, none headerBackgroundStyle, at 1400, simple', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - }, - }, - { - description: - 'high-contrast headerVariant, none headerBackgroundStyle, at 1400, with notifications and breadcrumbs', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'high-contrast headerVariant, none headerBackgroundStyle, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - maxContentWidth: '1000', - }, - }, - // high-contrast headerVariant, gradient - { - description: 'high-contrast headerVariant, gradient headerBackgroundStyle, at 1400, simple', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'gradient', - }, - }, - { - description: - 'high-contrast headerVariant, gradient headerBackgroundStyle, at 1400, with notifications and breadcrumbs', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'gradient', - }, - }, - { - description: 'high-contrast headerVariant, gradient headerBackgroundStyle, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'gradient', - maxContentWidth: '1000', - }, - }, - // divider headerVariant - { - description: 'divider headerVariant, at 1400, simple', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'divider', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'divider headerVariant, at 1400, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'true', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'false', - defaultPadding: 'true', - headerVariant: 'divider', - headerBackgroundStyle: 'none', - maxContentWidth: '1000', - }, - }, - { - description: 'divider headerVariant, at 600, simple', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'divider', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'divider headerVariant, at 600, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'true', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'false', - defaultPadding: 'true', - headerVariant: 'divider', - headerBackgroundStyle: 'none', - maxContentWidth: '1000', - }, - }, - // with app layout - { - description: 'with app layout, divider headerVariant, at 1400, simple', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'true', - hasAppLayout: 'true', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'false', - defaultPadding: 'true', - headerVariant: 'divider', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'with app layout, high-contrast gradient, at 1400, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'true', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'gradient', - maxContentWidth: '800', - }, - }, - { - description: 'with open navigation, without maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'true', - hasAppLayoutWithOpenNavigation: 'true', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'with open navigation, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'true', - hasNotifications: 'true', - disableOverlap: 'false', - hasAppLayout: 'true', - hasAppLayoutWithOpenNavigation: 'true', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'divider', - headerBackgroundStyle: 'none', - maxContentWidth: '700', - }, - }, - // with secondary header - { - description: 'with secondary header, at 1400', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'true', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'with secondary header, at 600', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'true', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'with secondary header, with maxContentWidth', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'true', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - maxContentWidth: '900', - }, - }, - // without header - { - description: 'without header, at 1400, defaultPadding=true, default headerVariant', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'true', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'default', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'without header, at 1400, defaultPadding=true, high-contrast headerVariant', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'true', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'without header, at 1400, defaultPadding=true, divider headerVariant', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'true', - hasContainer: 'true', - defaultPadding: 'true', - headerVariant: 'divider', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'without header, at 1400, defaultPadding=false, default headerVariant', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'true', - hasContainer: 'true', - defaultPadding: 'false', - headerVariant: 'default', - headerBackgroundStyle: 'none', - }, - }, - // without default padding - { - description: 'without default padding, at 1400', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'false', - headerVariant: 'default', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'without default padding, at 600', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'false', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'true', - defaultPadding: 'false', - headerVariant: 'default', - headerBackgroundStyle: 'none', - }, - }, - // with disabled overlap - { - description: 'with disabled overlap, at 1400', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'true', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'false', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - }, - }, - { - description: 'with disabled overlap, at 600', - path: 'content-layout/permutations', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { - hasBreadcrumbs: 'false', - hasNotifications: 'false', - disableOverlap: 'true', - hasAppLayout: 'false', - hasAppLayoutWithOpenNavigation: 'false', - hasSecondaryHeader: 'false', - removeHeader: 'false', - hasContainer: 'false', - defaultPadding: 'true', - headerVariant: 'high-contrast', - headerBackgroundStyle: 'none', - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/content-layout.ts b/test/definitions/visual/content-layout.ts deleted file mode 100644 index ea88b5de4c..0000000000 --- a/test/definitions/visual/content-layout.ts +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'ContentLayout', - componentName: 'content-layout', - tests: [ - { - description: 'fill content area', - path: 'content-layout/fill-content-area', - screenshotType: 'screenshotArea', - }, - { - description: 'standalone', - path: 'content-layout/standalone', - screenshotType: 'screenshotArea', - }, - { - description: 'with absolute components', - path: 'content-layout/with-absolute-components', - screenshotType: 'screenshotArea', - }, - { - description: 'form with form header', - path: 'content-layout/with-header-toggles', - screenshotType: 'screenshotArea', - }, - { - description: 'form with content layout header', - path: 'content-layout/with-header-toggles', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-testid="toggle-form-header"] input'); - await page.click('[data-testid="toggle-content-layout"] input'); - }, - }, - { - description: 'without header - at 1400 without breadcrumbs, without notifications, with overlap', - path: 'content-layout/without-header', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'false' }, - }, - { - description: 'without header - at 1400 with breadcrumbs, without notifications, with overlap', - path: 'content-layout/without-header', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'false', disableOverlap: 'false' }, - }, - { - description: 'without header - at 1400 without breadcrumbs, with notifications, with overlap', - path: 'content-layout/without-header', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'true', disableOverlap: 'false' }, - }, - { - description: 'without header - at 1400 with breadcrumbs, with notifications, with overlap', - path: 'content-layout/without-header', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'false' }, - }, - { - description: 'without header - at 1400 without breadcrumbs, without notifications, without overlap', - path: 'content-layout/without-header', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'true' }, - }, - { - description: 'without header - at 1400 with breadcrumbs, with notifications, without overlap', - path: 'content-layout/without-header', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'true' }, - }, - { - description: 'without header - at 600 without breadcrumbs, without notifications, with overlap', - path: 'content-layout/without-header', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'false' }, - }, - { - description: 'without header - at 600 with breadcrumbs, with notifications, with overlap', - path: 'content-layout/without-header', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'false' }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/copy-to-clipboard.ts b/test/definitions/visual/copy-to-clipboard.ts deleted file mode 100644 index 250ecbb6f4..0000000000 --- a/test/definitions/visual/copy-to-clipboard.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'CopyToClipboard', - componentName: 'copy-to-clipboard', - tests: [ - { - description: 'Variants', - path: 'copy-to-clipboard/simple', - screenshotType: 'screenshotArea', - }, - { - description: 'copy-to-clipboard below bottom split panel is not visible', - path: 'copy-to-clipboard/scenario-split-panel', - screenshotType: 'screenshotArea', - configuration: { width: 1280, height: 900 }, - setup: async ({ page, browser }) => { - await page.click('[aria-label="Copy dummy text"]'); - browser.$('[data-testid="scroll-me"]').scrollIntoView(); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/date-input.ts b/test/definitions/visual/date-input.ts deleted file mode 100644 index 431b274b22..0000000000 --- a/test/definitions/visual/date-input.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Date input', - componentName: 'date-input', - tests: [ - { - description: 'Permutations: states', - path: 'date-input/permutations-states', - screenshotType: 'permutations', - }, - { - description: 'Permutations: formats', - path: 'date-input/permutations-formats', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/date-picker.ts b/test/definitions/visual/date-picker.ts deleted file mode 100644 index c694cc816b..0000000000 --- a/test/definitions/visual/date-picker.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Date picker', - componentName: 'date-picker', - tests: [ - { - description: 'Permutations: states', - path: 'date-picker/permutations', - screenshotType: 'permutations', - setup: async ({ page }) => { - await page.click('[data-testid="date-picker-expanded-example"] button'); - }, - }, - { - description: 'Permutations: formats', - path: 'date-picker/permutations-formats', - screenshotType: 'permutations', - }, - { - description: 'Month picker', - componentName: 'date-picker', - tests: [ - { - description: 'focus ring on selected month', - path: 'date-picker/month-picker', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); - await page.keys(['Tab', 'Tab', 'Tab']); - }, - }, - { - description: 'focus ring on current month', - path: 'date-picker/month-picker', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); - await page.keys(['Tab', 'Tab', 'Tab']); - await page.keys(['ArrowRight']); - }, - }, - { - description: 'focus ring on non selected, non current month', - path: 'date-picker/month-picker', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); - await page.keys(['Tab', 'Tab', 'Tab']); - await page.keys(['ArrowRight', 'ArrowRight']); - }, - }, - ], - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/date-range-picker.ts b/test/definitions/visual/date-range-picker.ts deleted file mode 100644 index eb5ce32e32..0000000000 --- a/test/definitions/visual/date-range-picker.ts +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Date Range Picker', - componentName: 'date-range-picker', - tests: [ - { - description: 'Absolute range at 450px', - path: 'date-range-picker/with-value', - screenshotType: 'screenshotArea', - configuration: { width: 450, height: 950 }, - setup: async ({ page }) => { - await page.click('#focusable-before'); - await page.focusNextElement(); - await page.keys(['Enter']); - await page.click('[data-date="2018-01-09"]'); - await page.click('[data-date="2018-01-27"]'); - }, - }, - { - description: 'Absolute range at 1200px', - path: 'date-range-picker/with-value', - screenshotType: 'screenshotArea', - configuration: { width: 1200, height: 950 }, - setup: async ({ page }) => { - await page.click('#focusable-before'); - await page.focusNextElement(); - await page.keys(['Enter']); - await page.click('[data-date="2018-01-09"]'); - await page.click('[data-date="2018-02-24"]'); - }, - }, - { - description: 'Absolute range input permutations for day granularity', - path: 'date-range-picker/absolute-format-day-picker.permutations', - screenshotType: 'screenshotArea', - }, - { - description: 'Absolute range input permutations for month granularity', - path: 'date-range-picker/absolute-format-month-picker.permutations', - screenshotType: 'permutations', - }, - { - description: 'Relative range at 450px', - path: 'date-range-picker/with-value', - screenshotType: 'screenshotArea', - configuration: { width: 450, height: 950 }, - setup: async ({ page }) => { - await page.click('#focusable-before'); - await page.focusNextElement(); - await page.keys(['Enter']); - await page.focusNextElement(); - await page.keys(['Space']); - await page.keys(['ArrowUp']); - await page.keys(['Enter']); - await page.focusNextElement(); - await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown', 'ArrowDown']); - }, - }, - { - description: 'Relative range at 1200px', - path: 'date-range-picker/with-value', - screenshotType: 'screenshotArea', - configuration: { width: 1200, height: 950 }, - setup: async ({ page }) => { - await page.click('#focusable-before'); - await page.focusNextElement(); - await page.keys(['Enter']); - await page.focusNextElement(); - await page.keys(['ArrowLeft']); - await page.keys(['Enter']); - await page.focusNextElement(); - await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown', 'ArrowDown']); - }, - }, - { - description: 'Calendar permutations for day granularity', - path: 'date-range-picker/month-calendar-permutations', - screenshotType: 'permutations', - }, - { - description: 'Calendar permutations for month granularity', - path: 'date-range-picker/year-calendar-permutations', - screenshotType: 'permutations', - }, - { - description: 'selects text when double-clicking calendar header', - path: 'date-range-picker/with-value', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper, browser }) => { - await page.click('#focusable-before'); - await page.focusNextElement(); - await page.keys(['Enter']); - const firstCalendarHeaderSelector = wrapper - .findDateRangePicker() - .findDropdown() - .findHeader() - .find('h2 span') - .toSelector(); - browser.$(firstCalendarHeaderSelector).doubleClick(); - }, - }, - { - description: 'does not select text when double-clicking next button', - path: 'date-range-picker/with-value', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper, browser }) => { - await page.click('#focusable-before'); - await page.focusNextElement(); - await page.keys(['Enter']); - const nextButtonSelector = wrapper.findDateRangePicker().findDropdown().findNextMonthButton().toSelector(); - browser.$(nextButtonSelector).doubleClick(); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/divider.ts b/test/definitions/visual/divider.ts deleted file mode 100644 index 8dbed83984..0000000000 --- a/test/definitions/visual/divider.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Divider', - componentName: 'divider', - tests: [ - { - description: 'permutations', - path: 'divider/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/drawer.ts b/test/definitions/visual/drawer.ts deleted file mode 100644 index e81a979769..0000000000 --- a/test/definitions/visual/drawer.ts +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Drawer', - componentName: 'drawer', - tests: [ - { - description: 'permutations', - path: 'drawer/permutations', - screenshotType: 'permutations', - }, - { - description: 'short content, with footer, short footer', - path: 'app-layout/drawer-with-footer', - screenshotType: 'viewport', - queryParams: { longContent: 'false', longFooter: 'false', hasFooter: 'true' }, - }, - { - description: 'short content, with footer, long footer', - path: 'app-layout/drawer-with-footer', - screenshotType: 'viewport', - queryParams: { longContent: 'false', longFooter: 'true', hasFooter: 'true' }, - }, - { - description: 'long content, with footer, short footer', - path: 'app-layout/drawer-with-footer', - screenshotType: 'viewport', - queryParams: { longContent: 'true', longFooter: 'false', hasFooter: 'true' }, - }, - { - description: 'long content, with footer, long footer', - path: 'app-layout/drawer-with-footer', - screenshotType: 'viewport', - queryParams: { longContent: 'true', longFooter: 'true', hasFooter: 'true' }, - }, - { - description: 'Drawer with small view height', - path: 'app-layout/drawer-with-footer', - screenshotType: 'viewport', - configuration: { width: 1280, height: 268 }, - queryParams: { longContent: 'true', hasFooter: 'true', longFooter: 'true' }, - }, - { - description: 'Drawer footer with small view height', - path: 'app-layout/drawer-with-footer', - screenshotType: 'viewport', - configuration: { width: 1280, height: 268 }, - queryParams: { longContent: 'true', hasFooter: 'true' }, - setup: ({ browser, wrapper }) => { - browser.$(wrapper.findDrawer().findFooter().toSelector()).scrollIntoView(); - }, - }, - { - description: 'Drawer with absolute position', - path: 'drawer/drawer-position-absolute', - screenshotType: 'viewport', - configuration: { width: 1200, height: 1000 }, - }, - { - description: 'Drawer with absolute position and backdrops', - path: 'drawer/drawer-position-absolute', - screenshotType: 'viewport', - configuration: { width: 1200, height: 1000 }, - queryParams: { backdrops: 'start,end' }, - }, - { - description: 'Drawer with sticky position', - path: 'drawer/drawer-position-sticky', - screenshotType: 'viewport', - configuration: { width: 1200, height: 1000 }, - }, - { - description: 'Drawer with sticky position and offsets', - path: 'drawer/drawer-position-sticky', - screenshotType: 'viewport', - configuration: { width: 1200, height: 1000 }, - queryParams: { offsets: 'true' }, - }, - { - description: 'Drawer with sticky position and sticky offsets', - path: 'drawer/drawer-position-sticky', - screenshotType: 'viewport', - configuration: { width: 1200, height: 1000 }, - queryParams: { stickyOffsets: 'true' }, - }, - { - description: 'Drawer with fixed position', - path: 'drawer/drawer-position-fixed', - screenshotType: 'viewport', - configuration: { width: 1200, height: 1000 }, - }, - { - description: 'Drawer with fixed position, offsets and backdrop', - path: 'drawer/drawer-position-fixed', - screenshotType: 'viewport', - configuration: { width: 1200, height: 1000 }, - queryParams: { offsets: 'true', backdrop: 'true' }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/dropdown.ts b/test/definitions/visual/dropdown.ts deleted file mode 100644 index 4bbb716e5d..0000000000 --- a/test/definitions/visual/dropdown.ts +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Dropdown', - componentName: 'dropdown', - tests: [ - { - description: 'In fixed container', - path: 'dropdown/fixed-container', - screenshotType: 'viewport', - setup: async ({ page }) => { - const { height: windowHeight } = await page.getViewportSize(); - await page.windowScrollTo({ top: windowHeight }); - await page.click('button=Open dropdown'); - }, - }, - { - description: 'positions select inside modal, expandToViewport=false', - path: 'dropdown/expandable', - screenshotType: 'viewport', - queryParams: { componentType: 'Select', expandToViewport: 'false' }, - setup: async ({ page }) => { - await page.click('#show-modal'); - await page.click('#in-modal'); - }, - }, - { - description: 'positions select inside modal, expandToViewport=true', - path: 'dropdown/expandable', - screenshotType: 'viewport', - queryParams: { componentType: 'Select', expandToViewport: 'true' }, - setup: async ({ page }) => { - await page.click('#show-modal'); - await page.click('#in-modal'); - }, - }, - { - description: 'positions select inside popover, expandToViewport=false', - path: 'dropdown/expandable', - screenshotType: 'viewport', - queryParams: { componentType: 'Select', expandToViewport: 'false' }, - setup: async ({ page }) => { - await page.click('#show-popover'); - await page.click('#in-popover'); - await page.click('[data-test-index="5"]'); - }, - }, - { - description: 'positions select inside popover, expandToViewport=true', - path: 'dropdown/expandable', - screenshotType: 'viewport', - queryParams: { componentType: 'Select', expandToViewport: 'true' }, - setup: async ({ page }) => { - await page.click('#show-popover'); - await page.click('#in-popover'); - await page.click('[data-test-index="5"]'); - }, - }, - { - description: 'select has bottom borders when opened upwards, expandToViewport=false', - path: 'dropdown/expandable', - screenshotType: 'viewport', - queryParams: { componentType: 'Select', expandToViewport: 'false' }, - setup: async ({ page }) => { - await page.click('#bottom-left'); - }, - }, - { - description: 'select has bottom borders when opened upwards, expandToViewport=true', - path: 'dropdown/expandable', - screenshotType: 'viewport', - queryParams: { componentType: 'Select', expandToViewport: 'true' }, - setup: async ({ page }) => { - await page.click('#bottom-left'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/expandable-section.ts b/test/definitions/visual/expandable-section.ts deleted file mode 100644 index 9564685028..0000000000 --- a/test/definitions/visual/expandable-section.ts +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Expandable section', - componentName: 'expandable-section', - tests: [ - { - description: 'permutations', - path: 'expandable-section/permutations', - screenshotType: 'permutations', - }, - { - description: 'container variant', - path: 'expandable-section/container-variant.permutations', - screenshotType: 'permutations', - }, - { - description: 'focus - container variant with only heading', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { headerText: 'Header text', variant: 'container' }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - default variant with only heading', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { headerText: 'Header text', variant: 'default' }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - footer variant with only heading', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { headerText: 'Header text', variant: 'footer' }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - navigation variant with only heading', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { headerText: 'Header text', variant: 'navigation' }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - container variant with heading and description', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'container' }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - default variant with heading and description', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'default' }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - footer variant with heading and description', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'footer' }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - container variant with interactive elements', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { headerText: 'Header text', hasHeaderInfo: 'true', hasHeaderActions: 'true', variant: 'container' }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - container variant with interactive elements and description', - path: 'expandable-section/focus', - screenshotType: 'screenshotArea', - queryParams: { - headerText: 'Header text', - headerDescription: 'Header description', - hasHeaderInfo: 'true', - hasHeaderActions: 'true', - variant: 'container', - }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'stacked variant', - path: 'expandable-section/stacked-variant.permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/file-dropzone.ts b/test/definitions/visual/file-dropzone.ts deleted file mode 100644 index 5c845fa1ac..0000000000 --- a/test/definitions/visual/file-dropzone.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'File dropzone', - componentName: 'file-dropzone', - tests: [ - { - description: 'In container', - path: 'file-dropzone/container', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/file-input.ts b/test/definitions/visual/file-input.ts deleted file mode 100644 index c2d958d9c9..0000000000 --- a/test/definitions/visual/file-input.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'File input', - componentName: 'file-input', - tests: [ - { - description: 'Simple', - path: 'file-input/simple', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/file-token-group.ts b/test/definitions/visual/file-token-group.ts deleted file mode 100644 index 8aa68908d0..0000000000 --- a/test/definitions/visual/file-token-group.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'File token group', - componentName: 'file-token-group', - tests: [ - { - description: 'Permutations', - path: 'file-token-group/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/file-upload.ts b/test/definitions/visual/file-upload.ts deleted file mode 100644 index 7426504608..0000000000 --- a/test/definitions/visual/file-upload.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'FileUpload', - componentName: 'file-upload', - tests: [ - { - description: 'Permutations', - path: 'file-upload/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/flashbar-stacked.ts b/test/definitions/visual/flashbar-stacked.ts deleted file mode 100644 index 9b795791a6..0000000000 --- a/test/definitions/visual/flashbar-stacked.ts +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Flashbar stacked notifications', - componentName: 'flashbar', - tests: [ - { - description: '380px, collapsed', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 380 }, - }, - { - description: '380px, collapsed, notifications bar button focused', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 380 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: '380px, expanded', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 380 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['Space']); - await new Promise(resolve => setTimeout(resolve, 500)); - }, - }, - { - description: '450px, collapsed', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 450 }, - }, - { - description: '450px, collapsed, notifications bar button focused', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 450 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: '450px, expanded', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 450 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['Space']); - await new Promise(resolve => setTimeout(resolve, 500)); - }, - }, - { - description: '600px, collapsed', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - }, - { - description: '600px, collapsed, notifications bar button focused', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: '600px, expanded', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['Space']); - await new Promise(resolve => setTimeout(resolve, 500)); - }, - }, - { - description: '1200px, collapsed', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - }, - { - description: '1200px, collapsed, notifications bar button focused', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: '1200px, expanded', - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['Space']); - await new Promise(resolve => setTimeout(resolve, 500)); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/flashbar.ts b/test/definitions/visual/flashbar.ts deleted file mode 100644 index b620a4e031..0000000000 --- a/test/definitions/visual/flashbar.ts +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Flashbar', - componentName: 'flashbar', - tests: [ - { - description: 'permutations at 600', - path: 'flashbar/permutations', - screenshotType: 'permutations', - configuration: { width: 600 }, - }, - { - description: 'permutations at 1280', - path: 'flashbar/permutations', - screenshotType: 'permutations', - configuration: { width: 1280 }, - }, - { - description: 'runtime-action at 600', - path: 'flashbar/runtime-action', - screenshotType: 'permutations', - configuration: { width: 600 }, - }, - { - description: 'runtime-action at 1280', - path: 'flashbar/runtime-action', - screenshotType: 'permutations', - configuration: { width: 1280 }, - }, - { - description: 'content permutations', - path: 'flashbar/content-permutations', - screenshotType: 'permutations', - }, - { - description: 'style-custom', - path: 'flashbar/style-custom', - screenshotType: 'screenshotArea', - }, - { - description: 'small screen button layout', - path: 'flashbar/small-screen', - screenshotType: 'screenshotArea', - configuration: { width: 550 }, - }, - { - description: 'stacking of multiple flashbar items', - path: 'flashbar/stacking', - screenshotType: 'screenshotArea', - }, - { - description: 'focus border color', - path: 'flashbar/dismissal', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/form-field.ts b/test/definitions/visual/form-field.ts deleted file mode 100644 index 59a207a24a..0000000000 --- a/test/definitions/visual/form-field.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'FormField', - componentName: 'form-field', - tests: [ - { - description: 'Permutations at 576', - path: 'form-field/permutations', - screenshotType: 'permutations', - configuration: { width: 576 }, - }, - { - description: 'Scenarios at 576', - path: 'form-field/form-field-columns', - screenshotType: 'screenshotArea', - configuration: { width: 576 }, - }, - { - description: 'Permutations at 768', - path: 'form-field/permutations', - screenshotType: 'permutations', - configuration: { width: 768 }, - }, - { - description: 'Scenarios at 768', - path: 'form-field/form-field-columns', - screenshotType: 'screenshotArea', - configuration: { width: 768 }, - }, - { - description: 'Permutations at 992', - path: 'form-field/permutations', - screenshotType: 'permutations', - configuration: { width: 992 }, - }, - { - description: 'Scenarios at 992', - path: 'form-field/form-field-columns', - screenshotType: 'screenshotArea', - configuration: { width: 992 }, - }, - { - description: 'Permutations at 1200', - path: 'form-field/permutations', - screenshotType: 'permutations', - configuration: { width: 1200 }, - }, - { - description: 'Scenarios at 1200', - path: 'form-field/form-field-columns', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/form.ts b/test/definitions/visual/form.ts deleted file mode 100644 index 3a61c6e5a6..0000000000 --- a/test/definitions/visual/form.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Form', - componentName: 'form', - tests: [ - { - description: 'permutations', - path: 'form/permutations', - screenshotType: 'permutations', - }, - { - description: 'simple', - path: 'form/simple', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/grid.ts b/test/definitions/visual/grid.ts deleted file mode 100644 index 299f2bb6e8..0000000000 --- a/test/definitions/visual/grid.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Grid', - componentName: 'grid', - tests: [ - { - description: 'grid at "default"', - path: 'grid', - screenshotType: 'screenshotArea', - configuration: { width: 400 }, - }, - { - description: 'grid at "xs"', - path: 'grid', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - }, - { - description: 'grid at "m"', - path: 'grid', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - }, - { - description: 'grid at "l"', - path: 'grid', - screenshotType: 'screenshotArea', - configuration: { width: 1400 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/header.ts b/test/definitions/visual/header.ts deleted file mode 100644 index 9643607963..0000000000 --- a/test/definitions/visual/header.ts +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Header', - componentName: 'header', - tests: [ - { - description: 'level-1 at 1500px', - path: 'header/level-1', - screenshotType: 'screenshotArea', - configuration: { width: 1500 }, - }, - { - description: 'level-1 at 850px', - path: 'header/level-1', - screenshotType: 'screenshotArea', - configuration: { width: 850 }, - }, - { - description: 'level-1 at 400px', - path: 'header/level-1', - screenshotType: 'screenshotArea', - configuration: { width: 400 }, - }, - { - description: 'level-2 at 1500px', - path: 'header/level-2', - screenshotType: 'screenshotArea', - configuration: { width: 1500 }, - }, - { - description: 'level-2 at 850px', - path: 'header/level-2', - screenshotType: 'screenshotArea', - configuration: { width: 850 }, - }, - { - description: 'level-2 at 400px', - path: 'header/level-2', - screenshotType: 'screenshotArea', - configuration: { width: 400 }, - }, - { - description: 'level-3 at 1500px', - path: 'header/level-3', - screenshotType: 'screenshotArea', - configuration: { width: 1500 }, - }, - { - description: 'level-3 at 850px', - path: 'header/level-3', - screenshotType: 'screenshotArea', - configuration: { width: 850 }, - }, - { - description: 'level-3 at 400px', - path: 'header/level-3', - screenshotType: 'screenshotArea', - configuration: { width: 400 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/help-panel.ts b/test/definitions/visual/help-panel.ts deleted file mode 100644 index fabc497026..0000000000 --- a/test/definitions/visual/help-panel.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'HelpPanel', - componentName: 'help-panel', - tests: [ - { - description: 'permutations', - path: 'help-panel/permutations', - screenshotType: 'screenshotArea', - }, - { - description: 'with AppLayout', - path: 'help-panel/with-app-layout', - screenshotType: 'screenshotArea', - }, - { - description: 'loading state - with AppLayout', - path: 'help-panel/loading-with-app-layout', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/icon.ts b/test/definitions/visual/icon.ts deleted file mode 100644 index bd8b064b35..0000000000 --- a/test/definitions/visual/icon.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Icon', - componentName: 'icon', - tests: [ - { - description: 'Alignment with text', - path: 'icon/text-align', - screenshotType: 'screenshotArea', - configuration: { width: 300 }, - }, - { - description: 'Icons in normal variant', - path: 'icon/variant-normal', - screenshotType: 'screenshotArea', - }, - { - description: 'Icons in disabled variant', - path: 'icon/variant-disabled', - screenshotType: 'screenshotArea', - }, - { - description: 'Icons in error variant', - path: 'icon/variant-error', - screenshotType: 'screenshotArea', - }, - { - description: 'Icons in inverted variant', - path: 'icon/variant-inverted', - screenshotType: 'screenshotArea', - }, - { - description: 'Icons in subtle variant', - path: 'icon/variant-subtle', - screenshotType: 'screenshotArea', - }, - { - description: 'Icons in success variant', - path: 'icon/variant-success', - screenshotType: 'screenshotArea', - }, - { - description: 'Icons in warning variant', - path: 'icon/variant-warning', - screenshotType: 'screenshotArea', - }, - { - description: 'Custom icon', - path: 'icon/custom-icon', - screenshotType: 'screenshotArea', - }, - { - description: 'Custom svg icon', - path: 'icon/custom-svg', - screenshotType: 'screenshotArea', - }, - { - description: 'Inherit size property', - path: 'icon/size-inherit', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/input.ts b/test/definitions/visual/input.ts deleted file mode 100644 index 81c0145e88..0000000000 --- a/test/definitions/visual/input.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Input', - componentName: 'input', - tests: [ - { - description: 'permutations', - path: 'input/permutations', - screenshotType: 'permutations', - }, - { - description: 'style-permutations', - path: 'input/style-permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/item-card.ts b/test/definitions/visual/item-card.ts deleted file mode 100644 index bcf0439635..0000000000 --- a/test/definitions/visual/item-card.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Item card', - componentName: 'item-card', - tests: [ - { - description: 'permutations', - path: 'item-card/permutations', - screenshotType: 'permutations', - }, - { - description: 'padding permutations', - path: 'item-card/padding-permutations', - screenshotType: 'permutations', - }, - { - description: 'variant permutations', - path: 'item-card/variant-permutations', - screenshotType: 'permutations', - }, - { - description: 'style-custom', - path: 'item-card/style-custom', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/key-value-pairs.ts b/test/definitions/visual/key-value-pairs.ts deleted file mode 100644 index 89dc9ec268..0000000000 --- a/test/definitions/visual/key-value-pairs.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Key-value pairs', - componentName: 'key-value-pairs', - tests: [ - { - description: 'permutations', - path: 'key-value-pairs/permutations', - screenshotType: 'permutations', - }, - { - description: 'permutations on mobile (600px)', - path: 'key-value-pairs/permutations', - screenshotType: 'permutations', - configuration: { width: 600 }, - }, - { - description: 'wrapping text', - path: 'key-value-pairs/text-wrap', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/line-chart.ts b/test/definitions/visual/line-chart.ts deleted file mode 100644 index 560a355039..0000000000 --- a/test/definitions/visual/line-chart.ts +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { TestSuite } from '../types'; - -const TEST_CHART_FILTER_TRIGGER = '#chart button'; -const TEST_CHART_TOOLTIP_HEADER = '#chart h2'; - -const suite: TestSuite = { - description: 'Line chart', - componentName: 'line-chart', - tests: [ - { - description: 'permutations', - path: 'line-chart/permutations', - screenshotType: 'permutations', - }, - { - description: 'can highlight all data points at a given X coordinate with keyboard', - path: 'line-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'can navigate series with keyboard', - path: 'line-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'ArrowDown', 'ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'can pin popover for all data points at a given X coordinate with keyboard', - path: 'line-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - pixelDiffTolerance: 6, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - await page.keys(['Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - { - description: 'can pin popover for a point in a specific series with keyboard', - path: 'line-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click(TEST_CHART_FILTER_TRIGGER); - await page.keys(['Escape']); - await page.focusNextElement(); - await page.keys(['ArrowRight', 'ArrowDown', 'ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - await page.keys(['Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - { - description: 'shows popover on hover', - path: 'line-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.hoverElement('[aria-label="Line chart"]', 200, 50); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'shows popover with expandable sub-items - no series highlighted, sub-items collapsed', - path: 'line-chart/drilldown', - screenshotType: 'screenshotArea', - configuration: { width: 800, height: 1000 }, - queryParams: { expandableSubItems: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.waitForVisible(wrapper.findLineChart().findDetailPopover().toSelector()); - }, - }, - { - description: 'shows popover with expandable sub-items - no series highlighted, sub-items expanded', - path: 'line-chart/drilldown', - screenshotType: 'screenshotArea', - configuration: { width: 800, height: 1000 }, - queryParams: { expandableSubItems: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.waitForVisible(wrapper.findLineChart().findDetailPopover().toSelector()); - await page.keys(['Tab']); - await page.keys(['Enter']); - }, - }, - { - description: 'shows popover with expandable sub-items - one series highlighted, sub-items collapsed', - path: 'line-chart/drilldown', - screenshotType: 'screenshotArea', - configuration: { width: 800, height: 1000 }, - queryParams: { expandableSubItems: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowUp']); - await page.waitForVisible(wrapper.findLineChart().findDetailPopover().toSelector()); - }, - }, - { - description: 'shows popover with expandable sub-items - one series highlighted, sub-items expanded', - path: 'line-chart/drilldown', - screenshotType: 'screenshotArea', - configuration: { width: 800, height: 1000 }, - queryParams: { expandableSubItems: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowUp']); - await page.waitForVisible(wrapper.findLineChart().findDetailPopover().toSelector()); - await page.keys(['Tab']); - await page.keys(['Enter']); - }, - }, - { - description: 'correctly renders the chart inside an expandable section - X ticks do not overlap nor overflow', - path: 'line-chart/in-expandable-section-test', - screenshotType: 'screenshotArea', - configuration: { width: 800, height: 800 }, - setup: async ({ page, wrapper }) => { - const expandableSectionWrapper = wrapper.findExpandableSection(); - await page.waitForVisible(expandableSectionWrapper.toSelector()); - await page.click(expandableSectionWrapper.findExpandButton().toSelector()); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/link.ts b/test/definitions/visual/link.ts deleted file mode 100644 index c381876e14..0000000000 --- a/test/definitions/visual/link.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Link', - componentName: 'link', - tests: [ - { - description: 'Permutations', - path: 'link/permutations', - screenshotType: 'permutations', - }, - { - description: 'Permutations (long label)', - path: 'link/long-label-permutations', - screenshotType: 'permutations', - }, - { - description: 'Icon overflow permutations', - path: 'link/icon-overflow-permutations', - screenshotType: 'permutations', - }, - { - description: 'Inherit font size permutations', - path: 'link/inherit-permutations', - screenshotType: 'permutations', - }, - { - description: 'Style custom page', - path: 'link/style-custom-types', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/list.ts b/test/definitions/visual/list.ts deleted file mode 100644 index 9dbe349f8c..0000000000 --- a/test/definitions/visual/list.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'List', - componentName: 'list', - tests: [ - { - description: 'permutations', - path: 'list/permutations', - screenshotType: 'permutations', - }, - { - description: 'sortable-permutations', - path: 'list/sortable-permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/mixed-line-bar-chart.ts b/test/definitions/visual/mixed-line-bar-chart.ts deleted file mode 100644 index d97a37a9d2..0000000000 --- a/test/definitions/visual/mixed-line-bar-chart.ts +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const TEST_CHART_TOOLTIP_HEADER = '#chart h2'; - -const suite: TestSuite = { - description: 'Mixed line bar chart', - componentName: 'mixed-line-bar-chart', - tests: [ - { - description: 'permutations', - path: 'mixed-line-bar-chart/permutations', - screenshotType: 'permutations', - }, - { - description: 'fit-height', - path: 'mixed-line-bar-chart/fit-height', - screenshotType: 'screenshotArea', - }, - { - description: 'fit-height no filter, no legend', - path: 'mixed-line-bar-chart/fit-height', - screenshotType: 'screenshotArea', - queryParams: { hideFilter: 'true', hideLegend: 'true' }, - }, - { - description: 'fit-height, no legend', - path: 'mixed-line-bar-chart/fit-height', - screenshotType: 'screenshotArea', - queryParams: { hideLegend: 'true' }, - }, - { - description: 'chart plot has a focus outline', - path: 'mixed-line-bar-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.focusNextElement(); - }, - }, - { - description: 'can navigate series with keyboard', - path: 'mixed-line-bar-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - pixelDiffTolerance: 12, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowRight']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'can pin popover with keyboard', - path: 'mixed-line-bar-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - pixelDiffTolerance: 12, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.focusNextElement(); - await page.keys(['ArrowRight']); - await page.keys(['ArrowRight']); - await page.keys(['ArrowRight']); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - await page.keys(['Enter']); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - { - description: 'shows popover on hover', - path: 'mixed-line-bar-chart/test', - screenshotType: 'viewport', - configuration: { width: 800, height: 800 }, - setup: async ({ page }) => { - await page.hoverElement('#chart svg[aria-label="Mixed chart 1"]', 200, 100); - await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); - }, - }, - { - description: 'handles long left-labels at width 320px', - path: 'mixed-line-bar-chart/with-long-left-labels', - screenshotType: 'screenshotArea', - configuration: { width: 320 }, - }, - { - description: 'handles long left-labels at width 400px', - path: 'mixed-line-bar-chart/with-long-left-labels', - screenshotType: 'screenshotArea', - configuration: { width: 400 }, - }, - { - description: 'handles long left-labels at width 600px', - path: 'mixed-line-bar-chart/with-long-left-labels', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/modal.ts b/test/definitions/visual/modal.ts deleted file mode 100644 index 28fb043c6d..0000000000 --- a/test/definitions/visual/modal.ts +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Modal', - componentName: 'modal', - tests: [ - { - description: 'simple', - path: 'modal/simple', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Show modal'); - }, - }, - { - description: 'no-paddings', - path: 'modal/no-paddings', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Show modal'); - }, - }, - { - description: 'vertical-scroll', - path: 'modal/vertical-scroll', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Show modal'); - }, - }, - { - description: 'long-header', - path: 'modal/long-header', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Show modal'); - }, - }, - { - description: 'unbreakable-header', - path: 'modal/unbreakable-header', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Show modal'); - }, - }, - { - description: 'size-small', - path: 'modal/sizes', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/small'); - }, - }, - { - description: 'size-medium', - path: 'modal/sizes', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/medium'); - }, - }, - { - description: 'size-large', - path: 'modal/sizes', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/large'); - }, - }, - { - description: 'size-x-large', - path: 'modal/sizes', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/x-large'); - }, - }, - { - description: 'size-xx-large', - path: 'modal/sizes', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/xx-large'); - }, - }, - { - description: 'size-max', - path: 'modal/sizes', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/max'); - }, - }, - { - description: 'position-top', - path: 'modal/position-top', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Show modal'); - }, - }, - { - description: 'custom-dimensions with footer', - path: 'modal/custom-dimensions', - screenshotType: 'viewport', - queryParams: { width: '600', height: '400', footer: 'true' }, - setup: async ({ page }) => { - await page.click('[data-testid="modal-trigger"]'); - }, - }, - { - description: 'custom-dimensions without footer', - path: 'modal/custom-dimensions', - screenshotType: 'viewport', - queryParams: { width: '600', height: '400', footer: 'false' }, - setup: async ({ page }) => { - await page.click('[data-testid="modal-trigger"]'); - }, - }, - { - description: 'custom-dimensions very small width', - path: 'modal/custom-dimensions', - screenshotType: 'viewport', - queryParams: { width: '10' }, - setup: async ({ page }) => { - await page.click('[data-testid="modal-trigger"]'); - }, - }, - { - description: 'custom-dimensions very small height with footer', - path: 'modal/custom-dimensions', - screenshotType: 'viewport', - queryParams: { height: '10', footer: 'true' }, - setup: async ({ page }) => { - await page.click('[data-testid="modal-trigger"]'); - }, - }, - { - description: 'custom-dimensions very small height & width with footer', - path: 'modal/custom-dimensions', - screenshotType: 'viewport', - queryParams: { width: '10', height: '15', footer: 'true' }, - setup: async ({ page }) => { - await page.click('[data-testid="modal-trigger"]'); - }, - }, - { - description: 'custom-dimensions large height & width with footer', - path: 'modal/custom-dimensions', - screenshotType: 'viewport', - queryParams: { width: '10000', height: '10000', footer: 'true' }, - setup: async ({ page }) => { - await page.click('[data-testid="modal-trigger"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/multiselect.ts b/test/definitions/visual/multiselect.ts deleted file mode 100644 index d08b3b06b8..0000000000 --- a/test/definitions/visual/multiselect.ts +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Multiselect', - componentName: 'multiselect', - tests: [ - { - description: 'permutations', - path: 'multiselect/permutations', - screenshotType: 'permutations', - }, - { - description: 'inlineLabelText-permutations', - path: 'multiselect/inline-label-text-permutations', - screenshotType: 'permutations', - }, - { - description: 'expandToViewport=true virtualScroll=true filteringType=manual', - path: 'multiselect/screenshot', - screenshotType: 'screenshotArea', - queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'manual' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - }, - }, - { - description: 'expandToViewport=true virtualScroll=true filteringType=auto', - path: 'multiselect/screenshot', - screenshotType: 'screenshotArea', - queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'auto' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - }, - }, - { - description: 'expandToViewport=true virtualScroll=true filteringType=none', - path: 'multiselect/screenshot', - screenshotType: 'screenshotArea', - queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'none' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - }, - }, - { - description: 'expandToViewport=false virtualScroll=false filteringType=manual', - path: 'multiselect/screenshot', - screenshotType: 'screenshotArea', - queryParams: { expandToViewport: 'false', virtualScroll: 'false', filteringType: 'manual' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - }, - }, - { - description: 'error status wrapping - normal list', - path: 'multiselect/screenshot', - screenshotType: 'screenshotArea', - queryParams: { statusType: 'error' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - }, - }, - { - description: 'item selected (dropdown stays open) - normal list', - path: 'multiselect/screenshot', - screenshotType: 'screenshotArea', - queryParams: { virtualScroll: 'false' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - await page.click('[data-test-index="4"]'); - }, - }, - { - description: 'item selected (dropdown stays open) - virtual list', - path: 'multiselect/screenshot', - screenshotType: 'screenshotArea', - queryParams: { virtualScroll: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - await page.click('[data-test-index="4"]'); - }, - }, - { - description: 'custom render option - normal list', - path: 'multiselect/custom-render-option', - screenshotType: 'screenshotArea', - queryParams: { virtualScroll: 'false' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - }, - }, - { - description: 'custom render option - virtual list', - path: 'multiselect/custom-render-option', - screenshotType: 'screenshotArea', - queryParams: { virtualScroll: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findMultiselect().findTrigger().toSelector()); - }, - }, - { - description: 'Long virtual list - navigate to last item', - path: 'multiselect/virtual-scroll', - screenshotType: 'screenshotArea', - queryParams: { type: 'multiselect' }, - setup: async ({ page, wrapper }) => { - const triggerSelector = wrapper.findMultiselect().findTrigger().toSelector(); - page.waitForVisible(triggerSelector); - await page.click(triggerSelector); - await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { - top: 99999, - }); - await page.waitForJsTimers(); - }, - }, - { - description: 'Long virtual list (select all) - navigate to last item', - path: 'multiselect/virtual-scroll', - screenshotType: 'screenshotArea', - queryParams: { type: 'multiselect-select-all' }, - setup: async ({ page, wrapper }) => { - const triggerSelector = wrapper.findMultiselect().findTrigger().toSelector(); - page.waitForVisible(triggerSelector); - await page.click(triggerSelector); - await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { - top: 99999, - }); - await page.waitForJsTimers(); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/pagination.ts b/test/definitions/visual/pagination.ts deleted file mode 100644 index ed8ec25aee..0000000000 --- a/test/definitions/visual/pagination.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Pagination', - componentName: 'pagination', - tests: [ - { - description: 'permutations', - path: 'pagination/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/pie-chart.ts b/test/definitions/visual/pie-chart.ts deleted file mode 100644 index ee92299a71..0000000000 --- a/test/definitions/visual/pie-chart.ts +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Pie chart', - componentName: 'pie-chart', - tests: [ - { - description: 'permutations', - path: 'pie-chart/permutations', - screenshotType: 'permutations', - }, - { - description: 'permutations in narrow container', - path: 'pie-chart/permutations', - screenshotType: 'permutations', - configuration: { width: 350 }, - }, - { - description: 'fit-height', - path: 'pie-chart/fit-height', - screenshotType: 'screenshotArea', - }, - { - description: 'fit-height no filter, no legend', - path: 'pie-chart/fit-height', - screenshotType: 'screenshotArea', - queryParams: { hideFilter: 'true', hideLegend: 'true' }, - }, - { - description: 'can focus chart plot', - path: 'pie-chart/test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.focusNextElement(); - }, - }, - { - description: 'can navigate segments with keyboard', - path: 'pie-chart/test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.focusNextElement(); - await page.keys(['Enter']); - await page.keys(['ArrowRight']); - }, - }, - { - description: 'can pin segments with mouse', - path: 'pie-chart/test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('svg [aria-label~="Apples"] > path'); - await page.waitForVisible('[aria-label="Dismiss"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/popover.ts b/test/definitions/visual/popover.ts deleted file mode 100644 index 1acd509b76..0000000000 --- a/test/definitions/visual/popover.ts +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Popover', - componentName: 'popover', - tests: [ - { - description: 'text wrapping', - path: 'popover/text-wrap', - screenshotType: 'screenshotArea', - }, - { - description: 'alignment inside text', - path: 'popover/text-align', - screenshotType: 'screenshotArea', - }, - { - description: 'inside modal', - path: 'popover/scenario-in-modal', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Show modal'); - await page.click('#popover button'); - }, - }, - { - description: 'positioning with navigation v1.0', - path: 'popover/nav-v1-0-positioning', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#popover button'); - }, - }, - { - description: 'close icon positioned inside the popover (no header and fixed width)', - path: 'popover/header-variant', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('[data-testid="popover-without-title"] button'); - }, - }, - { - description: 'inside table - renderWithPortal=false', - path: 'popover/scenario-in-table', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('table button'); - }, - }, - { - description: 'inside table - renderWithPortal=true', - path: 'popover/scenario-in-table', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#renderWithPortal'); - await page.click('table button'); - }, - }, - { - description: 'scenario - copy - renderWithPortal=false', - path: 'popover/scenarios', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#scenario-copy button'); - }, - }, - { - description: 'scenario - copy - renderWithPortal=true', - path: 'popover/scenarios', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#renderWithPortal'); - await page.click('#scenario-copy button'); - }, - }, - { - description: 'scenario - medium-key-value - renderWithPortal=false', - path: 'popover/scenarios', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#scenario-medium-key-value button'); - }, - }, - { - description: 'scenario - large-key-value - renderWithPortal=false', - path: 'popover/scenarios', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#scenario-large-key-value button'); - }, - }, - { - description: 'inline popover - closed - renderWithPortal=false', - path: 'popover/inline', - screenshotType: 'screenshotArea', - }, - { - description: 'inline popover - closed - renderWithPortal=true', - path: 'popover/inline', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#renderWithPortal'); - }, - }, - { - description: 'inline popover - open - renderWithPortal=false', - path: 'popover/inline', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findPopover().findTrigger().toSelector()); - }, - }, - { - description: 'inline popover - open - renderWithPortal=true', - path: 'popover/inline', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - await page.click('#renderWithPortal'); - await page.click(wrapper.findPopover().findTrigger().toSelector()); - }, - }, - { - description: 'positioning - opens in the correct position - renderWithPortal=false', - path: 'popover/positioning', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#popover-2-2 button'); - }, - }, - { - description: 'positioning - flips to the opposite position - renderWithPortal=false', - path: 'popover/positioning', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#popover-1-2 button'); - }, - }, - { - description: 'focus - Permutation 1', - path: 'popover/focus-ring', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'focus - Permutation 2', - path: 'popover/focus-ring', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - for (let j = 0; j < 2; j++) { - await page.focusNextElement(); - } - }, - }, - { - description: 'focus - Permutation 3', - path: 'popover/focus-ring', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - for (let j = 0; j < 3; j++) { - await page.focusNextElement(); - } - }, - }, - { - description: 'focus - Permutation 6', - path: 'popover/focus-ring', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - for (let j = 0; j < 6; j++) { - await page.focusNextElement(); - } - }, - }, - { - description: 'focus - Permutation 12', - path: 'popover/focus-ring', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - for (let j = 0; j < 12; j++) { - await page.focusNextElement(); - } - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/progress-bar.ts b/test/definitions/visual/progress-bar.ts deleted file mode 100644 index 1280b00599..0000000000 --- a/test/definitions/visual/progress-bar.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'ProgressBar', - componentName: 'progress-bar', - tests: [ - { - description: 'permutations - standalone', - path: 'progress-bar/permutations-standalone', - screenshotType: 'permutations', - configuration: { width: 800 }, - }, - { - description: 'permutations - flash', - path: 'progress-bar/permutations-flash', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - }, - { - description: 'permutations - key-value', - path: 'progress-bar/permutations-key-value', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/prompt-input.ts b/test/definitions/visual/prompt-input.ts deleted file mode 100644 index 536f1a10d9..0000000000 --- a/test/definitions/visual/prompt-input.ts +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'PromptInput', - componentName: 'prompt-input', - tests: [ - { - description: 'Permutations at 250', - path: 'prompt-input/permutations', - screenshotType: 'permutations', - configuration: { width: 250 }, - }, - { - description: 'Permutations at 400', - path: 'prompt-input/permutations', - screenshotType: 'permutations', - configuration: { width: 400 }, - }, - { - description: 'Permutations at 800', - path: 'prompt-input/permutations', - screenshotType: 'permutations', - configuration: { width: 800 }, - }, - { - description: 'Style Permutations', - path: 'prompt-input/style-permutations', - screenshotType: 'permutations', - }, - { - description: 'focus ring on the last secondary action', - path: 'prompt-input/simple', - screenshotType: 'screenshotArea', - queryParams: { hasSecondaryActions: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findPromptInput('[data-testid="prompt-input"]').findNativeTextarea().toSelector()); - await page.keys(['Tab', 'ArrowRight', 'ArrowRight']); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/property-filter.ts b/test/definitions/visual/property-filter.ts deleted file mode 100644 index ef96c61725..0000000000 --- a/test/definitions/visual/property-filter.ts +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Property filter', - componentName: 'property-filter', - tests: [ - { - description: 'token editor popover', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('.property-filter-default [aria-haspopup=dialog]'); - }, - }, - { - description: 'filtering token select', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('.property-filter-default [aria-haspopup=listbox]'); - }, - }, - { - description: 'token editor popover overflow', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('.property-filter-overflow [aria-haspopup=dialog]'); - }, - }, - { - description: 'token editor custom property boolean', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('.property-filter-custom-prop-boolean [aria-haspopup=dialog]'); - }, - }, - { - description: 'token editor custom property datetime', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('.property-filter-custom-prop-datetime [aria-haspopup=dialog]'); - }, - }, - { - description: 'token editor group string property', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); - await page.click(propertyFilter.findTokens().get(1).findLabel().toSelector()); - }, - }, - { - description: 'token editor group datetime property', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); - await page.click(propertyFilter.findTokens().get(1).findLabel().toSelector()); - await page.keys(['Escape']); - await page.keys(['Tab', 'Tab', 'Tab', 'Enter']); - }, - }, - { - description: 'token editor enum property', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); - await page.click(propertyFilter.findNativeInput().toSelector()); - await page.keys('state = s'); - }, - }, - { - description: 'token editor enum property no matches', - path: 'property-filter/token-editor', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); - await page.click(propertyFilter.findNativeInput().toSelector()); - await page.keys('state = x'); - }, - }, - { - description: 'permutations', - path: 'property-filter/permutations', - screenshotType: 'permutations', - }, - { - description: 'tokens permutations', - path: 'property-filter/property-filter-tokens-permutations', - screenshotType: 'permutations', - }, - { - description: 'editor permutations at 400', - path: 'property-filter/property-filter-editor-permutations', - screenshotType: 'permutations', - configuration: { width: 400 }, - }, - { - description: 'editor permutations at 1200', - path: 'property-filter/property-filter-editor-permutations', - screenshotType: 'permutations', - configuration: { width: 1200 }, - }, - { - description: 'split panel integration - main content dropdown', - path: 'property-filter/split-panel-app-layout-integration', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('.main-content input[aria-label="your choice"]'); - }, - }, - { - description: 'split panel integration - main content popover', - path: 'property-filter/split-panel-app-layout-integration', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('.main-content button[aria-haspopup=dialog]'); - }, - }, - { - description: 'virtual scroll navigate through 100 items', - path: 'property-filter/virtual-scroll', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const propertyFilter = wrapper.findPropertyFilter(); - await page.click(propertyFilter.findNativeInput().toSelector()); - await page.keys('Property = '); - for (let i = 0; i < 100; i++) { - await page.keys('ArrowDown'); - } - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/radio-button.ts b/test/definitions/visual/radio-button.ts deleted file mode 100644 index 5554d51057..0000000000 --- a/test/definitions/visual/radio-button.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'RadioButton', - componentName: 'radio-button', - tests: [ - { - description: 'Permutations', - path: 'radio-button/permutations', - screenshotType: 'permutations', - }, - { - description: 'Focused and not checked', - path: 'radio-button/focus-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'Focused and checked', - path: 'radio-button/focus-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['Space']); - }, - }, - { - description: 'Custom style permutations', - path: 'radio-button/style-custom', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/radio-group.ts b/test/definitions/visual/radio-group.ts deleted file mode 100644 index 49bfbcdc32..0000000000 --- a/test/definitions/visual/radio-group.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'RadioGroup', - componentName: 'radio-group', - tests: [ - { - description: 'Permutations', - path: 'radio-group/permutations', - screenshotType: 'permutations', - }, - { - description: 'Radio button is focused', - path: 'radio-group/focus-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'Radio button has label with a correct width', - path: 'radio-group/labels-highlight', - screenshotType: 'screenshotArea', - }, - { - description: 'Horizontal radio group permutations at 600', - path: 'radio-group/horizontal.permutations', - screenshotType: 'permutations', - configuration: { width: 600 }, - }, - { - description: 'Horizontal radio group permutations at 1280', - path: 'radio-group/horizontal.permutations', - screenshotType: 'permutations', - configuration: { width: 1280 }, - }, - { - description: 'Style custom page', - path: 'radio-group/style-custom', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/s3-resource-selector.ts b/test/definitions/visual/s3-resource-selector.ts deleted file mode 100644 index 54c39daada..0000000000 --- a/test/definitions/visual/s3-resource-selector.ts +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'S3 Resource Selector', - componentName: 's3-resource-selector', - tests: [ - { - description: 'Permutations', - path: 's3-resource-selector/permutations', - screenshotType: 'permutations', - }, - { - description: 'Browse dialog - plain', - path: 's3-resource-selector/permutations', - screenshotType: 'viewport', - configuration: { height: 1000 }, - setup: async ({ page }) => { - await page.click('button=Browse S3'); - await page.waitForVisible('[role="dialog"]'); - }, - }, - { - description: 'Browse dialog - with alert', - path: 's3-resource-selector/with-alert', - screenshotType: 'viewport', - configuration: { height: 1100 }, - setup: async ({ page }) => { - await page.click('button=Browse S3'); - await page.waitForVisible('[role="dialog"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/segmented-control.ts b/test/definitions/visual/segmented-control.ts deleted file mode 100644 index 5be8493c37..0000000000 --- a/test/definitions/visual/segmented-control.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'SegmentedControl', - componentName: 'segmented-control', - tests: [ - { - description: 'Permutations', - path: 'segmented-control/permutations', - screenshotType: 'permutations', - }, - { - description: 'Style Permutations', - path: 'segmented-control/style-permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/select.ts b/test/definitions/visual/select.ts deleted file mode 100644 index 84d6b3ac04..0000000000 --- a/test/definitions/visual/select.ts +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Select', - componentName: 'select', - tests: [ - { - description: 'component - dropdown closed', - path: 'select/screenshot', - screenshotType: 'screenshotArea', - }, - { - description: 'component - dropdown open - plain list', - path: 'select/screenshot', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-testid="select-demo"] button'); - }, - }, - { - description: 'component - dropdown open - virtual list', - path: 'select/screenshot', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#toggle-virtual'); - await page.click('[data-testid="select-demo"] button'); - }, - }, - { - description: 'component - dropdown open limited width - plain list', - path: 'select/screenshot', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-testid="select-demo-with-no-filtering-and-limited-width"] button'); - }, - }, - { - description: 'keyboard interaction - plain list', - path: 'select/screenshot', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-testid="select-demo-no-filtering"] button'); - await page.keys(['ArrowDown', 'Space']); - }, - }, - { - description: 'component - custom render option - plain list', - path: 'select/custom-render-option', - screenshotType: 'screenshotArea', - queryParams: { virtualScroll: 'false' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findSelect().findTrigger().toSelector()); - }, - }, - { - description: 'component - custom render option - virtual list', - path: 'select/custom-render-option', - screenshotType: 'screenshotArea', - queryParams: { virtualScroll: 'true' }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findSelect().findTrigger().toSelector()); - }, - }, - { - description: 'item permutations', - path: 'select/item.permutations', - screenshotType: 'permutations', - }, - { - description: 'selectable-item permutations', - path: 'selectable-item/permutations', - screenshotType: 'permutations', - }, - { - description: 'trigger permutations', - path: 'select/trigger.permutations', - screenshotType: 'permutations', - }, - { - description: 'Long virtual list - navigate to last item', - path: 'select/virtual-scroll', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findSelect().findTrigger().toSelector()); - await page.elementScrollTo(wrapper.findSelect().findDropdown().findOptionsContainer().toSelector(), { - top: 99999, - }); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/side-navigation.ts b/test/definitions/visual/side-navigation.ts deleted file mode 100644 index fce8be8eb1..0000000000 --- a/test/definitions/visual/side-navigation.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'SideNavigation', - componentName: 'side-navigation', - tests: [ - { - description: 'Permutations', - path: 'side-navigation/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/slider.ts b/test/definitions/visual/slider.ts deleted file mode 100644 index 49f7df5dfb..0000000000 --- a/test/definitions/visual/slider.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Slider', - componentName: 'slider', - tests: [ - { - description: 'permutations - standalone', - path: 'slider/permutations', - screenshotType: 'permutations', - configuration: { width: 800 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/space-between.ts b/test/definitions/visual/space-between.ts deleted file mode 100644 index 636d375d42..0000000000 --- a/test/definitions/visual/space-between.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'SpaceBetween', - componentName: 'space-between', - tests: [ - { - description: 'Permutations', - path: 'space-between/permutations', - screenshotType: 'permutations', - }, - { - description: 'in ColumnLayout and Grid', - path: 'space-between/nested-components', - screenshotType: 'screenshotArea', - }, - { - description: 'Alignment permutations', - path: 'space-between/alignment.permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/spinner.ts b/test/definitions/visual/spinner.ts deleted file mode 100644 index 65aa6c6ca1..0000000000 --- a/test/definitions/visual/spinner.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Spinner', - componentName: 'spinner', - tests: [ - { - description: 'Alignment with text', - path: 'spinner/text-align', - screenshotType: 'screenshotArea', - }, - { - description: 'Permutations', - path: 'spinner/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/split-panel.ts b/test/definitions/visual/split-panel.ts deleted file mode 100644 index cfbca3b7a5..0000000000 --- a/test/definitions/visual/split-panel.ts +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Split panel', - componentName: 'split-panel', - tests: [ - { - description: 'position bottom', - path: 'app-layout/with-split-panel', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Open panel'); - }, - }, - { - description: 'position bottom - closed', - path: 'app-layout/with-split-panel', - screenshotType: 'viewport', - }, - { - description: 'position side', - path: 'app-layout/with-split-panel', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Open panel'); - await page.click('aria/Preferences'); - await page.click('aria/Side'); - await page.click('aria/Confirm'); - }, - }, - { - description: 'position side with tools open', - path: 'app-layout/with-split-panel', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Open panel'); - await page.click('aria/Preferences'); - await page.click('aria/Side'); - await page.click('aria/Confirm'); - await page.waitForVisible('aria/Open tools'); - await page.click('aria/Open tools'); - }, - }, - { - description: 'position side - closed', - path: 'app-layout/with-split-panel', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Open panel'); - await page.click('[aria-label="Preferences"]'); - await page.click('aria/Side'); - await page.click('aria/Confirm'); - await page.click('aria/Close panel'); - }, - }, - { - description: 'preferences open', - path: 'app-layout/with-split-panel', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('aria/Open panel'); - await page.click('aria/Preferences'); - }, - }, - { - description: 'popover in bottom panel', - path: 'app-layout/with-split-panel', - screenshotType: 'viewport', - setup: async ({ page, wrapper, browser }) => { - await page.click('aria/Open panel'); - await page.click(wrapper.findSplitPanel().findOpenPanelBottom().findPopover().findTrigger().toSelector()); - browser.$('[data-testid="scroll-me"]').scrollIntoView(); - }, - }, - { - description: 'headerBefore, info link, actions and description', - path: 'app-layout/split-panel-with-custom-header', - screenshotType: 'screenshotArea', - queryParams: { renderActionsButtonDropdown: 'true', renderBeforeBadge: 'true', renderInfoLink: 'true' }, - }, - { - description: 'Entire layout defined in headerBefore slot', - path: 'app-layout/split-panel-with-custom-header', - screenshotType: 'screenshotArea', - queryParams: { renderActionsButtonDropdown: 'true', renderBeforeButtons: 'true' }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/status-indicator.ts b/test/definitions/visual/status-indicator.ts deleted file mode 100644 index 526849f4fa..0000000000 --- a/test/definitions/visual/status-indicator.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'StatusIndicator', - componentName: 'status-indicator', - tests: [ - { - description: 'permutations', - path: 'status-indicator/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/steps.ts b/test/definitions/visual/steps.ts deleted file mode 100644 index 3c21b870fb..0000000000 --- a/test/definitions/visual/steps.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Steps', - componentName: 'steps', - tests: [ - { - description: 'permutations', - path: 'steps/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/table-cells.ts b/test/definitions/visual/table-cells.ts deleted file mode 100644 index 38ebde3b8e..0000000000 --- a/test/definitions/visual/table-cells.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Table cells', - componentName: 'table-cells', - tests: [ - { - description: 'vertical align', - path: 'table/cell-permutations', - screenshotType: 'screenshotArea', - configuration: { width: 1200 }, - queryParams: { verticalAlignTop: 'true' }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/table-embedded.ts b/test/definitions/visual/table-embedded.ts deleted file mode 100644 index 06da54e843..0000000000 --- a/test/definitions/visual/table-embedded.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Table embedded', - componentName: 'table-embedded', - tests: [ - { - description: 'in alert', - path: 'table/embedded-in-alert', - screenshotType: 'viewport', - }, - { - description: 'stacked and container variants', - path: 'table/stacked-and-container-variant', - screenshotType: 'viewport', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/table-inline-editing.ts b/test/definitions/visual/table-inline-editing.ts deleted file mode 100644 index 123df5b2f1..0000000000 --- a/test/definitions/visual/table-inline-editing.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Table inline editing', - componentName: 'table-inline-editing', - tests: [ - { - description: 'permutations', - path: 'table/inline-editor.permutations', - screenshotType: 'permutations', - }, - { - description: 'active select editing', - path: 'table/editable', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[aria-label="Edit EKXAM4L45YPC8 TLS Version"]'); - }, - }, - { - description: 'active select editing with open dropdown', - path: 'table/editable', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - await page.click('[aria-label="Edit EKXAM4L45YPC8 TLS Version"]'); - await page.click(wrapper.findSelect().findTrigger().toSelector()); - }, - }, - { - description: 'hovering over cell, resizableColumns=${resizableColumns}', - path: 'table/editable', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.hoverElement('[aria-label="Edit EKXAM4L45YPC8 Domain name"]'); - }, - }, - { - description: 'active select editing with keyboard navigation', - path: 'table/editable', - screenshotType: 'screenshotArea', - configuration: { width: 1600 }, - queryParams: { enableKeyboardNavigation: 'true' }, - setup: async ({ page }) => { - await page.click('[data-testid="focus"]'); - await page.keys(['Tab', 'ArrowDown', 'ArrowRight', 'Enter']); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/table-resizable-columns.ts b/test/definitions/visual/table-resizable-columns.ts deleted file mode 100644 index 7f373760d8..0000000000 --- a/test/definitions/visual/table-resizable-columns.ts +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Table', - componentName: 'table-resizable-columns', - tests: [ - { - description: 'resizable columns permutations', - path: 'table/resizable-columns-permutations', - screenshotType: 'permutations', - }, - { - description: 'focused column resizer', - path: 'table/resizable-columns', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.buttonDownOnElement('#reset-state'); - await page.keys(['Tab']); - }, - }, - { - description: 'active column resizer on sticky header', - path: 'table/resizable-columns', - screenshotType: 'viewport', - setup: async ({ page, wrapper }) => { - const table = wrapper.findTable(); - await page.click('#sticky-header-toggle input'); - await page.windowScrollTo({ top: 600 }); - await page.buttonDownOnElement(table.findColumnResizer(3).toSelector()); - }, - }, - { - description: 'active column resizer', - path: 'table/resizable-columns', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const table = wrapper.findTable(); - await page.buttonDownOnElement(table.findColumnResizer(3).toSelector()); - }, - }, - { - description: 'pressed column resizer', - path: 'table/resizable-columns', - screenshotType: 'screenshotArea', - queryParams: { enableKeyboardNavigation: 'true' }, - setup: async ({ page, wrapper }) => { - const table = wrapper.findTable(); - await page.click(table.findHeaderSlot().toSelector()); - await page.keys(['Tab', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'Enter']); - }, - }, - { - description: 'pressed last column resizer', - path: 'table/resizable-columns', - screenshotType: 'screenshotArea', - queryParams: { enableKeyboardNavigation: 'true' }, - setup: async ({ page, wrapper }) => { - const table = wrapper.findTable(); - await page.click(table.findHeaderSlot().toSelector()); - await page.keys(['Tab', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'Enter']); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/table-sticky-header.ts b/test/definitions/visual/table-sticky-header.ts deleted file mode 100644 index 1f30619287..0000000000 --- a/test/definitions/visual/table-sticky-header.ts +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Table sticky header', - componentName: 'table-sticky-header', - tests: [ - { - description: 'initial state', - path: 'table/sticky-header', - screenshotType: 'viewport', - }, - { - description: 'mid-scroll sticky state - container variant', - path: 'table/sticky-header', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#container'); - await page.windowScrollTo({ top: 400 }); - }, - }, - { - description: 'mid-scroll sticky state - embedded variant', - path: 'table/sticky-header', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.click('#embedded'); - await page.windowScrollTo({ top: 400 }); - }, - }, - { - description: 'bottom sticky state', - path: 'table/sticky-header', - screenshotType: 'viewport', - setup: async ({ page }) => { - await page.windowScrollTo({ top: 925 }); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/table-sticky-scrollbar.ts b/test/definitions/visual/table-sticky-scrollbar.ts deleted file mode 100644 index 1f661f5a48..0000000000 --- a/test/definitions/visual/table-sticky-scrollbar.ts +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Table sticky scrollbar', - componentName: 'table-sticky-scrollbar', - tests: [ - { - description: 'on a plain page is positioned at the bottom', - path: 'table/sticky-scrollbar', - screenshotType: 'screenshotArea', - configuration: { width: 600, height: 800 }, - }, - { - description: 'on a page with app layout is positioned over the footer', - path: 'app-layout/with-table', - screenshotType: 'screenshotArea', - configuration: { width: 600, height: 800 }, - }, - { - description: 'on a page with app layout in a container is positioned over the bottom of the container', - path: 'app-layout/with-table-in-container', - screenshotType: 'screenshotArea', - configuration: { width: 600, height: 800 }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/table.ts b/test/definitions/visual/table.ts deleted file mode 100644 index 15ed94bab1..0000000000 --- a/test/definitions/visual/table.ts +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Table', - componentName: 'table', - tests: [ - { - description: 'simple permutations at ${width}', - path: 'table/simple-permutations', - screenshotType: 'permutations', - }, - { - description: 'permutations at ${width}', - path: 'table/permutations', - screenshotType: 'permutations', - }, - { - description: 'expandable rows permutations', - path: 'table/expandable-rows.permutations', - screenshotType: 'permutations', - }, - { - description: 'empty state – default', - path: 'table/empty-state', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - }, - { - description: 'empty state – scrolled horizontally', - path: 'table/empty-state', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - setup: async ({ page }) => { - await page.click('#scroll-content'); - }, - }, - { - description: 'with features', - path: 'table/hooks', - screenshotType: 'screenshotArea', - }, - { - description: 'with striped rows', - path: 'table/striped-rows', - screenshotType: 'screenshotArea', - }, - { - description: 'inside stacked container', - path: 'table/sticky-header-in-stacked-container', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.windowScrollTo({ top: 10 }); - await page.click('button=Actions'); - }, - }, - { - description: 'sticky header with open action button dropdown', - path: 'table/sticky-header-with-actions', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('[data-test-id="actions-button"]'); - }, - }, - { - description: 'variants', - path: 'table/variants', - screenshotType: 'screenshotArea', - }, - { - description: 'focus on last cell inline action', - path: 'table/inline-actions', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const tableWrapper = wrapper.findTable('[data-testid="table-with-dropdown-actions"]'); - await page.click(tableWrapper.findRows().get(1).findButtonDropdown().findNativeButton().toSelector()); - await page.keys(['Escape']); - }, - }, - { - description: 'focus on first cell link', - path: 'table/inline-actions', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const tableWrapper = wrapper.findTable('[data-testid="table-with-dropdown-actions"]'); - await page.click(tableWrapper.findRowSelectionArea(1).toSelector()); - await page.keys(['Tab']); - }, - }, - { - description: 'first column sticky state', - path: 'table/sticky-columns', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - queryParams: { stickyColumnsFirst: '1' }, - }, - { - description: 'first column sticky state and selection', - path: 'table/sticky-columns', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - queryParams: { stickyColumnsFirst: '1', selectionType: 'single' }, - }, - { - description: 'first column sticky state and striped rows', - path: 'table/sticky-columns', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - queryParams: { stickyColumnsFirst: '1', stripedRows: 'true' }, - }, - { - description: 'first two columns sticky state', - path: 'table/sticky-columns', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - queryParams: { stickyColumnsFirst: '2' }, - }, - { - description: 'last column sticky state', - path: 'table/sticky-columns', - screenshotType: 'screenshotArea', - configuration: { width: 800 }, - queryParams: { stickyColumnsLast: '1' }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/tabs.ts b/test/definitions/visual/tabs.ts deleted file mode 100644 index 5f7dceb628..0000000000 --- a/test/definitions/visual/tabs.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Tabs', - componentName: 'tabs', - tests: [ - { - description: 'Permutations', - path: 'tabs/permutations', - screenshotType: 'permutations', - }, - { - description: 'Responsive permutations', - path: 'tabs/responsive-permutations', - screenshotType: 'screenshotArea', - }, - { - description: 'focuses next tab header after clicking on tab header without an href', - path: 'tabs/integration-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#first-tabs li:nth-child(3) button'); - await page.keys('ArrowRight'); - }, - }, - { - description: 'layout at ${width}px', - path: 'tabs/integration-test', - screenshotType: 'screenshotArea', - }, - { - description: 'focus active tab - default variant', - path: 'tabs/integration-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#click-this'); - await page.focusNextElement(); - }, - }, - { - description: 'focus active tab - container variant', - path: 'tabs/integration-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#click-this-2'); - await page.focusNextElement(); - }, - }, - { - description: 'focus content - default variant', - path: 'tabs/integration-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#click-this'); - await page.focusNextElement(); - await page.focusNextElement(); - }, - }, - { - description: 'focus content - container variant', - path: 'tabs/integration-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#click-this-2'); - await page.focusNextElement(); - await page.focusNextElement(); - }, - }, - { - description: 'Style Permutations', - path: 'tabs/style-permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/tag-editor.ts b/test/definitions/visual/tag-editor.ts deleted file mode 100644 index 041fdbef6a..0000000000 --- a/test/definitions/visual/tag-editor.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'TagEditor', - componentName: 'tag-editor', - tests: [ - { - description: 'Permutations', - path: 'tag-editor/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/text-content.ts b/test/definitions/visual/text-content.ts deleted file mode 100644 index 29b72be343..0000000000 --- a/test/definitions/visual/text-content.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'TextContainer', - componentName: 'text-content', - tests: [ - { - description: 'color property', - path: 'text-content/permutations', - screenshotType: 'screenshotArea', - }, - { - description: 'with nested Link components', - path: 'text-content/link-nesting', - screenshotType: 'screenshotArea', - }, - { - description: 'with nested Box components', - path: 'text-content/box-nesting', - screenshotType: 'screenshotArea', - }, - { - description: 'with icons in content', - path: 'text-content/iconography', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/text-filter.ts b/test/definitions/visual/text-filter.ts deleted file mode 100644 index f291e70574..0000000000 --- a/test/definitions/visual/text-filter.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'TextFilter', - componentName: 'text-filter', - tests: [ - { - description: 'permutations', - path: 'text-filter/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/textarea.ts b/test/definitions/visual/textarea.ts deleted file mode 100644 index c497230b61..0000000000 --- a/test/definitions/visual/textarea.ts +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const testStates = [ - { toggle: 'toggle-invalid', label: 'invalid', isInteractive: true }, - { toggle: 'toggle-disabled', label: 'disabled', isInteractive: false }, - { toggle: 'toggle-readonly', label: 'readonly', isInteractive: true }, - { toggle: 'toggle-warning', label: 'warning', isInteractive: true }, -]; - -function pseudoSelectorTests(withStyling: boolean): TestSuite { - return { - description: withStyling ? 'Pseudo Selectors with Custom Styling' : 'Pseudo Selectors', - tests: testStates.flatMap(state => [ - { - description: `${state.label} - focus`, - path: 'textarea/pseudo-selectors', - screenshotType: 'screenshotArea' as const, - setup: async ({ page, wrapper }) => { - const textareaSelector = wrapper - .findTextarea('[data-testid="test-textarea"]') - .findNativeTextarea() - .toSelector(); - if (withStyling) { - await page.click('#toggle-styling'); - } - await page.click(`#${state.toggle}`); - if (state.isInteractive) { - await page.click(textareaSelector); - } - }, - }, - { - description: `${state.label} - focus + hover`, - path: 'textarea/pseudo-selectors', - screenshotType: 'screenshotArea' as const, - setup: async ({ page, wrapper }) => { - const textareaSelector = wrapper - .findTextarea('[data-testid="test-textarea"]') - .findNativeTextarea() - .toSelector(); - if (withStyling) { - await page.click('#toggle-styling'); - } - await page.click(`#${state.toggle}`); - if (state.isInteractive) { - await page.click(textareaSelector); - } - await page.hoverElement(textareaSelector); - }, - }, - ]), - }; -} - -const suite: TestSuite = { - description: 'Textarea', - componentName: 'textarea', - tests: [ - { - description: 'Permutations', - path: 'textarea/permutations', - screenshotType: 'permutations', - }, - { - description: 'Style Permutations', - path: 'textarea/style-permutations', - screenshotType: 'permutations', - }, - pseudoSelectorTests(false), - pseudoSelectorTests(true), - ], -}; - -export default suite; diff --git a/test/definitions/visual/tiles.ts b/test/definitions/visual/tiles.ts deleted file mode 100644 index 51c290fc80..0000000000 --- a/test/definitions/visual/tiles.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Tiles', - componentName: 'tiles', - tests: [ - { - description: 'tiles at "${breakpoint}"', - path: 'tiles/simple', - screenshotType: 'screenshotArea', - }, - { - description: 'permutations at "${breakpoint}"', - path: 'tiles/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/time-input.ts b/test/definitions/visual/time-input.ts deleted file mode 100644 index b2e6fc2dcc..0000000000 --- a/test/definitions/visual/time-input.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Time Input', - componentName: 'time-input', - tests: [ - { - description: 'Permutations', - path: 'time-input/permutations', - screenshotType: 'permutations', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/toggle-button.ts b/test/definitions/visual/toggle-button.ts deleted file mode 100644 index c7b55e8079..0000000000 --- a/test/definitions/visual/toggle-button.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Toggle button', - componentName: 'toggle-button', - tests: [ - { - description: 'permutations', - path: 'toggle-button/permutations', - screenshotType: 'permutations', - }, - { - description: 'hovering over normal variant', - path: 'toggle-button/permutations', - screenshotType: 'permutations', - setup: async ({ page }) => { - await page.hoverElement('[aria-label="Favorite"][aria-pressed="false"]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/toggle.ts b/test/definitions/visual/toggle.ts deleted file mode 100644 index f2e3bd0cd1..0000000000 --- a/test/definitions/visual/toggle.ts +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Toggle', - componentName: 'toggle', - tests: [ - { - description: 'Permutations', - path: 'toggle/permutations', - screenshotType: 'permutations', - }, - { - description: 'Toggle is focused', - path: 'toggle/focus-test', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: 'Toggle has label with a correct width', - path: 'toggle/labels-highlight', - screenshotType: 'screenshotArea', - }, - { - description: 'Style custom page', - path: 'toggle/style-custom', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/token-group.ts b/test/definitions/visual/token-group.ts deleted file mode 100644 index 99ac6b49e6..0000000000 --- a/test/definitions/visual/token-group.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'TokenGroup', - componentName: 'token-group', - tests: [ - { - description: 'Permutations', - path: 'token-group/permutations', - screenshotType: 'permutations', - }, - { - description: 'Simple', - path: 'token-group/index', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/top-navigation.ts b/test/definitions/visual/top-navigation.ts deleted file mode 100644 index d21c71c1fb..0000000000 --- a/test/definitions/visual/top-navigation.ts +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Top navigation', - componentName: 'top-navigation', - tests: [ - { - description: 'Responsiveness', - path: 'top-navigation/screenshot', - screenshotType: 'screenshotArea', - configuration: { width: 1300 }, - }, - { - description: 'Dropdown menu utility', - path: 'top-navigation/scenario-full-page', - screenshotType: 'viewport', - configuration: { width: 1300, height: 800 }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findTopNavigation().findUtility(4).toSelector()); - }, - }, - { - description: 'Utility permutations', - path: 'top-navigation/utility.permutations', - screenshotType: 'permutations', - }, - { - description: 'Overflow menu - outer', - path: 'top-navigation/scenario-full-page', - screenshotType: 'viewport', - configuration: { width: 500, height: 800 }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findTopNavigation().findOverflowMenuButton().toSelector()); - await page.keys(['Tab']); - }, - }, - { - description: 'Overflow menu - dropdown', - path: 'top-navigation/scenario-full-page', - screenshotType: 'viewport', - configuration: { width: 500, height: 800 }, - setup: async ({ page, wrapper }) => { - await page.click(wrapper.findTopNavigation().findOverflowMenuButton().toSelector()); - await page.click(wrapper.findTopNavigation().findOverflowMenu().findUtility(3).toSelector()); - await page.keys(['Tab']); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/tree-view.ts b/test/definitions/visual/tree-view.ts deleted file mode 100644 index 4809c79fa2..0000000000 --- a/test/definitions/visual/tree-view.ts +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Tree View', - componentName: 'tree-view', - tests: [ - { - description: 'basic', - path: 'tree-view/basic', - screenshotType: 'screenshotArea', - }, - { - description: 'with different toggle icon', - path: 'tree-view/basic', - screenshotType: 'screenshotArea', - setup: async ({ page, wrapper }) => { - const select = wrapper.findSelect(); - await page.click(select.findTrigger().toSelector()); - await page.click(select.findDropdown().findOptionByValue('custom').toSelector()); - }, - }, - ], -}; - -export default suite; diff --git a/test/definitions/visual/wizard.ts b/test/definitions/visual/wizard.ts deleted file mode 100644 index 51864a6396..0000000000 --- a/test/definitions/visual/wizard.ts +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'Wizard', - componentName: 'wizard', - tests: [ - ...[600, 1280].map(width => ({ - description: `width ${width}px`, - tests: [ - { - description: 'first step', - path: 'wizard/wizard-screenshot', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - { - description: 'second step', - path: 'wizard/wizard-screenshot', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - setup: async ({ page }: { page: any }) => { - await page.click('#next'); - }, - }, - ], - })), - { - description: 'steps menu expanded in mobile view', - path: 'wizard/wizard-screenshot', - screenshotType: 'screenshotArea', - configuration: { width: 600 }, - setup: async ({ page }) => { - await page.click('[role="button"][aria-expanded]'); - }, - }, - ], -}; - -export default suite; diff --git a/test/visual/app-layout-content-paddings.test.ts b/test/visual/app-layout-content-paddings.test.ts deleted file mode 100644 index e566d79817..0000000000 --- a/test/visual/app-layout-content-paddings.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-content-paddings'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-drawers.test.ts b/test/visual/app-layout-drawers.test.ts deleted file mode 100644 index c66454010d..0000000000 --- a/test/visual/app-layout-drawers.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-drawers'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-flashbar.test.ts b/test/visual/app-layout-flashbar.test.ts deleted file mode 100644 index 333642e5f3..0000000000 --- a/test/visual/app-layout-flashbar.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-flashbar'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-header.test.ts b/test/visual/app-layout-header.test.ts deleted file mode 100644 index 682f71ffe2..0000000000 --- a/test/visual/app-layout-header.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-header'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-multi.test.ts b/test/visual/app-layout-multi.test.ts deleted file mode 100644 index 244019c8fb..0000000000 --- a/test/visual/app-layout-multi.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-multi'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1280.test.ts b/test/visual/app-layout-responsive-1280.test.ts deleted file mode 100644 index 0324b2bf39..0000000000 --- a/test/visual/app-layout-responsive-1280.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-1280'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1400.test.ts b/test/visual/app-layout-responsive-1400.test.ts deleted file mode 100644 index 745372c34e..0000000000 --- a/test/visual/app-layout-responsive-1400.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-1400'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-1920.test.ts b/test/visual/app-layout-responsive-1920.test.ts deleted file mode 100644 index bee5fc8763..0000000000 --- a/test/visual/app-layout-responsive-1920.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-1920'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-2540.test.ts b/test/visual/app-layout-responsive-2540.test.ts deleted file mode 100644 index 48bc8580da..0000000000 --- a/test/visual/app-layout-responsive-2540.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-2540'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-responsive-600.test.ts b/test/visual/app-layout-responsive-600.test.ts deleted file mode 100644 index cd9243cb32..0000000000 --- a/test/visual/app-layout-responsive-600.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-responsive-600'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-sticky-table-header-split-panel.test.ts b/test/visual/app-layout-sticky-table-header-split-panel.test.ts deleted file mode 100644 index c1ad3016a1..0000000000 --- a/test/visual/app-layout-sticky-table-header-split-panel.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-sticky-table-header-split-panel'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-toolbar.test.ts b/test/visual/app-layout-toolbar.test.ts deleted file mode 100644 index 398d6386f8..0000000000 --- a/test/visual/app-layout-toolbar.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-toolbar'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout-z-index.test.ts b/test/visual/app-layout-z-index.test.ts deleted file mode 100644 index 5f69f77b71..0000000000 --- a/test/visual/app-layout-z-index.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout-z-index'; - -runTestSuites([suite]); diff --git a/test/visual/app-layout.test.ts b/test/visual/app-layout.test.ts deleted file mode 100644 index 21c3a6ce25..0000000000 --- a/test/visual/app-layout.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/app-layout'; - -runTestSuites([suite]); diff --git a/test/visual/area-chart.test.ts b/test/visual/area-chart.test.ts deleted file mode 100644 index 4ffc4e042e..0000000000 --- a/test/visual/area-chart.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/area-chart'; - -runTestSuites([suite]); diff --git a/test/visual/attribute-editor.test.ts b/test/visual/attribute-editor.test.ts deleted file mode 100644 index 6a57853125..0000000000 --- a/test/visual/attribute-editor.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/attribute-editor'; - -runTestSuites([suite]); diff --git a/test/visual/autosuggest.test.ts b/test/visual/autosuggest.test.ts deleted file mode 100644 index d88cad7117..0000000000 --- a/test/visual/autosuggest.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/autosuggest'; - -runTestSuites([suite]); diff --git a/test/visual/badge.test.ts b/test/visual/badge.test.ts deleted file mode 100644 index 1163052443..0000000000 --- a/test/visual/badge.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/badge'; - -runTestSuites([suite]); diff --git a/test/visual/bar-chart.test.ts b/test/visual/bar-chart.test.ts deleted file mode 100644 index d54245d935..0000000000 --- a/test/visual/bar-chart.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/bar-chart'; - -runTestSuites([suite]); diff --git a/test/visual/box.test.ts b/test/visual/box.test.ts deleted file mode 100644 index 9422b04f64..0000000000 --- a/test/visual/box.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/box'; - -runTestSuites([suite]); diff --git a/test/visual/breadcrumb-group.test.ts b/test/visual/breadcrumb-group.test.ts deleted file mode 100644 index 77450f3700..0000000000 --- a/test/visual/breadcrumb-group.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/breadcrumb-group'; - -runTestSuites([suite]); diff --git a/test/visual/button-dropdown.test.ts b/test/visual/button-dropdown.test.ts deleted file mode 100644 index 16b90ff4da..0000000000 --- a/test/visual/button-dropdown.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/button-dropdown'; - -runTestSuites([suite]); diff --git a/test/visual/button-group.test.ts b/test/visual/button-group.test.ts deleted file mode 100644 index 100c7f9508..0000000000 --- a/test/visual/button-group.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/button-group'; - -runTestSuites([suite]); diff --git a/test/visual/button.test.ts b/test/visual/button.test.ts deleted file mode 100644 index d4b642af53..0000000000 --- a/test/visual/button.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/button'; - -runTestSuites([suite]); diff --git a/test/visual/cards.test.ts b/test/visual/cards.test.ts deleted file mode 100644 index ecb282b266..0000000000 --- a/test/visual/cards.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/cards'; - -runTestSuites([suite]); diff --git a/test/visual/checkbox.test.ts b/test/visual/checkbox.test.ts deleted file mode 100644 index f569e2e7ab..0000000000 --- a/test/visual/checkbox.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/checkbox'; - -runTestSuites([suite]); diff --git a/test/visual/code-editor.test.ts b/test/visual/code-editor.test.ts deleted file mode 100644 index 735638ea5d..0000000000 --- a/test/visual/code-editor.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/code-editor'; - -runTestSuites([suite]); diff --git a/test/visual/collection-preferences.test.ts b/test/visual/collection-preferences.test.ts deleted file mode 100644 index ac54029622..0000000000 --- a/test/visual/collection-preferences.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/collection-preferences'; - -runTestSuites([suite]); diff --git a/test/visual/column-layout.test.ts b/test/visual/column-layout.test.ts deleted file mode 100644 index 55749a8e8b..0000000000 --- a/test/visual/column-layout.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/column-layout'; - -runTestSuites([suite]); diff --git a/test/visual/container-sticky.test.ts b/test/visual/container-sticky.test.ts deleted file mode 100644 index ce014d9805..0000000000 --- a/test/visual/container-sticky.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/container-sticky'; - -runTestSuites([suite]); diff --git a/test/visual/container.test.ts b/test/visual/container.test.ts deleted file mode 100644 index d738bd8429..0000000000 --- a/test/visual/container.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/container'; - -runTestSuites([suite]); diff --git a/test/visual/content-layout-permutations.test.ts b/test/visual/content-layout-permutations.test.ts deleted file mode 100644 index 20de7543dc..0000000000 --- a/test/visual/content-layout-permutations.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/content-layout-permutations'; - -runTestSuites([suite]); diff --git a/test/visual/content-layout.test.ts b/test/visual/content-layout.test.ts deleted file mode 100644 index cc62ebf2a4..0000000000 --- a/test/visual/content-layout.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/content-layout'; - -runTestSuites([suite]); diff --git a/test/visual/copy-to-clipboard.test.ts b/test/visual/copy-to-clipboard.test.ts deleted file mode 100644 index 8f6f3afa5a..0000000000 --- a/test/visual/copy-to-clipboard.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/copy-to-clipboard'; - -runTestSuites([suite]); diff --git a/test/visual/date-input.test.ts b/test/visual/date-input.test.ts deleted file mode 100644 index d711364611..0000000000 --- a/test/visual/date-input.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/date-input'; - -runTestSuites([suite]); diff --git a/test/visual/date-picker.test.ts b/test/visual/date-picker.test.ts deleted file mode 100644 index fd7087585b..0000000000 --- a/test/visual/date-picker.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/date-picker'; - -runTestSuites([suite]); diff --git a/test/visual/date-range-picker.test.ts b/test/visual/date-range-picker.test.ts deleted file mode 100644 index df5ed2d355..0000000000 --- a/test/visual/date-range-picker.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/date-range-picker'; - -runTestSuites([suite]); diff --git a/test/visual/divider.test.ts b/test/visual/divider.test.ts deleted file mode 100644 index 30305f9eff..0000000000 --- a/test/visual/divider.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/divider'; - -runTestSuites([suite]); diff --git a/test/visual/drawer.test.ts b/test/visual/drawer.test.ts deleted file mode 100644 index 0a16626101..0000000000 --- a/test/visual/drawer.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/drawer'; - -runTestSuites([suite]); diff --git a/test/visual/dropdown.test.ts b/test/visual/dropdown.test.ts deleted file mode 100644 index 1dbb5ca9b2..0000000000 --- a/test/visual/dropdown.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/dropdown'; - -runTestSuites([suite]); diff --git a/test/visual/expandable-section.test.ts b/test/visual/expandable-section.test.ts deleted file mode 100644 index b052d5cf5f..0000000000 --- a/test/visual/expandable-section.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/expandable-section'; - -runTestSuites([suite]); diff --git a/test/visual/file-dropzone.test.ts b/test/visual/file-dropzone.test.ts deleted file mode 100644 index ad937625a3..0000000000 --- a/test/visual/file-dropzone.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/file-dropzone'; - -runTestSuites([suite]); diff --git a/test/visual/file-input.test.ts b/test/visual/file-input.test.ts deleted file mode 100644 index 918feeb9ff..0000000000 --- a/test/visual/file-input.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/file-input'; - -runTestSuites([suite]); diff --git a/test/visual/file-token-group.test.ts b/test/visual/file-token-group.test.ts deleted file mode 100644 index 96b0e7396b..0000000000 --- a/test/visual/file-token-group.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/file-token-group'; - -runTestSuites([suite]); diff --git a/test/visual/file-upload.test.ts b/test/visual/file-upload.test.ts deleted file mode 100644 index 06dafb87ca..0000000000 --- a/test/visual/file-upload.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/file-upload'; - -runTestSuites([suite]); diff --git a/test/visual/flashbar-stacked.test.ts b/test/visual/flashbar-stacked.test.ts deleted file mode 100644 index 5e37f288af..0000000000 --- a/test/visual/flashbar-stacked.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/flashbar-stacked'; - -runTestSuites([suite]); diff --git a/test/visual/flashbar.test.ts b/test/visual/flashbar.test.ts deleted file mode 100644 index d02aaae84c..0000000000 --- a/test/visual/flashbar.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/flashbar'; - -runTestSuites([suite]); diff --git a/test/visual/form-field.test.ts b/test/visual/form-field.test.ts deleted file mode 100644 index 8406e9351b..0000000000 --- a/test/visual/form-field.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/form-field'; - -runTestSuites([suite]); diff --git a/test/visual/form.test.ts b/test/visual/form.test.ts deleted file mode 100644 index 89a44f2ec8..0000000000 --- a/test/visual/form.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/form'; - -runTestSuites([suite]); diff --git a/test/visual/grid.test.ts b/test/visual/grid.test.ts deleted file mode 100644 index 28313ba4f2..0000000000 --- a/test/visual/grid.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/grid'; - -runTestSuites([suite]); diff --git a/test/visual/header.test.ts b/test/visual/header.test.ts deleted file mode 100644 index c08e211517..0000000000 --- a/test/visual/header.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/header'; - -runTestSuites([suite]); diff --git a/test/visual/help-panel.test.ts b/test/visual/help-panel.test.ts deleted file mode 100644 index 10fe7ed091..0000000000 --- a/test/visual/help-panel.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/help-panel'; - -runTestSuites([suite]); diff --git a/test/visual/icon.test.ts b/test/visual/icon.test.ts deleted file mode 100644 index ecd9f9656a..0000000000 --- a/test/visual/icon.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/icon'; - -runTestSuites([suite]); diff --git a/test/visual/input.test.ts b/test/visual/input.test.ts deleted file mode 100644 index 361de80dd5..0000000000 --- a/test/visual/input.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/input'; - -runTestSuites([suite]); diff --git a/test/visual/item-card.test.ts b/test/visual/item-card.test.ts deleted file mode 100644 index 1c06f6493b..0000000000 --- a/test/visual/item-card.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/item-card'; - -runTestSuites([suite]); diff --git a/test/visual/key-value-pairs.test.ts b/test/visual/key-value-pairs.test.ts deleted file mode 100644 index 7702a5645b..0000000000 --- a/test/visual/key-value-pairs.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/key-value-pairs'; - -runTestSuites([suite]); diff --git a/test/visual/line-chart.test.ts b/test/visual/line-chart.test.ts deleted file mode 100644 index d6d088adf9..0000000000 --- a/test/visual/line-chart.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/line-chart'; - -runTestSuites([suite]); diff --git a/test/visual/link.test.ts b/test/visual/link.test.ts deleted file mode 100644 index bd34346c9f..0000000000 --- a/test/visual/link.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/link'; - -runTestSuites([suite]); diff --git a/test/visual/list.test.ts b/test/visual/list.test.ts deleted file mode 100644 index aae3228105..0000000000 --- a/test/visual/list.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/list'; - -runTestSuites([suite]); diff --git a/test/visual/mixed-line-bar-chart.test.ts b/test/visual/mixed-line-bar-chart.test.ts deleted file mode 100644 index 0409acf8a6..0000000000 --- a/test/visual/mixed-line-bar-chart.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/mixed-line-bar-chart'; - -runTestSuites([suite]); diff --git a/test/visual/modal.test.ts b/test/visual/modal.test.ts deleted file mode 100644 index ad2c71ea48..0000000000 --- a/test/visual/modal.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/modal'; - -runTestSuites([suite]); diff --git a/test/visual/multiselect.test.ts b/test/visual/multiselect.test.ts deleted file mode 100644 index 632ad5afaa..0000000000 --- a/test/visual/multiselect.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/multiselect'; - -runTestSuites([suite]); diff --git a/test/visual/pagination.test.ts b/test/visual/pagination.test.ts deleted file mode 100644 index 0a868faa5d..0000000000 --- a/test/visual/pagination.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/pagination'; - -runTestSuites([suite]); diff --git a/test/visual/pie-chart.test.ts b/test/visual/pie-chart.test.ts deleted file mode 100644 index 31d111a8db..0000000000 --- a/test/visual/pie-chart.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/pie-chart'; - -runTestSuites([suite]); diff --git a/test/visual/popover.test.ts b/test/visual/popover.test.ts deleted file mode 100644 index 1573aa33d1..0000000000 --- a/test/visual/popover.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/popover'; - -runTestSuites([suite]); diff --git a/test/visual/progress-bar.test.ts b/test/visual/progress-bar.test.ts deleted file mode 100644 index 8bbd40ad30..0000000000 --- a/test/visual/progress-bar.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/progress-bar'; - -runTestSuites([suite]); diff --git a/test/visual/prompt-input.test.ts b/test/visual/prompt-input.test.ts deleted file mode 100644 index c8dbcac0b4..0000000000 --- a/test/visual/prompt-input.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/prompt-input'; - -runTestSuites([suite]); diff --git a/test/visual/property-filter.test.ts b/test/visual/property-filter.test.ts deleted file mode 100644 index d7290f1142..0000000000 --- a/test/visual/property-filter.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/property-filter'; - -runTestSuites([suite]); diff --git a/test/visual/radio-button.test.ts b/test/visual/radio-button.test.ts deleted file mode 100644 index 527390dcf0..0000000000 --- a/test/visual/radio-button.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/radio-button'; - -runTestSuites([suite]); diff --git a/test/visual/radio-group.test.ts b/test/visual/radio-group.test.ts deleted file mode 100644 index b267dc05e3..0000000000 --- a/test/visual/radio-group.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/radio-group'; - -runTestSuites([suite]); diff --git a/test/visual/s3-resource-selector.test.ts b/test/visual/s3-resource-selector.test.ts deleted file mode 100644 index f38b1750cc..0000000000 --- a/test/visual/s3-resource-selector.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/s3-resource-selector'; - -runTestSuites([suite]); diff --git a/test/visual/segmented-control.test.ts b/test/visual/segmented-control.test.ts deleted file mode 100644 index d96906c91d..0000000000 --- a/test/visual/segmented-control.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/segmented-control'; - -runTestSuites([suite]); diff --git a/test/visual/select.test.ts b/test/visual/select.test.ts deleted file mode 100644 index d3adc1dc91..0000000000 --- a/test/visual/select.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/select'; - -runTestSuites([suite]); diff --git a/test/visual/side-navigation.test.ts b/test/visual/side-navigation.test.ts deleted file mode 100644 index 91aa6d4895..0000000000 --- a/test/visual/side-navigation.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/side-navigation'; - -runTestSuites([suite]); diff --git a/test/visual/slider.test.ts b/test/visual/slider.test.ts deleted file mode 100644 index ea85f9c860..0000000000 --- a/test/visual/slider.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/slider'; - -runTestSuites([suite]); diff --git a/test/visual/space-between.test.ts b/test/visual/space-between.test.ts deleted file mode 100644 index 4afe1219bc..0000000000 --- a/test/visual/space-between.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/space-between'; - -runTestSuites([suite]); diff --git a/test/visual/spinner.test.ts b/test/visual/spinner.test.ts deleted file mode 100644 index 38e1ede756..0000000000 --- a/test/visual/spinner.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/spinner'; - -runTestSuites([suite]); diff --git a/test/visual/split-panel.test.ts b/test/visual/split-panel.test.ts deleted file mode 100644 index c9268d3c3a..0000000000 --- a/test/visual/split-panel.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/split-panel'; - -runTestSuites([suite]); diff --git a/test/visual/status-indicator.test.ts b/test/visual/status-indicator.test.ts deleted file mode 100644 index 88e5d5942d..0000000000 --- a/test/visual/status-indicator.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/status-indicator'; - -runTestSuites([suite]); diff --git a/test/visual/steps.test.ts b/test/visual/steps.test.ts deleted file mode 100644 index c82db29656..0000000000 --- a/test/visual/steps.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/steps'; - -runTestSuites([suite]); diff --git a/test/visual/table-cells.test.ts b/test/visual/table-cells.test.ts deleted file mode 100644 index 31e495ea22..0000000000 --- a/test/visual/table-cells.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/table-cells'; - -runTestSuites([suite]); diff --git a/test/visual/table-embedded.test.ts b/test/visual/table-embedded.test.ts deleted file mode 100644 index e158981079..0000000000 --- a/test/visual/table-embedded.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/table-embedded'; - -runTestSuites([suite]); diff --git a/test/visual/table-inline-editing.test.ts b/test/visual/table-inline-editing.test.ts deleted file mode 100644 index d6321b5b38..0000000000 --- a/test/visual/table-inline-editing.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/table-inline-editing'; - -runTestSuites([suite]); diff --git a/test/visual/table-resizable-columns.test.ts b/test/visual/table-resizable-columns.test.ts deleted file mode 100644 index bb0a535139..0000000000 --- a/test/visual/table-resizable-columns.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/table-resizable-columns'; - -runTestSuites([suite]); diff --git a/test/visual/table-sticky-header.test.ts b/test/visual/table-sticky-header.test.ts deleted file mode 100644 index 68a994750e..0000000000 --- a/test/visual/table-sticky-header.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/table-sticky-header'; - -runTestSuites([suite]); diff --git a/test/visual/table-sticky-scrollbar.test.ts b/test/visual/table-sticky-scrollbar.test.ts deleted file mode 100644 index 2ac31ef9b9..0000000000 --- a/test/visual/table-sticky-scrollbar.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/table-sticky-scrollbar'; - -runTestSuites([suite]); diff --git a/test/visual/table.test.ts b/test/visual/table.test.ts deleted file mode 100644 index 2b2bc6a6d9..0000000000 --- a/test/visual/table.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/table'; - -runTestSuites([suite]); diff --git a/test/visual/tabs.test.ts b/test/visual/tabs.test.ts deleted file mode 100644 index 1829134d38..0000000000 --- a/test/visual/tabs.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/tabs'; - -runTestSuites([suite]); diff --git a/test/visual/tag-editor.test.ts b/test/visual/tag-editor.test.ts deleted file mode 100644 index 8edbf8e05d..0000000000 --- a/test/visual/tag-editor.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/tag-editor'; - -runTestSuites([suite]); diff --git a/test/visual/text-content.test.ts b/test/visual/text-content.test.ts deleted file mode 100644 index 9bc8a0e784..0000000000 --- a/test/visual/text-content.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/text-content'; - -runTestSuites([suite]); diff --git a/test/visual/text-filter.test.ts b/test/visual/text-filter.test.ts deleted file mode 100644 index 218fd62481..0000000000 --- a/test/visual/text-filter.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/text-filter'; - -runTestSuites([suite]); diff --git a/test/visual/textarea.test.ts b/test/visual/textarea.test.ts deleted file mode 100644 index 3282ff813b..0000000000 --- a/test/visual/textarea.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/textarea'; - -runTestSuites([suite]); diff --git a/test/visual/tiles.test.ts b/test/visual/tiles.test.ts deleted file mode 100644 index 3d7d1e5329..0000000000 --- a/test/visual/tiles.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/tiles'; - -runTestSuites([suite]); diff --git a/test/visual/time-input.test.ts b/test/visual/time-input.test.ts deleted file mode 100644 index 0d96ace144..0000000000 --- a/test/visual/time-input.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/time-input'; - -runTestSuites([suite]); diff --git a/test/visual/toggle-button.test.ts b/test/visual/toggle-button.test.ts deleted file mode 100644 index be636ceb49..0000000000 --- a/test/visual/toggle-button.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/toggle-button'; - -runTestSuites([suite]); diff --git a/test/visual/toggle.test.ts b/test/visual/toggle.test.ts deleted file mode 100644 index 2ea4312468..0000000000 --- a/test/visual/toggle.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/toggle'; - -runTestSuites([suite]); diff --git a/test/visual/token-group.test.ts b/test/visual/token-group.test.ts deleted file mode 100644 index 7e20f831e4..0000000000 --- a/test/visual/token-group.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/token-group'; - -runTestSuites([suite]); diff --git a/test/visual/top-navigation.test.ts b/test/visual/top-navigation.test.ts deleted file mode 100644 index 12d9ed80d8..0000000000 --- a/test/visual/top-navigation.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/top-navigation'; - -runTestSuites([suite]); diff --git a/test/visual/tree-view.test.ts b/test/visual/tree-view.test.ts deleted file mode 100644 index e9cb8d76c7..0000000000 --- a/test/visual/tree-view.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/tree-view'; - -runTestSuites([suite]); diff --git a/test/visual/wizard.test.ts b/test/visual/wizard.test.ts deleted file mode 100644 index 45ee321225..0000000000 --- a/test/visual/wizard.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { runTestSuites } from '../definitions/utils'; -import suite from '../definitions/visual/wizard'; - -runTestSuites([suite]); From 4878b2e3de5ed8903d2f004c8444c0bbd588d2ed Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 25 Jun 2026 18:10:41 +0200 Subject: [PATCH 126/135] Syntax fix --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 833c6ef980..d454c0d441 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,7 +16,7 @@ jobs: # Produces the artifact consumed by deploy, allowing it to start # before the full build+test job finishes. quick-build: - name: quick-build $({{format('React {0}', matrix.react)}}) + name: quick-build (${{format('React {0}', matrix.react)}}) # skip this job for external contributions if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} strategy: From e5f55e93d5066dfe0cb798143abb261b6b5291c3 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 25 Jun 2026 18:11:57 +0200 Subject: [PATCH 127/135] Syntax fixes --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d454c0d441..127ea55dfc 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -62,7 +62,7 @@ jobs: deploy: needs: quick-build - name: deploy $({{format('React {0}', matrix.react)}}) + name: deploy (${{format('React {0}', matrix.react)}}) if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} strategy: matrix: From d8a9dbf7d46e6a867c4b0668a01f88259a305467 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 25 Jun 2026 18:13:23 +0200 Subject: [PATCH 128/135] Reduce shards --- .github/workflows/visual-regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index a462d2f549..8e9e17c9a8 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40] + shard: [1, 2] steps: - uses: actions/checkout@v4 @@ -103,7 +103,7 @@ jobs: run: | echo "NEW_HOST=${NEW_HOST}" echo "OLD_HOST=${OLD_HOST}" - NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard="${SHARD}/40" + NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard="${SHARD}/2" env: TZ: UTC SHARD: ${{ matrix.shard }} From a342cb57c6fd17bb3aa0a2ddda14e6b8573c80ac Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 26 Jun 2026 12:34:04 +0200 Subject: [PATCH 129/135] Make browser optional --- test/definitions/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/definitions/types.ts b/test/definitions/types.ts index 43884f3345..33ce6cbefd 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -24,7 +24,7 @@ export interface TestDefinition { queryParams?: Record; configuration?: ScreenshotTestConfiguration; pixelDiffTolerance?: number; - setup?: ({ page, wrapper, browser }: { page: BasePageObject; wrapper: Wrapper; browser: Browser }) => void; + setup?: ({ page, wrapper, browser }: { page: BasePageObject; wrapper: Wrapper; browser?: Browser }) => void; } export interface TestSuite { From 8569e52b82c81472a7859e0a197a5fa807d6f7ef Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 26 Jun 2026 12:38:56 +0200 Subject: [PATCH 130/135] Remove outdated imports --- test/definitions/index.ts | 205 +------------------------------------- 1 file changed, 1 insertion(+), 204 deletions(-) diff --git a/test/definitions/index.ts b/test/definitions/index.ts index f870e6df76..8c8c5e9eef 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -5,208 +5,5 @@ import { TestSuite } from './types'; export { TestSuite, TestDefinition, ScreenshotType, ScreenshotTestConfiguration } from './types'; import actionCard from './visual/action-card'; import alert from './visual/alert'; -import appLayout from './visual/app-layout'; -import appLayoutContentPaddings from './visual/app-layout-content-paddings'; -import appLayoutDrawers from './visual/app-layout-drawers'; -import appLayoutFlashbar from './visual/app-layout-flashbar'; -import appLayoutHeader from './visual/app-layout-header'; -import appLayoutMulti from './visual/app-layout-multi'; -import appLayoutResponsive600 from './visual/app-layout-responsive-600'; -import appLayoutResponsive1280 from './visual/app-layout-responsive-1280'; -import appLayoutResponsive1400 from './visual/app-layout-responsive-1400'; -import appLayoutResponsive1920 from './visual/app-layout-responsive-1920'; -import appLayoutResponsive2540 from './visual/app-layout-responsive-2540'; -import appLayoutStickyTableHeaderSplitPanel from './visual/app-layout-sticky-table-header-split-panel'; -import appLayoutToolbar from './visual/app-layout-toolbar'; -import appLayoutZIndex from './visual/app-layout-z-index'; -import areaChart from './visual/area-chart'; -import attributeEditor from './visual/attribute-editor'; -import autosuggest from './visual/autosuggest'; -import badge from './visual/badge'; -import barChart from './visual/bar-chart'; -import box from './visual/box'; -import breadcrumbGroup from './visual/breadcrumb-group'; -import button from './visual/button'; -import buttonDropdown from './visual/button-dropdown'; -import buttonGroup from './visual/button-group'; -import cards from './visual/cards'; -import checkbox from './visual/checkbox'; -import codeEditor from './visual/code-editor'; -import collectionPreferences from './visual/collection-preferences'; -import columnLayout from './visual/column-layout'; -import container from './visual/container'; -import containerSticky from './visual/container-sticky'; -import contentLayout from './visual/content-layout'; -import contentLayoutPermutations from './visual/content-layout-permutations'; -import copyToClipboard from './visual/copy-to-clipboard'; -import dateInput from './visual/date-input'; -import datePicker from './visual/date-picker'; -import dateRangePicker from './visual/date-range-picker'; -import divider from './visual/divider'; -import drawer from './visual/drawer'; -import dropdown from './visual/dropdown'; -import expandableSection from './visual/expandable-section'; -import fileDropzone from './visual/file-dropzone'; -import fileInput from './visual/file-input'; -import fileTokenGroup from './visual/file-token-group'; -import fileUpload from './visual/file-upload'; -import flashbar from './visual/flashbar'; -import flashbarStacked from './visual/flashbar-stacked'; -import form from './visual/form'; -import formField from './visual/form-field'; -import grid from './visual/grid'; -import header from './visual/header'; -import helpPanel from './visual/help-panel'; -import icon from './visual/icon'; -import input from './visual/input'; -import itemCard from './visual/item-card'; -import keyValuePairs from './visual/key-value-pairs'; -import lineChart from './visual/line-chart'; -import link from './visual/link'; -import list from './visual/list'; -import mixedLineBarChart from './visual/mixed-line-bar-chart'; -import modal from './visual/modal'; -import multiselect from './visual/multiselect'; -import pagination from './visual/pagination'; -import pieChart from './visual/pie-chart'; -import popover from './visual/popover'; -import progressBar from './visual/progress-bar'; -import promptInput from './visual/prompt-input'; -import propertyFilter from './visual/property-filter'; -import radioButton from './visual/radio-button'; -import radioGroup from './visual/radio-group'; -import s3ResourceSelector from './visual/s3-resource-selector'; -import segmentedControl from './visual/segmented-control'; -import select from './visual/select'; -import sideNavigation from './visual/side-navigation'; -import slider from './visual/slider'; -import spaceBetween from './visual/space-between'; -import spinner from './visual/spinner'; -import splitPanel from './visual/split-panel'; -import statusIndicator from './visual/status-indicator'; -import steps from './visual/steps'; -import table from './visual/table'; -import tableCells from './visual/table-cells'; -import tableEmbedded from './visual/table-embedded'; -import tableInlineEditing from './visual/table-inline-editing'; -import tableResizableColumns from './visual/table-resizable-columns'; -import tableStickyHeader from './visual/table-sticky-header'; -import tableStickyScrollbar from './visual/table-sticky-scrollbar'; -import tabs from './visual/tabs'; -import tagEditor from './visual/tag-editor'; -import textContent from './visual/text-content'; -import textFilter from './visual/text-filter'; -import textarea from './visual/textarea'; -import tiles from './visual/tiles'; -import timeInput from './visual/time-input'; -import toggle from './visual/toggle'; -import toggleButton from './visual/toggle-button'; -import tokenGroup from './visual/token-group'; -import topNavigation from './visual/top-navigation'; -import treeView from './visual/tree-view'; -import wizard from './visual/wizard'; -export const allSuites: TestSuite[] = [ - actionCard, - alert, - appLayout, - appLayoutContentPaddings, - appLayoutDrawers, - appLayoutFlashbar, - appLayoutHeader, - appLayoutMulti, - appLayoutResponsive1280, - appLayoutResponsive1400, - appLayoutResponsive1920, - appLayoutResponsive2540, - appLayoutResponsive600, - appLayoutStickyTableHeaderSplitPanel, - appLayoutToolbar, - appLayoutZIndex, - areaChart, - attributeEditor, - autosuggest, - badge, - barChart, - box, - breadcrumbGroup, - button, - buttonDropdown, - buttonGroup, - cards, - checkbox, - codeEditor, - collectionPreferences, - columnLayout, - container, - containerSticky, - contentLayout, - contentLayoutPermutations, - copyToClipboard, - dateInput, - datePicker, - dateRangePicker, - divider, - drawer, - dropdown, - expandableSection, - fileDropzone, - fileInput, - fileTokenGroup, - fileUpload, - flashbar, - flashbarStacked, - form, - formField, - grid, - header, - helpPanel, - icon, - input, - itemCard, - keyValuePairs, - lineChart, - link, - list, - mixedLineBarChart, - modal, - multiselect, - pagination, - pieChart, - popover, - progressBar, - promptInput, - propertyFilter, - radioButton, - radioGroup, - s3ResourceSelector, - segmentedControl, - select, - sideNavigation, - slider, - spaceBetween, - spinner, - splitPanel, - statusIndicator, - steps, - table, - tableCells, - tableEmbedded, - tableInlineEditing, - tableResizableColumns, - tableStickyHeader, - tableStickyScrollbar, - tabs, - tagEditor, - textContent, - textFilter, - textarea, - tiles, - timeInput, - toggle, - toggleButton, - tokenGroup, - topNavigation, - treeView, - wizard, -]; +export const allSuites: TestSuite[] = [actionCard, alert]; From e5ee43222fda05fa4a84548a44a347436d9c8a1a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 26 Jun 2026 14:57:47 +0200 Subject: [PATCH 131/135] Minor changes to workflows --- .github/workflows/deploy.yml | 2 ++ .github/workflows/visual-regression.yml | 12 +----------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 127ea55dfc..3e4d7d5626 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,6 +53,7 @@ jobs: name: dev-pages-react${{ matrix.react }} path: pages/lib/static-default + # Make the integration test utils available to the visual regression tests - name: Upload test utils selectors artifact if: matrix.react == 18 uses: actions/upload-artifact@v4 @@ -73,6 +74,7 @@ jobs: artifact-name: dev-pages-react${{ matrix.react }} deployment-path: pages/lib/static-default + # Pages to be served locally as mainline reference for visual regression tests build-baseline: name: Build baseline pages if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 8e9e17c9a8..74f7c0aecd 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -67,8 +67,6 @@ jobs: github-token: ${{ github.token }} run-id: ${{ inputs.caller-run-id }} - # ── Run tests ───────────────────────────────────────────────────────── - - name: Resolve PR deployment URL id: hosts run: | @@ -93,7 +91,7 @@ jobs: GH_TOKEN: ${{ github.token }} REPO: ${{ github.repository }} - - name: Start baseline server (port 8080) + - name: Start baseline server run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-visual-baseline & - name: Wait for baseline server @@ -110,14 +108,6 @@ jobs: NEW_HOST: ${{ steps.hosts.outputs.new }} OLD_HOST: http://localhost:8080 - - name: Upload diff artifacts - if: failure() - uses: actions/upload-artifact@v4 - with: - name: visual-regression-diffs-shard-${{ matrix.shard }} - path: visual-regression-output/ - retention-days: 14 - - name: Upload Allure results if: always() uses: actions/upload-artifact@v4 From 97226da55670eba72a46b902a4778a847084f7dd Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 26 Jun 2026 17:53:25 +0200 Subject: [PATCH 132/135] Update docs --- docs/RUNNING_TESTS.md | 68 +++++++++++++------------------------------ 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/docs/RUNNING_TESTS.md b/docs/RUNNING_TESTS.md index b7793be83e..36e36a0d79 100644 --- a/docs/RUNNING_TESTS.md +++ b/docs/RUNNING_TESTS.md @@ -18,6 +18,7 @@ The npm scripts use gulp tasks that handle env vars (`TZ=UTC`, `NODE_OPTIONS=--e - **Integration tests** — test against real browser behavior on Chrome, with motion disabled. - **Motion tests** — run a specific set of tests on Chrome, with motion enabled. - **Accessibility tests** — run on all test pages across themes and color modes (`src/__a11y__/`). +- **Visual regression tests** — compare screenshots between a PR and `main` to detect unintended visual changes. CI-only (see below). ## Targeting Specific Files @@ -46,7 +47,7 @@ npm i -g chromedriver ## Updating Snapshots -When component APIs change, you may need to update test snapshots. When design tokens are touched, you must also run integration tests to update their snapshots Before updating, run a full build (`npm run build`) so that documenter docs are generated. Use the `-u` flag to update: +When component APIs change, you may need to update test snapshots. When design tokens are touched, you must also run integration tests to update their snapshots. Before updating, run a full build (`npm run build`) so that documenter docs are generated. Use the `-u` flag to update: ``` # Unit snapshots @@ -58,62 +59,33 @@ NODE_OPTIONS=--experimental-vm-modules npx jest -u -c jest.integ.config.js src/_ # Snapshots inside components, e.g. when custom-css-properties.js changes (runs all unit tests) TZ=UTC npx jest -u -c jest.unit.config.js src/ ``` + ## Visual Regression Tests -Visual regression tests run automatically when opening a pull request in GitHub (see `.github/workflows/visual-regression.yml`). +Visual regression tests run automatically on pull requests via the CI pipeline (`.github/workflows/deploy.yml` and `.github/workflows/visual-regression.yml`). They cannot be run locally at the moment. -They compare permutation pages between the PR build and a baseline build of `main`, both served locally in the same CI job. Each side installs from its own `package-lock.json` via a git worktree, so dependency changes in the PR are handled correctly and unpinned updates in sister repositories affect both sides equally. +### How it works (CI) -### How it works +The deploy workflow (`.github/workflows/deploy.yml`) orchestrates the full pipeline: -1. The visual regression job waits for the deployment job —the deployment URL is used as the host containing the changes in the PR. -2. A git worktree of `origin/main` is created, its dependencies installed, and its pages built and served on port 8080. -3. Each of the test runners under `test/visual` imports their corresponding test definitions, captures the `.screenshot-area` element from both servers for each test, and fails if any pixels differ. +1. **Quick build** — builds dev pages for React 16 and React 18 in parallel using the `.github/actions/quick-build` composite action. +2. **Deploy** — deploys the React 18 pages to a preview environment (the PR deployment URL). +3. **Build baseline** — creates a git worktree of `origin/main`, installs its dependencies, and builds its pages. +4. **Visual regression** — once deploy and baseline are ready, calls the reusable `visual-regression.yml` workflow. -### Running locally +The visual regression workflow (`.github/workflows/visual-regression.yml`): -``` -npm run test:visual -``` +1. Resolves the PR deployment URL from the GitHub Deployments API. +2. Serves the baseline pages locally. +3. Runs the test suite sharded across multiple runners. Each test navigates to a page on both hosts, captures screenshots, and compares them pixel-by-pixel. +4. Produces an Allure report with image diffs for any failures, deployed to a preview environment. -This handles the full build and comparison in one command. If both outputs are already built, skip the build step: - -``` -NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js -``` +### Reviewing failures -(Requires both servers to be running — start the PR build with `npm run start:integ` on port 8080 and the baseline build on port 8080, or set `NEW_HOST` / `OLD_HOST` env vars to point at different hosts.) +When the CI job fails, check the deployed Allure report (linked from the GitHub deployment). It shows expected vs actual vs diff images for each failing test. If the diff is expected (intentional visual change), note it in your PR description. ### Adding tests for a new component -Create `test/definitions/visual/.ts`: - -```ts -import { TestSuite } from '../types'; - -const suite: TestSuite = { - description: 'my-component', - tests: [ - { - description: 'permutations', - path: 'my-component/permutations', - }, - ], -}; - -export default suite; -``` - -Then run the generation script to pick it up automatically: - -```bash -node build-tools/visual/generate-tests.js -``` - -This generates both the test runner (`test/visual/my-component.test.ts`) and updates `test/definitions/index.ts`. No manual imports needed. - -### Reviewing failures - -If the CI job fails, download the `visual-regression-diffs` artifact from the Actions summary. - -If the diff is expected (intentional visual change), note it in your PR description. +1. Create `test/definitions/visual/.ts` exporting a `TestSuite`. +2. Create `test/visual/.test.ts` that imports and runs the suite. +3. Add the import to `test/definitions/index.ts`. From eee210a0da52658e2086c3cfc81d6a224532476b Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 26 Jun 2026 18:11:04 +0200 Subject: [PATCH 133/135] Refactor --- .../__integ__/page-objects/select-page.ts | 2 +- test/definitions/screenshot-utils.ts | 60 +++++++++++++++ test/definitions/utils.ts | 75 +++---------------- 3 files changed, 70 insertions(+), 67 deletions(-) create mode 100644 test/definitions/screenshot-utils.ts diff --git a/src/select/__integ__/page-objects/select-page.ts b/src/select/__integ__/page-objects/select-page.ts index 26587fe536..38682eb564 100644 --- a/src/select/__integ__/page-objects/select-page.ts +++ b/src/select/__integ__/page-objects/select-page.ts @@ -36,7 +36,7 @@ export default class SelectPageObject< async selectOptionUsingDrag(optionNumber: number) { const triggerSelector = this.wrapper.findTrigger().toSelector(); // actions API does not work when element is not in the viewport - await this.browser.$(triggerSelector).scrollIntoView(); + await (await this.browser.$(triggerSelector)).scrollIntoView(); await this.buttonDownOnElement(triggerSelector); const { left, width, top, height } = await this.getBoundingBox( diff --git a/test/definitions/screenshot-utils.ts b/test/definitions/screenshot-utils.ts new file mode 100644 index 0000000000..ee4fc941dc --- /dev/null +++ b/test/definitions/screenshot-utils.ts @@ -0,0 +1,60 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; + +import { RawScreenshot, RawScreenshotPageObject } from './raw-screenshot-page-object'; +import { TestDefinition } from './types'; + +export const screenshotAreaSelector = '.screenshot-area'; + +export interface CropAndCompareResult { + firstImage: Buffer; + secondImage: Buffer; + diffImage: Buffer | null; + isEqual: boolean; + diffPixels: number; +} + +export function captureSingleScreenshot( + page: RawScreenshotPageObject, + testDef: TestDefinition +): Promise { + if (testDef.screenshotType === 'viewport') { + return page.captureViewport(); + } + return page.captureBySelector(screenshotAreaSelector); +} + +/** + * Compares two raw screenshots. If the compressed bytes are identical, + * skips the expensive parsePng + cropAndCompare pipeline entirely. + */ +export async function compareScreenshots(newRaw: RawScreenshot, oldRaw: RawScreenshot): Promise { + // Fast path: identical compressed PNG bytes → images are the same. + if (newRaw.rawBase64 === oldRaw.rawBase64) { + const imageBuffer = Buffer.from(newRaw.rawBase64, 'base64'); + return { firstImage: imageBuffer, secondImage: imageBuffer, diffImage: null, isEqual: true, diffPixels: 0 }; + } + + // Images differ — decode and run pixelmatch to produce a diff image for the Allure report. + // This only runs for failing tests, so the cost is acceptable. + const firstPng = await parsePng(newRaw.rawBase64); + const secondPng = await parsePng(oldRaw.rawBase64); + + // No cropping needed since takeElementScreenshot already produces cropped images. + const firstScreenshot = { + image: firstPng, + offset: { top: 0, left: 0 }, + width: firstPng.width, + height: firstPng.height, + }; + + const secondScreenshot = { + image: secondPng, + offset: { top: 0, left: 0 }, + width: secondPng.width, + height: secondPng.height, + }; + return cropAndCompare(firstScreenshot, secondScreenshot); +} diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 72133bc7a2..d14b67d106 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -2,12 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { attachment, step } from 'allure-js-commons'; -import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; - import createWrapper from '../../lib/components/test-utils/selectors'; -import { RawScreenshot, RawScreenshotPageObject } from './raw-screenshot-page-object'; +import { RawScreenshotPageObject } from './raw-screenshot-page-object'; +import { + captureSingleScreenshot, + compareScreenshots, + CropAndCompareResult, + screenshotAreaSelector, +} from './screenshot-utils'; import { TestDefinition, TestSuite } from './types'; -const screenshotAreaSelector = '.screenshot-area'; const defaultWindowSize = { width: 1200, height: 800 }; // NEW_HOST serves the PR's pages, OLD_HOST serves the baseline (main) pages. @@ -16,18 +19,6 @@ const oldHost = process.env.OLD_HOST || 'http://localhost:8080'; const wrapper = createWrapper(); -// ─── Types ─────────────────────────────────────────────────────────────────── - -interface CropAndCompareResult { - firstImage: Buffer; - secondImage: Buffer; - diffImage: Buffer | null; - isEqual: boolean; - diffPixels: number; -} - -// ─── Helpers ───────────────────────────────────────────────────────────────── - function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams({ motionDisabled: 'true', ...queryParams }); const qs = params.toString(); @@ -51,8 +42,6 @@ async function attachDiffImages(result: CropAndCompareResult, testName: string): await attachment(testName, diffPayload, 'application/vnd.allure.image.diff'); } -// ─── Capture functions ─────────────────────────────────────────────────────── - /** * Navigates to a URL, waits for the screenshot area, and runs setup. */ @@ -75,52 +64,6 @@ async function preparePage( } } -/** - * Captures a raw screenshot (base64 + metadata) WITHOUT decoding the PNG. - * Uses takeElementScreenshot for the screenshot area to avoid full-page capture. - */ -function captureRaw(page: RawScreenshotPageObject, testDef: TestDefinition): Promise { - if (testDef.screenshotType === 'viewport') { - return page.captureViewport(); - } - return page.captureBySelector(screenshotAreaSelector); -} - -/** - * Compares two raw screenshots. If the compressed bytes are identical, - * skips the expensive parsePng + cropAndCompare pipeline entirely. - */ -async function compareScreenshots(newRaw: RawScreenshot, oldRaw: RawScreenshot): Promise { - // Fast path: identical compressed PNG bytes → images are the same. - if (newRaw.rawBase64 === oldRaw.rawBase64) { - const imageBuffer = Buffer.from(newRaw.rawBase64, 'base64'); - return { firstImage: imageBuffer, secondImage: imageBuffer, diffImage: null, isEqual: true, diffPixels: 0 }; - } - - // Images differ — decode and run pixelmatch to produce a diff image for the Allure report. - // This only runs for failing tests, so the cost is acceptable. - const firstPng = await parsePng(newRaw.rawBase64); - const secondPng = await parsePng(oldRaw.rawBase64); - - // No cropping needed since takeElementScreenshot already produces cropped images. - const firstScreenshot = { - image: firstPng, - offset: { top: 0, left: 0 }, - width: firstPng.width, - height: firstPng.height, - }; - - const secondScreenshot = { - image: secondPng, - offset: { top: 0, left: 0 }, - width: secondPng.width, - height: secondPng.height, - }; - return cropAndCompare(firstScreenshot, secondScreenshot); -} - -// ─── Test runner ───────────────────────────────────────────────────────────── - export function runTestSuites(suites: Array) { let browser: WebdriverIO.Browser; @@ -190,10 +133,10 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro // Non-permutation: single screenshot comparison await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newRaw = await captureRaw(page, testDef); + const newRaw = await captureSingleScreenshot(page, testDef); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldRaw = await captureRaw(page, testDef); + const oldRaw = await captureSingleScreenshot(page, testDef); const result = await compareScreenshots(newRaw, oldRaw); await attachDiffImages(result, testDef.description); From b3acc8db260d6c2abc234b1570e9133e93d6c216 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 26 Jun 2026 19:23:30 +0200 Subject: [PATCH 134/135] Refinements --- .../definitions/raw-screenshot-page-object.ts | 5 ++-- test/definitions/types.ts | 1 - test/definitions/utils.ts | 25 ++++++++----------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/test/definitions/raw-screenshot-page-object.ts b/test/definitions/raw-screenshot-page-object.ts index b95a5d1705..9373f6eaa9 100644 --- a/test/definitions/raw-screenshot-page-object.ts +++ b/test/definitions/raw-screenshot-page-object.ts @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 // Local copy of RawScreenshotPageObject from browser-test-tools -// (dev-v3-jotresse-optimized-screenshots branch). Once that branch is merged, -// this file can be replaced with a direct import from browser-test-tools. +// (https://github.com/cloudscape-design/browser-test-tools/pull/237). +// Once that PR is merged and released, this file can be replaced with +// a direct import from browser-test-tools. import { BasePageObject } from '@cloudscape-design/browser-test-tools/page-objects'; diff --git a/test/definitions/types.ts b/test/definitions/types.ts index 33ce6cbefd..020b6efc5a 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -23,7 +23,6 @@ export interface TestDefinition { screenshotType: ScreenshotType; queryParams?: Record; configuration?: ScreenshotTestConfiguration; - pixelDiffTolerance?: number; setup?: ({ page, wrapper, browser }: { page: BasePageObject; wrapper: Wrapper; browser?: Browser }) => void; } diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index d14b67d106..32b71a2998 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -13,6 +13,8 @@ import { import { TestDefinition, TestSuite } from './types'; const defaultWindowSize = { width: 1200, height: 800 }; +const tolerance = 0; + // NEW_HOST serves the PR's pages, OLD_HOST serves the baseline (main) pages. const newHost = process.env.NEW_HOST || 'http://localhost:8080'; const oldHost = process.env.OLD_HOST || 'http://localhost:8080'; @@ -22,16 +24,14 @@ const wrapper = createWrapper(); function buildUrl(host: string, path: string, queryParams?: Record): string { const params = new URLSearchParams({ motionDisabled: 'true', ...queryParams }); const qs = params.toString(); - return `${host}#/${path}${qs ? `?${qs}` : ''}`; + return `${host}#/${path}?${qs}`; } function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinition { return (item as TestDefinition).path !== undefined; } -/** - * Attaches a visual comparison to the Allure report. - */ +// Attaches a visual comparison to the Allure report. async function attachDiffImages(result: CropAndCompareResult, testName: string): Promise { const diffPayload = JSON.stringify({ expected: `data:image/png;base64,${result.secondImage.toString('base64')}`, @@ -42,9 +42,6 @@ async function attachDiffImages(result: CropAndCompareResult, testName: string): await attachment(testName, diffPayload, 'application/vnd.allure.image.diff'); } -/** - * Navigates to a URL, waits for the screenshot area, and runs setup. - */ async function preparePage( browser: WebdriverIO.Browser, page: RawScreenshotPageObject, @@ -96,7 +93,6 @@ function registerSuites(suites: Array, getBrowser: ( function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { test(testDef.description, async () => { - const tolerance = testDef.pixelDiffTolerance ?? 0; const browser = getBrowser(); const page = new RawScreenshotPageObject(browser); @@ -104,22 +100,21 @@ function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Bro const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); if (testDef.screenshotType === 'permutations') { - // Capture permutations as raw (no PNG decode) await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newPerms = await page.capturePermutations(); + const newPermutations = await page.capturePermutations(); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldPerms = await page.capturePermutations(); + const oldPermutations = await page.capturePermutations(); - expect(newPerms.length).toBe(oldPerms.length); + expect(newPermutations.length).toBe(oldPermutations.length); // Compare each permutation individually, wrapping each in an Allure step. let failures = 0; - for (let i = 0; i < newPerms.length; i++) { - const id = newPerms[i].id || ''; + for (let i = 0; i < newPermutations.length; i++) { + const id = newPermutations[i].id || ''; const index = `#${(i + 1).toString().padStart(3, '0')}`; await step(`Permutation ${index}`, async () => { - const permResult = await compareScreenshots(newPerms[i], oldPerms[i]); + const permResult = await compareScreenshots(newPermutations[i], oldPermutations[i]); await attachDiffImages(permResult, index); if (permResult.diffPixels > tolerance) { failures++; From f7c0b95969d40ef52608bd50352828a3f78c392f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 26 Jun 2026 19:38:44 +0200 Subject: [PATCH 135/135] Remove superfluous env var --- .github/workflows/visual-regression.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 74f7c0aecd..df50e3f3f5 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -100,13 +100,11 @@ jobs: - name: Run visual regression tests run: | echo "NEW_HOST=${NEW_HOST}" - echo "OLD_HOST=${OLD_HOST}" NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard="${SHARD}/2" env: TZ: UTC SHARD: ${{ matrix.shard }} NEW_HOST: ${{ steps.hosts.outputs.new }} - OLD_HOST: http://localhost:8080 - name: Upload Allure results if: always()