Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | /** * Session expiry → login popup → re-login. * * Deterministic trigger: after login the test revokes the server-side session * via `login.token.revoke` (through the app's own handler), waits for the * short test access-token TTL (30s) to elapse, then triggers a direct * protected call. The codec attempts renewal → the session is revoked → * renewal is refused → 401 → the login popup appears. After logging in * through the popup the same operation succeeds again. * * Screenshots (baseline via `npm run playwright:update`, verify via * `npm run playwright`): * - `login-popup.png` — the login popup after a revoked/expired session * - `re-login-save.png` — the operation succeeds after re-login */ import {expect, test} from '@feasibleone/blong-browser/playwright'; test.use({blongPermissions: true}); test.describe('Access session login popup', () => { test('expired session shows the login popup and re-login recovers', async ({ page, portal, }) => { test.setTimeout(120_000); // The portal fixture has logged in (session created + cookie set). await expect(page.locator('.blong-portal-menubar')).toBeVisible(); // Revoke the server-side session WITHOUT clearing the local auth state. // `login.token.revoke` resolves through the portal/backend adapter via // blong-login's browser subject namespace (no `backend/` prefix needed). await page.evaluate(async () => { const handler = (window as unknown as {__blongHandler?: Record<string, unknown>}) .__blongHandler; await (handler?.loginTokenRevoke as (p: object, m: object) => Promise<unknown>)( {}, {}, ); }); // Let the access token expire — the codec renews a few seconds before // expiry, so after this wait the next request attempts a renewal. await page.waitForTimeout(26_000); // Trigger a protected call → renewal refused (session revoked) → 401 → // login popup. await page.evaluate(async () => { const handler = (window as unknown as {__blongHandler?: Record<string, unknown>}) .__blongHandler; try { await (handler?.accessUserFind as (p: object, m: object) => Promise<unknown>)( {paging: {}}, {}, ); } catch { // 401 expected — the popup is asserted below. } }); await expect(page.locator('.blong-login-popup')).toBeVisible(); await expect(page).toHaveScreenshot('login-popup.png'); // Re-login through the popup. await page.fill('.blong-login-popup input[name="username"]', 'testAdmin'); await page.fill('.blong-login-popup input[name="password"]', 'testPassword'); await page.locator('.blong-login-popup').getByRole('button', {name: /login/i}).click(); // Popup closes and the portal is back; the operation now succeeds. await expect(page.locator('.blong-login-popup')).toHaveCount(0); await expect(page.locator('.blong-portal-menubar')).toBeVisible(); const result = await page.evaluate(async () => { const handler = (window as unknown as {__blongHandler?: Record<string, unknown>}) .__blongHandler; return (await (handler?.accessUserFind as ( p: object, m: object, ) => Promise<unknown>)({paging: {}}, {})) as unknown; }); expect(Array.isArray(result), 'protected call succeeds after re-login').toBe(true); await expect(page).toHaveScreenshot('re-login-save.png'); }); }); |