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

0% Statements 0/82
0% Branches 0/1
0% Functions 0/1
0% Lines 0/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 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');
    });
});