Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
d990721
chore: Add visual regression testing
jperals May 11, 2026
bb56f3e
Install dependencies with npm i
jperals May 11, 2026
93bcfdb
Use node 20
jperals May 11, 2026
795064f
Add pixelmatch types
jperals May 11, 2026
dfb8590
Install Chromedriver in CI
jperals May 11, 2026
663864c
Start servers
jperals May 11, 2026
61a61b0
Install Puppeteer
jperals May 11, 2026
1043a03
Capture screenshot area or permutations
jperals May 11, 2026
a951e54
Reuse build
jperals May 13, 2026
13736cf
Fix wofklow deps
jperals May 13, 2026
0ca63b6
Again
jperals May 13, 2026
785e6fd
Fix npm install command
jperals May 13, 2026
689a1ef
Wait only for the build
jperals May 13, 2026
2cbb9e5
Revert "Wait only for the build"
jperals May 13, 2026
7e7e758
Revert "Fix npm install command"
jperals May 13, 2026
f65463c
Revert "Again"
jperals May 13, 2026
f868757
Revert "Fix wofklow deps"
jperals May 13, 2026
aa00fb6
Revert "Reuse build"
jperals May 13, 2026
66506b7
Include alert tests
jperals May 13, 2026
48bddb7
Add more alert tests
jperals May 13, 2026
88c91b2
chore: Export visual test definitions
jperals May 13, 2026
a2aa017
Use the quick build
jperals May 18, 2026
c66d3b7
Fix tsconfig
jperals May 18, 2026
0bd90be
Fix workflow
jperals May 18, 2026
e0cbbb9
Fix workflow
jperals May 18, 2026
f244200
Use serve
jperals May 18, 2026
670ef1a
Run tests on Safari
jperals May 18, 2026
4456fd3
Prevent visual regression workflow from running twice
jperals May 18, 2026
b508370
Reuse baseline build across browsers
jperals May 18, 2026
e540d32
Limit Safari concurrency
jperals May 18, 2026
5912a0a
Fix workflow
jperals May 18, 2026
9e10886
Fix workflows
jperals May 18, 2026
5c7e8ea
Fix workflow
jperals May 18, 2026
c42c2e1
Fix workflow
jperals May 18, 2026
a1a4d92
Fix workflow
jperals May 18, 2026
df04eb6
Fix workflow
jperals May 18, 2026
4b394dc
Fix workflow
jperals May 19, 2026
cfb6a3a
Add delay between retries in Safari
jperals May 19, 2026
e5f7cc6
Fine tune Safari delay
jperals May 19, 2026
2ee27a9
Release Safari session between tests
jperals May 19, 2026
911ede4
Do not retry with Safari
jperals May 19, 2026
c580c7f
Change target directory
jperals May 19, 2026
10d9a9e
Remove local testing setup
jperals May 21, 2026
596ae23
Refactor
jperals May 21, 2026
56086de
Refactor
jperals May 27, 2026
7bfe43e
Fix setup
jperals May 27, 2026
009f977
Fix setup
jperals May 27, 2026
eede7d9
Create one single browser session for all tests
jperals May 27, 2026
2ae6d2a
Try to fix Safari
jperals May 27, 2026
e4045ad
Try again
jperals May 27, 2026
a40d768
Start safaridriver from workflow
jperals May 27, 2026
6efec5b
Move Safaridriver initialization back to local test setup
jperals May 27, 2026
aebe762
Remove outdated npm script
jperals May 28, 2026
68ab563
Remove Safari setup
jperals May 29, 2026
6c6c43b
use captureScreenshotArea for permutation comparison unless there is …
jperals May 29, 2026
ac78f9c
Add more test definitions
jperals May 29, 2026
68e2fe7
Refine config
jperals May 29, 2026
963d42d
Shard tests
jperals May 29, 2026
e1f74ad
Fix config
jperals May 29, 2026
a41f94c
Fix config
jperals May 29, 2026
3c153d1
Increase timeout
jperals May 29, 2026
f065097
Use 3 shards
jperals May 29, 2026
de46e47
Fix tests
jperals May 29, 2026
b8f16c7
Build the test selectors before running the tests
jperals May 29, 2026
14714bb
Optimizations
jperals May 29, 2026
1a063b1
Upload the entire lib/components directory
jperals May 29, 2026
dc4fea3
Remove unnecessary step
jperals May 29, 2026
5c18355
Add more components tests
jperals May 29, 2026
ff74340
Split app layout test definition files
jperals May 30, 2026
d2de716
Add viewport screenshot type
jperals May 30, 2026
67d68ef
Split app layout responsive test definitions
jperals Jun 5, 2026
6a15aaf
Autogenerate test files
jperals Jun 5, 2026
950d87c
Generate Allure reports
jperals Jun 5, 2026
f319108
Increase to 10 shards
jperals Jun 5, 2026
19eab2b
Download Allure directly
jperals Jun 5, 2026
dda1cca
Deploy Allure results
jperals Jun 5, 2026
4961b1b
Add image diffs to Allure reports
jperals Jun 5, 2026
036e37d
Use Allure 3
jperals Jun 5, 2026
1576564
Fix image attachments
jperals Jun 6, 2026
480ac33
Optimize comparison
jperals Jun 6, 2026
b776692
Export types
jperals Jun 6, 2026
780db53
Remove new test definitions
jperals Jun 8, 2026
f37634c
Reduce to 1 shard
jperals Jun 8, 2026
5b6e7c4
Do not generate visual test files
jperals Jun 11, 2026
6af1143
Attach images correctly
jperals Jun 11, 2026
d39c0e2
Always attach image to report
jperals Jun 11, 2026
9c95fe3
Make sure all permutations are compared
jperals Jun 11, 2026
a36a79d
Adjust window size correctly
jperals Jun 12, 2026
d854993
Attach permutation images in bulk
jperals Jun 12, 2026
477664c
Refine permutation test descriptions
jperals Jun 12, 2026
07e504f
Refine setting of window size
jperals Jun 12, 2026
b7810bb
Disable motion
jperals Jun 12, 2026
612c136
Do not capture only viewport by default
jperals Jun 12, 2026
2ffba79
Optimize permutation comparison
jperals Jun 12, 2026
e0e27d2
Include browser in Testdefinition setup callback
jperals Jun 12, 2026
6c61274
Add all components
jperals Jun 12, 2026
b1ae9c6
Refactor
jperals Jun 12, 2026
f9ddc27
Fixes
jperals Jun 12, 2026
50f107a
Fixes
jperals Jun 12, 2026
7946d51
Fixes
jperals Jun 12, 2026
6ba70dd
Increase timeout
jperals Jun 12, 2026
4d07240
Fixes
jperals Jun 12, 2026
9fc1ec9
Add missing width
jperals Jun 12, 2026
69c7efa
Fix query parameters
jperals Jun 12, 2026
470069b
Increase timeout
jperals Jun 12, 2026
0db3567
instrument and log
jperals Jun 13, 2026
a950516
Extend logging
jperals Jun 13, 2026
0ee95da
Avoid calling parsePng if possible
jperals Jun 13, 2026
2896863
Fix permutation selector
jperals Jun 13, 2026
210f801
Add delay
jperals Jun 13, 2026
e189bd2
Refine attachment name
jperals Jun 13, 2026
8119dd8
Apply tolerance
jperals Jun 13, 2026
b3b74b0
Use takeElementScreenshot
jperals Jun 15, 2026
c5da1f6
Attach screenshots in all cases
jperals Jun 15, 2026
8e05873
Use PR deployment as new host
jperals Jun 23, 2026
1435fef
Add optimizations
jperals Jun 23, 2026
5674948
Fix buildUrl
jperals Jun 23, 2026
63f0490
Add logging
jperals Jun 23, 2026
dd64d04
Fixes
jperals Jun 23, 2026
6069f69
Various adjustments
jperals Jun 23, 2026
1edddb2
Refinements
jperals Jun 23, 2026
6ecd15a
Refinements
jperals Jun 23, 2026
ef0a5d6
Refactor
jperals Jun 25, 2026
2085a99
Syntax fixes
jperals Jun 25, 2026
d007cbd
Remove test definitions out of scope
jperals Jun 25, 2026
4878b2e
Syntax fix
jperals Jun 25, 2026
e5f55e9
Syntax fixes
jperals Jun 25, 2026
d8a9dbf
Reduce shards
jperals Jun 25, 2026
a342cb5
Make browser optional
jperals Jun 26, 2026
8569e52
Remove outdated imports
jperals Jun 26, 2026
e5ee432
Minor changes to workflows
jperals Jun 26, 2026
97226da
Update docs
jperals Jun 26, 2026
eee210a
Refactor
jperals Jun 26, 2026
b3acc8d
Refinements
jperals Jun 26, 2026
f7c0b95
Remove superfluous env var
jperals Jun 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Deploy
name: Deploy and visual regression

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The visual regression workflow is triggered by the deploy workflow because the former needs outputs from the latter. Otherwise, workflows cannot read outputs from jobs defined in other workflows.


on:
pull_request:
Expand All @@ -16,7 +16,7 @@ jobs:
# Produces the artifact consumed by deploy, allowing it to start
# before the full build+test job finishes.
quick-build:
name: quick-build${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }}
name: quick-build (${{format('React {0}', matrix.react)}})

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's also show "quick-build (React 16)" instead of just "quick-build" when building for React 16. It is not any less specific than the React 18 build.

# skip this job for external contributions
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
strategy:
Expand Down Expand Up @@ -53,9 +53,17 @@ jobs:
name: dev-pages-react${{ matrix.react }}
path: pages/lib/static-default

# Make the integration test utils available to the visual regression tests
- name: Upload test utils selectors artifact
if: matrix.react == 18
uses: actions/upload-artifact@v4
with:
name: test-utils-selectors
path: lib/components

deploy:
needs: quick-build
name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }}
name: deploy (${{format('React {0}', matrix.react)}})

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, show "deploy (React 16)" the React 16 deploy

if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
strategy:
matrix:
Expand All @@ -65,3 +73,56 @@ jobs:
with:
artifact-name: dev-pages-react${{ matrix.react }}
deployment-path: pages/lib/static-default

# Pages to be served locally as mainline reference for visual regression tests
build-baseline:
name: Build baseline pages
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Create baseline worktree from origin/main
run: git worktree add /tmp/baseline origin/main

