feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
/*
* 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, act } from '@testing-library/react-hooks';
import { type ShortCutCommand } from '@coze-agent-ide/tool-config';
import {
useLoadMore,
getNextActiveItem,
getPreviousItem,
} from '../../../src/hooks/shortcut-bar/use-load-more';
describe('useLoadMore', () => {
it('should initialize with default values', async () => {
const { result } = renderHook(() =>
useLoadMore<ShortCutCommand>({
getId: item => item.command_id,
listRef: { current: null },
getMoreListService: async () =>
Promise.resolve({ list: [], hasMore: false }),
}),
);
expect(result.current.activeId).toBe('');
expect(result.current.loadingMore).toBe(false);
expect(result.current.data).toEqual({ list: [], hasMore: false });
expect(result.current.loading).toBe(true);
await act(() => {});
expect(result.current.loading).toBe(false);
});
it('should load more when reaching limit', async () => {
const getMoreListService = vi
.fn()
.mockResolvedValue({ list: [{ id: '2' }], hasMore: false });
const { result, waitForNextUpdate } = renderHook(() =>
useLoadMore({
getId: item => item.id,
listRef: { current: null },
getMoreListService,
defaultList: [{ id: '1' }],
}),
);
act(() => {
result.current.goNext();
});
await waitForNextUpdate();
expect(getMoreListService).toHaveBeenCalled();
expect(result.current.data.list).toEqual([{ id: '1' }, { id: '2' }]);
});
});
describe('getNextActiveItem', () => {
it('should return next item and reach limit flag', () => {
const result = getNextActiveItem({
curItem: { id: '1' },
list: [{ id: '1' }, { id: '2' }, { id: '3' }],
getId: item => item.id,
});
expect(result).toEqual({ reachLimit: true, item: { id: '2' } });
});
});
describe('getPreviousItem', () => {
it('should return previous item and reach limit flag', () => {
const result = getPreviousItem({
curItem: { id: '2' },
list: [{ id: '1' }, { id: '2' }, { id: '3' }],
getId: item => item.id,
});
expect(result).toEqual({ reachLimit: false, item: { id: '1' } });
});
});

View File

@@ -0,0 +1,318 @@
/*
* 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 { ContentType, useSendTextMessage } from '@coze-common/chat-area';
import { sendTeaEvent, EVENT_NAMES } from '@coze-arch/bot-tea';
import { type ShortCutCommand } from '@coze-agent-ide/tool-config';
import {
useSendTextQueryMessage,
useSendUseToolMessage,
getTemplateQuery,
getImageAndFileList,
} from '../../src/hooks/shortcut';
const sendTextMessageMock = vi.fn();
const sendMultimodalMessage = vi.fn();
vi.mock('@coze-arch/bot-tea', () => ({
sendTeaEvent: vi.fn(),
EVENT_NAMES: {
page_view: 'page_view',
},
}));
vi.mock('../../src/shortcut-tool/shortcut-edit/method', () => ({
enableSendTypePanelHideTemplate: vi.fn(),
}));
vi.mock('@coze-common/chat-area', () => ({
useSendTextMessage: () => sendTextMessageMock,
useSendMultimodalMessage: () => sendMultimodalMessage,
ContentType: {
Image: 'image',
File: 'file',
},
}));
vi.mock('@coze-common/chat-core', () => ({
default: () => vi.fn(),
getFileInfo: vi.fn().mockImplementation(file => {
if (file.type === 'image/png') {
return {
fileType: 'image',
};
}
return {
fileType: 'file',
};
}),
}));
const mockShortcut: ShortCutCommand = {
command_id: '7374755905893793836',
command_name: 'muti',
components_list: [
{
name: 'news',
description: 'Keywords to search for news, must in English',
input_type: 0,
parameter: 'q',
options: [],
},
],
description: '',
object_id: '7374633552917479468',
plugin_api_name: 'getNews',
plugin_id: '7373521805258014764',
send_type: 1,
shortcut_command: '/muti',
template_query: '查询{{news}}',
tool_type: 2,
tool_info: {
tool_name: 'News',
tool_params_list: [
{
default_value: '',
desc: 'Keywords to search for news, must in English',
name: 'q',
refer_component: true,
required: true,
type: 'string',
},
],
// @ts-expect-error -- test ignore
tool_type: 2,
},
work_flow_id: '',
};
describe('useSendTextQueryMessage', () => {
it('should send text message with query template', () => {
const sendTextMessage = useSendTextMessage();
const sendTextQueryMessage = useSendTextQueryMessage();
mockShortcut.tool_type = undefined;
sendTextQueryMessage({
queryTemplate: 'test',
shortcut: mockShortcut,
});
expect(sendTextMessage).toHaveBeenCalledWith(
{ text: 'test', mentionList: [] },
'shortcut',
{
extendFiled: {
device_id: expect.any(String),
},
},
);
expect(sendTeaEvent).toHaveBeenCalledWith(EVENT_NAMES.shortcut_use, {
show_panel: undefined,
tool_type: undefined,
use_components: true,
});
});
it('should send modify message with onBeforeSend', () => {
const sendTextQueryMessage = useSendTextQueryMessage();
const defaultOptions = {
extendFiled: {
device_id: expect.any(String),
},
};
const onBeforeSendMock = vi.fn().mockReturnValue({
message: {
payload: {
text: 'modified query template',
mention_list: [{ id: 123 }],
},
},
options: {
...defaultOptions,
test: '123',
},
});
sendTextQueryMessage({
queryTemplate: 'test',
onBeforeSend: onBeforeSendMock,
shortcut: mockShortcut,
});
});
});
describe('useSendUseToolMessage', () => {
it('should send multimodal message with shortcut command', () => {
const sendUseToolMessage = useSendUseToolMessage();
const shortcut = {
command_id: '7374755905893793836',
command_name: 'muti',
components_list: [
{
name: 'news',
description: 'Keywords to search for news, must in English',
input_type: 0,
parameter: 'q',
options: [],
},
],
description: '',
object_id: '7374633552917479468',
plugin_api_name: 'getNews',
plugin_id: '7373521805258014764',
send_type: 1,
tool_type: 2,
shortcut_command: '/muti',
template_query: '查询{{news}}',
tool_info: {
tool_name: 'News',
tool_params_list: [
{
default_value: '',
desc: 'Keywords to search for news, must in English',
name: 'q',
refer_component: true,
required: true,
type: 'string',
},
],
tool_type: 2,
},
work_flow_id: '',
};
const componentsFormValues = { news: '查询北京news' };
// @ts-expect-error --单测忽略
sendUseToolMessage({ shortcut, componentsFormValues });
expect(sendMultimodalMessage).toHaveBeenCalled();
expect(sendTeaEvent).toHaveBeenCalledWith(EVENT_NAMES.shortcut_use, {
show_panel: true,
tool_type: 2,
use_components: true,
});
});
});
describe('getTemplateQuery', () => {
it('should return query from template', () => {
const shortcut = {
command_id: '7374755905893793836',
command_name: 'muti',
components_list: [
{
name: 'news',
description: 'Keywords to search for news, must in English',
input_type: 0,
parameter: 'q',
options: [],
},
],
description: '',
object_id: '7374633552917479468',
plugin_api_name: 'getNews',
plugin_id: '7373521805258014764',
send_type: 1,
shortcut_command: '/muti',
template_query: '查询{{news}}',
tool_info: {
tool_name: 'News',
tool_params_list: [
{
default_value: '',
desc: 'Keywords to search for news, must in English',
name: 'q',
refer_component: true,
required: true,
type: 'string',
},
],
tool_type: 2,
},
work_flow_id: '',
};
const componentsFormValues = { news: '北京新闻' };
// @ts-expect-error --单测忽略
const result = getTemplateQuery(shortcut, componentsFormValues);
expect(result).toBe('查询北京新闻');
});
it('should throw error when template_query is not defined', () => {
const shortcut = {
command_id: '7374755905893793836',
command_name: 'muti',
components_list: [
{
name: 'news',
description: 'Keywords to search for news, must in English',
input_type: 0,
parameter: 'q',
options: [],
},
],
description: '',
object_id: '7374633552917479468',
plugin_api_name: 'getNews',
plugin_id: '7373521805258014764',
send_type: 1,
shortcut_command: '/muti',
tool_info: {
tool_name: 'News',
tool_params_list: [
{
default_value: '',
desc: 'Keywords to search for news, must in English',
name: 'q',
refer_component: true,
required: true,
type: 'string',
},
],
tool_type: 2,
},
work_flow_id: '',
};
const componentsFormValues = { news: '北京新闻' };
// @ts-expect-error --单测忽略
expect(() => getTemplateQuery(shortcut, componentsFormValues)).toThrowError(
'template_query is not defined',
);
});
});
describe('getImageAndFileList', () => {
it('should return list of images and files', () => {
const componentsFormValues = {
image: {
fileInstance: new File([''], 'filename', { type: 'image/png' }),
url: 'http://example.com/image.png',
width: 100,
height: 100,
},
file: {
fileInstance: new File([''], 'filename', { type: 'text/plain' }),
url: 'http://example.com/file.txt',
},
};
const result = getImageAndFileList(componentsFormValues);
expect(result).toEqual([
{
type: ContentType.Image,
file: componentsFormValues.image.fileInstance,
uri: componentsFormValues.image.url,
width: componentsFormValues.image.width,
height: componentsFormValues.image.height,
},
{
type: ContentType.File,
file: componentsFormValues.file.fileInstance,
uri: componentsFormValues.file.url,
},
]);
});
});

View File

@@ -0,0 +1,111 @@
/*
* 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 WorkFlowItemType } from '@coze-studio/bot-detail-store';
import { type PluginApi, ToolType } from '@coze-arch/bot-api/playground_api';
import {
initToolInfoByToolApi,
initToolInfoByWorkFlow,
initToolInfoByPlugin,
MAX_TOOL_PARAMS_COUNT,
} from '../../../../../../src/shortcut-tool/shortcut-edit/action-switch-area/skill-switch/method';
describe('initToolInfoByToolApi', () => {
it('returns null when no toolApi is provided', () => {
expect(initToolInfoByToolApi()).toBeNull();
});
it('initializes tool info by workflow when workflow_id is present', () => {
// @ts-expect-error -- workflow_id is not required
const workflow: WorkFlowItemType = {
workflow_id: '1',
name: 'Test Workflow',
parameters: [],
};
const result = initToolInfoByToolApi(workflow);
expect(result?.tool_type).toBe(ToolType.ToolTypeWorkFlow);
});
it('initializes tool info by plugin when workflow_id is not present', () => {
const plugin: PluginApi = {
name: 'Test Plugin',
plugin_name: 'Test Plugin',
parameters: [],
};
const result = initToolInfoByToolApi(plugin);
expect(result?.tool_type).toBe(ToolType.ToolTypePlugin);
});
it('sorts parameters by required field and limits to MAX_TOOL_PARAMS_COUNT', () => {
const parameters = Array(MAX_TOOL_PARAMS_COUNT + 2)
.fill(null)
.map((_, index) => ({
name: `param${index}`,
desc: `desc${index}`,
required: index < MAX_TOOL_PARAMS_COUNT,
type: 'string',
}));
const plugin: PluginApi = {
name: 'Test Plugin',
plugin_name: 'Test Plugin',
parameters,
};
const result = initToolInfoByToolApi(plugin);
expect(result?.tool_params_list.length).toBe(MAX_TOOL_PARAMS_COUNT + 2);
// 前10个是required=true的参数
expect(
result?.tool_params_list
.slice(0, MAX_TOOL_PARAMS_COUNT)
.every(param => param.required),
).toBeTruthy();
});
});
describe('initToolInfoByWorkFlow', () => {
it('initializes tool info from a workflow item', () => {
// @ts-expect-error -- workflow_id is not required
const workflow: WorkFlowItemType = {
workflow_id: '1',
name: 'Test Workflow',
parameters: [],
};
const result = initToolInfoByWorkFlow(workflow);
expect(result.tool_type).toBe(ToolType.ToolTypeWorkFlow);
expect(result.tool_name).toBe(workflow.name);
expect(result.work_flow_id).toBe(workflow.workflow_id);
});
});
describe('initToolInfoByPlugin', () => {
it('initializes tool info from a plugin item', () => {
const plugin: PluginApi = {
name: 'Test Plugin',
plugin_name: 'Test Plugin',
parameters: [],
};
const result = initToolInfoByPlugin(plugin);
expect(result.tool_type).toBe(ToolType.ToolTypePlugin);
expect(result.tool_name).toBe(plugin.plugin_name);
expect(result.plugin_api_name).toBe(plugin.name);
});
});

View File

@@ -0,0 +1,88 @@
/*
* 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 { InputType } from '@coze-arch/bot-api/playground_api';
import { type ShortcutEditFormValues } from '../../../../../src/shortcut-tool/types';
import {
initComponentsByToolParams,
getUnusedComponents,
} from '../../../../../src/shortcut-tool/shortcut-edit/action-switch-area/method';
describe('initComponentsByToolParams', () => {
it('should initialize components correctly', () => {
const params = [
{ name: 'param1', desc: 'description1', refer_component: true },
{ name: 'param2', desc: 'description2', refer_component: false },
];
const expected = [
{
name: 'param1',
parameter: 'param1',
description: 'description1',
input_type: InputType.TextInput,
default_value: { value: '' },
hide: false,
},
{
name: 'param2',
parameter: 'param2',
description: 'description2',
input_type: InputType.TextInput,
default_value: { value: '' },
hide: true,
},
];
expect(initComponentsByToolParams(params)).toEqual(expected);
});
it('should handle empty params', () => {
expect(initComponentsByToolParams([])).toEqual([]);
});
});
describe('getUnusedComponents', () => {
it('should return unused components', () => {
// @ts-expect-error -- hide is missing
const shortcut: ShortcutEditFormValues = {
components_list: [
{ name: 'comp1', hide: false },
{ name: 'comp2', hide: false },
],
template_query: '{{comp1}}',
};
const expected = [{ name: 'comp2', hide: false }];
expect(getUnusedComponents(shortcut)).toEqual(expected);
});
it('should handle empty components_list', () => {
// @ts-expect-error -- hide is missing
const shortcut: ShortcutEditFormValues = {
components_list: [],
template_query: '',
};
expect(getUnusedComponents(shortcut)).toEqual([]);
});
it('should handle no unused components', () => {
// @ts-expect-error -- hide is missing
const shortcut: ShortcutEditFormValues = {
components_list: [{ name: 'comp1', hide: false }],
template_query: '{{comp1}}',
};
expect(getUnusedComponents(shortcut)).toEqual([]);
});
});

View File

@@ -0,0 +1,237 @@
/*
* 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 {
InputType,
// eslint-disable-next-line camelcase
type shortcut_command,
} from '@coze-arch/bot-api/playground_api';
import {
type ComponentsWithId,
type ComponentTypeItem,
} from '../../../../../src/shortcut-tool/shortcut-edit/components-table/types';
import {
attachIdToComponents,
checkDuplicateName,
formatSubmitValues,
getComponentTypeFormBySubmitField,
getComponentTypeSelectFormInitValues,
getSubmitFieldFromComponentTypeForm,
isUploadType,
modifyComponentWhenSwitchChange,
type SubmitComponentTypeFields,
} from '../../../../../src/shortcut-tool/shortcut-edit/components-table/method';
describe('attachIdToComponents', () => {
it('should attach unique id to each component', () => {
// eslint-disable-next-line camelcase
const components: shortcut_command.Components[] = [
{ input_type: InputType.TextInput },
{ input_type: InputType.Select },
];
const result = attachIdToComponents(components);
expect(result[0]?.id).toBeDefined();
expect(result[1]?.id).toBeDefined();
expect(result[0]?.id).not.toEqual(result[1]?.id);
});
});
describe('formatSubmitValues', () => {
it('should format values correctly', () => {
const values: ComponentsWithId[] = [
{ id: '1', input_type: InputType.TextInput, options: ['option1'] },
{
id: '2',
input_type: InputType.Select,
options: ['option1', 'option2'],
},
];
const result = formatSubmitValues(values);
expect(result[0]?.options).toEqual([]);
expect(result[1]?.options).toEqual(['option1', 'option2']);
});
});
describe('checkDuplicateName', () => {
it('should return true if duplicate names exist', () => {
const values: ComponentsWithId[] = [
{ id: '1', name: 'component1' },
{ id: '2', name: 'component1' },
];
const formApi = { setError: vi.fn() };
const result = checkDuplicateName(values, formApi as any);
expect(result).toBe(true);
});
it('should return false if no duplicate names exist', () => {
const values: ComponentsWithId[] = [
{ id: '1', name: 'component1' },
{ id: '2', name: 'component2' },
];
const formApi = { setError: vi.fn() };
const result = checkDuplicateName(values, formApi as any);
expect(result).toBe(false);
});
});
describe('getComponentTypeSelectFormInitValues', () => {
it('should return initial values', () => {
const result = getComponentTypeSelectFormInitValues();
expect(result).toEqual({ type: 'text' });
});
});
describe('getSubmitFieldFromComponentTypeForm', () => {
it('returns TextInput type for text', () => {
const values: ComponentTypeItem = { type: 'text' };
const result = getSubmitFieldFromComponentTypeForm(values);
expect(result).toEqual({ input_type: InputType.TextInput });
});
it('returns Select type with options for select', () => {
const values: ComponentTypeItem = {
type: 'select',
options: ['option1', 'option2'],
};
const result = getSubmitFieldFromComponentTypeForm(values);
expect(result).toEqual({
input_type: InputType.Select,
options: ['option1', 'option2'],
});
});
it('returns MixUpload type with upload options for multiple upload types', () => {
const values: ComponentTypeItem = {
type: 'upload',
uploadTypes: [InputType.UploadImage, InputType.UploadDoc],
};
const result = getSubmitFieldFromComponentTypeForm(values);
expect(result).toEqual({
input_type: InputType.MixUpload,
upload_options: [InputType.UploadImage, InputType.UploadDoc],
});
});
it('returns specific Upload type for single upload type', () => {
const values: ComponentTypeItem = {
type: 'upload',
uploadTypes: [InputType.UploadImage],
};
const result = getSubmitFieldFromComponentTypeForm(values);
expect(result).toEqual({ input_type: InputType.UploadImage });
});
it('returns TextInput type for unrecognized type', () => {
// @ts-expect-error -- 无视
const values: ComponentTypeItem = { type: 'unknown' };
const result = getSubmitFieldFromComponentTypeForm(values);
expect(result).toEqual({ input_type: InputType.TextInput });
});
});
describe('isUploadType', () => {
it('should return true for upload types', () => {
const result = isUploadType(InputType.UploadImage);
expect(result).toBe(true);
});
it('should return false for non-upload types', () => {
const result = isUploadType(InputType.TextInput);
expect(result).toBe(false);
});
});
describe('getComponentTypeFormBySubmitField', () => {
it('returns initial values when input_type is not provided', () => {
const values: SubmitComponentTypeFields = {};
const result = getComponentTypeFormBySubmitField(values);
expect(result).toEqual({ type: 'text' });
});
it('returns correct form for TextInput type', () => {
const values: SubmitComponentTypeFields = {
input_type: InputType.TextInput,
};
const result = getComponentTypeFormBySubmitField(values);
expect(result).toEqual({ type: 'text' });
});
it('returns correct form for Select type with options', () => {
const values: SubmitComponentTypeFields = {
input_type: InputType.Select,
options: ['option1', 'option2'],
};
const result = getComponentTypeFormBySubmitField(values);
expect(result).toEqual({ type: 'select', options: ['option1', 'option2'] });
});
it('returns correct form for Upload type with upload options', () => {
const values: SubmitComponentTypeFields = {
input_type: InputType.UploadImage,
upload_options: [InputType.UploadAudio, InputType.VIDEO],
};
const result = getComponentTypeFormBySubmitField(values);
expect(result).toEqual({
type: 'upload',
uploadTypes: [InputType.UploadAudio, InputType.VIDEO],
});
});
it('returns initial values when input_type is not recognized', () => {
const values: SubmitComponentTypeFields = {
input_type: 'unknown' as unknown as InputType,
};
const result = getComponentTypeFormBySubmitField(values);
expect(result).toEqual({ type: 'text' });
});
});
describe('isUploadType', () => {
it('returns true for upload types', () => {
expect(isUploadType(InputType.UploadImage)).toBeTruthy();
expect(isUploadType(InputType.UploadDoc)).toBeTruthy();
expect(isUploadType(InputType.UploadTable)).toBeTruthy();
expect(isUploadType(InputType.UploadAudio)).toBeTruthy();
expect(isUploadType(InputType.CODE)).toBeTruthy();
expect(isUploadType(InputType.ARCHIVE)).toBeTruthy();
expect(isUploadType(InputType.PPT)).toBeTruthy();
expect(isUploadType(InputType.VIDEO)).toBeTruthy();
expect(isUploadType(InputType.TXT)).toBeTruthy();
expect(isUploadType(InputType.MixUpload)).toBeTruthy();
});
it('returns false for non-upload types', () => {
expect(isUploadType(InputType.TextInput)).toBeFalsy();
expect(isUploadType(InputType.Select)).toBeFalsy();
});
});
describe('modifyComponentWhenSwitchChange', () => {
it('should modify component hide property correctly', () => {
const components: ComponentsWithId[] = [
{ id: '1', hide: false },
{ id: '2', hide: false },
];
const record: ComponentsWithId = { id: '1', hide: false };
const result = modifyComponentWhenSwitchChange({
components,
record,
checked: false,
});
expect(result[0]?.hide).toBe(true);
expect(result[1]?.hide).toBe(false);
});
});

View File

@@ -0,0 +1,18 @@
/*
* 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.
*/
vi.stubGlobal('IS_OVERSEA', false);
vi.stubGlobal('IS_RELEASE_VERSION', false);

