diff --git a/tests/core/auth.test.ts b/tests/core/auth.test.ts index e946c644..94dace0f 100644 --- a/tests/core/auth.test.ts +++ b/tests/core/auth.test.ts @@ -46,7 +46,10 @@ describe('HttpAuthStore', () => { await expect(failure).rejects.toThrow(/boom \(500\)/); }); - it('does not mistake the firewall page for a wrong password', async () => { + // The firewall in front of nockal.com used to answer every non-2xx with this page; since + // 2026-09-20 it hands the API's own status through. The rule stays: only a 401 from the API + // means a wrong pair, anything a box in between makes up does not. + it('does not mistake a proxy’s own page for a wrong password', async () => { stubFetch(FIREWALL_418, { status: 418 }); const failure = new HttpAuthStore('/api').logIn('user', 'secret'); await expect(failure).rejects.not.toBeInstanceOf(WrongCredentialsError); diff --git a/tools/e2e/45-login.mjs b/tools/e2e/45-login.mjs index 14fa0e4d..a02479d7 100644 --- a/tools/e2e/45-login.mjs +++ b/tools/e2e/45-login.mjs @@ -80,13 +80,12 @@ export async function login(t) { await typeText('not-the-password'); await press('Enter'); await sleep(600); - // What the message says about a *wrong* password is checked in the unit tests: the live server - // sits behind a firewall that answers every non-2xx with its own 418 page, so the wording of the - // API's 401 only survives where nothing sits in between. That it is refused at all, and that - // nobody gets in, holds everywhere – and that is what this check is about. + // The wording is checked here again since the firewall stopped turning every non-2xx into its own + // 418 page (measured 2026-09-20: `nockal.com` hands out the API's 401 and its body unchanged). + // Should something sit in between again, this is the check that says so. check( 'a wrong password is refused and leaves the screen standing', - (await json('__gm.login()')).message !== '' && + (await json('__gm.login()')).message === 'Wrong user name or password' && Boolean(await find('Sign in')) && !(await evaluate('Boolean(window.__gm?.state)')), await json('__gm.login()'), @@ -106,11 +105,17 @@ export async function login(t) { await press('Enter'); await waitForScreen('editor'); const loaded = await json( - '({ editor: Boolean(window.__gm?.state), project: __gm?.state?.project?.name ?? null, title: document.title })', + // The project's *name* is no check: locally the file carries the seeder's "E2E fixture", in CI + // the shell's key "fixture" (tools/ci/project-shells.mjs, and the seeder keeps the name it + // finds). The id and the seeded scene say the same thing and say it in both places. + `({ editor: Boolean(window.__gm?.state), project: __gm?.state?.project?.id ?? null, scene: __gm?.state?.project?.assets?.find((a) => a.id === 'scene1')?.name ?? null, title: document.title })`, ); check( 'the right pair opens the editor, with the account in the window title', - loaded.editor && loaded.project === 'E2E fixture' && loaded.title.endsWith(E2E_USER), + loaded.editor && + loaded.project === projectId && + loaded.scene === 'Level 1' && + loaded.title.endsWith(E2E_USER), loaded, ); @@ -147,10 +152,13 @@ export async function login(t) { await typeText(password); await press('Enter'); await waitForScreen('editor'); + const again = await json( + `({ project: __gm?.state?.project?.id ?? null, scene: __gm?.state?.project?.assets?.find((a) => a.id === 'scene1')?.name ?? null })`, + ); check( 'signing in again brings the editor back, with the project in it', - (await json('__gm?.state?.project?.name ?? null')) === 'E2E fixture', - await json('__gm?.state?.project?.name ?? null'), + again.project === projectId && again.scene === 'Level 1', + again, ); // The rebuilt editor has to be laid out, not sitting unlaid-out in the corner: `resize` lays out // only when the size changed, and the sign-in screen has set `root` to the size of the window