- name: Install baseline dependencies
run: npm i
working-directory: /tmp/baseline

- name: Build baseline pages
run: npx gulp quick-build
working-directory: /tmp/baseline
env:
NODE_ENV: production

- name: Bundle baseline pages
run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path "${GITHUB_WORKSPACE}/pages/lib/static-visual-baseline"
working-directory: /tmp/baseline
env:
NODE_ENV: production

- name: Upload baseline artifact
uses: actions/upload-artifact@v4
with:
name: visual-baseline-pages
path: pages/lib/static-visual-baseline
retention-days: 1

visual:
name: Visual regression
needs: [deploy, build-baseline]
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
uses: ./.github/workflows/visual-regression.yml
secrets: inherit
with:
test-utils-artifact-name: test-utils-selectors
baseline-artifact-name: visual-baseline-pages
caller-run-id: ${{ github.run_id }}
153 changes: 153 additions & 0 deletions .github/workflows/visual-regression.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
name: Visual Regression Tests

on:
workflow_call:
inputs:
test-utils-artifact-name:
description: 'Name of the artifact containing test-utils selectors.'
required: true
type: string
caller-run-id:
description: 'The run ID of the calling workflow, used to download artifacts it uploaded.'
required: true
type: string
baseline-artifact-name:
description: 'Name of the artifact containing baseline pages (built by the caller workflow).'
required: true
type: string

defaults:
run:
shell: bash

permissions:
id-token: write
contents: read
actions: read
deployments: write

