Skip to content

B-MNIZ-goto-in-e2e-defeats-unmount-assertions

A Playwright page.goto is a real document load. Every useState in the app is rebuilt from its initializer, every store is fresh, and the component tree is new. So a spec that navigates with goto and then asserts something about what a component did ON THE WAY OUT is asserting against a blank slate: it passes whether or not the teardown behaviour it means to pin exists at all.

Found while pinning the find-bar fix in #1719. The claim was “opening another note closes the find bar instead of greeting the reader with one” — real behaviour, backed by MdSurface emitting update:findOpen(false) from onBeforeUnmount. The first version of the test reached the second note with open() (a goto) and passed. It also passed with the fix reverted, because useState("notes:find") had been rebuilt by the reload either way.

Navigate IN-APP. pages/apps/notes/[...path].vue keys <NotesNoteView :key="noteKey(address)">, so a router navigation genuinely unmounts the old surface and mounts a new one against one live flag — which is the thing under test. In the notes suite that means clicking the tab strip:

await page.getByTestId("notes-tab-strip").getByRole("tab", { name: title }).click()

solutions/determined/tests/ui/notes/scroll-memory.e2e.ts:326 already uses this deliberately and comments on why (“an in-app navigation, no reload”), so the pattern is established in the codebase.

To confirm a navigation really was in-app, set a marker on window before it and read it after:

await page.evaluate(() => { (window as any).__probeMark = "set-on-A" })
// …navigate…
await page.evaluate(() => (window as any).__probeMark) // survives an in-app nav, gone after a reload

That is the check that distinguished the two cases here: absent after open(), "set-on-A" after the tab click.

Sweep the existing e2e specs for assertions about cross-navigation or post-unmount state that are reached through goto, and convert the ones that need a live app to in-app navigation. The tell is a spec that navigates and then asserts an ABSENCE — no bar, no dialog, no leftover selection — since an absence is exactly what a fresh page gives you for free. Not every goto is wrong: a spec whose subject is the cold-load path should keep it.

The class is not notes-specific. Any app-frame product with shared useState or a store that outlives a keyed component has the same hole.


← Back to Backlog