import { expect } from '@playwright/test'; import { test } from './helpers/fixtures'; import { mock } from './helpers/mock'; // ─── Staff scanner ──────────────────────────────────────────────────────────── test('Lv10 user is blocked from /checkin with 403', async ({ page, loggedInUser }) => { void loggedInUser; await page.goto('/app/checkin'); await page.waitForLoadState('networkidle'); await expect(page.getByText('权限不足')).toBeVisible(); }); test('staff scanner: valid code shows success alert', async ({ page, staffUser }) => { void staffUser; await mock.override('POST', '/event/checkin/submit', { status: 200, body: { status: 200, data: {} } }); await page.goto('/app/checkin'); await page.waitForLoadState('networkidle'); // Fill OTP boxes — 6 individual inputs const boxes = page.locator('[data-otp-input] input'); const digits = ['4', '8', '3', '9', '1', '7']; for (let i = 0; i < 6; i++) { await boxes.nth(i).fill(digits[i]); } // Last digit fill auto-submits; wait for success alert await expect(page.getByText('签到成功')).toBeVisible({ timeout: 5000 }); }); test('staff scanner: backend error shows error alert', async ({ page, staffUser }) => { void staffUser; await mock.override('POST', '/event/checkin/submit', { status: 400, body: { status: 400, msg: '签到码无效或已过期' } }); await page.goto('/app/checkin'); await page.waitForLoadState('networkidle'); const boxes = page.locator('[data-otp-input] input'); for (let i = 0; i < 6; i++) await boxes.nth(i).fill('0'); await expect(page.getByText('签到码无效或已过期')).toBeVisible({ timeout: 5000 }); }); // ─── Attendee QR dialog ─────────────────────────────────────────────────────── const mockEventBody = (overrides: Record = {}) => ({ status: 200, data: { event_id: 'evt1', name: 'Test Event', subtitle: '', type: 'official', start_time: new Date(Date.now() - 3600_000).toISOString(), end_time: new Date(Date.now() + 3600_000).toISOString(), enable_kyc: false, is_joined: true, is_checked_in: false, is_agenda_published: false, ...overrides } }); test('attendee QR dialog shows code and QR image', async ({ page, loggedInUser }) => { void loggedInUser; await mock.override('GET', '/event/checkin', { status: 200, body: { status: 200, data: { checkin_code: '483917' } } }); await mock.override('GET', '/event/checkin/query', { status: 200, body: { status: 200, data: { checkin_at: null } } }); await mock.override('GET', '/event/info', { status: 200, body: mockEventBody() }); await page.goto('/app/events/evt1'); await page.waitForLoadState('networkidle'); await page.getByRole('button', { name: '签到' }).click(); await expect(page.getByText('483917')).toBeVisible({ timeout: 5000 }); await expect(page.locator('img[alt="checkin-qr"]')).toBeVisible(); }); test('attendee QR dialog shows success when poll detects check-in', async ({ page, loggedInUser }) => { void loggedInUser; await mock.override('GET', '/event/checkin', { status: 200, body: { status: 200, data: { checkin_code: '111111' } } }); await mock.override('GET', '/event/checkin/query', { status: 200, body: { status: 200, data: { checkin_at: '2026-04-16T12:00:00Z' } } }); await mock.override('GET', '/event/info', { status: 200, body: mockEventBody() }); await page.goto('/app/events/evt1'); await page.waitForLoadState('networkidle'); await page.getByRole('button', { name: '签到' }).click(); await expect(page.getByText('签到成功')).toBeVisible({ timeout: 6000 }); }); test('attendee QR dialog shows error when code fetch fails', async ({ page, loggedInUser }) => { void loggedInUser; await mock.override('GET', '/event/checkin', { status: 500, body: { status: 500, msg: '服务器错误' } }); await mock.override('GET', '/event/info', { status: 200, body: mockEventBody() }); await page.goto('/app/events/evt1'); await page.waitForLoadState('networkidle'); await page.getByRole('button', { name: '签到' }).click(); await expect(page.getByText('获取签到码失败')).toBeVisible({ timeout: 5000 }); });