Files
portal/app/tests/Frontend/notifications-api.spec.ts
T
Дмитрий 95f5f94a6b test(api): Q.DEFER.003 sub-A — 43 unit tests for api/*.ts layer
User chose (A) api/* unit tests first (highest ROI per blocked.md). 5 new
spec files covering auth/deals/notifications/reminders/reports api modules.

- auth-api.spec.ts (13 tests): login/register/me/logout/verifyTwoFactor/
  useRecoveryCode/twoFactorInit/Confirm/Disable/RegenerateRecoveryCodes/
  forgotPassword/resetPassword/updateNotificationPreferences
- deals-api.spec.ts (12 tests): createDeal/bulkDelete/bulkRestore/update/
  transition/exportCSV/exportXLSX/getDeal/listDeals×2/listManagers/
  listProjects
- notifications-api.spec.ts (6 tests): listNotifications×3 (unreadOnly
  variants)/markRead/markAllRead/delete
- reminders-api.spec.ts (6 tests): listReminders×2/create/update/complete/
  delete
- reports-api.spec.ts (6 tests): listReportJobs×2/create/retry/cancel/delete

Approach: vi.mock('../../resources/js/api/client') replaces apiClient with
{get,post,patch,delete} mocks + ensureCsrfCookie mock. Each test verifies:
(1) correct HTTP method, (2) correct URL, (3) correct params/body
(camelCase→snake_case mapping for query params), (4) data unwrap from
wrapper objects ({user}/{deal}/{job}/{reminder}/{managers}/{projects}),
(5) ensureCsrfCookie called for mutating endpoints.

Vitest delta: 614 → 657 passed (+43 / 0 failed); 79 → 84 files (+5).
3 skipped unchanged. Q.DEFER.003 sub-B (security cards) + sub-C (router
guards) remain deferred — sub-A api/* was highest ROI per blocked.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 22:14:51 +03:00

72 lines
3.0 KiB
TypeScript

import { describe, it, expect, beforeEach, vi } from 'vitest';
vi.mock('../../resources/js/api/client', () => ({
apiClient: {
get: vi.fn(),
post: vi.fn(),
patch: vi.fn(),
delete: vi.fn(),
},
ensureCsrfCookie: vi.fn(),
}));
import {
listNotifications,
markNotificationRead,
markAllNotificationsRead,
deleteNotification,
} from '../../resources/js/api/notifications';
import { apiClient, ensureCsrfCookie } from '../../resources/js/api/client';
describe('api/notifications', () => {
beforeEach(() => vi.clearAllMocks());
it('listNotifications() GET /api/notifications без params → unread_only=undefined', async () => {
vi.mocked(apiClient.get).mockResolvedValue({ data: { items: [], unread_count: 0, total: 0 } });
const r = await listNotifications();
expect(apiClient.get).toHaveBeenCalledWith('/api/notifications', {
params: { unread_only: undefined, limit: undefined },
});
expect(r.total).toBe(0);
});
it('listNotifications({unreadOnly:true}) → unread_only=1 (1, не true)', async () => {
vi.mocked(apiClient.get).mockResolvedValue({ data: { items: [], unread_count: 0, total: 0 } });
await listNotifications({ unreadOnly: true, limit: 20 });
expect(apiClient.get).toHaveBeenCalledWith('/api/notifications', {
params: { unread_only: 1, limit: 20 },
});
});
it('listNotifications({unreadOnly:false}) → unread_only=undefined (не 0)', async () => {
vi.mocked(apiClient.get).mockResolvedValue({ data: { items: [], unread_count: 0, total: 0 } });
await listNotifications({ unreadOnly: false });
expect(apiClient.get).toHaveBeenCalledWith('/api/notifications', {
params: { unread_only: undefined, limit: undefined },
});
});
it('markNotificationRead() PATCH /api/notifications/{id}/read + ensureCsrfCookie', async () => {
vi.mocked(apiClient.patch).mockResolvedValue({ data: { id: 5, read_at: '2026-05-12T20:00:00Z' } });
const r = await markNotificationRead(5);
expect(ensureCsrfCookie).toHaveBeenCalledOnce();
expect(apiClient.patch).toHaveBeenCalledWith('/api/notifications/5/read');
expect(r.id).toBe(5);
});
it('markAllNotificationsRead() POSTs /api/notifications/mark-all-read', async () => {
vi.mocked(apiClient.post).mockResolvedValue({ data: { updated: 12 } });
const r = await markAllNotificationsRead();
expect(ensureCsrfCookie).toHaveBeenCalledOnce();
expect(apiClient.post).toHaveBeenCalledWith('/api/notifications/mark-all-read');
expect(r.updated).toBe(12);
});
it('deleteNotification() DELETE /api/notifications/{id} + ensureCsrfCookie', async () => {
vi.mocked(apiClient.delete).mockResolvedValue({ data: undefined });
await deleteNotification(7);
expect(ensureCsrfCookie).toHaveBeenCalledOnce();
expect(apiClient.delete).toHaveBeenCalledWith('/api/notifications/7');
});
});