From d04b45d1b69fdc52c643519b6a54bead45557f44 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 19:31:39 +0200 Subject: [PATCH 01/24] chore: Add visual regression testing --- .github/workflows/visual-regression.yml | 86 +++++++++++++++ build-tools/tasks/index.js | 1 + 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 ++ 19 files changed, 505 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/index.js b/build-tools/tasks/index.js index 982a8f6a72..c653c71684 100644 --- a/build-tools/tasks/index.js +++ b/build-tools/tasks/index.js @@ -21,4 +21,5 @@ module.exports = { themeableSource: require('./themeable-source'), bundleVendorFiles: require('./bundle-vendor-files'), sizeLimit: require('./size-limit'), + visual: require('./visual'), }; 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 9d290b74f1..216581b858 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,6 +19,7 @@ const { generateI18nMessages, integ, motion, + visual, copyFiles, themeableSource, bundleVendorFiles, @@ -41,6 +42,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 39b9206218..bfb1d807fd 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 d25b70e50fcddb057845e1fcf8fe1c377018ea2f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 19:36:06 +0200 Subject: [PATCH 02/24] 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 304a0992e4ee609b24bdaa2fe5635064eeafe99d Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 20:05:09 +0200 Subject: [PATCH 03/24] 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 b425e0282f3e6383af9b96c2828cfc3921e94901 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 20:12:53 +0200 Subject: [PATCH 04/24] 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 c8880d1fc4..bd5c093ac2 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", @@ -4844,6 +4845,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 bfb1d807fd..0d985a210e 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 b3348bbda02a7831d4b75c4bc7d4f183d9300d1e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 20:44:04 +0200 Subject: [PATCH 05/24] 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 28479e5cb43366152e12997d15958961ea9085d8 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 21:01:25 +0200 Subject: [PATCH 06/24] 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 d4c317092970b3ce07b6c6ba9966f5ce51331b95 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 21:39:49 +0200 Subject: [PATCH 07/24] Install Puppeteer --- package-lock.json | 75 ++++++++++++++++++++++++++++++++++++++++++++--- package.json | 1 + 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index bd5c093ac2..ae3d2d5545 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", @@ -3522,9 +3523,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": { @@ -7271,6 +7272,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, @@ -8787,6 +8802,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, @@ -17743,6 +17765,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, @@ -21155,6 +21196,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, @@ -21672,6 +21720,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", @@ -22278,7 +22333,9 @@ } }, "node_modules/ws": { - "version": "8.18.2", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "dev": true, "license": "MIT", "engines": { @@ -22431,6 +22488,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 0d985a210e..22336b244c 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 dabf43ceea820a11f757dbdd6d44d421693a61a3 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 11 May 2026 22:19:28 +0200 Subject: [PATCH 08/24] 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 1f71f10a69ef3b99627b3f8bec675dc7a2e4802f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 11:54:13 +0200 Subject: [PATCH 09/24] Reuse build --- .github/workflows/build-lint-test.yml | 9 +++++ .github/workflows/visual-regression.yml | 45 ++++++++++++------------- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 21062b4a41..6e4c4ed8eb 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -43,3 +43,12 @@ jobs: with: artifact-name: dev-pages-react${{ matrix.react }} deployment-path: pages/lib/static-default + + visual: + name: Visual regression + needs: 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 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 b1eee0b5ba3596aa6e90c6be8e917edf7af898ba Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 12:16:27 +0200 Subject: [PATCH 10/24] Fix wofklow deps --- .github/workflows/build-lint-test.yml | 77 ++++++++++++++++++- .github/workflows/visual-regression.yml | 98 ------------------------- 2 files changed, 73 insertions(+), 102 deletions(-) delete mode 100644 .github/workflows/visual-regression.yml diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 6e4c4ed8eb..6dc5cff1bd 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -48,7 +48,76 @@ jobs: name: Visual regression needs: 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 + 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: dev-pages-react18 + path: 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 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 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 b36875054d389a573fa1794a05152719e73d2ea8 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 12:21:53 +0200 Subject: [PATCH 11/24] Again --- .github/workflows/build-lint-test.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 6dc5cff1bd..91fbbb7a5c 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -30,8 +30,23 @@ jobs: artifact-path: pages/lib/static-default artifact-name: dev-pages-react${{ matrix.react }} react-version: ${{ matrix.react }} - deploy: + # Gate job: waits for all build matrix variants to complete so that + # downstream jobs (visual, deploy) can depend on a single non-matrix job. + build-complete: + name: build complete needs: build + if: always() + runs-on: ubuntu-latest + steps: + - name: Check build result + run: | + if [[ "${{ needs.build.result }}" != "success" ]]; then + echo "Build failed or was cancelled" + exit 1 + fi + + deploy: + needs: build-complete name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} # skip this job for external contributions as they aren't supported and cause the job's failure if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} @@ -46,7 +61,7 @@ jobs: visual: name: Visual regression - needs: build + needs: build-complete if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} runs-on: ubuntu-latest steps: From 8b15bf9b7757cfbb502b5ac8accafea930296b25 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 12:26:43 +0200 Subject: [PATCH 12/24] Again --- .github/workflows/build-lint-test.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 91fbbb7a5c..23c68f3fb9 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -29,9 +29,11 @@ jobs: with: artifact-path: pages/lib/static-default artifact-name: dev-pages-react${{ matrix.react }} - react-version: ${{ matrix.react }} - # Gate job: waits for all build matrix variants to complete so that - # downstream jobs (visual, deploy) can depend on a single non-matrix job. + react-version: ${{ matrix.react }} + + # Gate job: waits for all build matrix variants to complete. + # Regular (runs-on) jobs cannot depend on matrix+reusable-workflow jobs directly, + # so this non-matrix gate job bridges the dependency. build-complete: name: build complete needs: build @@ -46,7 +48,7 @@ jobs: fi deploy: - needs: build-complete + needs: build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} # skip this job for external contributions as they aren't supported and cause the job's failure if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} @@ -81,7 +83,7 @@ jobs: chrome-version: stable - name: Install dependencies - run: npm i + run: npm ci # ── PR pages: reuse the artifact built by the build job ─────────────── - name: Download PR pages artifact @@ -95,7 +97,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 263a253c442ce54ea385252ef8291cce91839172 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 12:36:12 +0200 Subject: [PATCH 13/24] Again --- .github/workflows/build-lint-test.yml | 95 ------------------- .github/workflows/visual-regression.yml | 120 ++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 95 deletions(-) create mode 100644 .github/workflows/visual-regression.yml diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 23c68f3fb9..3ed836d3e2 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -30,23 +30,6 @@ jobs: artifact-path: pages/lib/static-default artifact-name: dev-pages-react${{ matrix.react }} react-version: ${{ matrix.react }} - - # Gate job: waits for all build matrix variants to complete. - # Regular (runs-on) jobs cannot depend on matrix+reusable-workflow jobs directly, - # so this non-matrix gate job bridges the dependency. - build-complete: - name: build complete - needs: build - if: always() - runs-on: ubuntu-latest - steps: - - name: Check build result - run: | - if [[ "${{ needs.build.result }}" != "success" ]]; then - echo "Build failed or was cancelled" - exit 1 - fi - deploy: needs: build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} @@ -60,81 +43,3 @@ jobs: with: artifact-name: dev-pages-react${{ matrix.react }} deployment-path: pages/lib/static-default - - visual: - name: Visual regression - needs: build-complete - if: ${{ github.event_name == 'pull_request' && 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: Setup Chrome and ChromeDriver - uses: browser-actions/setup-chrome@v1 - with: - chrome-version: stable - - - name: Install dependencies - run: npm ci - - # ── PR pages: reuse the artifact built by the build job ─────────────── - - name: Download PR pages artifact - uses: actions/download-artifact@v4 - with: - name: dev-pages-react18 - path: 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 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 ffb88c1feb2fa9076c3463d4800c921f05fa66d7 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 12:50:50 +0200 Subject: [PATCH 14/24] 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 23a62c420b23f83e90190060649e3b6895e84e50 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 13:09:44 +0200 Subject: [PATCH 15/24] Wait only for the build --- .github/workflows/build-lint-test.yml | 33 ++++++++----------------- .github/workflows/build.yml | 31 +++++++++++++++++++++++ .github/workflows/visual-regression.yml | 2 +- 3 files changed, 42 insertions(+), 24 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 3ed836d3e2..ebc236fa02 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 @@ -19,22 +15,13 @@ permissions: deployments: 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 }} deploy: - needs: build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} - # skip this job for external contributions as they aren't supported and cause the job's failure - if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + # Only deploy for PRs from the same repo (not forks) 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 strategy: matrix: react: [16, 18] 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 eef1996e33285f3d7d0c5e4978506650c6098024 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:37 +0200 Subject: [PATCH 16/24] Revert "Wait only for the build" This reverts commit 23a62c420b23f83e90190060649e3b6895e84e50. --- .github/workflows/build-lint-test.yml | 33 +++++++++++++++++-------- .github/workflows/build.yml | 31 ----------------------- .github/workflows/visual-regression.yml | 2 +- 3 files changed, 24 insertions(+), 42 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 ebc236fa02..3ed836d3e2 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 @@ -15,13 +19,22 @@ permissions: deployments: 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 }} deploy: + needs: build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} - # Only deploy for PRs from the same repo (not forks) 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 + # skip this job for external contributions as they aren't supported and cause the job's failure + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} strategy: matrix: react: [16, 18] 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 989d0a6e9180f856be7709060e6c279a3ea84c21 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:44 +0200 Subject: [PATCH 17/24] 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 ba9048b2e8b1f0e9120f28fff9e24e053be6bba7 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:46 +0200 Subject: [PATCH 18/24] Revert "Again" This reverts commit 263a253c442ce54ea385252ef8291cce91839172. --- .github/workflows/build-lint-test.yml | 95 +++++++++++++++++++ .github/workflows/visual-regression.yml | 120 ------------------------ 2 files changed, 95 insertions(+), 120 deletions(-) delete mode 100644 .github/workflows/visual-regression.yml diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 3ed836d3e2..23c68f3fb9 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -30,6 +30,23 @@ jobs: artifact-path: pages/lib/static-default artifact-name: dev-pages-react${{ matrix.react }} react-version: ${{ matrix.react }} + + # Gate job: waits for all build matrix variants to complete. + # Regular (runs-on) jobs cannot depend on matrix+reusable-workflow jobs directly, + # so this non-matrix gate job bridges the dependency. + build-complete: + name: build complete + needs: build + if: always() + runs-on: ubuntu-latest + steps: + - name: Check build result + run: | + if [[ "${{ needs.build.result }}" != "success" ]]; then + echo "Build failed or was cancelled" + exit 1 + fi + deploy: needs: build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} @@ -43,3 +60,81 @@ jobs: with: artifact-name: dev-pages-react${{ matrix.react }} deployment-path: pages/lib/static-default + + visual: + name: Visual regression + needs: build-complete + if: ${{ github.event_name == 'pull_request' && 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: Setup Chrome and ChromeDriver + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable + + - name: Install dependencies + run: npm ci + + # ── PR pages: reuse the artifact built by the build job ─────────────── + - name: Download PR pages artifact + uses: actions/download-artifact@v4 + with: + name: dev-pages-react18 + path: 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 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 4090655d0e459de5479ab87be02346e7f6fae189 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:48 +0200 Subject: [PATCH 19/24] Revert "Again" This reverts commit 8b15bf9b7757cfbb502b5ac8accafea930296b25. --- .github/workflows/build-lint-test.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 23c68f3fb9..91fbbb7a5c 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -29,11 +29,9 @@ jobs: with: artifact-path: pages/lib/static-default artifact-name: dev-pages-react${{ matrix.react }} - react-version: ${{ matrix.react }} - - # Gate job: waits for all build matrix variants to complete. - # Regular (runs-on) jobs cannot depend on matrix+reusable-workflow jobs directly, - # so this non-matrix gate job bridges the dependency. + react-version: ${{ matrix.react }} + # Gate job: waits for all build matrix variants to complete so that + # downstream jobs (visual, deploy) can depend on a single non-matrix job. build-complete: name: build complete needs: build @@ -48,7 +46,7 @@ jobs: fi deploy: - needs: build + needs: build-complete name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} # skip this job for external contributions as they aren't supported and cause the job's failure if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} @@ -83,7 +81,7 @@ jobs: chrome-version: stable - name: Install dependencies - run: npm ci + run: npm i # ── PR pages: reuse the artifact built by the build job ─────────────── - name: Download PR pages artifact @@ -97,7 +95,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 75b0663321e6b0e49ee685f730f30e86d4eaf670 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:50 +0200 Subject: [PATCH 20/24] Revert "Again" This reverts commit b36875054d389a573fa1794a05152719e73d2ea8. --- .github/workflows/build-lint-test.yml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 91fbbb7a5c..6dc5cff1bd 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -30,23 +30,8 @@ jobs: artifact-path: pages/lib/static-default artifact-name: dev-pages-react${{ matrix.react }} react-version: ${{ matrix.react }} - # Gate job: waits for all build matrix variants to complete so that - # downstream jobs (visual, deploy) can depend on a single non-matrix job. - build-complete: - name: build complete - needs: build - if: always() - runs-on: ubuntu-latest - steps: - - name: Check build result - run: | - if [[ "${{ needs.build.result }}" != "success" ]]; then - echo "Build failed or was cancelled" - exit 1 - fi - deploy: - needs: build-complete + needs: build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} # skip this job for external contributions as they aren't supported and cause the job's failure if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} @@ -61,7 +46,7 @@ jobs: visual: name: Visual regression - needs: build-complete + needs: build if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository }} runs-on: ubuntu-latest steps: From f706784e84e1de05fa733780bc300a978c4ef21a Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:52 +0200 Subject: [PATCH 21/24] Revert "Fix wofklow deps" This reverts commit b1eee0b5ba3596aa6e90c6be8e917edf7af898ba. --- .github/workflows/build-lint-test.yml | 77 +------------------ .github/workflows/visual-regression.yml | 98 +++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 73 deletions(-) create mode 100644 .github/workflows/visual-regression.yml diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 6dc5cff1bd..6e4c4ed8eb 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -48,76 +48,7 @@ jobs: name: Visual regression needs: build if: ${{ github.event_name == 'pull_request' && 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: 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: dev-pages-react18 - path: 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 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 + uses: ./.github/workflows/visual-regression.yml + secrets: inherit + with: + pr-artifact-name: dev-pages-react18 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 659b057d13d7a1b533ea5bb51e88ec9171b68828 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 14:05:53 +0200 Subject: [PATCH 22/24] Revert "Reuse build" This reverts commit 1f71f10a69ef3b99627b3f8bec675dc7a2e4802f. --- .github/workflows/build-lint-test.yml | 9 ----- .github/workflows/visual-regression.yml | 45 +++++++++++++------------ 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build-lint-test.yml b/.github/workflows/build-lint-test.yml index 6e4c4ed8eb..21062b4a41 100644 --- a/.github/workflows/build-lint-test.yml +++ b/.github/workflows/build-lint-test.yml @@ -43,12 +43,3 @@ jobs: with: artifact-name: dev-pages-react${{ matrix.react }} deployment-path: pages/lib/static-default - - visual: - name: Visual regression - needs: 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 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 8725363134896f34c1372cd488cae88268303f2f Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 15:20:37 +0200 Subject: [PATCH 23/24] 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 4ef11e8dbea325f1e4d8dfe44c9921a5c5c0beaa Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Wed, 13 May 2026 15:21:01 +0200 Subject: [PATCH 24/24] 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, + }, + ], + })), ], };