Fixed a flaky members import E2E test opening a member detail - #30429
Conversation
no ref The members list writes its own query string back after something changes it — closing the import modal applies the import's label filter — and that write is a navigation of its own. One landing right after a member row is clicked replaces the detail route with the list again: the detail chunk loads, the member is never fetched, and the assertion waits out its timeout on the list. `import-custom-fields.test.ts` hit this on roughly a quarter of attempts and lost all three retries on 0b8af54, taking the E2E shard red. Re-click the row when the detail screen does not appear, rather than waiting out a timeout on a screen the app navigated away from. The app-side race is real and stays open: a member opened straight after an import can send the reader back to the list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx run @tryghost/e2e:test:fixtures |
✅ Succeeded | 5s | View ↗ |
nx run-many --target=build --projects=tag:publi... |
✅ Succeeded | <1s | View ↗ |
nx run ghost-monorepo:lint:boundaries |
✅ Succeeded | <1s | View ↗ |
nx run-many -t lint -p @tryghost/e2e |
✅ Succeeded | 5s | View ↗ |
nx run @tryghost/admin:build |
✅ Succeeded | 9s | View ↗ |
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗
☁️ Nx Cloud last updated this comment at 2026-09-01 15:12:33 UTC
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: QUIET Plan: Team Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📜 Recent review details⏰ Context from checks skipped due to timeout. (6)
🧰 Additional context used📓 Path-based instructions (6)Review fixture/page-object lifecycle, concurrency, reset timing, reusable⚙️ CodeRabbit configuration file Files:
Review lens: "where does this data become trusted?"⚙️ CodeRabbit configuration file Files:
Prioritise concrete correctness, security, data-integrity, compatibility,⚙️ CodeRabbit configuration file Files:
Follow the locator priority in the E2E writing guide; do not copy generated📄 CodeRabbit inference engine (e2e/AGENTS.md) Files:
Type-safe boundaries: Fail only if the PR:📄 CodeRabbit inference engine (Custom checks) Files:
Always use `pnpm`, never npm or Yarn.📄 CodeRabbit inference engine (e2e/AGENTS.md) Files:
🔇 Additional comments (1)
Walkthrough
Suggested reviewers: Merge Risk: ⚪ Minimal · up to This change makes the members import test retry the row click when the detail view does not open, without changing product behavior. No actionable merge-blocking risk remains beyond normal checks and review. 🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
Full details: Type-Safe BoundariesExplanation PASS. The PR changes only the E2E page helper Full details: New Files Are TypescriptExplanation PASS: The pull-request commit changes only the pre-existing ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |

Why
E2E Tests (Main 1/10) went red on
mainat 0b8af54, withimport-custom-fields.test.ts:92failing all three attempts. It is not new: the same test failed once and passed on retry in 3 of the 12 precedingmainruns — about a quarter of attempts — and this run simply lost every retry.From the failing run's Playwright trace, the failure is not about the imported value. After the row click, the
member-detailchunk loads butGET /members/<id>/is never issued, and the URL timeline reads:The members list mirrors its filter state into the URL from an effect (
use-members-filter-state.ts), and that write is a navigation of its own. One landing right after the click replaces the detail route with the list again, so the assertion waits out its timeout on the screen it started from.What
openMemberByNamere-clicks the row when the detail screen does not appear, instead of waiting out a timeout on a screen the app navigated away from. Three attempts, 5s each, against a 60s CI test timeout on a test that normally runs in ~13s.The app bug stays open
A member opened straight after an import can send the reader back to the list — this only stops the test from being red. Two fixes to the URL sync were tried and rejected:
currentQuerylags the last-written query by one render after everysetFilters, so the adopt branch fires on the app's own writes and resets the draft: the composite custom-field filter cascade loses its operator selection andcustom-field-filter-valuenever renders. Failedan address filter matches on the chosen part only2/2, which passes 3/3 without the change.useNavigation().state !== 'idle'. Needs a data router; every MemoryRouter-based harness inapps/adminthen throws, failing the members unit suite, and no unit test could be made to fail without the guard.The real fix looks like making the URL the single source of truth for committed filters and keeping in-progress predicates local, so no background write can preempt a navigation.
Testing
Locally, against the dev stack:
Members import with custom fields— 4 passedcustom-field-filter-*tests, run individually — passlint0 errors; e2etest:typesclean🤖 Generated with Claude Code