Files
cms-client/tests/e2e/checkin.spec.ts
2026-04-18 12:14:30 +08:00

135 lines
4.3 KiB
TypeScript

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<string, unknown> = {}) => ({
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 });
});