import { test, expect, type Page } from './fixtures'; // E2E for the bot API token management page: list existing tokens, create one // (the raw bearer is shown once), and revoke one. const existing = { id: 't1111111-1111-1111-1111-111111111111', user_id: 'u1', name: 'existing-bot', created_at: '2026-01-01T00:00:00Z', last_used_at: null, expires_at: null }; type Captured = { created: Record | null; deleted: string | null }; async function mockTokens(page: Page, cap: Captured) { await page.route('**/api/v1/auth/config', (r) => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ self_register_enabled: true, private_mode: false }) }) ); await page.route('**/api/v1/auth/me', (r) => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ user: { id: 'u1', username: 'reader', created_at: '2026-01-01T00:00:00Z', is_admin: false } }) }) ); await page.route('**/api/v1/auth/me/preferences', (r) => r.fulfill({ status: 200, contentType: 'application/json', body: '{}' }) ); await page.route('**/api/v1/me/bookmarks*', (r) => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ items: [], page: { limit: 50, offset: 0, total: 0 } }) }) ); // Specific id-scoped DELETE first (last-match-wins ordering). await page.route('**/api/v1/auth/tokens/*', async (r) => { cap.deleted = r.request().url().split('/').pop() ?? null; await r.fulfill({ status: 204, body: '' }); }); await page.route('**/api/v1/auth/tokens', async (r) => { if (r.request().method() === 'POST') { cap.created = r.request().postDataJSON(); await r.fulfill({ status: 201, contentType: 'application/json', body: JSON.stringify({ id: 't2', user_id: 'u1', name: cap.created?.name, created_at: '2026-03-01T00:00:00Z', last_used_at: null, expires_at: null, bearer: 'secret-raw-bearer-xyz' }) }); return; } await r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ items: [existing] }) }); }); } test('lists, creates (shows the bearer once), and revokes tokens', async ({ page }) => { const cap: Captured = { created: null, deleted: null }; await mockTokens(page, cap); await page.goto('/profile/tokens'); // Existing token is listed. await expect(page.getByTestId(`token-row-${existing.id}`)).toContainText('existing-bot'); // Create a token — the raw bearer is revealed exactly once. await page.getByTestId('token-name').fill('new-bot'); await page.getByRole('button', { name: 'Create token' }).click(); await expect(page.getByTestId('token-fresh')).toContainText('secret-raw-bearer-xyz'); await expect.poll(() => cap.created).toEqual({ name: 'new-bot' }); // Revoke the existing token (confirm dialog accepted). page.on('dialog', (d) => d.accept()); await page.getByTestId(`token-revoke-${existing.id}`).click(); await expect.poll(() => cap.deleted).toBe(existing.id); });