Skip to content

fix(e2e): dismiss the first-login welcome modal deterministically in devBypassAuth#1812

Merged
ascorbic merged 1 commit into
emdash-cms:mainfrom
masonjames:fix/e2e-onboarding-modal-race
Jul 4, 2026
Merged

fix(e2e): dismiss the first-login welcome modal deterministically in devBypassAuth#1812
ascorbic merged 1 commit into
emdash-cms:mainfrom
masonjames:fix/e2e-onboarding-modal-race

Conversation

@masonjames

Copy link
Copy Markdown
Contributor

What does this PR do?

Removes a timing race in the E2E auth fixture that intermittently fails unrelated PRs on the Cloudflare suite.

The admin shell opens the first-login "Welcome to EmDash" modal whenever its currentUser query resolves with isFirstLogin — on a cold workerd start that can be several seconds after the sidebar renders. AdminPage.waitForShell() only dismisses the modal if it is visible within a 2-second window, so when the query resolves late the modal survives and swallows the test's clicks/assertions.

Observed in the wild on actions run 28678460523, E2E Cloudflare (3/8) (failing #1810, whose diff touches only the menus resolver): content-types.spec.ts › displays seeded collections in a table failed with element(s) not found for the table links, and the Playwright error context shows the page covered by dialog "Welcome to EmDash, Dev!". The equivalent SQLite shard and the other seven Cloudflare shards passed.

The fix makes the standard auth path deterministic instead of retiming the race:

  • devBypassAuth() now lands the bypass redirect on the shell-free /_emdash/api/auth/me JSON endpoint, clears the flag server-side (POST /_emdash/api/auth/me with action: "dismissWelcome", via page.request so the session cookie and CSRF header are supplied), and only then loads the admin shell — so the shell's first currentUser fetch already reports isFirstLogin: false and the modal can never mount.
  • The existing UI-side dismissOnboardingModal() fallback in waitForShell() is left in place for flows that authenticate outside the bypass (e.g. the setup-wizard spec's real first-run path).

No product code changes; the fixture ends in the same post-condition as before (authenticated, dashboard loaded, shell hydrated).

Issue: none filed — happy to open one instead if you prefer tracking flakes that way.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

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) — the change is itself test infrastructure; affected specs run green on both targets (output below)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). N/A: E2E fixture only.
  • I have added a changeset (if this PR changes a published package). N/A: test-only change, no published package touched.
  • New features link to an approved Discussion: N/A, test fix.

AI-generated code disclosure

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

Screenshots / test output

  • pnpm exec playwright test e2e/tests/content-types.spec.ts e2e/tests/setup-wizard.spec.ts17 passed (42.0s)
  • EMDASH_E2E_TARGET=cloudflare pnpm exec playwright test e2e/tests/content-types.spec.ts e2e/tests/setup-wizard.spec.ts17 passed (52.7s)
  • pnpm lint:json | jq '.diagnostics | length'0
  • pnpm format → ran

…devBypassAuth

The admin shell opens the welcome modal whenever its currentUser query
resolves with isFirstLogin — on a cold workerd start that can be
seconds after the sidebar renders, past dismissOnboardingModal()'s 2s
visibility window, so the modal survives and swallows the test's
clicks (actions run 28678460523, E2E Cloudflare shard 3/8).

devBypassAuth now lands the bypass on the shell-free auth/me endpoint,
clears the welcomeDismissed flag over HTTP, and only then loads the
admin shell, so the first currentUser fetch already reports
isFirstLogin: false and the modal can never mount on the standard auth
path. The UI-side dismissal stays as a fallback for flows that
authenticate outside the bypass.
@changeset-bot

changeset-bot Bot commented Jul 3, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a415de6

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 review/needs-review No maintainer or bot review yet size/S labels Jul 3, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

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

@emdash-cms/auth

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

@emdash-cms/auth-atproto

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

@emdash-cms/blocks

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

@emdash-cms/cloudflare

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

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

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

emdash

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

create-emdash

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

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

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

@emdash-cms/plugin-cli

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

@emdash-cms/plugin-types

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

@emdash-cms/registry-client

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

@emdash-cms/registry-lexicons

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

@emdash-cms/sandbox-workerd

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

@emdash-cms/x402

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

@emdash-cms/plugin-ai-moderation

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

@emdash-cms/plugin-atproto

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

@emdash-cms/plugin-audit-log

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

@emdash-cms/plugin-color

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

@emdash-cms/plugin-embeds

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

@emdash-cms/plugin-field-kit

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

@emdash-cms/plugin-forms

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

@emdash-cms/plugin-webhook-notifier

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

commit: a415de6

@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 fix for the right problem: the E2E auth fixture was racing the shell's first-login modal, and clearing the flag server-side before the shell loads eliminates the race instead of just widening the timeout.

I checked the changed fixture against the relevant server code:

  • POST /_emdash/api/auth/me with action: "dismissWelcome" is the same endpoint the admin UI uses; it persists welcomeDismissed in the DB and returns { success: true } on success.
  • The auth middleware requires X-EmDash-Request: 1 for cookie-authenticated state-changing API requests; the fixture supplies it.
  • The setup/dev-bypass redirect to /_emdash/api/auth/me is a safe local path (isSafeRedirect accepts it), and waitForURL matching on pathname avoids matching the redirect query string while the meta-refresh page is still loaded.
  • page.request shares the browser context's cookies, so the session established by the bypass is present for the POST.
  • The existing dismissOnboardingModal() fallback in waitForShell() is preserved for non-bypass flows.

The change is test-only, touches no published package, and needs no changeset. No AGENTS.md convention violations found. The PR is clean.

@github-actions github-actions Bot added cla: signed review/approved Approved; no new commits since and removed review/needs-review No maintainer or bot review yet labels Jul 3, 2026

@ascorbic ascorbic left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Well found

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

Labels

cla: signed review/approved Approved; no new commits since size/S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants