Files
maestro/ui/e2e/notifications.spec.ts
T
oss-sync 57702b4f34
CI / build-and-test (push) Canceled after 0s
sync: update from private repo (2336949a)
2026-09-17 11:29:31 +00:00

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&notificationTab=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);
});