| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- import { beforeEach, describe, expect, it, vi } from 'vitest';
- const toast = vi.hoisted(() => ({
- success: vi.fn(),
- error: vi.fn(),
- warning: vi.fn(),
- info: vi.fn(),
- loading: vi.fn(),
- }));
- vi.mock('@/api/http-init', () => ({
- httpRequest: vi.fn(),
- HttpError: class HttpError extends Error {
- status: number;
- response: { status: number; statusText: string; data: unknown };
- constructor(status: number, statusText: string, data: unknown) {
- super(`Request failed with status ${status}`);
- this.status = status;
- this.response = { status, statusText, data };
- }
- },
- }));
- vi.mock('@/utils/messageBus', () => ({
- getMessage: () => toast,
- }));
- import { HttpUtil } from '@/utils';
- import { HttpError, httpRequest } from '@/api/http-init';
- import type { HttpResponse } from '@/api/http-init';
- const mockRequest = vi.mocked(httpRequest);
- const envelope = (data: unknown): HttpResponse => ({ ok: true, status: 200, statusText: 'OK', data });
- describe('HttpUtil', () => {
- beforeEach(() => {
- vi.clearAllMocks();
- vi.spyOn(console, 'error').mockImplementation(() => undefined);
- });
- it('unwraps a success envelope and shows a success toast', async () => {
- mockRequest.mockResolvedValue(envelope({ success: true, msg: 'done', obj: { id: 1 } }));
- const msg = await HttpUtil.post('/x', { a: 1 });
- expect(msg.success).toBe(true);
- expect(msg.obj).toEqual({ id: 1 });
- expect(toast.success).toHaveBeenCalledWith('done');
- });
- it('suppresses the success toast with silentSuccess but still warns on nodePending', async () => {
- mockRequest.mockResolvedValue(envelope({ success: true, msg: 'saved', obj: { nodePending: true } }));
- await HttpUtil.post('/x', { a: 1 }, { silentSuccess: true });
- expect(toast.success).not.toHaveBeenCalled();
- expect(toast.warning).toHaveBeenCalled();
- });
- it('shows an error toast for a failure envelope', async () => {
- mockRequest.mockResolvedValue(envelope({ success: false, msg: 'nope', obj: null }));
- const msg = await HttpUtil.post('/x');
- expect(msg.success).toBe(false);
- expect(toast.error).toHaveBeenCalledWith('nope');
- });
- it('suppresses all toasts with silent', async () => {
- mockRequest.mockResolvedValue(envelope({ success: false, msg: 'nope', obj: null }));
- await HttpUtil.post('/x', undefined, { silent: true });
- expect(toast.error).not.toHaveBeenCalled();
- });
- it('surfaces the backend error text from a thrown HttpError body (msg field)', async () => {
- mockRequest.mockRejectedValue(new HttpError(400, 'Bad Request', { success: false, msg: 'bad input' }));
- const msg = await HttpUtil.post('/x', undefined, { silent: true });
- expect(msg.success).toBe(false);
- expect(msg.msg).toBe('bad input');
- expect(console.error).not.toHaveBeenCalled();
- });
- it('maps a thrown native error to a failure Msg via its message', async () => {
- mockRequest.mockRejectedValue(new Error('Network down'));
- const msg = await HttpUtil.get('/x', undefined, { silent: true });
- expect(msg.msg).toBe('Network down');
- expect(console.error).not.toHaveBeenCalled();
- });
- it('returns "No response data" for an empty body', async () => {
- mockRequest.mockResolvedValue(envelope(''));
- const msg = await HttpUtil.get('/x', undefined, { silent: true });
- expect(msg.success).toBe(false);
- expect(msg.msg).toBe('No response data');
- });
- });
|