Skip to content

test(e2e): wait for dashboard metrics before asserting layout - #2434

Merged
ascorbic merged 1 commit into
emdash-cms:mainfrom
MA2153:fix/dashboard-metric-e2e-flake
Aug 12, 2026
Merged

test(e2e): wait for dashboard metrics before asserting layout#2434
ascorbic merged 1 commit into
emdash-cms:mainfrom
MA2153:fix/dashboard-metric-e2e-flake

Conversation

@MA2153

@MA2153 MA2153 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a race in the two dashboard layout E2E tests that fails runs on the Cloudflare shard.

Both tests read dashboard-metric cards immediately after admin.waitForLoading(). That helper only waits for the text Loading and for .animate-spin to disappear (e2e/fixtures/admin.ts:171), but the dashboard renders Kumo SkeletonLine placeholders while the dashboard-stats query is in flight (packages/admin/src/components/Dashboard.tsx:133-152) — the skeleton cards carry no data-testid. Neither selector matches, both waits time out silently into .catch(() => {}), and the helper returns immediately.

expect(await metricCards.count()) is a plain assertion rather than a web-first one, so it takes a single snapshot with no retry. When the stats fetch is slow the count is 0:

Error: expect(received).toBeGreaterThanOrEqual(expected)
Expected: >= 3
Received:    0

The failure artifact's page snapshot confirms the loading state — main contained only the <h1>, quick-action links, and the "Content"/"Recent Activity" headings.

The fix uses assertions that retry: expect.poll for the card count, and a web-first visibility check before the tracking test's evaluate (that one throws Dashboard typography is missing under the same race).

No product code changes — the dashboard behaves correctly; only the tests were reading it too early.

Type of change

  • Tests

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation — n/a, no UI strings changed
  • I have added a changeset — n/a, test-only change, no published package touched
  • New features link to an approved Discussion — n/a, not a feature

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 5 (Claude Code)

Screenshots / test output

Verified by temporarily delaying the dashboard stats response by 3s locally, which makes the race deterministic.

Before (delay, original assertions) — both tests fail, reproducing CI exactly:

Error: expect(received).toBeGreaterThanOrEqual(expected)
Expected: >= 3
Received:    0
  1 failed
    dashboard card headings and metric values share an inset
Error: locator.evaluate: Error: Dashboard typography is missing
  1 failed
    dashboard headings keep the font's default tracking across scripts

After (same 3s delay, with this change) — both pass.

Clean run of all three dashboard tests without the delay:

Running 3 tests using 1 worker
[1/3] dashboard should have no WCAG 2.x AA violations
[2/3] dashboard card headings and metric values share an inset
[3/3] dashboard headings keep the font's default tracking across scripts
  3 passed (48.1s)

The temporary delay was local-only and is not part of the diff.

The two dashboard layout tests read `dashboard-metric` cards immediately
after `waitForLoading()`, which only waits on the text "Loading" and
`.animate-spin`. The dashboard renders Kumo `SkeletonLine` placeholders
while the stats query is in flight, so neither selector matches and the
wait returns straight away. `expect(await count())` takes a single
snapshot with no retry, so a slow stats response fails the run --
observed on the Cloudflare E2E shard as "Expected: >= 3, Received: 0"
with the page snapshot still showing the skeleton state.

Use `expect.poll` for the card count and a web-first visibility
assertion before the tracking test's `evaluate`, both of which retry.

Verified by delaying the dashboard stats response 3s locally: both tests
fail without these changes and pass with them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 8110043

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions Bot added size/XS review/needs-review No maintainer or bot review yet labels Aug 12, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is the right change for the reported flake. The race happens because waitForLoading() only waits for the literal "Loading" text and .animate-spin to disappear, while the dashboard renders SkeletonLine placeholders during the separate dashboard-stats fetch. Turning the snapshot-style assertion into a polling web-first assertion (expect.poll for the metric count, toBeVisible() before the typography evaluate) makes the tests wait for the actual DOM they inspect instead of locking in a moment while skeletons are still displayed.

The fix is localized to the two failing tests in e2e/tests/accessibility.spec.ts, which is correct: waitForLoading() is a generic helper used across many pages, so teaching it dashboard-specific skeletons would be the wrong scope. No product code is changed, so the PR correctly omits a changeset and skips the i18n/Discussion checklist items.

I checked the changed file, the fixture helper, and the dashboard component. The new assertions use Playwright's expect re-exported from @playwright/test, so expect.poll is available. The await on expect.poll(...) and on the toBeVisible() call are both present. No other E2E tests reference dashboard-metric, so there are no sibling assertions left with the same race. No AGENTS.md conventions are violated: no new UI strings, SQL, API routes, auth, localization issues, logged-out queries, indexes, or comment problems.

Clean test-only fix.

@pkg-pr-new

pkg-pr-new Bot commented Aug 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2434

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2434

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2434

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2434

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2434

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2434

emdash

npm i https://pkg.pr.new/emdash@2434

create-emdash

npm i https://pkg.pr.new/create-emdash@2434

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2434

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2434

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2434

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2434

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2434

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2434

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2434

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2434

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2434

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2434

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2434

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2434

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2434

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2434

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2434

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2434

commit: 8110043

@ascorbic
ascorbic merged commit a15a226 into emdash-cms:main Aug 12, 2026
47 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review/needs-review No maintainer or bot review yet size/XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants