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,133 @@
/*
* 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, act } from '@testing-library/react-hooks';
import { initEditorByPrologue } from '@/component/onboarding-message/onboarding-editor/method/init-editor';
import { useInitEditor } from '@/component/onboarding-message/onboarding-editor/hooks/use-init-editor';
import type { OnboardingEditorContext } from '@/component/onboarding-message/onboarding-editor';
vi.mock(
'@/component/onboarding-message/onboarding-editor/method/init-editor',
() => ({
initEditorByPrologue: vi.fn().mockResolvedValue(undefined),
}),
);
describe('useInitEditor', () => {
let props;
let editorRef;
beforeEach(() => {
props = {
initValues: {
prologue: 'prologue',
},
};
editorRef = {
current: {
scrollModule: {
scrollTo: vi.fn(),
},
},
};
(initEditorByPrologue as Mock).mockClear();
});
it('calls initEditorByPrologue when prologue and editorRef.current are defined', () => {
renderHook(() => useInitEditor({ api: undefined, props, editorRef }));
expect(initEditorByPrologue).toHaveBeenCalledWith({
prologue: props.initValues.prologue,
editorRef,
});
});
it('does not call initEditorByPrologue when prologue is not defined', () => {
props.initValues.prologue = undefined;
const { result } = renderHook(() =>
useInitEditor({ api: undefined, props, editorRef }),
);
act(() => {
result.current;
});
expect(initEditorByPrologue).not.toHaveBeenCalled();
});
it('does not call initEditorByPrologue when editorRef.current is not defined', () => {
editorRef.current = undefined;
const { result } = renderHook(() =>
useInitEditor({ api: undefined, props, editorRef }),
);
act(() => {
result.current;
});
expect(initEditorByPrologue).not.toHaveBeenCalled();
});
it('should not call initEditorByPrologue when it has been initialized', () => {
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
const { rerender } = renderHook<OnboardingEditorContext, void>(hookProps =>
useInitEditor({
api: hookProps.api ?? undefined,
props: hookProps.props ?? props,
editorRef,
}),
);
rerender({
api: undefined,
props: {
initValues: {
prologue: 'iwdfasdfa',
},
},
editorRef,
});
rerender({
api: undefined,
props: {
initValues: {
prologue: 'acbcfaa',
},
},
editorRef,
});
expect(initEditorByPrologue).toHaveBeenCalledTimes(1);
});
it('should not call initEditorByPrologue when initValues is not defined', () => {
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
const { rerender } = renderHook<OnboardingEditorContext, void>(hookProps =>
useInitEditor({
api: hookProps.api ?? undefined,
props: hookProps.props ?? props,
editorRef,
}),
);
rerender({
api: undefined,
props: {},
editorRef,
});
expect(initEditorByPrologue).not.toHaveBeenCalled();
});
});

View 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 { I18n } from '@coze-arch/i18n';
import { Toast } from '@coze-arch/bot-semi';
import { PlaygroundApi } from '@coze-arch/bot-api';
import { getImageUrl } from '@/component/onboarding-message/onboarding-editor/method/get-image-url';
vi.mock('@coze-arch/bot-api', () => ({
PlaygroundApi: {
GetImagexShortUrl: vi.fn(),
},
}));
vi.mock('@coze-arch/bot-semi', () => ({
Toast: {
error: vi.fn(),
},
}));
vi.mock('@coze-arch/i18n');
describe('getImageUrl', () => {
beforeEach(() => {
vi.resetAllMocks();
});
it('returns image URL when API call is successful and image is appropriate', async () => {
const mockRequest = { Key: 'testUri' };
const mockResponse = {
code: '200',
msg: 'Success',
data: {
url_info: {
testUri: {
review_status: true,
url: 'http://test.com',
},
},
},
};
(PlaygroundApi.GetImagexShortUrl as Mock).mockResolvedValue(mockResponse);
const result = await getImageUrl(mockRequest);
expect(result).toEqual({
code: 200,
message: 'Success',
data: {
url: 'http://test.com',
},
});
});
it('throw error && shows error toast when image is inappropriate', async () => {
const mockRequest = { Key: 'testUri' };
const mockResponse = {
code: '200',
msg: 'Success',
data: {
url_info: {
testUri: {
review_status: false,
url: 'http://test.com',
},
},
},
};
(PlaygroundApi.GetImagexShortUrl as Mock).mockResolvedValue(mockResponse);
await expect(getImageUrl(mockRequest)).rejects.toThrow(
'inappropriate_contents',
);
expect(Toast.error).toHaveBeenCalledWith({
content: I18n.t('inappropriate_contents'),
showClose: false,
});
});
it('throw error when image URL is not present', () => {
const mockRequest = { Key: 'testUri' };
const mockResponse = {
code: '200',
msg: 'Success',
data: {
url_info: {
testUri: {
review_status: 'appropriate',
},
},
},
};
(PlaygroundApi.GetImagexShortUrl as Mock).mockResolvedValue(mockResponse);
expect(getImageUrl(mockRequest)).rejects.toThrow('inappropriate_contents');
});
});

View File

@@ -0,0 +1,62 @@
/*
* 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 { DeveloperApi } from '@coze-arch/bot-api';
import { getUploadToken } from '@/component/onboarding-message/onboarding-editor/method/get-upload-token';
vi.mock('@coze-arch/bot-api', () => ({
DeveloperApi: {
GetUploadAuthToken: vi.fn(),
},
}));
describe('getUploadToken', () => {
beforeEach(() => {
vi.resetAllMocks();
});
it('returns the expected response on successful API call', async () => {
const mockResponse = {
code: 200,
msg: 'Success',
data: {
auth: {
token: 'mockToken',
},
},
};
(DeveloperApi.GetUploadAuthToken as Mock).mockResolvedValue(mockResponse);
const result = await getUploadToken();
expect(result).toEqual({
code: 200,
message: 'Success',
data: {
...mockResponse.data,
...mockResponse.data.auth,
},
});
});
it('throws an error when the API call fails', async () => {
const mockError = new Error('API call failed');
(DeveloperApi.GetUploadAuthToken as Mock).mockRejectedValue(mockError);
await expect(getUploadToken()).rejects.toThrow('API call failed');
});
});

View File

@@ -0,0 +1,59 @@
/*
* 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 { md2html } from '@coze-common/md-editor-adapter';
import { type Editor } from '@coze-common/md-editor-adapter';
import {
initEditorByPrologue,
type InitEditorByPrologueProps,
} from '@/component/onboarding-message/onboarding-editor/method/init-editor';
vi.mock('@coze-common/md-editor-adapter', () => ({
md2html: vi.fn(),
}));
describe('initEditorByPrologue', () => {
let editorRef: React.RefObject<Editor>;
beforeEach(() => {
editorRef = { current: { setHTML: vi.fn() } } as any;
});
it('should convert markdown to html and set to editor', () => {
const prologue = '**Hello**';
const htmlContent = '<strong>Hello</strong>';
vi.mocked(md2html).mockReturnValue(htmlContent);
const props: InitEditorByPrologueProps = { prologue, editorRef };
initEditorByPrologue(props);
expect(md2html).toHaveBeenCalledWith(prologue);
expect(editorRef.current?.setHTML).toHaveBeenCalledWith(htmlContent);
});
it('should not set html to editor if editorRef is not defined', () => {
const prologue = '**Hello**';
const htmlContent = '<strong>Hello</strong>';
vi.mocked(md2html).mockReturnValue(htmlContent);
const props: InitEditorByPrologueProps = { prologue, editorRef: {} as any };
initEditorByPrologue(props);
expect(md2html).toHaveBeenCalledWith(prologue);
expect(editorRef.current?.setHTML).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,116 @@
/*
* 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 { sliceEditor } from '@/component/onboarding-message/onboarding-editor/method/slice-editor';
// vi.mock需要在顶部因为它会被提升
vi.mock('@coze-common/md-editor-adapter', () => ({
ZoneDelta: vi.fn().mockImplementation(() => ({
retain: vi.fn().mockReturnThis(),
delete: vi.fn().mockReturnThis(),
})),
}));
// 导入模拟后的ZoneDelta
import { ZoneDelta } from '@coze-common/md-editor-adapter';
describe('sliceEditor', () => {
let editorRef;
let maxCount;
let mockedEditor;
let mockZoneDeltaInstance;
beforeEach(() => {
vi.resetAllMocks();
// 创建一个新的模拟实例
mockZoneDeltaInstance = {
retain: vi.fn().mockReturnThis(),
delete: vi.fn().mockReturnThis(),
};
// 重置ZoneDelta构造函数的实现
vi.mocked(ZoneDelta).mockImplementation(() => mockZoneDeltaInstance);
mockedEditor = {
selection: {
getSelection: vi.fn(),
},
getContentState: vi.fn().mockReturnValue({
getZoneState: vi.fn(),
apply: vi.fn(),
}),
};
editorRef = { current: mockedEditor };
maxCount = 5;
});
it('returns early when editorRef is not current', () => {
editorRef.current = null;
const result = sliceEditor(editorRef, maxCount);
expect(result).toBeUndefined();
});
it('returns early when zoneState is not found', () => {
mockedEditor.selection.getSelection.mockReturnValue({
start: { zoneId: 'zone1' },
});
mockedEditor.getContentState.mockReturnValue({
getZoneState: vi.fn().mockReturnValue(null),
});
const result = sliceEditor(editorRef, maxCount);
expect(result).toBeUndefined();
});
it('does not slice when currentCount is less than maxCount', () => {
mockedEditor.selection.getSelection.mockReturnValue({
start: { zoneId: 'zone1' },
});
mockedEditor.getContentState.mockReturnValue({
getZoneState: vi
.fn()
.mockReturnValue({ totalWidth: vi.fn().mockReturnValue(4) }),
});
const result = sliceEditor(editorRef, maxCount);
expect(result).toBeUndefined();
});
it('slices when currentCount is more than maxCount', () => {
const mockApply = vi.fn();
mockedEditor.selection.getSelection.mockReturnValue({
start: { zoneId: 'zone1' },
});
mockedEditor.getContentState.mockReturnValue({
apply: mockApply,
getZoneState: vi
.fn()
.mockReturnValue({ totalWidth: vi.fn().mockReturnValue(7) }),
});
sliceEditor(editorRef, maxCount);
expect(ZoneDelta).toHaveBeenCalledWith({ zoneId: 'zone1' });
expect(mockZoneDeltaInstance.retain).toHaveBeenCalledWith(maxCount);
expect(mockZoneDeltaInstance.delete).toHaveBeenCalledWith(1); // 实际计算出的值是1
expect(mockApply).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,85 @@
/*
* 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 { act, renderHook } from '@testing-library/react-hooks';
import { useCustomPlatformController } from '@/hook/publish-platform-setting/use-custom-platform-controller';
vi.mock('@coze-foundation/enterprise-store-adapter', () => ({
useCurrentEnterpriseInfo: () => ({}),
}));
vi.mock('@coze-arch/bot-api', () => ({
connectorApi: {
ListConnector: vi
.fn()
.mockResolvedValueOnce({
data: [
{
id: 1,
name: 'test',
type: 'mysql',
config: {},
status: 'ok',
},
],
})
.mockRejectedValueOnce([]),
},
}));
describe('use-custom-platform-controller', () => {
it('use-custom-platform-controller datasource and action should be right', async () => {
const { result, waitForValueToChange } = renderHook(() =>
useCustomPlatformController(),
);
act(() => result.current.doRefreshDatasource());
await waitForValueToChange(() => result.current.dataSource);
expect(result.current.dataSource.length).toBe(1);
expect(result.current.loading).toBeFalsy();
act(() => result.current.doRefreshDatasource());
await waitForValueToChange(() => result.current.dataSource);
expect(result.current.dataSource.length).toBe(0);
});
it('use-custom-platform-controller actionTarget should be right', () => {
const { result, waitForValueToChange } = renderHook(() =>
useCustomPlatformController(),
);
act(() =>
result.current.doSetActionTarget({ target: 'oauth', action: 'create' }),
);
waitForValueToChange(() => result.current.actionTarget);
expect(result.current.actionTarget).toEqual({
target: 'oauth',
action: 'create',
});
});
it('use-custom-platform-controller copy-action should be right', () => {
const { result } = renderHook(() => useCustomPlatformController());
act(() => result.current.doCopy('test'));
});
});

View File

@@ -0,0 +1,518 @@
/*
* 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 { useCustomPlatformSettingModalController } from '@/hook/publish-platform-setting/use-custom-platform-setting-modal-controller';
vi.mock('@coze-foundation/enterprise-store-adapter', () => ({
useIsCurrentPersonalEnterprise: () => true,
useCurrentEnterpriseRoles: () => [],
useCurrentEnterpriseInfo: () => ({ enterprise_id: 'personal' }),
}));
vi.mock('@coze-arch/bot-api', () => ({
PlaygroundApi: {
GetSpaceListV2: vi.fn().mockResolvedValue({
data: {
bot_space_list: [
{
app_ids: [],
connectors: [
{
icon: '',
id: '',
name: '豆包',
},
{
icon: '',
id: '',
name: '飞书',
},
{
icon: '',
id: '',
name: '微信客服',
},
{
icon: '',
id: '',
name: '【即将下线】微信公众号(服务号)',
},
{
icon: '',
id: '',
name: 'Web SDK',
},
{
icon: '',
id: '',
name: '掘金',
},
{
icon: '',
id: '',
name: 'Bot Store',
},
{
icon: '',
id: '',
name: 'Bot as API',
},
{
icon: '',
id: '',
name: '飞书多维表格',
},
{
icon: '',
id: '',
name: '豆包',
},
{
icon: '',
id: '',
name: '飞书',
},
{
icon: '',
id: '',
name: '微信客服',
},
{
icon: '',
id: '',
name: '【即将下线】微信公众号(服务号)',
},
{
icon: '',
id: '',
name: 'Web SDK',
},
{
icon: '',
id: '',
name: '掘金',
},
{
icon: '',
id: '',
name: 'Bot Store',
},
{
icon: '',
id: '',
name: 'Bot as API',
},
{
icon: '',
id: '',
name: '飞书多维表格',
},
{
icon: '',
id: '',
name: '测试渠道02',
},
{
icon: '',
id: '',
name: 'Bot as API',
},
],
description: 'Personal Space',
hide_operation: false,
icon_url: '',
id: '',
name: '个人空间',
role_type: 1,
space_mode: 0,
space_type: 1,
},
{
app_ids: [],
connectors: [
{
icon: '',
id: '',
name: '豆包',
},
{
icon: '',
id: '',
name: '飞书',
},
{
icon: '',
id: '',
name: '微信客服',
},
{
icon: '',
id: '',
name: '【即将下线】微信公众号(服务号)',
},
{
icon: '',
id: '',
name: 'Web SDK',
},
{
icon: '',
id: '',
name: '掘金',
},
{
icon: '',
id: '',
name: 'Bot Store',
},
{
icon: '',
id: '',
name: 'Bot as API',
},
{
icon: '',
id: '',
name: '飞书多维表格',
},
{
icon: '',
id: '',
name: '测试渠道03测试渠道03',
},
],
description: 'This is a description for liwei_1019',
hide_operation: false,
icon_url: '',
id: '',
name: 'liwei_1019',
role_type: 1,
space_mode: 0,
space_type: 2,
},
{
app_ids: [],
connectors: [
{
icon: '',
id: '',
name: '豆包',
},
{
icon: '',
id: '',
name: '飞书',
},
{
icon: '',
id: '',
name: '微信客服',
},
{
icon: '',
id: '',
name: '【即将下线】微信公众号(服务号)',
},
{
icon: '',
id: '',
name: 'Web SDK',
},
{
icon: '',
id: '',
name: '掘金',
},
{
icon: '',
id: '',
name: 'Bot Store',
},
{
icon: '',
id: '',
name: 'Bot as API',
},
{
icon: '',
id: '',
name: '飞书多维表格',
},
],
description: '',
hide_operation: false,
icon_url: '',
id: '',
name: 'LYH的boe测试团队',
role_type: 3,
space_mode: 0,
space_type: 2,
},
{
app_ids: [],
connectors: [
{
icon: '',
id: '',
name: '豆包',
},
{
icon: '',
id: '',
name: '飞书',
},
{
icon: '',
id: '',
name: '微信客服',
},
{
icon: '',
id: '',
name: '【即将下线】微信公众号(服务号)',
},
{
icon: '',
id: '',
name: 'Web SDK',
},
{
icon: '',
id: '',
name: '掘金',
},
{
icon: '',
id: '',
name: 'Bot Store',
},
{
icon: '',
id: '',
name: 'Bot as API',
},
{
icon: '',
id: '',
name: '飞书多维表格',
},
{
icon: '',
id: '',
name: 'Bot Store',
},
],
description: '',
hide_operation: false,
icon_url: '',
id: '',
name: 'ByteDance Demo',
role_type: 3,
space_mode: 0,
space_type: 2,
},
{
app_ids: [],
connectors: [
{
icon: '',
id: '',
name: '豆包',
},
{
icon: '',
id: '',
name: '飞书',
},
{
icon: '',
id: '',
name: '微信客服',
},
{
icon: '',
id: '',
name: '【即将下线】微信公众号(服务号)',
},
{
icon: '',
id: '',
name: 'Web SDK',
},
{
icon: '',
id: '',
name: '掘金',
},
{
icon: '',
id: '',
name: 'Bot Store',
},
{
icon: '',
id: '',
name: 'Bot as API',
},
{
icon: '',
id: '',
name: '飞书多维表格',
},
{
icon: '',
id: '',
name: '测试渠道03测试渠道03',
},
{
icon: '',
id: '',
name: 'Bot as API',
},
],
description: '',
hide_operation: false,
icon_url: '',
id: '',
name: 'liwei_1019_v2',
role_type: 1,
space_mode: 0,
space_type: 2,
},
],
},
}),
},
patPermissionApi: {
ListAppMeta: vi.fn().mockResolvedValue({
data: {
apps: [
{
appid: '',
app_owner_id: '',
name: 'showcase渠道',
description: 'showcase渠道',
created_at: 1722240368,
app_type: 'Connector',
declared_permission: [],
client_id: '',
status: 'Active',
certificated: 'Noncertificated',
connector: {
connector_id: '',
},
},
{
appid: '',
app_owner_id: '',
name: 'OauthApp',
description: 'OauthApp',
created_at: 1721987816,
app_type: 'Connector',
declared_permission: [
{
resource_type: 'Connector',
actions: ['botChat', 'WorkflowTest', 'botEdit', 'botPublish'],
},
],
client_id: '',
status: 'Active',
certificated: 'Noncertificated',
connector: {
connector_id: '',
},
},
{
appid: '',
app_owner_id: '',
name: '1019的测试应用',
description: '1019的测试应用',
created_at: 1721812683,
app_type: 'Normal',
declared_permission: [
{
resource_type: 'Bot',
actions: ['chat', 'getMetadata'],
},
],
client_id: '',
status: 'Active',
certificated: 'Noncertificated',
connector: {
connector_id: '',
},
},
],
},
}),
},
connectorApi: {
CreateConnector: vi.fn().mockResolvedValue({ code: 0 }),
UpdateConnector: vi.fn().mockResolvedValue({ code: 0 }),
DeleteConnector: vi.fn().mockResolvedValue({ code: 0 }),
},
}));
describe('useCustomPlatformSettingModalController', () => {
it('useCustomPlatformSettingModalController fetch data should be right', async () => {
const onOk = vi.fn();
const { result, waitForNextUpdate } = renderHook(() =>
useCustomPlatformSettingModalController(onOk),
);
await waitForNextUpdate();
expect(result.current.oauthOptionsList.length).toBe(3);
expect(result.current.spaceOptionList.length).toBe(5);
expect(result.current.isLoadingOauthDatasource).toBeFalsy();
expect(result.current.isLoadingSpace).toBeFalsy();
});
it('useCustomPlatformSettingModalController create & update & delete should be right', async () => {
const onOk = vi.fn();
const { result, waitForNextUpdate } = renderHook(() =>
useCustomPlatformSettingModalController(onOk),
);
await waitForNextUpdate();
expect(result.current.isIdle).toBeTruthy();
await result.current.doCreate({});
await result.current.doUpdate({});
await result.current.doDel({ id: 'id' });
expect(onOk).toBeCalledTimes(3);
});
it('useCustomPlatformSettingModalController copy should be right', async () => {
const onOk = vi.fn();
const { result, waitForNextUpdate } = renderHook(() =>
useCustomPlatformSettingModalController(onOk),
);
await waitForNextUpdate();
expect(result.current.isIdle).toBeTruthy();
result.current.doCopy('id');
});
});

View File

@@ -0,0 +1,59 @@
/*
* 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 { AuthStatus } from '@coze-arch/idl/developer_api';
import { useNormalPlatformController } from '@/hook/publish-platform-setting/use-normal-platform-controller';
vi.mock('@coze-studio/user-store', () => ({
userStoreService: {
useUserAuthInfo: vi.fn().mockReturnValue([
{
id: 'id',
name: 'name',
icon: 'icon',
auth_status: AuthStatus.Authorized,
},
{
id: 'id',
name: 'name',
icon: 'icon',
auth_status: AuthStatus.Unauthorized,
},
]),
getUserAuthInfos: vi
.fn()
.mockResolvedValueOnce(0)
.mockRejectedValueOnce(-1),
},
}));
describe('useNormalPlatformController', () => {
it('useNormalPlatformController should return userAuthInfos', () => {
const { result } = renderHook(() => useNormalPlatformController());
expect(result.current.userAuthInfos.length).toEqual(2);
});
it('useNormalPlatformController should return revokeSuccess', async () => {
const { result } = renderHook(() => useNormalPlatformController());
await result.current.revokeSuccess();
await result.current.revokeSuccess();
});
});

View File

@@ -0,0 +1,247 @@
/*
* 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 { useOauthSettingModalController } from '@/hook/publish-platform-setting/use-oauth-setting-modal-controller';
vi.mock('@coze-arch/bot-api', () => ({
connectorApi: {
GetOauthConfigSchema: vi.fn().mockResolvedValue({
oauth_schema: {
title_text: '填写自定义渠道 oauth 配置信息',
start_text:
'按照[**自定义渠道配置流程**](https://www.coze.cn/docs/guides/isv_connector)配置并注册自定义渠道,支持 Bot 发布至自定义渠道',
schema_area: {
schema_list: [
{
type: 'string',
rules: [
{
required: true,
message: '跳转地址 必填',
},
],
placeholder: '请输入跳转地址',
name: 'redirect_url',
title: '跳转地址',
component: 'Input',
enums: [],
},
{
component: 'Input',
enums: [],
type: 'string',
rules: [
{
message: '获取 token 地址 必填',
required: true,
},
],
placeholder: '请输入获取 token 地址',
name: 'access_token_url',
title: '获取 token 地址',
},
{
component: 'Input',
enums: [],
type: 'string',
rules: [
{
message: '获取用户信息地址 必填',
required: true,
},
],
placeholder: '',
name: 'user_info_url',
title: '获取用户信息地址',
},
{
enums: [],
type: 'string',
rules: [
{
message: 'APP ID 必填',
required: true,
},
],
placeholder: '请输入APP ID',
name: 'app_id',
title: 'APP ID',
component: 'Input',
},
{
name: 'app_secret',
title: 'APP 密钥',
component: 'Input',
enums: [],
type: 'string',
rules: [
{
message: 'APP 密钥 必填',
required: true,
},
],
placeholder: '请输入APP 密钥',
},
{
component: 'InputNumber',
enums: [],
type: 'string',
rules: [],
placeholder: '',
name: 'input_number_test',
title: 'input_number_test',
},
{
name: 'select_test',
title: 'select_test',
component: 'Select',
enums: [
{
label: 'label_1',
value: 'value_1',
},
{
label: 'label_2',
value: 'value_2',
},
],
type: 'string',
rules: [],
placeholder: '',
},
{
placeholder: '',
name: 'radio_test',
title: 'radio_test',
component: 'Radio',
enums: [
{
label: 'label_1',
value: 'value_1',
},
{
label: 'label_2',
value: 'value_2',
},
],
type: 'string',
rules: [],
},
{
name: 'Checkbox_test',
title: 'Checkbox_test',
component: 'Checkbox',
enums: [
{
label: 'label_1',
value: 'value_1',
},
{
label: 'label_2',
value: 'value_2',
},
],
type: 'string',
rules: [],
placeholder: '',
},
{
enums: [],
type: 'string',
rules: [
{
pattern: '^\\S(.*\\S)?$',
message: '请检查输入前后是否有空格',
},
],
placeholder: '',
name: 'no_space_input',
title: '前后不能有空格',
component: 'Input',
},
{
name: 'max_ten_words',
title: '最大 10 个字符',
component: 'Input',
enums: [],
type: 'string',
rules: [
{
max: 10,
message: '最大 10 个字符',
},
],
placeholder: '',
},
],
title_text: '填写 oauth 配置信息',
description: '',
step_order: 1,
},
copy_link_area: {},
},
}),
UpdateOauthConfig: vi.fn().mockResolvedValue({ code: 0 }),
},
}));
describe('useOauthSettingModalController', () => {
it('useOauthSettingModalController fetch form config should be right', async () => {
const onOk = vi.fn();
const { result, waitForNextUpdate } = renderHook(() =>
useOauthSettingModalController(
{
target: 'oauth',
action: 'update',
},
onOk,
),
);
await waitForNextUpdate();
expect(result.current.isOauthConfigLoading).toBeFalsy();
expect(result.current.oauthFormItemConfigs.length).toBe(11);
expect(result.current.oauthModalTitle).toBe('填写 oauth 配置信息');
});
it('useOauthSettingModalController update action should be right', async () => {
const onOk = vi.fn();
const { result, waitForNextUpdate } = renderHook(() =>
useOauthSettingModalController(
{
target: 'oauth',
action: 'update',
},
onOk,
),
);
await waitForNextUpdate();
expect(result.current.isOauthConfigLoading).toBeFalsy();
expect(result.current.oauthFormItemConfigs.length).toBe(11);
expect(result.current.oauthModalTitle).toBe('填写 oauth 配置信息');
await result.current.doUpdate({});
expect(onOk).toBeCalled();
});
});

View File

@@ -0,0 +1,77 @@
/*
* 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 { vi, describe, it, expect, type Mock } from 'vitest';
import { renderHook } from '@testing-library/react-hooks';
import { userStoreService } from '@coze-studio/user-store';
import { useBotInfoStore } from '@coze-studio/bot-detail-store/bot-info';
import { useBotDetailIsReadonly } from '@coze-studio/bot-detail-store';
import { useMonetizeConfigReadonly } from '../../src/hook/use-monetize-config-readonly';
vi.mock('@coze-studio/user-store', () => ({
userStoreService: {
useUserInfo: vi.fn(),
},
}));
vi.mock('@coze-studio/bot-detail-store', () => ({
useBotDetailIsReadonly: vi.fn(),
}));
vi.mock('@coze-studio/bot-detail-store/bot-info', () => ({
useBotInfoStore: vi.fn(),
}));
describe('use monetize config readonly', () => {
// mock returned user id
(userStoreService.useUserInfo as unknown as Mock).mockReturnValue({
user_id_str: '114',
});
it('bot detail 只读 & 是作者本人 -> 只读', () => {
(useBotInfoStore as unknown as Mock).mockReturnValueOnce('114');
(useBotDetailIsReadonly as unknown as Mock).mockReturnValueOnce(true);
const {
result: { current: isReadonly },
} = renderHook(() => useMonetizeConfigReadonly());
expect(isReadonly).toBe(true);
});
it('bot detail 可编辑 & 是作者本人 -> 可编辑', () => {
(useBotInfoStore as unknown as Mock).mockReturnValueOnce('114');
(useBotDetailIsReadonly as unknown as Mock).mockReturnValueOnce(false);
const {
result: { current: isReadonly },
} = renderHook(() => useMonetizeConfigReadonly());
expect(isReadonly).toBe(false);
});
it('bot detail 只读 & 不是作者本人 -> 只读', () => {
(useBotInfoStore as unknown as Mock).mockReturnValueOnce('514');
(useBotDetailIsReadonly as unknown as Mock).mockReturnValueOnce(true);
const {
result: { current: isReadonly },
} = renderHook(() => useMonetizeConfigReadonly());
expect(isReadonly).toBe(true);
});
it('bot detail 可编辑 & 不是作者本人 -> 只读', () => {
(useBotInfoStore as unknown as Mock).mockReturnValueOnce('514');
(useBotDetailIsReadonly as unknown as Mock).mockReturnValueOnce(false);
const {
result: { current: isReadonly },
} = renderHook(() => useMonetizeConfigReadonly());
expect(isReadonly).toBe(true);
});
});

View 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 { vi } from 'vitest';
vi.mock('@coze-arch/logger', () => ({
useErrorHandler: vi.fn().mockReturnValue(vi.fn()),
logger: {
error: vi.fn(),
info: vi.fn(),
},
reporter: {
tracer: vi.fn().mockReturnValue(vi.fn()),
event: vi.fn(),
errorEvent: vi.fn(),
},
}));
vi.mock('@coze-arch/i18n', () => ({
I18n: {
t: (key, params = {}) => `Translated: ${key} ${JSON.stringify(params)}`,
},
}));
vi.mock('@coze-arch/bot-tea', () => ({
sendTeaEvent: vi.fn(),
EVENT_NAMES: {
use_mockset_front: 'use_mockset_front',
del_mockset_front: 'del_mockset_front',
},
ParamsTypeDefine: {},
PluginMockDataGenerateMode: {
MANUAL: 0, // 手动创建
RANDOM: 1, // 随机生成
LLM: 2,
},
}));
vi.mock('@coze-arch/bot-hooks', () => ({
SceneType: {
BOT__VIEW__WORKFLOW: 'botViewWorkflow',
/** bot 详情页查看 workflow或新建 workflow 但未发布,点击返回 */
WORKFLOW__BACK__BOT: 'workflowBackBot',
/** bot 详情页创建 workflow在 workflow 发布后返回 */
WORKFLOW_PUBLISHED__BACK__BOT: 'workflowPublishedBackBot',
/** bot 详情页进入 mock data 页面 */
BOT__TO__PLUGIN_MOCK_DATA: 'botToPluginMockData',
/** workflow 详情页进入 mock data 页面 */
WORKFLOW__TO__PLUGIN_MOCK_DATA: 'workflowToPluginMockData',
/** mock set 页进入 mock data 页面 */
PLUGIN_MOCK_SET__TO__PLUGIN_MOCK_DATA: 'pluginMockSetToPluginMockData',
/** bot 详情页进入 knowledge 页面 */
BOT__VIEW__KNOWLEDGE: 'botViewKnowledge',
/** knowledge 页面点击退出返回 bot 详情页(未点击添加) */
KNOWLEDGE__BACK__BOT: 'knowledgeBackBot',
/** knowledge 页面点击返回 bot 详情页,并添加到 bot */
KNOWLEDGE__ADD_TO__BOT: 'knowledgeAddToBot',
},
usePageJumpService: vi.fn().mockReturnValue({
jump: vi.fn(),
}),
}));
vi.mock('@coze-arch/bot-studio-store', () => ({
useSpaceStore: {
getState: vi.fn().mockReturnValue({
getSpaceId: vi.fn().mockReturnValue('spaceId'),
}),
},
}));
vi.mock('@coze-arch/report-events', () => ({
createReportEvent: vi.fn().mockReturnValue({
success: vi.fn(),
error: vi.fn(),
}),
REPORT_EVENTS: {
pluginIdeInitTrace: 'pluginIdeInitTrace',
pluginIdeInit: 'pluginIdeInit',
},
}));
vi.mock('@coze-arch/bot-error', () => ({
CustomError: vi.fn(),
}));
vi.mock('react-router-dom', () => ({
useNavigate: vi.fn().mockReturnValue(vi.fn()),
useParams: vi.fn().mockReturnValue({
space_id: 'space_id',
plugin_id: 'plugin_id',
}),
}));
vi.mock('@coze-arch/coze-design', () => ({
Toast: {
success: vi.fn(),
warning: vi.fn(),
},
withField: vi.fn(),
}));