jobs:
visual:
name: Visual regression (shard ${{ matrix.shard }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shard: [1, 2]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will be adding more shards as I add more test definitions for more components

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Set up Chrome and ChromeDriver
uses: browser-actions/setup-chrome@v1
with:
chrome-version: stable

- name: Install dependencies
run: npm i

- name: Download baseline artifact
uses: actions/download-artifact@v4
with:
name: ${{ inputs.baseline-artifact-name }}
path: pages/lib/static-visual-baseline
github-token: ${{ github.token }}
run-id: ${{ inputs.caller-run-id }}

- name: Download test utils artifact
uses: actions/download-artifact@v4
with:
name: ${{ inputs.test-utils-artifact-name }}
path: lib/components
github-token: ${{ github.token }}
run-id: ${{ inputs.caller-run-id }}

- name: Resolve PR deployment URL
id: hosts
run: |
DEPLOY_ENV="dev-pages-react18"
echo "Looking for deployment with environment '${DEPLOY_ENV}'..."
DEPLOY_ID=$(gh api \
"repos/${REPO}/deployments?environment=${DEPLOY_ENV}&per_page=5" \
--jq '.[0].id // empty' 2>/dev/null || true)
NEW_URL=""
if [ -n "$DEPLOY_ID" ]; then
NEW_URL=$(gh api \
"repos/${REPO}/deployments/${DEPLOY_ID}/statuses" \
--jq '[.[] | select(.state == "success")] | first | .environment_url // empty' 2>/dev/null || true)
echo "Found PR deployment: ID=${DEPLOY_ID}, URL=${NEW_URL}"
fi
if [ -z "$NEW_URL" ]; then
echo "::error::Could not resolve PR deployment URL"
exit 1
fi
echo "new=${NEW_URL}" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}

- name: Start baseline server
run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-visual-baseline &

- name: Wait for baseline server
run: node_modules/.bin/wait-on http://localhost:8080

- name: Run visual regression tests
run: |
echo "NEW_HOST=${NEW_HOST}"
NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard="${SHARD}/2"
env:
TZ: UTC
SHARD: ${{ matrix.shard }}
NEW_HOST: ${{ steps.hosts.outputs.new }}

- name: Upload Allure results
if: always()
uses: actions/upload-artifact@v4
with:
name: allure-results-shard-${{ matrix.shard }}
path: allure-results/
retention-days: 3

report:
name: Generate Allure Report
if: always()
needs: [visual]
runs-on: ubuntu-latest
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Download all Allure results
uses: actions/download-artifact@v4
with:
pattern: allure-results-shard-*
path: allure-results
merge-multiple: true

- name: Generate Allure HTML report
run: npx --yes allure generate allure-results -o allure-report

- name: Upload Allure report artifact
uses: actions/upload-artifact@v4
with:
name: allure-report
path: allure-report/
retention-days: 14

deploy-report:
name: Deploy Allure Report
if: always()
needs: [report]
uses: cloudscape-design/actions/.github/workflows/deploy.yml@main
secrets: inherit
with:
artifact-name: allure-report
deployment-path: allure-report
4 changes: 4 additions & 0 deletions build-tools/tasks/package-json.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ const devPagesPackageJson = generatePackageJson(path.join(workspace.targetPath,

const testDefinitionsPackageJson = generatePackageJson(path.join(workspace.targetPath, 'test-definitions'), {
name: '@cloudscape-design/test-definitions',
exports: {
'.': './index.js',
'./types': './types.js',
},
});

module.exports = parallel([
Expand Down
7 changes: 7 additions & 0 deletions build-tools/visual/global-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

module.exports = async () => {
const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher');
await startWebdriver();
};
7 changes: 7 additions & 0 deletions build-tools/visual/global-teardown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

module.exports = () => {
const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher');
shutdownWebdriver();
};
16 changes: 16 additions & 0 deletions build-tools/visual/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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');

configure({
browserName: 'ChromeHeadlessIntegration',
browserCreatorOptions: {
seleniumUrl: 'http://localhost:9515',
},
webdriverOptions: {
baseUrl: 'http://localhost:8080',
},
});

jest.retryTimes(2, { logErrorsBeforeRetry: true });
33 changes: 27 additions & 6 deletions docs/RUNNING_TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The npm scripts use gulp tasks that handle env vars (`TZ=UTC`, `NODE_OPTIONS=--e
- **Integration tests** — test against real browser behavior on Chrome, with motion disabled.
- **Motion tests** — run a specific set of tests on Chrome, with motion enabled.
- **Accessibility tests** — run on all test pages across themes and color modes (`src/__a11y__/`).
- **Visual regression tests** — compare screenshots between a PR and `main` to detect unintended visual changes. CI-only (see below).

## Targeting Specific Files

Expand Down Expand Up @@ -46,7 +47,7 @@ npm i -g chromedriver

## Updating Snapshots

When component APIs change, you may need to update test snapshots. When design tokens are touched, you must also run integration tests to update their snapshots Before updating, run a full build (`npm run build`) so that documenter docs are generated. Use the `-u` flag to update:
When component APIs change, you may need to update test snapshots. When design tokens are touched, you must also run integration tests to update their snapshots. Before updating, run a full build (`npm run build`) so that documenter docs are generated. Use the `-u` flag to update:

```
# Unit snapshots
Expand All @@ -58,13 +59,33 @@ NODE_OPTIONS=--experimental-vm-modules npx jest -u -c jest.integ.config.js src/_
# Snapshots inside components, e.g. when custom-css-properties.js changes (runs all unit tests)
TZ=UTC npx jest -u -c jest.unit.config.js src/
```

## Visual Regression Tests

> **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 on pull requests via the CI pipeline (`.github/workflows/deploy.yml` and `.github/workflows/visual-regression.yml`). They cannot be run locally at the moment.

### How it works (CI)

The deploy workflow (`.github/workflows/deploy.yml`) orchestrates the full pipeline:

1. **Quick build** — builds dev pages for React 16 and React 18 in parallel using the `.github/actions/quick-build` composite action.
2. **Deploy** — deploys the React 18 pages to a preview environment (the PR deployment URL).
3. **Build baseline** — creates a git worktree of `origin/main`, installs its dependencies, and builds its pages.
4. **Visual regression** — once deploy and baseline are ready, calls the reusable `visual-regression.yml` workflow.

The visual regression workflow (`.github/workflows/visual-regression.yml`):

1. Resolves the PR deployment URL from the GitHub Deployments API.
2. Serves the baseline pages locally.
3. Runs the test suite sharded across multiple runners. Each test navigates to a page on both hosts, captures screenshots, and compares them pixel-by-pixel.
4. Produces an Allure report with image diffs for any failures, deployed to a preview environment.

### Reviewing failures

Visual regression tests for permutation pages run automatically when opening a pull request in GitHub.
When the CI job fails, check the deployed Allure report (linked from the GitHub deployment). It shows expected vs actual vs diff images for each failing test. If the diff is expected (intentional visual change), note it in your PR description.

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.
### Adding tests for a new component

If there are unexpected regressions, fix your pull request.
If the changes are expected, call this out in your pull request comments.
1. Create `test/definitions/visual/<component>.ts` exporting a `TestSuite`.
2. Create `test/visual/<component>.test.ts` that imports and runs the suite.
3. Add the import to `test/definitions/index.ts`.
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ export default tsEslint.config(
},
},
{
files: ['**/__integ__/**', '**/__motion__/**', '**/__a11y__/**'],
files: ['**/__integ__/**', '**/__motion__/**', '**/__a11y__/**', 'test/definitions/**'],
rules: {
// useBrowser is not a hook
'react-hooks/rules-of-hooks': 'off',
Expand Down
28 changes: 28 additions & 0 deletions jest.visual.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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: 'allure-jest/node',
testEnvironmentOptions: {
resultsDir: 'allure-results',
},
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: 'tsconfig.visual.json',
},
],
},
reporters: ['default', 'github-actions'],
testTimeout: 240_000, // 4min — pages can be tall and slow to capture
maxWorkers: os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1),
globalSetup: '<rootDir>/build-tools/visual/global-setup.js',
globalTeardown: '<rootDir>/build-tools/visual/global-teardown.js',
setupFilesAfterEnv: [path.join(__dirname, 'build-tools', 'visual', 'setup.js')],
moduleFileExtensions: ['js', 'ts'],
testMatch: ['<rootDir>/test/visual/**/*.test.ts'],
};
Loading
Loading