-
Notifications
You must be signed in to change notification settings - Fork 234
chore: Add visual regression testing #4509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d990721
bb56f3e
93bcfdb
795064f
dfb8590
663864c
61a61b0
1043a03
a951e54
13736cf
0ca63b6
785e6fd
689a1ef
2cbb9e5
7e7e758
f65463c
f868757
aa00fb6
66506b7
48bddb7
88c91b2
a2aa017
c66d3b7
0bd90be
e0cbbb9
f244200
670ef1a
4456fd3
b508370
e540d32
5912a0a
9e10886
5c7e8ea
c42c2e1
a1a4d92
df04eb6
4b394dc
cfb6a3a
e5f7cc6
2ee27a9
911ede4
c580c7f
10d9a9e
596ae23
56086de
7bfe43e
009f977
eede7d9
2ae6d2a
e4045ad
a40d768
6efec5b
aebe762
68ab563
6c6c43b
ac78f9c
68e2fe7
963d42d
e1f74ad
a41f94c
3c153d1
f065097
de46e47
b8f16c7
14714bb
1a063b1
dc4fea3
5c18355
ff74340
d2de716
67d68ef
6a15aaf
950d87c
f319108
19eab2b
dda1cca
4961b1b
036e37d
1576564
480ac33
b776692
780db53
f37634c
5b6e7c4
6af1143
d39c0e2
9c95fe3
a36a79d
d854993
477664c
07e504f
b7810bb
612c136
2ffba79
e0e27d2
6c61274
b1ae9c6
f9ddc27
50f107a
7946d51
6ba70dd
4d07240
9fc1ec9
69c7efa
470069b
0db3567
a950516
0ee95da
2896863
210f801
e189bd2
8119dd8
b3b74b0
c5da1f6
8e05873
1435fef
5674948
63f0490
dd64d04
6069f69
1edddb2
6ecd15a
ef0a5d6
2085a99
d007cbd
4878b2e
e5f55e9
d8a9dbf
a342cb5
8569e52
e5ee432
97226da
eee210a
b3acc8d
f7c0b95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| name: Deploy | ||
| name: Deploy and visual regression | ||
|
|
||
| on: | ||
| pull_request: | ||
|
|
@@ -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)}}) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
|
|
@@ -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)}}) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
|
|
@@ -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 }} | ||
| 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] | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
| 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(); | ||
| }; |
| 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(); | ||
| }; |
| 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 }); |
| 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'], | ||
| }; |
There was a problem hiding this comment.
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.