95f5f94a6b
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>
72 lines
3.0 KiB
TypeScript
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');
|
|
});
|
|
});
|