All files / realm/blong-access/test session-popup.play.ts

73.17% Statements 60/82
100% Branches 2/2
100% Functions 0/0
73.17% Lines 60/82

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 831x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x             1x 1x 1x 1x 1x 1x 1x 1x 1x                     1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x             1x 1x 1x 2x 1x  
/**
 * 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');
    });
});