feat(m15): full test completion + quality gate + validation fixes
- Add E2E notifications.spec (member-added notification -> list -> mark read) and activity-log.spec (board_posted + todo_created recorded) - Set Playwright workers=1 for deterministic full-suite runs (SSE test under parallel load); full E2E suite (18 tests / 13 specs incl. 12 mandated scenarios) green - Quality gate: 251 unit/integration tests, lint clean, typecheck clean, build succeeds, coverage 91.13% Repository/Service (>=80%) - Validation fixes (mandatory): SearchService now searches ALL items (large pageSize) instead of first page only; MeetingService. checkScheduleConflicts now requires membership (actorId) to prevent non-member info enumeration
This commit is contained in:
69
tests/e2e/notifications.spec.ts
Normal file
69
tests/e2e/notifications.spec.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { test, expect, type Page } from '@playwright/test';
|
||||
|
||||
function unique(prefix: string): string {
|
||||
return `${prefix}-${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`;
|
||||
}
|
||||
|
||||
async function registerAndLogin(page: Page, email: string, name: string) {
|
||||
await page.goto('/login');
|
||||
await page.getByRole('button', { name: '新規登録はこちら' }).click();
|
||||
await page.getByLabel('表示名').fill(name);
|
||||
await page.getByLabel('メールアドレス').fill(email);
|
||||
await page.getByLabel('パスワード').fill('password123');
|
||||
await page.getByRole('button', { name: '登録する' }).click();
|
||||
await expect(page).toHaveURL(/\/dashboard/);
|
||||
}
|
||||
|
||||
test.describe('notifications', () => {
|
||||
test('member-added notification appears and can be marked read', async ({
|
||||
browser,
|
||||
}) => {
|
||||
const ownerEmail = unique('owner') + '@example.com';
|
||||
const memberEmail = unique('member') + '@example.com';
|
||||
|
||||
// メンバーを先に登録(UIで作成+ログイン)
|
||||
const memberContext = await browser.newContext();
|
||||
const memberPage = await memberContext.newPage();
|
||||
await registerAndLogin(memberPage, memberEmail, 'Member');
|
||||
|
||||
// オーナー登録+プロジェクト作成
|
||||
const ownerContext = await browser.newContext();
|
||||
const ownerPage = await ownerContext.newPage();
|
||||
await registerAndLogin(ownerPage, ownerEmail, 'Owner');
|
||||
await ownerPage.getByLabel('プロジェクト名').fill(unique('Proj'));
|
||||
await ownerPage.getByRole('button', { name: '新規プロジェクト' }).click();
|
||||
await expect(ownerPage).toHaveURL(/\/projects\/\d+$/);
|
||||
const projectId = Number(ownerPage.url().match(/\/projects\/(\d+)/)![1]);
|
||||
|
||||
// メンバー追加 → project_added 通知がメンバーへ
|
||||
const addRes = await ownerPage.request.post(
|
||||
`/api/projects/${projectId}/members`,
|
||||
{ data: { email: memberEmail, role: 'member' } }
|
||||
);
|
||||
expect(addRes.ok()).toBeTruthy();
|
||||
|
||||
// メンバーの通知一覧に表示
|
||||
await memberPage.goto('/notifications');
|
||||
await expect(
|
||||
memberPage.getByText('プロジェクトに追加されました')
|
||||
).toBeVisible();
|
||||
|
||||
// 既読化(API) → 一覧が空になる
|
||||
const listRes = await memberPage.request.get('/api/notifications?page=1');
|
||||
const { items } = (await listRes.json()) as {
|
||||
items: { id: number }[];
|
||||
};
|
||||
expect(items).toHaveLength(1);
|
||||
const readRes = await memberPage.request.post(
|
||||
`/api/notifications/${items[0].id}/read`
|
||||
);
|
||||
expect(readRes.ok()).toBeTruthy();
|
||||
await memberPage.reload();
|
||||
await expect(
|
||||
memberPage.getByText('未読の通知はありません。')
|
||||
).toBeVisible();
|
||||
|
||||
await ownerContext.close();
|
||||
await memberContext.close();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user