feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
70
frontend/packages/arch/bot-utils/__tests__/array.test.ts
Normal file
70
frontend/packages/arch/bot-utils/__tests__/array.test.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 { ArrayUtil } from '../src/array';
|
||||
|
||||
describe('array', () => {
|
||||
it('array2Map', () => {
|
||||
const testItem1 = { id: '1', name: 'Alice', age: 20 };
|
||||
const testItem2 = { id: '2', name: 'Bob', age: 25 };
|
||||
const { array2Map } = ArrayUtil;
|
||||
const array1 = [testItem1, testItem2];
|
||||
|
||||
const mapById = array2Map(array1, 'id');
|
||||
expect(mapById).toEqual({
|
||||
'1': testItem1,
|
||||
'2': testItem2,
|
||||
});
|
||||
|
||||
const mapByName = array2Map(array1, 'name', 'age');
|
||||
expect(mapByName).toEqual({ Alice: 20, Bob: 25 });
|
||||
|
||||
const array = [testItem1, testItem2];
|
||||
const mapByIdFunc = array2Map(
|
||||
array,
|
||||
'id',
|
||||
item => `${item.name}-${item.age}`,
|
||||
);
|
||||
expect(mapByIdFunc).toEqual({ '1': 'Alice-20', '2': 'Bob-25' });
|
||||
});
|
||||
|
||||
it('mapAndFilter', () => {
|
||||
const { mapAndFilter } = ArrayUtil;
|
||||
const array = [
|
||||
{ id: 1, name: 'Alice', value: 100 },
|
||||
{ id: 2, name: 'Bob', value: 200 },
|
||||
];
|
||||
|
||||
// filter
|
||||
const result1 = mapAndFilter(array, {
|
||||
filter: item => item.name === 'Alice',
|
||||
});
|
||||
expect(result1).toEqual([{ id: 1, name: 'Alice', value: 100 }]);
|
||||
|
||||
// map
|
||||
const result2 = mapAndFilter(array, {
|
||||
map: item => ({ value: item.value }),
|
||||
});
|
||||
expect(result2).toEqual([{ value: 100 }, { value: 200 }]);
|
||||
|
||||
// filter & map
|
||||
const result3 = mapAndFilter(array, {
|
||||
filter: item => item.value > 100,
|
||||
map: item => ({ id: item.id, name: item.name }),
|
||||
});
|
||||
expect(result3).toEqual([{ id: 2, name: 'Bob' }]);
|
||||
});
|
||||
});
|
||||
114
frontend/packages/arch/bot-utils/__tests__/date.test.ts
Normal file
114
frontend/packages/arch/bot-utils/__tests__/date.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 dayjs from 'dayjs';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
|
||||
import {
|
||||
getCurrentTZ,
|
||||
getFormatDateType,
|
||||
formatDate,
|
||||
getRemainTime,
|
||||
getTimestampByAdd,
|
||||
formatTimestamp,
|
||||
} from '../src/date';
|
||||
|
||||
vi.mock('@coze-arch/i18n');
|
||||
vi.spyOn(I18n, 't');
|
||||
|
||||
describe('Date', () => {
|
||||
it('#getFormatDateType', () => {
|
||||
const now = dayjs();
|
||||
expect(getFormatDateType(now.unix())).toEqual('HH:mm');
|
||||
expect(
|
||||
getFormatDateType(
|
||||
dayjs(now)
|
||||
.date(now.date() === 1 ? 2 : 1)
|
||||
.unix(),
|
||||
),
|
||||
).toEqual('MM-DD HH:mm');
|
||||
expect(getFormatDateType(dayjs(now).add(1, 'year').unix())).toEqual(
|
||||
'YYYY-MM-DD HH:mm',
|
||||
);
|
||||
});
|
||||
|
||||
it('#dayjsForRegion Oversea should return UTC format', () => {
|
||||
vi.stubGlobal('IS_OVERSEA', true);
|
||||
expect(getCurrentTZ().isUTC()).toBe(true);
|
||||
expect(getCurrentTZ().utcOffset()).toBe(0);
|
||||
});
|
||||
it('#dayjsForRegion China should return UTC+8 format', () => {
|
||||
vi.stubGlobal('IS_OVERSEA', false);
|
||||
expect(getCurrentTZ().isUTC()).toBe(false);
|
||||
expect(getCurrentTZ().utcOffset()).toBe(60 * 8);
|
||||
});
|
||||
it('#formatDate', () => {
|
||||
const date = formatDate(1718782764);
|
||||
expect(date).toBe('2024/06/19 15:39:24');
|
||||
});
|
||||
it('#getRemainTime', () => {
|
||||
vi.useFakeTimers();
|
||||
const date = new Date('2024-08-19T15:30:00+08:00');
|
||||
vi.setSystemTime(date);
|
||||
expect(getRemainTime()).toBe('16h 30m');
|
||||
vi.useRealTimers();
|
||||
});
|
||||
it('#dayjsAdd', () => {
|
||||
vi.useFakeTimers();
|
||||
const date = new Date('2024-08-19T15:30:00+08:00');
|
||||
vi.setSystemTime(date);
|
||||
const unix = getTimestampByAdd(1, 'd');
|
||||
|
||||
expect(unix).toBe(dayjs('2024-08-20T15:30:00+08:00').unix());
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
describe('format timestamp', () => {
|
||||
beforeEach(() => {
|
||||
const MOCK_NOW = dayjs('2024-09-24 20:00:00');
|
||||
vi.setSystemTime(MOCK_NOW);
|
||||
});
|
||||
|
||||
it('just now', () => {
|
||||
formatTimestamp(dayjs('2024-09-24 19:59:01').valueOf());
|
||||
expect(I18n.t).toHaveBeenCalledWith('community_time_just_now');
|
||||
});
|
||||
|
||||
it('n min', () => {
|
||||
formatTimestamp(dayjs('2024-09-24 19:58:01').valueOf());
|
||||
expect(I18n.t).toHaveBeenCalledWith('community_time_min', { n: 1 });
|
||||
});
|
||||
|
||||
it('n hours', () => {
|
||||
formatTimestamp(dayjs('2024-09-24 17:58:01').valueOf());
|
||||
expect(I18n.t).toHaveBeenCalledWith('community_time_hour', { n: 2 });
|
||||
});
|
||||
|
||||
it('n days', () => {
|
||||
formatTimestamp(dayjs('2024-09-21 17:58:01').valueOf());
|
||||
expect(I18n.t).toHaveBeenCalledWith('community_time_day', { n: 3 });
|
||||
});
|
||||
|
||||
it('full date', () => {
|
||||
formatTimestamp(dayjs('2024-07-21 17:58:01').valueOf());
|
||||
expect(I18n.t).toHaveBeenCalledWith('community_time_date', {
|
||||
yyyy: 2024,
|
||||
mm: 7,
|
||||
dd: 21,
|
||||
});
|
||||
});
|
||||
});
|
||||
45
frontend/packages/arch/bot-utils/__tests__/dom.test.ts
Normal file
45
frontend/packages/arch/bot-utils/__tests__/dom.test.ts
Normal file
@@ -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 { openNewWindow } from '../src/dom';
|
||||
|
||||
it('openNewWindow', async () => {
|
||||
const testUrl = 'test_url';
|
||||
const testOrigin = 'test_origin';
|
||||
|
||||
const newWindow = {
|
||||
close: vi.fn(),
|
||||
location: '',
|
||||
};
|
||||
vi.stubGlobal('window', {
|
||||
open: vi.fn(() => newWindow),
|
||||
});
|
||||
vi.stubGlobal('location', {
|
||||
origin: testOrigin,
|
||||
});
|
||||
|
||||
const cb = vi.fn(() => Promise.resolve(testUrl));
|
||||
const cbWithError = vi.fn(() => Promise.reject(new Error()));
|
||||
await openNewWindow(cb);
|
||||
expect(newWindow.close).not.toHaveBeenCalled();
|
||||
expect(newWindow.location).equal(testUrl);
|
||||
|
||||
await openNewWindow(cbWithError);
|
||||
expect(newWindow.close).toHaveBeenCalled();
|
||||
expect(newWindow.location).equal(`${testOrigin}/404`);
|
||||
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 { getReportError } from '../src/get-report-error';
|
||||
|
||||
describe('getReportError', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('common error', () => {
|
||||
const error = new Error('123');
|
||||
const result = getReportError(error, 'testError');
|
||||
expect(result).toMatchObject({ error, meta: { reason: 'testError' } });
|
||||
});
|
||||
|
||||
test('stringify error', () => {
|
||||
const result = getReportError('123', 'testError');
|
||||
expect(result).toMatchObject({
|
||||
error: new Error('123'),
|
||||
meta: { reason: 'testError' },
|
||||
});
|
||||
});
|
||||
|
||||
test('object error', () => {
|
||||
const result = getReportError(
|
||||
{ foo: 'bar', reason: 'i am fool' },
|
||||
'testError',
|
||||
);
|
||||
expect(result).toMatchObject({
|
||||
error: new Error(''),
|
||||
meta: { reason: 'testError', reasonOfInputError: 'i am fool' },
|
||||
});
|
||||
});
|
||||
});
|
||||
30
frontend/packages/arch/bot-utils/__tests__/html.test.tsx
Normal file
30
frontend/packages/arch/bot-utils/__tests__/html.test.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 ReactNode } from 'react';
|
||||
|
||||
import { renderHtmlTitle } from '../src/html';
|
||||
|
||||
vi.mock('@coze-arch/i18n', () => ({
|
||||
I18n: { t: vi.fn(k => k) },
|
||||
}));
|
||||
|
||||
describe('html', () => {
|
||||
test('renderHtmlTitle', () => {
|
||||
expect(renderHtmlTitle('test')).equal('test - platform_name');
|
||||
expect(renderHtmlTitle({} as unknown as ReactNode)).equal('platform_name');
|
||||
});
|
||||
});
|
||||
47
frontend/packages/arch/bot-utils/__tests__/image.test.ts
Normal file
47
frontend/packages/arch/bot-utils/__tests__/image.test.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { loadImage } from '../src/image';
|
||||
|
||||
describe('image', () => {
|
||||
test('loadImage with success', async () => {
|
||||
vi.stubGlobal(
|
||||
'Image',
|
||||
class Image {
|
||||
onload!: () => void;
|
||||
set src(url: string) {
|
||||
this.onload();
|
||||
}
|
||||
},
|
||||
);
|
||||
await expect(loadImage('test')).resolves.toBeUndefined();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('loadImage with fail', async () => {
|
||||
vi.stubGlobal(
|
||||
'Image',
|
||||
class Image {
|
||||
onerror!: () => void;
|
||||
set src(url: string) {
|
||||
this.onerror();
|
||||
}
|
||||
},
|
||||
);
|
||||
await expect(loadImage('test')).rejects.toBeUndefined();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
});
|
||||
21
frontend/packages/arch/bot-utils/__tests__/index.test.ts
Normal file
21
frontend/packages/arch/bot-utils/__tests__/index.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
describe('Hello World', () => {
|
||||
it('test', () => {
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
});
|
||||
38
frontend/packages/arch/bot-utils/__tests__/is-mobile.test.ts
Normal file
38
frontend/packages/arch/bot-utils/__tests__/is-mobile.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 { isMobile } from '../src/is-mobile';
|
||||
|
||||
describe('is-mobile', () => {
|
||||
const TARGET_WIDTH = 640;
|
||||
test('isMobile with false', () => {
|
||||
vi.stubGlobal('document', {
|
||||
documentElement: {
|
||||
clientWidth: TARGET_WIDTH + 10,
|
||||
},
|
||||
});
|
||||
expect(isMobile()).toBeFalsy();
|
||||
});
|
||||
|
||||
test('isMobile with true', () => {
|
||||
vi.stubGlobal('document', {
|
||||
documentElement: {
|
||||
clientWidth: TARGET_WIDTH - 10,
|
||||
},
|
||||
});
|
||||
expect(isMobile()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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 { createReportEvent } from '@coze-arch/report-events';
|
||||
|
||||
import { messageReportEvent } from '../src/message-report';
|
||||
|
||||
const TEST_LOG_ID = 'test_log_id';
|
||||
const TEST_BOT_ID = 'test_bot_id';
|
||||
|
||||
vi.mock('@coze-arch/web-context', () => ({
|
||||
globalVars: {
|
||||
LAST_EXECUTE_ID: 'test_log_id',
|
||||
},
|
||||
}));
|
||||
const mockAddDurationPoint = vi.fn();
|
||||
const mockSuccess = vi.fn();
|
||||
const mockError = vi.fn();
|
||||
vi.mock('@coze-arch/report-events', async () => {
|
||||
const actual: Record<string, unknown> = await vi.importActual(
|
||||
'@coze-arch/report-events',
|
||||
);
|
||||
return {
|
||||
...actual,
|
||||
createReportEvent: vi.fn(() => ({
|
||||
addDurationPoint: mockAddDurationPoint,
|
||||
success: mockSuccess,
|
||||
error: mockError,
|
||||
})),
|
||||
};
|
||||
});
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
reporter: vi.fn(),
|
||||
}));
|
||||
vi.mock('@coze-arch/bot-error', () => ({}));
|
||||
|
||||
describe('message-report', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('Should setup correctly', () => {
|
||||
const { log_id } = messageReportEvent.getLogID();
|
||||
expect(log_id).equal(TEST_LOG_ID);
|
||||
|
||||
messageReportEvent.start(TEST_BOT_ID);
|
||||
const { bot_id, log_id: logId } = messageReportEvent.getMetaCtx();
|
||||
expect(bot_id).equal(TEST_BOT_ID);
|
||||
expect(logId).equal(TEST_LOG_ID);
|
||||
});
|
||||
|
||||
/// messageReceiveSuggestsEvent & receiveMessageEvent
|
||||
test('messageReceiveSuggestsEvent & receiveMessageEvent should not trigger report event if `start` has not been called', () => {
|
||||
[
|
||||
messageReportEvent.messageReceiveSuggestsEvent,
|
||||
messageReportEvent.receiveMessageEvent,
|
||||
].forEach(event => {
|
||||
event.success();
|
||||
event.finish('' as any);
|
||||
event.error({
|
||||
error: new Error(),
|
||||
reason: '',
|
||||
});
|
||||
if (event === messageReportEvent.receiveMessageEvent) {
|
||||
event.receiveMessage({ message_id: '' });
|
||||
}
|
||||
expect(createReportEvent).not.toHaveBeenCalled();
|
||||
expect(mockAddDurationPoint).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('messageReceiveSuggestsEvent & receiveMessageEvent should trigger reporter correctly by calling `receiveSuggest`', () => {
|
||||
messageReportEvent.messageReceiveSuggestsEvent.start();
|
||||
messageReportEvent.messageReceiveSuggestsEvent.receiveSuggest();
|
||||
expect(createReportEvent).toHaveBeenCalled();
|
||||
expect(mockAddDurationPoint).toHaveBeenCalledWith('first');
|
||||
});
|
||||
|
||||
test('`success` should trigger reporter correctly', () => {
|
||||
[
|
||||
messageReportEvent.messageReceiveSuggestsEvent,
|
||||
messageReportEvent.messageReceiveSuggestsEvent,
|
||||
].forEach(event => {
|
||||
['success', 'finish'].forEach(tag => {
|
||||
event.start();
|
||||
event[tag]();
|
||||
expect(createReportEvent).toHaveBeenCalled();
|
||||
expect(mockAddDurationPoint).toHaveBeenCalledWith('success');
|
||||
expect(mockSuccess).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('messageReceiveSuggestsEvent & receiveMessageEvent should trigger reporter correctly by calling `error`', () => {
|
||||
[
|
||||
messageReportEvent.messageReceiveSuggestsEvent,
|
||||
messageReportEvent.messageReceiveSuggestsEvent,
|
||||
].forEach(event => {
|
||||
event.start();
|
||||
event.error({
|
||||
error: new Error(),
|
||||
reason: '',
|
||||
});
|
||||
expect(createReportEvent).toHaveBeenCalled();
|
||||
expect(mockAddDurationPoint).toHaveBeenCalledWith('failed');
|
||||
expect(mockError).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test('executeDraftBotEvent should report correctly by calling start', () => {
|
||||
const event = messageReportEvent.executeDraftBotEvent;
|
||||
|
||||
event.start();
|
||||
event.success();
|
||||
expect(createReportEvent).toHaveBeenCalled();
|
||||
expect(mockAddDurationPoint).toHaveBeenCalledWith('finish');
|
||||
expect(mockSuccess).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('executeDraftBotEvent should report correctly by calling error', () => {
|
||||
const event = messageReportEvent.executeDraftBotEvent;
|
||||
|
||||
event.start();
|
||||
event.error({ error: new Error(), reason: '' });
|
||||
expect(createReportEvent).toHaveBeenCalled();
|
||||
expect(mockError).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('interrupt', () => {
|
||||
[
|
||||
messageReportEvent.messageReceiveSuggestsEvent,
|
||||
messageReportEvent.messageReceiveSuggestsEvent,
|
||||
].forEach((event, index) => {
|
||||
if (index === 0) {
|
||||
event.receiveSuggest();
|
||||
}
|
||||
event.start();
|
||||
messageReportEvent.interrupt();
|
||||
expect(mockSuccess).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
123
frontend/packages/arch/bot-utils/__tests__/number.test.ts
Normal file
123
frontend/packages/arch/bot-utils/__tests__/number.test.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 { ceil } from 'lodash-es';
|
||||
|
||||
import {
|
||||
formatBytes,
|
||||
formatNumber,
|
||||
getEllipsisCount,
|
||||
simpleformatNumber,
|
||||
sleep,
|
||||
formatPercent,
|
||||
formatTime,
|
||||
} from '../src/number';
|
||||
|
||||
describe('Number', () => {
|
||||
it('#simpleformatNumber', () => {
|
||||
expect(simpleformatNumber('100')).toEqual('100');
|
||||
expect(simpleformatNumber('100.1')).toEqual('100');
|
||||
expect(simpleformatNumber(100.1)).toEqual('100');
|
||||
expect(simpleformatNumber(1100)).toEqual('1,100');
|
||||
expect(simpleformatNumber('1100')).toEqual('1,100');
|
||||
});
|
||||
|
||||
it('formatBytes', () => {
|
||||
const k = 1024;
|
||||
const decimals = 2;
|
||||
const genRandomNum = (bytes: number) =>
|
||||
Array(bytes)
|
||||
.fill(0)
|
||||
.reduce(
|
||||
(prev, _, idx) =>
|
||||
prev +
|
||||
Math.floor(((Math.random() + 1) * (k - 1) * Math.pow(k, idx)) / 2),
|
||||
0,
|
||||
);
|
||||
const calDigit = (num: number, unit: number) =>
|
||||
parseFloat((num / Math.pow(k, unit)).toFixed(decimals));
|
||||
|
||||
expect(formatBytes(0)).equal('0 Byte');
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
sizes.forEach((size, idx) => {
|
||||
const num = genRandomNum(idx + 1);
|
||||
const digit = calDigit(num, idx);
|
||||
expect(formatBytes(num)).equal(`${digit} ${size}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('formatNumber', () => {
|
||||
const base = 1000;
|
||||
const units = ['', 'K', 'M', 'B', 'T'];
|
||||
const genRandomNum = (order: number) =>
|
||||
Array(order)
|
||||
.fill(0)
|
||||
.reduce(
|
||||
(prev, _, idx) =>
|
||||
prev +
|
||||
Math.floor(
|
||||
((Math.random() + 1) * (base - 1) * Math.pow(base, idx)) / 2,
|
||||
),
|
||||
0,
|
||||
);
|
||||
const calDigit = (num: number, unit: number) =>
|
||||
ceil(Math.abs(num) / Math.pow(base, unit), 1);
|
||||
units.forEach((unit, idx) => {
|
||||
const num = genRandomNum(idx + 1);
|
||||
const digit = calDigit(num, idx);
|
||||
expect(formatNumber(num).toString()).equal(`${digit}${unit}`);
|
||||
});
|
||||
});
|
||||
|
||||
it('getEllipsisCount', () => {
|
||||
const max = 1000;
|
||||
const num1 = max - 1;
|
||||
const num2 = max;
|
||||
const num3 = max + 1;
|
||||
expect(getEllipsisCount(num1, max)).equal(`${num1}`);
|
||||
expect(getEllipsisCount(num2, max)).equal(`${num2}`);
|
||||
expect(getEllipsisCount(num3, max)).equal(`${max}+`);
|
||||
});
|
||||
|
||||
it('sleep', async () => {
|
||||
const mockFn = vi.fn();
|
||||
const interval = 3000;
|
||||
vi.useFakeTimers();
|
||||
const promisedSleep = sleep(interval).then(() => {
|
||||
mockFn();
|
||||
});
|
||||
vi.advanceTimersByTime(interval);
|
||||
await promisedSleep;
|
||||
expect(mockFn).toHaveBeenCalled();
|
||||
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('formatPercent', () => {
|
||||
expect(formatPercent(0.1)).equal('10%');
|
||||
expect(formatPercent(0.123456)).equal('12.3%');
|
||||
expect(formatPercent(0.12556)).equal('12.6%');
|
||||
expect(formatPercent(1)).equal('100%');
|
||||
expect(formatPercent()).equal('NaN%');
|
||||
});
|
||||
|
||||
it('formatTime', () => {
|
||||
expect(formatTime(1000)).equal('1000ms');
|
||||
expect(formatTime(12000)).equal('12s');
|
||||
expect(formatTime(12330)).equal('12.3s');
|
||||
expect(formatTime(1000.12332)).equal('1000ms');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
表格单元格宽度适配
|
||||
当 width 小于 WidthThresholds.Small 时应该返回 ColumnSize.Default。
|
||||
当 width 大于等于 WidthThresholds.Small 但小于 WidthThresholds.Medium 时应该返回 ColumnSize.Small。
|
||||
当 width 大于等于 WidthThresholds.Medium 但小于 WidthThresholds.Large 时应该返回 ColumnSize.Medium。
|
||||
当 width 大于等于 WidthThresholds.Large 时应该返回 ColumnSize.Large。
|
||||
当 minWidth 为 'auto' 时应该返回 'auto'。
|
||||
当 minWidth 是一个指定数字时,应该返回 minWidth 和 columnWidth 中较大的一个。*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import {
|
||||
responsiveTableColumn,
|
||||
ColumnSize,
|
||||
WidthThresholds,
|
||||
} from '../src/responsive-table-column';
|
||||
|
||||
describe('responsiveTableColumn', () => {
|
||||
it('returns auto for minWidth auto', () => {
|
||||
expect(responsiveTableColumn(1000, 'auto')).toBe('auto');
|
||||
});
|
||||
|
||||
it('returns minWidth when minWidth is a number and greater than columnWidth', () => {
|
||||
expect(responsiveTableColumn(1000, 80)).toBe(80);
|
||||
});
|
||||
|
||||
it('returns ColumnSize.Small for width less than WidthThresholds.Small', () => {
|
||||
expect(responsiveTableColumn(WidthThresholds.Small - 1, 50)).toBe(
|
||||
ColumnSize.Default,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns ColumnSize.Medium for width between WidthThresholds.Small and WidthThresholds.Medium', () => {
|
||||
expect(responsiveTableColumn(WidthThresholds.Medium - 1, 50)).toBe(
|
||||
ColumnSize.Small,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns ColumnSize.Large for width between WidthThresholds.Medium and WidthThresholds.Large', () => {
|
||||
expect(responsiveTableColumn(WidthThresholds.Large - 1, 50)).toBe(
|
||||
ColumnSize.Medium,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns ColumnSize.Large for width greater than or equal to WidthThresholds.Large', () => {
|
||||
expect(responsiveTableColumn(WidthThresholds.Large, 50)).toBe(
|
||||
ColumnSize.Large,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 { retryImport } from '../src/retry-import';
|
||||
|
||||
describe('retry-import tests', () => {
|
||||
it('retryImport', async () => {
|
||||
const maxRetryCount = 3;
|
||||
let maxCount = 0;
|
||||
const mockImport = () =>
|
||||
new Promise<number>((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
if (maxCount >= maxRetryCount) {
|
||||
resolve(maxCount);
|
||||
return;
|
||||
}
|
||||
maxCount++;
|
||||
reject(new Error('load error!'));
|
||||
}, 1000);
|
||||
});
|
||||
expect(await retryImport<number>(() => mockImport(), maxRetryCount)).toBe(
|
||||
3,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
import { logger } from '@coze-arch/logger';
|
||||
|
||||
import { safeJSONParse, typeSafeJSONParse } from '../src/safe-json-parse';
|
||||
|
||||
vi.mock('@coze-arch/logger', () => ({
|
||||
logger: {
|
||||
persist: {
|
||||
error: vi.fn(),
|
||||
},
|
||||
},
|
||||
reporter: {
|
||||
errorEvent: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('safe-json-parse', () => {
|
||||
test('safeJSONParse without error', () => {
|
||||
const test1 = '{}';
|
||||
const res1 = safeJSONParse(test1);
|
||||
expect(res1).toStrictEqual({});
|
||||
|
||||
const test2 = '[]';
|
||||
const res2 = safeJSONParse(test2);
|
||||
expect(res2).toStrictEqual([]);
|
||||
|
||||
expect(logger.persist.error).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('safeJSONParse with error', () => {
|
||||
const test = '';
|
||||
const res1 = safeJSONParse(test);
|
||||
expect(res1).equal(undefined);
|
||||
expect(logger.persist.error).toHaveBeenCalledTimes(1);
|
||||
|
||||
const expectValue = 'empty_value';
|
||||
const res2 = safeJSONParse(test, expectValue);
|
||||
expect(res2).equal(expectValue);
|
||||
expect(logger.persist.error).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('type safe json parse', () => {
|
||||
it('parse obj return as input', () => {
|
||||
const ob = {};
|
||||
expect(typeSafeJSONParse(ob)).toBe(ob);
|
||||
});
|
||||
|
||||
it('parse legally', () => {
|
||||
const ob = { a: 1 };
|
||||
expect(typeSafeJSONParse(JSON.stringify(ob))).toMatchObject(ob);
|
||||
});
|
||||
|
||||
it('throw error when illegal', () => {
|
||||
const str = '{';
|
||||
expect(typeSafeJSONParse(str)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
23
frontend/packages/arch/bot-utils/__tests__/setup.ts
Normal file
23
frontend/packages/arch/bot-utils/__tests__/setup.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
global.IS_OVERSEA = false;
|
||||
global.APP_ID = '';
|
||||
global.IMAGE_FALLBACK_HOST = '';
|
||||
global.BYTE_UPLOADER_REGION = '';
|
||||
global.SAMI_WS_ORIGIN = '';
|
||||
global.SAMI_CHAT_WS_URL = '';
|
||||
global.SAMI_APP_KEY = '';
|
||||
34
frontend/packages/arch/bot-utils/__tests__/skill.test.ts
Normal file
34
frontend/packages/arch/bot-utils/__tests__/skill.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 { SkillKeyEnum } from '@coze-agent-ide/tool-config';
|
||||
|
||||
import { skillKeyToApiStatusKeyTransformer } from '../src/skill';
|
||||
|
||||
vi.stubGlobal('IS_DEV_MODE', false);
|
||||
|
||||
vi.mock('@coze-agent-ide/tool', () => ({
|
||||
SkillKeyEnum: {
|
||||
TEXT_TO_SPEECH: 'tts',
|
||||
},
|
||||
}));
|
||||
|
||||
describe('skill', () => {
|
||||
test('skillKeyToApiStatusKeyTransformer', () => {
|
||||
const test = SkillKeyEnum.TEXT_TO_SPEECH;
|
||||
expect(skillKeyToApiStatusKeyTransformer(test)).equal(`${test}_tab_status`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 { uploadFileV2 } from '../src/upload-file-v2';
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
DeveloperApi: {
|
||||
GetUploadAuthToken: vi.fn(() =>
|
||||
Promise.resolve({ data: { service_id: '', upload_host: '' } }),
|
||||
),
|
||||
},
|
||||
}));
|
||||
vi.mock('@coze-studio/uploader-adapter', () => {
|
||||
class MockUploader {
|
||||
start = () => 0;
|
||||
addFile = () => '12312341';
|
||||
test: 'test';
|
||||
on(event: string, cb: (data: any) => void) {
|
||||
if (event === 'complete') {
|
||||
cb({ uploadResult: { Uri: 'test_url' } });
|
||||
} else if (event === 'error') {
|
||||
cb({ extra: 'error' });
|
||||
} else if (event === 'progress') {
|
||||
cb(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
getUploader: vi.fn((props: any, isOverSea?: boolean) => new MockUploader()),
|
||||
};
|
||||
});
|
||||
|
||||
describe('upload-file', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('upLoadFile should resolve Url of result if upload success', () =>
|
||||
new Promise((resolve, reject) => {
|
||||
global.IS_OVERSEA = true;
|
||||
uploadFileV2({
|
||||
fileItemList: [{ file: new File([], 'test_file'), fileType: 'image' }],
|
||||
userId: '123',
|
||||
timeout: undefined,
|
||||
signal: new AbortSignal(),
|
||||
onSuccess: event => {
|
||||
try {
|
||||
expect(event.uploadResult.Uri).equal('test_url');
|
||||
resolve('ok');
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
onUploadAllSuccess(event) {
|
||||
try {
|
||||
expect(event[0].uploadResult.Uri).equal('test_url');
|
||||
resolve('ok');
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
global.IS_OVERSEA = false;
|
||||
uploadFileV2({
|
||||
fileItemList: [{ file: new File([], 'test_file'), fileType: 'image' }],
|
||||
userId: '123',
|
||||
timeout: undefined,
|
||||
signal: new AbortSignal(),
|
||||
onSuccess: event => {
|
||||
try {
|
||||
expect(event.uploadResult.Uri).equal('test_url');
|
||||
resolve('ok');
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
onUploadAllSuccess(event) {
|
||||
try {
|
||||
expect(event[0].uploadResult.Uri).equal('test_url');
|
||||
resolve('ok');
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
},
|
||||
});
|
||||
}));
|
||||
});
|
||||
110
frontend/packages/arch/bot-utils/__tests__/upload-file.test.ts
Normal file
110
frontend/packages/arch/bot-utils/__tests__/upload-file.test.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 { userStoreService } from '@coze-studio/user-store';
|
||||
|
||||
import { upLoadFile } from '../src/upload-file';
|
||||
|
||||
vi.mock('@coze-arch/bot-api', () => ({
|
||||
DeveloperApi: {
|
||||
GetUploadAuthToken: vi.fn(() =>
|
||||
Promise.resolve({ data: { service_id: '', upload_host: '' } }),
|
||||
),
|
||||
},
|
||||
}));
|
||||
vi.mock('@coze-studio/user-store', () => ({
|
||||
userStoreService: {
|
||||
getUserInfo: vi.fn(() => ({
|
||||
user_id_str: '',
|
||||
})),
|
||||
},
|
||||
}));
|
||||
vi.mock('@coze-studio/uploader-adapter', () => {
|
||||
class MockUploader {
|
||||
userId: string;
|
||||
constructor({ userId }) {
|
||||
this.userId = userId;
|
||||
}
|
||||
on(event: string, cb: (data: any) => void) {
|
||||
if (event === 'complete' && this.userId) {
|
||||
cb({ uploadResult: { Uri: 'test_url' } });
|
||||
} else if (event === 'error' && !this.userId) {
|
||||
cb({ extra: 'error' });
|
||||
} else if (event === 'progress') {
|
||||
cb(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
getUploader: vi.fn(
|
||||
(props: any, isOverSea?: boolean) => new MockUploader(props),
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
describe('upload-file', () => {
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('upLoadFile should resolve Url of result if upload success', async () => {
|
||||
// mock `userId` non-empty to invoke upload success
|
||||
(userStoreService.getUserInfo as Mock).mockReturnValue({
|
||||
user_id_str: 'test',
|
||||
});
|
||||
const res = await upLoadFile({
|
||||
file: new File([], 'test_file'),
|
||||
fileType: 'image',
|
||||
});
|
||||
expect(res).equal('test_url');
|
||||
global.IS_OVERSEA = false;
|
||||
(userStoreService.getUserInfo as Mock).mockReturnValue({
|
||||
user_id_str: 'test',
|
||||
});
|
||||
const res2 = await upLoadFile({
|
||||
file: new File([], 'test_file'),
|
||||
fileType: 'image',
|
||||
});
|
||||
expect(res2).equal('test_url');
|
||||
});
|
||||
|
||||
test('upLoadFile should reject extra info of result if upload failed', () => {
|
||||
// mock `userId` empty to invoke upload failed
|
||||
(userStoreService.getUserInfo as Mock).mockReturnValue({ user_id_str: '' });
|
||||
expect(
|
||||
upLoadFile({
|
||||
file: new File([], 'test_file'),
|
||||
fileType: 'image',
|
||||
}),
|
||||
).rejects.toThrow('error');
|
||||
});
|
||||
|
||||
test('upLoadFile should use getUploadAuthToken if biz is not bot or workflow ', () => {
|
||||
// mock `userId` empty to invoke upload failed
|
||||
(userStoreService.getUserInfo as Mock).mockReturnValue({ user_id_str: '' });
|
||||
expect(
|
||||
upLoadFile({
|
||||
biz: 'community',
|
||||
file: new File([], 'test_file'),
|
||||
fileType: 'image',
|
||||
getUploadAuthToken: vi.fn(() =>
|
||||
Promise.resolve({ data: { service_id: '', upload_host: '' } }),
|
||||
),
|
||||
}),
|
||||
).rejects.toThrow('error');
|
||||
});
|
||||
});
|
||||
100
frontend/packages/arch/bot-utils/__tests__/url.test.ts
Normal file
100
frontend/packages/arch/bot-utils/__tests__/url.test.ts
Normal file
@@ -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 { appendUrlParam, getParamsFromQuery, openUrl } from '../src/url';
|
||||
import { getIsMobile, getIsSafari } from '../src/platform';
|
||||
|
||||
vi.mock('../src/platform', () => ({
|
||||
getIsMobile: vi.fn(),
|
||||
getIsSafari: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockParseQuery = (queryStr: string) =>
|
||||
Object.fromEntries(
|
||||
queryStr
|
||||
.split('&')
|
||||
.map(str => {
|
||||
const parts = str.split('=');
|
||||
return [parts[0] ?? '', parts?.[1] ?? ''];
|
||||
})
|
||||
.filter(entries => !!entries[0]),
|
||||
);
|
||||
vi.mock('query-string', () => ({
|
||||
default: {
|
||||
parseUrl: (url: string) => {
|
||||
const [rawUrl, queryStr = ''] = url.split('?');
|
||||
return {
|
||||
url: rawUrl,
|
||||
query: mockParseQuery(queryStr),
|
||||
};
|
||||
},
|
||||
parse: (queryStr: string) => mockParseQuery(queryStr.slice(1)),
|
||||
stringifyUrl: ({ url, query }: { url: string; query: string }) =>
|
||||
`${url}${Object.entries(query).length ? '?' : ''}${Object.entries(query)
|
||||
.map(entry => entry.join('='))
|
||||
.join('&')}`,
|
||||
},
|
||||
}));
|
||||
|
||||
describe('URL', () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('location', { href: '' });
|
||||
vi.stubGlobal('window', {
|
||||
open: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('#getParamsFromQuery', () => {
|
||||
vi.stubGlobal('location', { search: '' });
|
||||
expect(getParamsFromQuery({ key: '' })).toEqual('');
|
||||
expect(getParamsFromQuery({ key: 'a' })).toEqual('');
|
||||
|
||||
vi.stubGlobal('location', { search: '?a=b' });
|
||||
expect(getParamsFromQuery({ key: '' })).toEqual('');
|
||||
expect(getParamsFromQuery({ key: 'a' })).toEqual('b');
|
||||
});
|
||||
|
||||
it('#appendUrlParam', () => {
|
||||
expect(appendUrlParam('http://test.com', 'k1', 'v1')).equal(
|
||||
'http://test.com?k1=v1',
|
||||
);
|
||||
expect(appendUrlParam('http://test.com?k1=v1', 'k2', 'v2')).equal(
|
||||
'http://test.com?k1=v1&k2=v2',
|
||||
);
|
||||
expect(appendUrlParam('http://test.com?k1=v1', 'k1', '')).equal(
|
||||
'http://test.com',
|
||||
);
|
||||
});
|
||||
|
||||
it('#openUrl', () => {
|
||||
openUrl(undefined);
|
||||
expect(window.open).not.toHaveBeenCalled();
|
||||
expect(location.href).toBe('');
|
||||
|
||||
vi.mocked(getIsMobile).mockReturnValue(true);
|
||||
vi.mocked(getIsSafari).mockReturnValue(true);
|
||||
|
||||
openUrl('https://example.com');
|
||||
|
||||
expect(location.href).toBe('https://example.com');
|
||||
expect(window.open).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
39
frontend/packages/arch/bot-utils/__tests__/viewport.test.ts
Normal file
39
frontend/packages/arch/bot-utils/__tests__/viewport.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 { setMobileBody, setPCBody } from '../src/viewport';
|
||||
|
||||
describe('viewport', () => {
|
||||
it('#setMobileBody', () => {
|
||||
setMobileBody();
|
||||
const bodyStyle = document?.body?.style;
|
||||
const htmlStyle = document?.getElementsByTagName('html')?.[0]?.style;
|
||||
expect(bodyStyle.minWidth).toEqual('0');
|
||||
expect(bodyStyle.minHeight).toEqual('0');
|
||||
expect(htmlStyle.minWidth).toEqual('0');
|
||||
expect(htmlStyle.minHeight).toEqual('0');
|
||||
});
|
||||
|
||||
it('#setPCBody', () => {
|
||||
setPCBody();
|
||||
const bodyStyle = document?.body?.style;
|
||||
const htmlStyle = document?.getElementsByTagName('html')?.[0]?.style;
|
||||
expect(bodyStyle.minWidth).toEqual('1200px');
|
||||
expect(bodyStyle.minHeight).toEqual('600px');
|
||||
expect(htmlStyle.minWidth).toEqual('1200px');
|
||||
expect(htmlStyle.minHeight).toEqual('600px');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user