feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
138
frontend/packages/arch/bot-error/__tests__/certain-error.test.ts
Normal file
138
frontend/packages/arch/bot-error/__tests__/certain-error.test.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type Mock } from 'vitest';
|
||||
import { isAxiosError } from 'axios';
|
||||
import { isApiError } from '@coze-arch/bot-http';
|
||||
|
||||
import {
|
||||
getErrorName,
|
||||
isCertainError,
|
||||
sendCertainError,
|
||||
} from '../src/certain-error';
|
||||
import { isCustomError, isChunkError } from '../src';
|
||||
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
logger: {
|
||||
info: vi.fn(),
|
||||
createLoggerWith: vi.fn(() => ({
|
||||
info: vi.fn(),
|
||||
persist: {
|
||||
error: vi.fn(),
|
||||
},
|
||||
})),
|
||||
},
|
||||
reporter: {
|
||||
errorEvent: vi.fn(),
|
||||
info: vi.fn(),
|
||||
},
|
||||
}));
|
||||
vi.mock('axios', async () => {
|
||||
const actual: Record<string, unknown> = await vi.importActual('axios');
|
||||
return {
|
||||
...actual,
|
||||
isAxiosError: vi.fn(),
|
||||
};
|
||||
});
|
||||
vi.mock('@coze-arch/bot-http', () => ({
|
||||
isApiError: vi.fn(),
|
||||
}));
|
||||
vi.mock('../src/custom-error', () => ({
|
||||
isCustomError: vi.fn(),
|
||||
}));
|
||||
vi.mock('../src/source-error', () => ({
|
||||
isChunkError: vi.fn(),
|
||||
}));
|
||||
const isNoInstanceError = vi.fn();
|
||||
const errorFuncList = [
|
||||
{
|
||||
func: isCustomError,
|
||||
name: 'CustomError',
|
||||
},
|
||||
{
|
||||
func: isAxiosError,
|
||||
name: 'AxiosError',
|
||||
},
|
||||
{
|
||||
func: isApiError,
|
||||
name: 'ApiError',
|
||||
},
|
||||
{
|
||||
func: isChunkError,
|
||||
name: 'ChunkLoadError',
|
||||
},
|
||||
{
|
||||
func: isNoInstanceError,
|
||||
name: 'notInstanceError',
|
||||
},
|
||||
];
|
||||
|
||||
describe('bot-error-certain-error', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('getErrorName', () => {
|
||||
const resNull = getErrorName(null);
|
||||
expect(resNull).equal('unknown');
|
||||
errorFuncList.forEach(item => {
|
||||
const { func } = item;
|
||||
(func as Mock).mockReturnValueOnce(true);
|
||||
const error = item.name === 'notInstanceError' ? {} : new Error();
|
||||
const res = getErrorName(error as Error);
|
||||
expect(res).equal(item.name);
|
||||
});
|
||||
});
|
||||
|
||||
test('isCertainError', () => {
|
||||
errorFuncList.forEach(item => {
|
||||
const { func } = item;
|
||||
(func as Mock).mockReturnValueOnce(true);
|
||||
const error = item.name === 'notInstanceError' ? {} : new Error();
|
||||
const res = isCertainError(error as Error);
|
||||
expect(res).equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('sendCertainError', () => {
|
||||
errorFuncList.forEach(item => {
|
||||
const handle = vi.fn();
|
||||
(item.func as Mock).mockReturnValue(true);
|
||||
const error = item.name === 'notInstanceError' ? {} : new Error();
|
||||
sendCertainError(error as Error, handle);
|
||||
expect(handle).not.toHaveBeenCalled();
|
||||
(item.func as Mock).mockReturnValue(false);
|
||||
sendCertainError(new Error(), handle);
|
||||
expect(handle).toHaveBeenCalled();
|
||||
});
|
||||
// notInstanceError json stringify 失败的单测
|
||||
errorFuncList.forEach(item => {
|
||||
const handle = vi.fn();
|
||||
if (item.name !== 'notInstanceError') {
|
||||
return;
|
||||
}
|
||||
(item.func as Mock).mockReturnValue(true);
|
||||
// JSON stringify 会报错的 case
|
||||
const b = { a: {} };
|
||||
const a = { b: {}, name: 'notInstanceError' };
|
||||
b.a = a;
|
||||
a.b = b;
|
||||
const error = item.name === 'notInstanceError' ? a : new Error();
|
||||
sendCertainError(error as Error, handle);
|
||||
expect(handle).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CustomError, isCustomError } from '../src/custom-error';
|
||||
|
||||
describe('bot-error-custom-error', () => {
|
||||
test('should create custom-error correctly', () => {
|
||||
const eventName = 'custom_error';
|
||||
const eventMsg = 'err_msg';
|
||||
const customError = new CustomError(eventName, eventMsg);
|
||||
expect(customError).toBeInstanceOf(Error);
|
||||
expect(customError.name).equal('CustomError');
|
||||
expect(customError.eventName).equal(eventName);
|
||||
expect(customError.msg).equal(customError.message).equal(eventMsg);
|
||||
});
|
||||
|
||||
test('should judge custom-error correctly', () => {
|
||||
const nonCustomError = new Error();
|
||||
const customError = new CustomError('test', 'test');
|
||||
expect(isCustomError(nonCustomError)).toBeFalsy();
|
||||
expect(isCustomError(customError)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
isWebpackChunkError,
|
||||
isThirdPartyJsChunkError,
|
||||
isCssChunkError,
|
||||
isChunkError,
|
||||
} from '../src/source-error';
|
||||
|
||||
describe('bot-error-source-error', () => {
|
||||
test('isWebpackChunkError', () => {
|
||||
const chunkError = new Error();
|
||||
chunkError.name = 'ChunkLoadError';
|
||||
expect(isWebpackChunkError(chunkError)).toBeTruthy();
|
||||
expect(isChunkError(chunkError)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('isThirdPartyJsChunkError', () => {
|
||||
const loadingChunkError = new Error();
|
||||
loadingChunkError.message = 'Loading chunk xxxx';
|
||||
expect(isThirdPartyJsChunkError(loadingChunkError)).toBeTruthy();
|
||||
expect(isChunkError(loadingChunkError)).toBeTruthy();
|
||||
});
|
||||
|
||||
test('isCssChunkError', () => {
|
||||
const cssLoadingChunkError = new Error();
|
||||
cssLoadingChunkError.message = 'Loading CSS chunk xxx';
|
||||
expect(isCssChunkError(cssLoadingChunkError)).toBeTruthy();
|
||||
expect(isChunkError(cssLoadingChunkError)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type Mock } from 'vitest';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { type SlardarInstance } from '@coze-arch/logger';
|
||||
|
||||
import { useErrorCatch } from '../src/use-error-catch';
|
||||
import { isCertainError, sendCertainError } from '../src/certain-error';
|
||||
import { CustomError } from '../src';
|
||||
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
logger: {
|
||||
info: vi.fn(),
|
||||
createLoggerWith: vi.fn(() => ({
|
||||
info: vi.fn(),
|
||||
persist: {
|
||||
error: vi.fn(),
|
||||
},
|
||||
})),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock window event listeners without overriding the entire window object
|
||||
const mockAddEventListener = vi.fn(
|
||||
(e: string, cb: (event: unknown) => void) => {
|
||||
if (e === 'unhandledrejection') {
|
||||
cb({
|
||||
promise: Promise.reject(new Error()),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
const mockRemoveEventListener = vi.fn();
|
||||
|
||||
// Spy on and mock the window methods instead of replacing the entire window
|
||||
Object.defineProperty(window, 'addEventListener', {
|
||||
value: mockAddEventListener,
|
||||
writable: true,
|
||||
});
|
||||
Object.defineProperty(window, 'removeEventListener', {
|
||||
value: mockRemoveEventListener,
|
||||
writable: true,
|
||||
});
|
||||
|
||||
vi.mock('../src/certain-error');
|
||||
|
||||
describe('use-error-catch', () => {
|
||||
test('Should handle promise rejection correctly', () => {
|
||||
const slardarInstance = {
|
||||
on: vi.fn(),
|
||||
off: vi.fn(),
|
||||
};
|
||||
|
||||
// Mock normal error
|
||||
(isCertainError as Mock).mockReturnValue(false);
|
||||
slardarInstance.on.mockImplementationOnce(
|
||||
(_: string, cb: (e: { payload: { error: Error } }) => void) => {
|
||||
cb({ payload: { error: new Error() } });
|
||||
},
|
||||
);
|
||||
const { unmount } = renderHook(() =>
|
||||
useErrorCatch(slardarInstance as unknown as SlardarInstance),
|
||||
);
|
||||
unmount();
|
||||
expect(mockAddEventListener).toHaveBeenCalled();
|
||||
expect(mockRemoveEventListener).toHaveBeenCalled();
|
||||
expect(slardarInstance.on).toHaveBeenCalled();
|
||||
expect(slardarInstance.off).toHaveBeenCalled();
|
||||
expect(sendCertainError).not.toHaveBeenCalled();
|
||||
|
||||
// Mock certain error
|
||||
(isCertainError as Mock).mockReturnValue(true);
|
||||
slardarInstance.on.mockImplementationOnce(
|
||||
(_: string, cb: (e: { payload: { error: Error } }) => void) => {
|
||||
const error = new CustomError('test', 'test');
|
||||
error.name = 'CustomError';
|
||||
cb({ payload: { error } });
|
||||
},
|
||||
);
|
||||
renderHook(() =>
|
||||
useErrorCatch(slardarInstance as unknown as SlardarInstance),
|
||||
);
|
||||
unmount();
|
||||
expect(sendCertainError).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
|
||||
import { sendCertainError } from '../src/certain-error';
|
||||
import { CustomError, useRouteErrorCatch } from '../src';
|
||||
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
logger: {
|
||||
info: vi.fn(),
|
||||
createLoggerWith: vi.fn(() => ({
|
||||
info: vi.fn(),
|
||||
persist: {
|
||||
error: vi.fn(),
|
||||
},
|
||||
})),
|
||||
},
|
||||
}));
|
||||
vi.mock('../src/custom-error');
|
||||
vi.mock('../src/certain-error');
|
||||
|
||||
describe('useRouteErrorCatch', () => {
|
||||
test('Should handle route error correctly', () => {
|
||||
// normal error
|
||||
renderHook(() => useRouteErrorCatch(new Error()));
|
||||
expect(sendCertainError).toHaveBeenCalled();
|
||||
|
||||
// custom error
|
||||
renderHook(() => useRouteErrorCatch(new CustomError('test', 'test')));
|
||||
expect(sendCertainError).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user