The "立即签到" button is rendered by bits-ui Dialog.Trigger as a
<button>, not <a>. Change getByRole('link') to getByRole('button')
on lines 120, 133, and 149 for selector consistency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
219 lines
8.2 KiB
TypeScript
219 lines
8.2 KiB
TypeScript
import { test, expect } from './helpers/fixtures';
|
|
import { mock } from './helpers/mock';
|
|
import type { ServiceEventEventListItems } from '$lib/api';
|
|
|
|
// Minimal event factory matching the SDK type
|
|
function mkEvent(overrides: Partial<ServiceEventEventListItems> = {}): ServiceEventEventListItems {
|
|
return {
|
|
event_id: 'evt-1',
|
|
name: '测试活动',
|
|
subtitle: '测试地点',
|
|
start_time: new Date(Date.now() + 3_600_000).toISOString(),
|
|
end_time: new Date(Date.now() + 7_200_000).toISOString(),
|
|
is_joined: true,
|
|
is_checked_in: false,
|
|
is_agenda_published: false,
|
|
enable_kyc: false,
|
|
type: 'party',
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
function eventListResponse(items: ServiceEventEventListItems[]) {
|
|
return { status: 200, body: { status: 200, data: { items } } };
|
|
}
|
|
|
|
// ── Workbench structure ──────────────────────────────────────────────────────
|
|
|
|
test('empty state: no events shows zero count and empty cards', async ({ page, loggedInUser }) => {
|
|
void loggedInUser;
|
|
await mock.override('GET', '/event/list', eventListResponse([]));
|
|
await page.goto('/app/');
|
|
|
|
// Welcome card: 0 joined events
|
|
await expect(page.getByText('0', { exact: true })).toBeVisible();
|
|
// Current event empty state
|
|
await expect(page.getByText('暂无进行中或即将到来的活动')).toBeVisible();
|
|
// Upcoming: three dashed placeholders
|
|
await expect(page.getByText('暂无更多活动')).toHaveCount(3);
|
|
// No GET /event/list unexpected requests
|
|
const reqs = await mock.requests({ method: 'GET', path: '/event/list' });
|
|
expect(reqs.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('welcome card shows nickname and joined count', async ({ page, loggedInUser }) => {
|
|
void loggedInUser;
|
|
await mock.override(
|
|
'GET',
|
|
'/event/list',
|
|
eventListResponse([
|
|
mkEvent({ event_id: 'a', is_joined: true }),
|
|
mkEvent({ event_id: 'b', is_joined: false })
|
|
])
|
|
);
|
|
await page.goto('/app/');
|
|
|
|
// nickname 'Alice' from loggedInUser fixture — scope to the <em> element
|
|
await expect(page.locator('em').filter({ hasText: 'Alice' })).toBeVisible();
|
|
// 1 joined (the non-joined one is excluded)
|
|
await expect(page.getByText('1', { exact: true })).toBeVisible();
|
|
});
|
|
|
|
// ── Current event card ───────────────────────────────────────────────────────
|
|
|
|
test('joined ongoing event shows "进行中" badge', async ({ page, loggedInUser }) => {
|
|
void loggedInUser;
|
|
const start = new Date(Date.now() - 3_600_000).toISOString();
|
|
const end = new Date(Date.now() + 3_600_000).toISOString();
|
|
await mock.override(
|
|
'GET',
|
|
'/event/list',
|
|
eventListResponse([mkEvent({ event_id: 'live', start_time: start, end_time: end })])
|
|
);
|
|
await page.goto('/app/');
|
|
await expect(page.getByText('进行中')).toBeVisible();
|
|
await expect(page.getByText('暂无进行中或即将到来的活动')).toHaveCount(0);
|
|
});
|
|
|
|
test('joined upcoming event shows "待开始" badge in current event card', async ({
|
|
page,
|
|
loggedInUser
|
|
}) => {
|
|
void loggedInUser;
|
|
const start = new Date(Date.now() + 3_600_000).toISOString();
|
|
const end = new Date(Date.now() + 7_200_000).toISOString();
|
|
await mock.override(
|
|
'GET',
|
|
'/event/list',
|
|
eventListResponse([mkEvent({ event_id: 'soon', start_time: start, end_time: end })])
|
|
);
|
|
await page.goto('/app/');
|
|
// Current event card shows "待开始"
|
|
await expect(page.getByText('待开始').first()).toBeVisible();
|
|
});
|
|
|
|
test('is_checked_in shows "已签到" badge', async ({ page, loggedInUser }) => {
|
|
void loggedInUser;
|
|
const start = new Date(Date.now() - 3_600_000).toISOString();
|
|
const end = new Date(Date.now() + 3_600_000).toISOString();
|
|
await mock.override(
|
|
'GET',
|
|
'/event/list',
|
|
eventListResponse([mkEvent({ start_time: start, end_time: end, is_checked_in: true })])
|
|
);
|
|
await page.goto('/app/');
|
|
await expect(page.getByText('已签到')).toBeVisible();
|
|
});
|
|
|
|
// ── Checkin button ───────────────────────────────────────────────────────────
|
|
|
|
test('attendee sees "立即签到" button during ongoing event', async ({ page, loggedInUser }) => {
|
|
void loggedInUser;
|
|
const start = new Date(Date.now() - 3_600_000).toISOString();
|
|
const end = new Date(Date.now() + 3_600_000).toISOString();
|
|
await mock.override(
|
|
'GET',
|
|
'/event/list',
|
|
eventListResponse([mkEvent({ start_time: start, end_time: end })])
|
|
);
|
|
await page.goto('/app/');
|
|
await expect(page.getByRole('button', { name: /立即签到/ })).toBeVisible();
|
|
});
|
|
|
|
test('"立即签到" button is hidden when already checked in', async ({ page, loggedInUser }) => {
|
|
void loggedInUser;
|
|
const start = new Date(Date.now() - 3_600_000).toISOString();
|
|
const end = new Date(Date.now() + 3_600_000).toISOString();
|
|
await mock.override(
|
|
'GET',
|
|
'/event/list',
|
|
eventListResponse([mkEvent({ start_time: start, end_time: end, is_checked_in: true })])
|
|
);
|
|
await page.goto('/app/');
|
|
await expect(page.getByRole('button', { name: /立即签到/ })).toHaveCount(0);
|
|
});
|
|
|
|
test('"立即签到" button is hidden for an upcoming (not yet started) event', async ({
|
|
page,
|
|
loggedInUser
|
|
}) => {
|
|
void loggedInUser;
|
|
const start = new Date(Date.now() + 3_600_000).toISOString();
|
|
const end = new Date(Date.now() + 7_200_000).toISOString();
|
|
await mock.override(
|
|
'GET',
|
|
'/event/list',
|
|
eventListResponse([mkEvent({ start_time: start, end_time: end })])
|
|
);
|
|
await page.goto('/app/');
|
|
await expect(page.getByRole('button', { name: /立即签到/ })).toHaveCount(0);
|
|
});
|
|
|
|
// ── Upcoming schedule ────────────────────────────────────────────────────────
|
|
|
|
test('upcoming card shows next 3 events, current event excluded', async ({
|
|
page,
|
|
loggedInUser
|
|
}) => {
|
|
void loggedInUser;
|
|
const t = (offset: number) => new Date(Date.now() + offset).toISOString();
|
|
const events = [
|
|
mkEvent({ event_id: 'a', name: '活动A', start_time: t(1_000), end_time: t(3_600_000) }),
|
|
mkEvent({ event_id: 'b', name: '活动B', start_time: t(3_600_001), end_time: t(7_200_000) }),
|
|
mkEvent({ event_id: 'c', name: '活动C', start_time: t(7_200_001), end_time: t(10_800_000) }),
|
|
mkEvent({ event_id: 'd', name: '活动D', start_time: t(10_800_001), end_time: t(14_400_000) })
|
|
];
|
|
await mock.override('GET', '/event/list', eventListResponse(events));
|
|
await page.goto('/app/');
|
|
|
|
// 'A' is currentEvent; B, C, D are upcoming (D should be excluded — only 3 slots)
|
|
await expect(page.getByText('活动B')).toBeVisible();
|
|
await expect(page.getByText('活动C')).toBeVisible();
|
|
await expect(page.getByText('活动D')).toBeVisible();
|
|
// Only 1 empty slot (3 events fill 3 slots)
|
|
await expect(page.getByText('暂无更多活动')).toHaveCount(0);
|
|
});
|
|
|
|
// ── Profile completeness card ────────────────────────────────────────────────
|
|
|
|
test('profile ring shows 100% when all fields set', async ({ page, loggedInUser }) => {
|
|
// loggedInUser already has nickname and email; override to add subtitle, avatar, bio
|
|
await mock.override('GET', '/user/info', {
|
|
status: 200,
|
|
body: {
|
|
status: 200,
|
|
data: {
|
|
...loggedInUser,
|
|
subtitle: 'Developer',
|
|
avatar: 'https://example.com/a.jpg',
|
|
bio: Buffer.from('简介').toString('base64')
|
|
}
|
|
}
|
|
});
|
|
await mock.override('GET', '/event/list', eventListResponse([]));
|
|
await page.goto('/app/');
|
|
|
|
await expect(page.getByText('100%')).toBeVisible();
|
|
});
|
|
|
|
test('profile ring shows 75% when bio is missing', async ({ page, loggedInUser }) => {
|
|
// loggedInUser has nickname; add subtitle + avatar but no bio
|
|
await mock.override('GET', '/user/info', {
|
|
status: 200,
|
|
body: {
|
|
status: 200,
|
|
data: {
|
|
...loggedInUser,
|
|
subtitle: 'Developer',
|
|
avatar: 'https://example.com/a.jpg'
|
|
}
|
|
}
|
|
});
|
|
await mock.override('GET', '/event/list', eventListResponse([]));
|
|
await page.goto('/app/');
|
|
|
|
await expect(page.getByText('75%')).toBeVisible();
|
|
// Bio checklist item shows as incomplete (dashed circle, no checkmark sibling)
|
|
await expect(page.getByText('个人简介')).toBeVisible();
|
|
});
|