Skip to content

Commit 09f24ab

Browse files
committed
add automated snapshot testing with Playwright and PR comment reporting
1 parent a601135 commit 09f24ab

2 files changed

Lines changed: 119 additions & 22 deletions

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: Playwright Snapshot Report
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
jobs:
8+
snapshot-report:
9+
runs-on: ubuntu-latest
10+
11+
if: ${{ github.actor != 'dependabot[bot]' }}
12+
13+
permissions:
14+
contents: write
15+
pull-requests: write
16+
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
21+
- name: Set up GitHub CLI
22+
uses: cli/cli-action@v2
23+
24+
- name: Ensure snapshot dirs are writable
25+
run: |
26+
mkdir -p e2e/tests/screenshot-test.spec.js-snapshots
27+
sudo chown -R 1000:1000 e2e/tests/screenshot-test.spec.js-snapshots
28+
29+
- name: Create Docker network
30+
run: docker network create sf-website-development || true
31+
32+
- name: Run Playwright tests with snapshot update
33+
run: |
34+
docker compose -f docker-compose.yml -f docker-compose.e2e.yml run --name apostrophe-playwright-test --build \
35+
--entrypoint "sh -c 'until wget -qO- http://apostrophe:3000/ > /dev/null; do echo Waiting for Apostrophe...; sleep 2; done && npm run test:e2e:update -c playwright.config.js'" \
36+
playwright-test
37+
continue-on-error: false
38+
39+
- name: Copy snapshots from Docker volume
40+
run: |
41+
mkdir -p website/e2e/pr-snapshots/screenshot-test.spec.js-snapshots
42+
docker cp $(docker ps -aqf "name=apostrophe-playwright-test"):/e2e/tests/screenshot-test.spec.js-snapshots ./website/e2e/pr-snapshots/screenshot-test.spec.js-snapshots
43+
44+
- name: Upload snapshots as artifact
45+
uses: actions/upload-artifact@v4
46+
with:
47+
name: playwright-snapshots-pr
48+
path: website/e2e/pr-snapshots/screenshot-test.spec.js-snapshots
49+
overwrite: true
50+
retention-days: 20
51+
52+
- name: Generate comment body with links
53+
id: comment
54+
run: |
55+
ARTIFACT_URL="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
56+
COMMENT_BODY="🧪 **Playwright Snapshot Report**
57+
58+
🖼️ Screenshots have been updated and attached as an artifact.
59+
60+
🔗 [View Artifacts & Download Snapshots]($ARTIFACT_URL)
61+
62+
_This comment updates automatically with each commit._"
63+
64+
echo "body<<EOF" >> $GITHUB_OUTPUT
65+
echo "$COMMENT_BODY" >> $GITHUB_OUTPUT
66+
echo "EOF" >> $GITHUB_OUTPUT
67+
68+
- name: Post or update comment on PR
69+
env:
70+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
71+
run: |
72+
PR_NUMBER=${{ github.event.pull_request.number }}
73+
COMMENT_BODY="${{ steps.comment.outputs.body }}"
74+
75+
COMMENT_ID=$(gh pr view $PR_NUMBER --json comments \
76+
--jq '.comments[] | select(.body | contains("🧪 **Playwright Snapshot Report**")) | .id')
77+
78+
if [ -n "$COMMENT_ID" ]; then
79+
echo "Updating existing comment ID: $COMMENT_ID"
80+
gh pr comment $PR_NUMBER --edit "$COMMENT_ID" --body "$COMMENT_BODY"
81+
else
82+
echo "Creating new comment"
83+
gh pr comment $PR_NUMBER --body "$COMMENT_BODY"
84+
fi
85+
86+
- name: Cleanup Docker
87+
run: |
88+
docker compose -f docker-compose.yml -f docker-compose.e2e.yml down --volumes --remove-orphans
89+
docker network rm sf-website-development || true
Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,37 @@
11
import { test, expect } from '@playwright/test';
22

3-
test('Snapshot for Home Page', async ({ page }) => {
4-
await page.setViewportSize({
5-
width: 1280,
6-
height: 720,
7-
});
3+
const routes = [
4+
{ path: '/', name: 'homepage.png' },
5+
{ path: '/cases', name: 'cases.png' },
6+
{ path: '/about-us', name: 'about-us.png' },
7+
];
88

9-
await page.goto(process.env.BASE_URL ?? 'http://localhost:3000', {
10-
timeout: 60000,
11-
waitUntil: 'networkidle',
12-
});
9+
for (const { path, name } of routes) {
10+
test(`Snapshot for ${path}`, async ({ page }) => {
11+
await page.setViewportSize({ width: 1280, height: 720 });
1312

14-
await Promise.all([
15-
page.waitForLoadState('domcontentloaded'),
16-
page.waitForLoadState('networkidle'),
17-
]);
18-
await page.evaluate(() => document.fonts.ready);
13+
await page.goto(
14+
`${process.env.BASE_URL ?? 'http://localhost:3000'}${path}`,
15+
{
16+
timeout: 60000,
17+
waitUntil: 'networkidle',
18+
},
19+
);
1920

20-
const snapshot = await page.screenshot({
21-
fullPage: true,
22-
timeout: 30000,
23-
});
21+
await Promise.all([
22+
page.waitForLoadState('domcontentloaded'),
23+
page.waitForLoadState('networkidle'),
24+
]);
25+
await page.evaluate(() => document.fonts.ready);
26+
27+
const snapshot = await page.screenshot({
28+
fullPage: true,
29+
timeout: 30000,
30+
});
2431

25-
expect(snapshot).toMatchSnapshot('homepage.png', {
26-
maxDiffPixels: 300,
27-
threshold: 0.5,
32+
expect(snapshot).toMatchSnapshot(name, {
33+
maxDiffPixels: 300,
34+
threshold: 0.5,
35+
});
2836
});
29-
});
37+
}

0 commit comments

Comments
 (0)