SPB Git

Repository Public

auth.spec.ts 4.6 KB 91 lines typescript

1import { expect, test } from '@playwright/test';2import { apiLogin, resetMock, SEED, totp, watch } from './helpers';34test.afterAll(async ({ browser }) => {5  const page = await browser.newPage();6  await resetMock(page, true);7  await page.close();8});910test('first-run setup: token + account, mandatory TOTP enrolment with a locally rendered QR', async ({ page }) => {11  const w = watch(page);12  await resetMock(page, false);13  await page.goto('/');14  await expect(page).toHaveURL(/\/setup\//);15  await expect(page.getByRole('heading', { name: 'Set up Agent OS' })).toBeVisible();16  await page.getByTestId('setup-token').fill('mock-setup-token-0000');17  await page.getByTestId('setup-username').fill('owner');18  await page.getByTestId('setup-password').fill('short');19  await expect(page.getByTestId('setup-submit')).toBeDisabled(); // < 12 characters20  await page.getByTestId('setup-password').fill('a-long-passphrase-42');21  await page.getByTestId('setup-confirm').fill('a-long-passphrase-42');22  await page.getByTestId('setup-submit').click();2324  // the account is not usable yet: the app shell refuses to load before a second factor exists25  await expect(page.getByTestId('second-factor-enroll')).toBeVisible();26  expect((await (await page.request.get('/api/v1/auth/status')).json()).authenticated).toBe(false);27  await page.getByTestId('totp-start').click();28  await expect(page.locator('canvas[role="img"]')).toBeVisible();29  expect(await page.locator('canvas[role="img"]').evaluate((c: HTMLCanvasElement) => c.width)).toBeGreaterThan(100);30  const secret = (await page.getByTestId('totp-secret').getAttribute('data-secret'))!;31  await page.getByTestId('totp-enroll-code').fill(totp(secret));32  await page.getByRole('button', { name: 'Verify and enable' }).click();33  await expect(page).toHaveURL(/\/home\//);34  // a fresh instance: honest empty states, no invented data35  await expect(page.getByTestId('needs-you-empty')).toBeVisible();36  await expect(page.getByText('No mission is running')).toBeVisible();37  await expect(page.getByTestId('today-usd')).toHaveText('$0.00');38  expect(await page.evaluate(() => JSON.stringify({ ...localStorage }) + JSON.stringify({ ...sessionStorage }))).not.toMatch(/csrf|token|secret|passphrase/i);39  w.expectClean();40});4142test('sign in with password then TOTP; wrong code is refused; sign out', async ({ page }) => {43  const w = watch(page);44  await resetMock(page, true);45  await page.goto('/missions/');46  await expect(page).toHaveURL(/\/login\/\?next=/);47  await expect(page.getByText('Sign in to Agent OS')).toBeVisible();48  await page.getByTestId('login-username').fill(SEED.username);49  await page.getByTestId('login-password').fill(SEED.password);50  await page.getByTestId('login-submit').click();51  const code = page.getByLabel('Authentication code');52  await code.fill('000000');53  await page.getByTestId('totp-submit').click();54  await expect(page.getByTestId('login-error')).toBeVisible();55  await code.fill(totp(SEED.totpSecret));56  await page.getByTestId('totp-submit').click();57  await expect(page).toHaveURL(/\/missions\//); // back where the owner was going58  await expect(page.getByTestId('wordmark')).toHaveText('Agent OS');59  await expect(page).toHaveTitle('Agent OS');60  await page.getByRole('button', { name: 'Sign out' }).click();61  await expect(page).toHaveURL(/\/login\//);62  w.expectClean();63});6465test('offline: the cached shell loads and says missions keep running; /api is never cached', async ({ page, context }) => {66  await resetMock(page, true);67  await apiLogin(page);68  await page.goto('/home/');69  await expect(page.getByTestId('needs-you')).toBeVisible();70  await page.evaluate(async () => { await navigator.serviceWorker.ready; });71  await page.reload();72  await expect(page.getByTestId('needs-you')).toBeVisible();73  await expect.poll(() => page.evaluate(() => !!navigator.serviceWorker.controller)).toBe(true);74  const cached = await page.evaluate(async () => {75    const out: string[] = [];76    for (const name of await caches.keys()) for (const req of await (await caches.open(name)).keys()) out.push(`${name}:${new URL(req.url).pathname}`);77    return out;78  });79  expect(cached.every((p) => p.startsWith('aos-shell-'))).toBe(true);80  expect(cached.some((p) => p.includes('/_next/static/'))).toBe(true);81  expect(cached.some((p) => p.endsWith(':/home/'))).toBe(true);82  expect(cached.filter((p) => p.includes('/api'))).toEqual([]);8384  await context.setOffline(true);85  await page.reload();86  await expect(page.getByTestId('offline-state')).toHaveText('Offline — missions keep running on the server');87  await context.setOffline(false);88  await page.getByRole('button', { name: 'Retry' }).click();89  await expect(page.getByTestId('needs-you')).toBeVisible();90});91
Files main
Menu

Theme