Skip to content

Commit 6379873

Browse files
authored
fix(e2e): make visual-regression editor captures deterministic (#2192)
Blur the active element before capturing so a hydration focus race on the TipTap toolbar cannot leave a button highlighted in some runs and not others, which produced spurious diffs on the content-editor and content-new screens. Also decide visual drift from git status of the snapshots directory rather than from a non-zero Playwright exit. A flaky render can fail the first-pass diff yet regenerate byte-identical baselines under --update-snapshots; the old candidate-emptiness check always saw the committed baselines and flagged drift regardless. Now a clean tree after regeneration is treated as flake or infra failure and keeps the check green with a warning.
1 parent ab351d2 commit 6379873

2 files changed

Lines changed: 35 additions & 14 deletions

File tree

.github/workflows/visual.yml

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,19 @@ jobs:
6767
6868
# Always record PR metadata so the Report workflow can upsert *or* clear
6969
# the sticky comment. Candidate baselines + diff images are added only
70-
# when the suite actually diffed (exit != 0 AND regeneration produced
71-
# snapshots) -- an infra failure exits non-zero but yields no candidates,
72-
# so we don't mislabel it as a visual change.
70+
# when the regenerated baselines actually differ from the committed ones.
71+
# A non-zero exit alone is not drift: a flaky render can fail the diff
72+
# check on the first pass yet regenerate byte-identical baselines under
73+
# --update-snapshots, and an infra failure exits non-zero without
74+
# regenerating anything. Both leave the snapshots dir git-clean, so we
75+
# decide drift from `git status`, not from the exit code.
7376
- name: Collect result
7477
id: collect
7578
env:
7679
PR_NUMBER: ${{ github.event.pull_request.number }}
7780
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
7881
RUN_EXIT: ${{ steps.run.outputs.exit }}
82+
SNAP_DIR: e2e/tests/visual-regression.spec.ts-snapshots
7983
run: |
8084
mkdir -p visual-out
8185
echo "$PR_NUMBER" > visual-out/pr-number
@@ -99,21 +103,27 @@ jobs:
99103
# Regenerate the accepted-state baselines (the candidates the Apply
100104
# workflow commits verbatim on accept). These are Linux/chromium PNGs.
101105
pnpm exec playwright test visual-regression --update-snapshots --reporter=line || true
102-
mkdir -p visual-out/candidates
103-
if [ -d e2e/tests/visual-regression.spec.ts-snapshots ]; then
104-
cp -r e2e/tests/visual-regression.spec.ts-snapshots/. visual-out/candidates/
105-
fi
106106
107-
if [ -z "$(ls -A visual-out/candidates 2>/dev/null)" ]; then
108-
# Non-zero exit but nothing regenerated -> infra failure, not drift.
107+
# Real drift == the regeneration changed tracked baselines or added
108+
# new ones. Untracked new files count (bootstrap: a PR that adds a
109+
# page has no committed baseline yet). A clean tree means the failure
110+
# did not correspond to a stable pixel difference -- treat it as flake
111+
# or infra and keep the check green with a warning.
112+
if [ -z "$(git status --porcelain -- "$SNAP_DIR")" ]; then
109113
echo "false" > visual-out/has-drift
110114
echo "has_drift=false" >> "$GITHUB_OUTPUT"
111-
echo "::warning::Visual suite failed without producing candidate baselines (likely an infra failure, not a UI diff)."
112-
else
113-
echo "true" > visual-out/has-drift
114-
echo "has_drift=true" >> "$GITHUB_OUTPUT"
115-
echo "Visual drift detected — candidates and diff images staged for the Report workflow."
115+
echo "::warning::Visual suite exited non-zero but regenerated baselines are identical to the committed ones (flaky render or infra failure, not a UI diff)."
116+
exit 0
117+
fi
118+
119+
# Stage the candidate baselines for the Report/Apply workflows.
120+
mkdir -p visual-out/candidates
121+
if [ -d "$SNAP_DIR" ]; then
122+
cp -r "$SNAP_DIR/." visual-out/candidates/
116123
fi
124+
echo "true" > visual-out/has-drift
125+
echo "has_drift=true" >> "$GITHUB_OUTPUT"
126+
echo "Visual drift detected — candidates and diff images staged for the Report workflow."
117127
118128
- name: Upload artifact
119129
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1

e2e/tests/visual-regression.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,17 @@ async function openAdmin(admin: AdminPage, path: string, dir: string): Promise<v
110110
/** Settle fonts and freeze animation before capturing. */
111111
async function stabilize(admin: AdminPage): Promise<void> {
112112
await admin.page.addStyleTag({ content: FREEZE_CSS });
113+
// Drop focus before capturing. The editor pages mount a TipTap toolbar
114+
// whose buttons reflect the editor's focus/active state; whether the editor
115+
// grabs focus during hydration is a race, so a run can capture a focused
116+
// (highlighted) toolbar button or an unfocused one. Blurring makes the
117+
// capture depend on the rendered page, not on who won the focus race.
118+
await admin.page.evaluate(() => {
119+
const active = document.activeElement;
120+
if (active instanceof HTMLElement && active !== document.body) {
121+
active.blur();
122+
}
123+
});
113124
// Await font loading without returning the FontFaceSet that
114125
// document.fonts.ready fulfils with -- Playwright cannot serialize it.
115126
await admin.page.evaluate(async () => {

0 commit comments

Comments
 (0)