👨🏻‍💻👩🏻‍💻 Use Ant Design like a Pro!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

268 lines
7.0 KiB

import { message, notification } from 'antd';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { errorConfig } from './requestErrorConfig';
vi.mock('antd', () => ({
message: {
warning: vi.fn(),
error: vi.fn(),
},
notification: {
open: vi.fn(),
},
}));
vi.mock('@umijs/max', () => ({
getIntl: vi.fn(() => ({
formatMessage: vi.fn(({ defaultMessage }) => defaultMessage),
})),
}));
describe('requestErrorConfig', () => {
// biome-ignore lint/style/noNonNullAssertion: config handlers are always defined
const errorThrower = errorConfig.errorConfig!.errorThrower!;
// biome-ignore lint/style/noNonNullAssertion: config handlers are always defined
const errorHandler = errorConfig.errorConfig!.errorHandler!;
beforeEach(() => {
vi.clearAllMocks();
});
describe('errorThrower', () => {
it('should throw error when success is false', () => {
const response = {
success: false,
data: null,
errorCode: 400,
errorMessage: 'Bad Request',
showType: 2,
};
expect(() => {
errorThrower(response);
}).toThrow('Bad Request');
});
it('should not throw error when success is true', () => {
const response = {
success: true,
data: { id: 1 },
};
expect(() => {
errorThrower(response);
}).not.toThrow();
});
it('should throw BizError with correct info', () => {
const response = {
success: false,
data: { detail: 'more info' },
errorCode: 403,
errorMessage: 'Forbidden',
showType: 3,
};
expect.assertions(5);
try {
errorThrower(response);
} catch (error: any) {
expect(error.name).toBe('BizError');
expect(error.info.errorCode).toBe(403);
expect(error.info.errorMessage).toBe('Forbidden');
expect(error.info.showType).toBe(3);
expect(error.info.data).toEqual({ detail: 'more info' });
}
});
});
describe('errorHandler', () => {
it('should rethrow error when skipErrorHandler is true', () => {
const error = new Error('Test error');
const opts = { skipErrorHandler: true };
expect(() => {
errorHandler(error, opts);
}).toThrow('Test error');
});
it('should handle SILENT showType', () => {
const error: any = new Error('Silent error');
error.name = 'BizError';
error.info = {
errorCode: 1001,
errorMessage: 'Silent error',
showType: 0,
};
errorHandler(error, {});
expect(message.warning).not.toHaveBeenCalled();
expect(message.error).not.toHaveBeenCalled();
expect(notification.open).not.toHaveBeenCalled();
});
it('should handle WARN_MESSAGE showType', () => {
const error: any = new Error('Warning');
error.name = 'BizError';
error.info = {
errorCode: 1002,
errorMessage: 'This is a warning',
showType: 1,
};
errorHandler(error, {});
expect(message.warning).toHaveBeenCalledWith('This is a warning');
});
it('should handle ERROR_MESSAGE showType', () => {
const error: any = new Error('Error message');
error.name = 'BizError';
error.info = {
errorCode: 1003,
errorMessage: 'This is an error',
showType: 2,
};
errorHandler(error, {});
expect(message.error).toHaveBeenCalledWith('This is an error');
});
it('should handle NOTIFICATION showType', () => {
const error: any = new Error('Notification');
error.name = 'BizError';
error.info = {
errorCode: 1004,
errorMessage: 'This is a notification',
showType: 3,
};
errorHandler(error, {});
expect(notification.open).toHaveBeenCalledWith({
title: 1004,
description: 'This is a notification',
});
});
it('should handle REDIRECT showType', () => {
const error: any = new Error('Redirect');
error.name = 'BizError';
error.info = {
errorCode: 401,
errorMessage: 'Unauthorized',
showType: 9,
};
errorHandler(error, {});
// REDIRECT 分支不应触发任何消息/通知提示
expect(message.warning).not.toHaveBeenCalled();
expect(message.error).not.toHaveBeenCalled();
expect(notification.open).not.toHaveBeenCalled();
});
it('should handle default case for unknown showType', () => {
const error: any = new Error('Unknown type');
error.name = 'BizError';
error.info = {
errorCode: 1005,
errorMessage: 'Unknown error type',
showType: 99,
};
errorHandler(error, {});
expect(message.error).toHaveBeenCalledWith('Unknown error type');
});
it('should handle axios response error', () => {
const error: any = new Error('Axios error');
error.response = {
status: 500,
data: {},
};
errorHandler(error, {});
expect(message.error).toHaveBeenCalledWith('Response status:500');
});
it('should handle offline error', () => {
const error: any = new Error('Network error');
error.request = {};
const originalOnLine = navigator.onLine;
Object.defineProperty(navigator, 'onLine', {
writable: true,
value: false,
});
try {
errorHandler(error, {});
expect(message.error).toHaveBeenCalledWith(
'Network unavailable. Please check your connection and try again.',
);
} finally {
Object.defineProperty(navigator, 'onLine', {
writable: true,
value: originalOnLine,
});
}
});
it('should handle request error with no response', () => {
const error: any = new Error('Request error');
error.request = {};
errorHandler(error, {});
expect(message.error).toHaveBeenCalledWith(
'None response! Please retry.',
);
});
it('should handle generic error', () => {
const error: any = new Error('Generic error');
errorHandler(error, {});
expect(message.error).toHaveBeenCalledWith(
'Request error, please retry.',
);
});
});
describe('requestInterceptors', () => {
// The interceptor is registered as a plain function (not a tuple),
// so narrow the union type to a callable for the test.
const interceptor = errorConfig.requestInterceptors?.[0] as (config: {
url?: string;
method?: string;
}) => { url?: string };
it('should pass through config without modification', () => {
const config = {
url: 'https://api.example.com/users',
method: 'GET',
};
const result = interceptor(config);
// Token attachment is intentionally commented out in the source;
// interceptor currently returns config as-is
expect(result.url).toBe('https://api.example.com/users');
});
it('should handle URL without config', () => {
const config = {};
const result = interceptor(config);
expect(result.url).toBeUndefined();
});
});
});