View File

@@ -0,0 +1,31 @@
/*
* 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 ItemType } from '../../src/utils/data-helper';
describe('ItemType', () => {
it('returns array item type for array input', () => {
type Result = ItemType<string[]>;
const result: Result = 'test';
expect(typeof result).to.equal('string');
});
it('returns same type for non-array input', () => {
type Result = ItemType<number>;
const result: Result = 123;
expect(typeof result).to.equal('number');
});
});

View File

@@ -0,0 +1,40 @@
/*
* 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 { getUIModeByBizScene } from '../../src/utils/get-ui-mode-by-biz-scene';
describe('ItemType', () => {
it('returns UIMode correctly', () => {
const res1 = getUIModeByBizScene({
bizScene: 'agentApp',
showBackground: false,
});
const res2 = getUIModeByBizScene({
bizScene: 'home',
showBackground: false,
});
const res3 = getUIModeByBizScene({
bizScene: 'agentApp',
showBackground: false,
});
expect(res1).toBe('grey');
expect(res2).toBe('white');
expect(res3).toBe('grey');
});
});

View File

@@ -0,0 +1,42 @@
/*
* 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 { isApiError } from '../../src/utils/handle-error';
describe('isApiError', () => {
it('identifies ApiError correctly', () => {
const error = { name: 'ApiError' };
const result = isApiError(error);
expect(result).to.be.true;
});
it('returns false for non-ApiError', () => {
const error = { name: 'OtherError' };
const result = isApiError(error);
expect(result).to.be.false;
});
it('returns false for error without name', () => {
const error = { message: 'An error occurred' };
const result = isApiError(error);
expect(result).to.be.false;
});
it('handles null and undefined', () => {
expect(isApiError(null)).to.be.false;
expect(isApiError(undefined)).to.be.false;
});
});

View File

@@ -0,0 +1,54 @@
/*
* 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 { getQueryFromTemplate } from '../../src/utils/shortcut-query';
describe('getQueryFromTemplate', () => {
it('should replace placeholders with corresponding values', () => {
const templateQuery = 'Hello, {{name}}!';
const values = { name: 'John' };
const result = getQueryFromTemplate(templateQuery, values);
expect(result).to.equal('Hello, John!');
});
it('should handle multiple placeholders', () => {
const templateQuery = '{{greeting}}, {{name}}!';
const values = { greeting: 'Hi', name: 'John' };
const result = getQueryFromTemplate(templateQuery, values);
expect(result).to.equal('Hi, John!');
});
it('should leave unreplaced placeholders intact', () => {
const templateQuery = 'Hello, {{name}}!';
const values = { greeting: 'Hi' };
const result = getQueryFromTemplate(templateQuery, values);
expect(result).to.equal('Hello, {{name}}!');
});
it('should handle empty values object', () => {
const templateQuery = 'Hello, {{name}}!';
const values = {};
const result = getQueryFromTemplate(templateQuery, values);
expect(result).to.equal('Hello, {{name}}!');
});
it('should handle empty template string', () => {
const templateQuery = '';
const values = { name: 'John' };
const result = getQueryFromTemplate(templateQuery, values);
expect(result).to.equal('');
});
});