103 lines
4.9 KiB
TypeScript
103 lines
4.9 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
import { createRequire } from 'node:module';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
const e2eTmp = process.env.E2E_TMP;
|
|
if (!e2eTmp) throw new Error('E2E_TMP must be set by playwright.config.ts');
|
|
const dbPath = resolve(e2eTmp, 'e2e.db');
|
|
|
|
type SeedRepository = {
|
|
ensureLocalUser: () => void;
|
|
createUser: (input: Record<string, unknown>) => { id: string };
|
|
createSpace: (input: Record<string, unknown>) => Promise<{ id: string }>;
|
|
createLocalTask: (input: Record<string, unknown>) => Promise<{ id: number }>;
|
|
recordEvent: (input: Record<string, unknown>) => { id: number };
|
|
createNotificationItem: (input: Record<string, unknown>) => { id: string };
|
|
createAgentReminder: (input: Record<string, unknown>) => { id: string };
|
|
close: () => void;
|
|
};
|
|
|
|
const openRepository = (): SeedRepository => {
|
|
const { Repository } = require(resolve(repoRoot, 'dist/db/repository.js')) as {
|
|
Repository: new (path: string) => SeedRepository;
|
|
};
|
|
const repository = new Repository(dbPath);
|
|
// The notification_items recipient FK must not depend on the web server
|
|
// having completed its no-auth bootstrap before this fixture runs.
|
|
repository.ensureLocalUser();
|
|
return repository;
|
|
};
|
|
|
|
test('notification bell shows unread inbox and opens the notification center', async ({ page }) => {
|
|
const title = `E2E未読通知-${Date.now()}`;
|
|
const repo = openRepository();
|
|
try {
|
|
const space = await repo.createSpace({ kind: 'case', title: `E2E通知-${Date.now()}`, ownerId: 'local', visibility: 'private' });
|
|
const task = await repo.createLocalTask({ title, body: '', ownerId: 'local', spaceId: space.id });
|
|
const event = repo.recordEvent({
|
|
eventType: 'task.failed', resourceType: 'task', resourceId: `e2e-${Date.now()}`,
|
|
result: 'failed', dedupeKey: crypto.randomUUID(), correlationId: crypto.randomUUID(),
|
|
});
|
|
repo.createNotificationItem({
|
|
eventId: event.id, recipientUserId: 'local', category: 'failed', title,
|
|
summary: 'E2E notification summary', spaceId: space.id,
|
|
resourceType: 'job', resourceId: `e2e-${event.id}`, occurredAt: new Date().toISOString(),
|
|
metadata: { localTaskId: task.id },
|
|
});
|
|
} finally {
|
|
repo.close();
|
|
}
|
|
|
|
await page.goto('/ui');
|
|
const seededInbox = await page.request.get('/api/notifications/inbox?limit=5');
|
|
expect(seededInbox.ok()).toBeTruthy();
|
|
expect((await seededInbox.json() as { items: Array<{ title: string }> }).items.map(item => item.title)).toContain(title);
|
|
const bell = page.getByTestId('notification-bell-trigger');
|
|
await expect(bell).toBeVisible();
|
|
await expect(page.getByTestId('notification-bell-badge')).toBeVisible();
|
|
await bell.click();
|
|
const panel = page.getByTestId('notification-bell-panel');
|
|
await expect(panel).toBeVisible();
|
|
await expect(panel.getByRole('button', { name: new RegExp(title) })).toBeVisible();
|
|
await panel.getByRole('button', { name: /すべて表示|View all/ }).click();
|
|
|
|
await expect(page).toHaveURL(/[?&]page=notifications(&|$)/);
|
|
await expect(page.getByRole('heading', { name: /通知センター|Notification center/ })).toBeVisible();
|
|
const row = page.getByRole('button', { name: new RegExp(title) });
|
|
await expect(row).toBeVisible();
|
|
await expect(row.getByText(/未読|Unread/)).toBeAttached();
|
|
await row.click();
|
|
await expect(page).toHaveURL(/[?&]space=[^&]+.*[?&]chat=\d+/);
|
|
await expect(page.getByTestId('notification-bell-badge')).toHaveCount(0);
|
|
});
|
|
|
|
test('upcoming reminders show only reminders assigned to the current user', async ({ page }) => {
|
|
const mine = `E2E本人の予定-${Date.now()}`;
|
|
const other = `E2E他人の予定-${Date.now()}`;
|
|
const repo = openRepository();
|
|
try {
|
|
const otherUser = repo.createUser({
|
|
email: `other-reminder-${crypto.randomUUID()}@example.test`, name: 'Other reminder owner',
|
|
role: 'user', status: 'active',
|
|
});
|
|
repo.createAgentReminder({ ownerId: 'local', title: mine, body: mine, dueAt: '2099-01-02T03:04:00.000Z', timezoneId: 'UTC' });
|
|
repo.createAgentReminder({ ownerId: otherUser.id, title: other, body: other, dueAt: '2099-01-02T04:04:00.000Z', timezoneId: 'UTC' });
|
|
} finally {
|
|
repo.close();
|
|
}
|
|
|
|
await page.goto('/ui?page=notifications¬ificationTab=upcoming');
|
|
await expect(page.getByRole('tab', { name: /予定|Upcoming/ })).toHaveAttribute('aria-selected', 'true');
|
|
await expect(page.getByText(mine, { exact: true })).toBeVisible();
|
|
await expect(page.getByText(other, { exact: true })).toHaveCount(0);
|
|
|
|
const response = await page.request.get('/api/notifications/upcoming');
|
|
expect(response.ok()).toBeTruthy();
|
|
const body = await response.json() as { items: Array<{ title: string }> };
|
|
expect(body.items.map(item => item.title)).toContain(mine);
|
|
expect(body.items.map(item => item.title)).not.toContain(other);
|
|
});
|