@steerable/agent-shell-web 0.6.15 → 0.6.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -9
- package/src/brand.test.ts +0 -22
- package/src/components/AgentSidebar.test.tsx +0 -127
- package/src/components/ChatHeader.test.tsx +0 -230
- package/src/components/LocalLlmSettingsModal.test.tsx +0 -372
- package/src/components/chat/ApprovalModal.test.tsx +0 -83
- package/src/components/chat/AskUserModalHost.test.tsx +0 -176
- package/src/components/chat/AssistantMessage.test.tsx +0 -68
- package/src/components/chat/ChatInput.test.tsx +0 -609
- package/src/components/chat/ExecPolicyPicker.test.tsx +0 -26
- package/src/components/chat/Markdown.test.tsx +0 -170
- package/src/components/chat/ModelPicker.test.tsx +0 -149
- package/src/components/chat/SessionTreeModal.test.tsx +0 -161
- package/src/components/chat/TaskOutcomeCards.test.tsx +0 -97
- package/src/components/chat/TaskPanelModal.test.tsx +0 -198
- package/src/components/chat/TaskProcessPanel.test.tsx +0 -111
- package/src/components/chat/TurnProcessGroup.test.tsx +0 -161
- package/src/components/chat/elapsed.test.ts +0 -42
- package/src/components/chat/executed-actions-model.test.ts +0 -134
- package/src/components/chat/orchestration-children-model.test.ts +0 -115
- package/src/components/chat/turn-timeline.test.ts +0 -97
- package/src/components/chat/useChatTasks.test.ts +0 -136
- package/src/components/settings/AgentsSettingsPanel.test.tsx +0 -229
- package/src/components/settings/InsightsSettingsPanel.test.tsx +0 -132
- package/src/components/settings/ModelIdCombobox.test.tsx +0 -88
- package/src/components/settings/SecuritySettingsPanel.test.tsx +0 -122
- package/src/components/settings/SkillsSettingsPanel.test.tsx +0 -62
- package/src/components/settings/WebSearchSettingsPanel.test.tsx +0 -90
- package/src/components/settings/compat-flags-model.test.ts +0 -71
- package/src/components/settings/llm-vendors.test.ts +0 -171
- package/src/components/settings/model-capabilities.test.ts +0 -88
- package/src/components/settings/preset-choice-model.test.ts +0 -96
- package/src/lib/pending-first-message.test.ts +0 -17
- package/src/pages/SettingsPage.test.tsx +0 -102
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerable/agent-shell-web",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Steerable framework — product-neutral renderer SPA source for @steerable/agent-shell (React + Vite). Consumed by product web entry points via the `@/` alias (anchored at this package's src/) and the createProductViteConfig factory. Published as a source package (src/ + vite.base.ts), not a build artifact.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"src",
|
|
22
|
+
"!src/**/*.test.ts",
|
|
23
|
+
"!src/**/*.test.tsx",
|
|
22
24
|
"public",
|
|
23
25
|
"vite.base.ts",
|
|
24
26
|
"README.md"
|
|
@@ -28,6 +30,8 @@
|
|
|
28
30
|
"provenance": true
|
|
29
31
|
},
|
|
30
32
|
"dependencies": {
|
|
33
|
+
"@tailwindcss/vite": "^4.3.0",
|
|
34
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
31
35
|
"@xterm/addon-fit": "^0.11.0",
|
|
32
36
|
"@xterm/addon-web-links": "^0.12.0",
|
|
33
37
|
"@xterm/xterm": "^6.0.0",
|
|
@@ -39,21 +43,19 @@
|
|
|
39
43
|
"react-router-dom": "^6.30.0",
|
|
40
44
|
"rehype-raw": "^7.0.0",
|
|
41
45
|
"remark-gfm": "^4.0.1",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
46
|
+
"tailwindcss": "^4.3.0",
|
|
47
|
+
"vite": "^5.4.21",
|
|
48
|
+
"@steerable/agent-protocol": "0.6.16",
|
|
49
|
+
"@steerable/pack-sdk": "0.6.16",
|
|
50
|
+
"@steerable/agent-ui": "0.6.16"
|
|
44
51
|
},
|
|
45
52
|
"devDependencies": {
|
|
46
|
-
"@tailwindcss/vite": "^4.3.0",
|
|
47
53
|
"@testing-library/react": "^16.1.0",
|
|
48
54
|
"@types/react": "^18.3.12",
|
|
49
55
|
"@types/react-dom": "^18.3.1",
|
|
50
|
-
"@vitejs/plugin-react": "^4.7.0",
|
|
51
56
|
"happy-dom": "^15.11.7",
|
|
52
|
-
"tailwindcss": "^4.3.0",
|
|
53
57
|
"typescript": "^5.8.3",
|
|
54
|
-
"
|
|
55
|
-
"vitest": "^3.2.4",
|
|
56
|
-
"@steerable/pack-sdk": "0.6.15"
|
|
58
|
+
"vitest": "^3.2.4"
|
|
57
59
|
},
|
|
58
60
|
"scripts": {
|
|
59
61
|
"gen": "echo agent-shell-web",
|
package/src/brand.test.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { LOCAL_ASSISTANT_AGENT_ID, pickDefaultAgentId } from './brand';
|
|
3
|
-
|
|
4
|
-
describe('pickDefaultAgentId', () => {
|
|
5
|
-
const catalog = [
|
|
6
|
-
{ id: LOCAL_ASSISTANT_AGENT_ID },
|
|
7
|
-
{ id: 'data-analyst' },
|
|
8
|
-
{ id: 'all-round-assistant' },
|
|
9
|
-
];
|
|
10
|
-
|
|
11
|
-
it('returns null when the catalog is empty', () => {
|
|
12
|
-
expect(pickDefaultAgentId([])).toBeNull();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('prefers the build-time default (shell: 电脑操作员) even when it is not first', () => {
|
|
16
|
-
expect(pickDefaultAgentId(catalog)).toBe(LOCAL_ASSISTANT_AGENT_ID);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('falls back to the first visible agent when the build default is absent', () => {
|
|
20
|
-
expect(pickDefaultAgentId([{ id: 'writer' }, { id: 'reader' }])).toBe('writer');
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
|
2
|
-
import { MemoryRouter, Outlet, Route, Routes, useLocation } from 'react-router-dom';
|
|
3
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import type { UseChatsAndAgentsResult } from '@/hooks/useChatsAndAgents';
|
|
5
|
-
import type { LocalChat, LocalChatAgent } from '@/lib/local-api';
|
|
6
|
-
|
|
7
|
-
vi.mock('@/lib/electron-bridge', () => ({
|
|
8
|
-
isElectron: () => false,
|
|
9
|
-
getElectronBridge: () => null,
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
vi.mock('@/brand', () => ({
|
|
13
|
-
BRAND_NAME: '测试助手',
|
|
14
|
-
getBrandLogoUrl: () => 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"/>',
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
const { AgentSidebar } = await import('./AgentSidebar');
|
|
18
|
-
|
|
19
|
-
afterEach(() => cleanup());
|
|
20
|
-
|
|
21
|
-
const agent: LocalChatAgent = {
|
|
22
|
-
id: 'local-assistant',
|
|
23
|
-
slug: 'local-assistant',
|
|
24
|
-
name: '电脑操作员',
|
|
25
|
-
icon: null,
|
|
26
|
-
color: '#4f46e5',
|
|
27
|
-
description: null,
|
|
28
|
-
rolePrompt: null,
|
|
29
|
-
isBuiltin: true,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const existingChat: LocalChat = {
|
|
33
|
-
id: 'chat-with-content',
|
|
34
|
-
projectId: null,
|
|
35
|
-
userId: 'local',
|
|
36
|
-
title: '已经聊过的对话',
|
|
37
|
-
agentId: agent.id,
|
|
38
|
-
createdAt: '2026-01-01T00:00:00.000Z',
|
|
39
|
-
updatedAt: '2026-01-01T00:01:00.000Z',
|
|
40
|
-
isPinned: false,
|
|
41
|
-
systemPrompt: null,
|
|
42
|
-
pinnedRefs: null,
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
function LocationProbe() {
|
|
46
|
-
const loc = useLocation();
|
|
47
|
-
return (
|
|
48
|
-
<div data-testid="loc">
|
|
49
|
-
{loc.pathname}
|
|
50
|
-
{loc.search}
|
|
51
|
-
</div>
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function renderSidebar(
|
|
56
|
-
initialEntry: string,
|
|
57
|
-
createChat = vi.fn(),
|
|
58
|
-
) {
|
|
59
|
-
const data: UseChatsAndAgentsResult = {
|
|
60
|
-
chats: [existingChat],
|
|
61
|
-
agents: [agent],
|
|
62
|
-
isLoading: false,
|
|
63
|
-
error: null,
|
|
64
|
-
selectedAgentId: agent.id,
|
|
65
|
-
setSelectedAgentId: vi.fn(),
|
|
66
|
-
refreshChats: vi.fn(async () => {}),
|
|
67
|
-
refreshAgents: vi.fn(async () => {}),
|
|
68
|
-
createChat,
|
|
69
|
-
deleteChat: vi.fn(async () => true),
|
|
70
|
-
patchChatTitle: vi.fn(),
|
|
71
|
-
isLoadingMoreChats: false,
|
|
72
|
-
hasMoreChats: false,
|
|
73
|
-
loadMoreChats: vi.fn(async () => {}),
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
render(
|
|
77
|
-
<MemoryRouter initialEntries={[initialEntry]}>
|
|
78
|
-
<Routes>
|
|
79
|
-
<Route
|
|
80
|
-
element={
|
|
81
|
-
<>
|
|
82
|
-
<AgentSidebar
|
|
83
|
-
data={data}
|
|
84
|
-
rightPanel={null}
|
|
85
|
-
onToggleRightPanel={() => {}}
|
|
86
|
-
chatSlots={[]}
|
|
87
|
-
onCollapse={() => {}}
|
|
88
|
-
/>
|
|
89
|
-
<LocationProbe />
|
|
90
|
-
<Outlet />
|
|
91
|
-
</>
|
|
92
|
-
}
|
|
93
|
-
>
|
|
94
|
-
<Route path="/" element={<div />} />
|
|
95
|
-
<Route path="/agent" element={<div />} />
|
|
96
|
-
<Route path="/agent/:chatId" element={<div />} />
|
|
97
|
-
<Route path="/settings" element={<div />} />
|
|
98
|
-
</Route>
|
|
99
|
-
</Routes>
|
|
100
|
-
</MemoryRouter>,
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
return { createChat };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
describe('AgentSidebar 新对话不落库', () => {
|
|
107
|
-
it('打开落地页且不调用 createChat', () => {
|
|
108
|
-
const { createChat } = renderSidebar('/agent/chat-with-content');
|
|
109
|
-
fireEvent.click(screen.getByTestId('sidebar-new-chat'));
|
|
110
|
-
expect(createChat).not.toHaveBeenCalled();
|
|
111
|
-
expect(screen.getByTestId('loc').textContent).toBe('/agent');
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('连点两次也不会多出侧栏行', () => {
|
|
115
|
-
renderSidebar('/agent');
|
|
116
|
-
const before = screen.getAllByTestId('sidebar-chat-row').length;
|
|
117
|
-
fireEvent.click(screen.getByTestId('sidebar-new-chat'));
|
|
118
|
-
fireEvent.click(screen.getByTestId('sidebar-new-chat'));
|
|
119
|
-
expect(screen.getAllByTestId('sidebar-chat-row')).toHaveLength(before);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('打开智能体管理页', () => {
|
|
123
|
-
renderSidebar('/agent');
|
|
124
|
-
fireEvent.click(screen.getByTestId('sidebar-agent-settings'));
|
|
125
|
-
expect(screen.getByTestId('loc').textContent).toBe('/settings?section=agents');
|
|
126
|
-
});
|
|
127
|
-
});
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ChatHeader 任务角标契约:后台任务在面板关着的时候也要能被看见,
|
|
3
|
-
* 且只有一件事等着处理时点角标要能跳过任务列表这一层。
|
|
4
|
-
* - 无任务 → 按钮保持素净,不显示计数;
|
|
5
|
-
* - 有任务 → 按钮上色 + 计数,配色按「该不该现在看一眼」取最高优先级
|
|
6
|
-
* (运行中 > 待合并 > 失败 > 全部跑完);
|
|
7
|
-
* - 唯一失败任务 → 点角标直接开推理过程,不经弹层;
|
|
8
|
-
* - 唯一待合并任务 → 点角标开弹层并展开该行(合并/丢弃按钮在那儿);
|
|
9
|
-
* - 有任务在跑或多件待处理 → 仍然先给列表,用户得先挑。
|
|
10
|
-
* 列表本身的订阅与刷新归 useChatTasks,见 chat/useChatTasks.test.ts。
|
|
11
|
-
*/
|
|
12
|
-
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
13
|
-
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
14
|
-
import type { LocalChat, LocalTask } from '@/lib/local-api';
|
|
15
|
-
import { ChatHeader } from './ChatHeader';
|
|
16
|
-
|
|
17
|
-
afterEach(() => {
|
|
18
|
-
cleanup();
|
|
19
|
-
delete (window as { electron?: unknown }).electron;
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
const CHAT: LocalChat = {
|
|
23
|
-
id: 'chat_1',
|
|
24
|
-
projectId: null,
|
|
25
|
-
userId: 'u1',
|
|
26
|
-
title: '循环任务',
|
|
27
|
-
agentId: null,
|
|
28
|
-
createdAt: '2026-09-13T01:00:00.000Z',
|
|
29
|
-
updatedAt: '2026-09-13T01:00:00.000Z',
|
|
30
|
-
isPinned: false,
|
|
31
|
-
systemPrompt: null,
|
|
32
|
-
pinnedRefs: null,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
function makeTask(overrides: Partial<LocalTask> = {}): LocalTask {
|
|
36
|
-
return {
|
|
37
|
-
id: 'task-1',
|
|
38
|
-
chatId: 'chat_1',
|
|
39
|
-
task: '每 5 分钟问好',
|
|
40
|
-
status: 'completed',
|
|
41
|
-
answer: '你好',
|
|
42
|
-
error: null,
|
|
43
|
-
worktreePath: null,
|
|
44
|
-
worktreeBranch: null,
|
|
45
|
-
worktreeState: null,
|
|
46
|
-
createdAt: '2026-09-13T01:00:00.000Z',
|
|
47
|
-
updatedAt: '2026-09-13T01:01:00.000Z',
|
|
48
|
-
...overrides,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/** 弹层自带订阅:展开它的用例需要一个能应答 GET /tasks 的 bridge。 */
|
|
53
|
-
function installBridge(tasks: LocalTask[]) {
|
|
54
|
-
(window as { electron?: unknown }).electron = {
|
|
55
|
-
localBackend: {
|
|
56
|
-
request: vi.fn((input: { method: string; path: string }) => {
|
|
57
|
-
if (input.method === 'GET' && input.path.endsWith('/tasks')) {
|
|
58
|
-
return Promise.resolve({ tasks });
|
|
59
|
-
}
|
|
60
|
-
return Promise.reject(new Error(`unexpected request: ${input.path}`));
|
|
61
|
-
}),
|
|
62
|
-
},
|
|
63
|
-
onTaskUpdated: () => () => {},
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function taskButton() {
|
|
68
|
-
return screen.getByRole('button', { name: /后台任务/ });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
describe('ChatHeader 后台任务角标', () => {
|
|
72
|
-
it('没有任务时不显示计数', () => {
|
|
73
|
-
render(<ChatHeader chat={CHAT} agent={null} tasks={[]} />);
|
|
74
|
-
|
|
75
|
-
expect(taskButton().getAttribute('data-task-state')).toBeNull();
|
|
76
|
-
expect(taskButton().textContent).toBe('');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('有运行中的任务时显示运行中计数并标成 running', () => {
|
|
80
|
-
render(
|
|
81
|
-
<ChatHeader
|
|
82
|
-
chat={CHAT}
|
|
83
|
-
agent={null}
|
|
84
|
-
tasks={[
|
|
85
|
-
makeTask({ id: 'a', status: 'running' }),
|
|
86
|
-
makeTask({ id: 'b', status: 'running' }),
|
|
87
|
-
makeTask({ id: 'c', status: 'completed' }),
|
|
88
|
-
]}
|
|
89
|
-
/>,
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
expect(taskButton().getAttribute('data-task-state')).toBe('running');
|
|
93
|
-
expect(taskButton().textContent).toBe('2');
|
|
94
|
-
expect(taskButton().getAttribute('title')).toBe('共 3 个后台任务(2 个运行中)');
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('等依赖的任务也算在推进中', () => {
|
|
98
|
-
render(
|
|
99
|
-
<ChatHeader chat={CHAT} agent={null} tasks={[makeTask({ id: 'a', status: 'blocked' })]} />,
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
expect(taskButton().getAttribute('data-task-state')).toBe('running');
|
|
103
|
-
expect(taskButton().getAttribute('title')).toBe('共 1 个后台任务(1 个等依赖)');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('待合并的 worktree 任务优先于失败任务上色', () => {
|
|
107
|
-
render(
|
|
108
|
-
<ChatHeader
|
|
109
|
-
chat={CHAT}
|
|
110
|
-
agent={null}
|
|
111
|
-
tasks={[
|
|
112
|
-
makeTask({ id: 'a', status: 'completed', worktreeState: 'pending' }),
|
|
113
|
-
makeTask({ id: 'b', status: 'failed', error: '炸了' }),
|
|
114
|
-
]}
|
|
115
|
-
/>,
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
expect(taskButton().getAttribute('data-task-state')).toBe('review');
|
|
119
|
-
expect(taskButton().textContent).toBe('1');
|
|
120
|
-
expect(taskButton().getAttribute('title')).toBe('共 2 个后台任务(1 个待合并,1 个失败)');
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('只有失败任务时标成 failed', () => {
|
|
124
|
-
render(
|
|
125
|
-
<ChatHeader
|
|
126
|
-
chat={CHAT}
|
|
127
|
-
agent={null}
|
|
128
|
-
tasks={[makeTask({ id: 'a', status: 'failed', error: '炸了' })]}
|
|
129
|
-
/>,
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
expect(taskButton().getAttribute('data-task-state')).toBe('failed');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('全部跑完时显示总数,不再抢注意力', () => {
|
|
136
|
-
render(
|
|
137
|
-
<ChatHeader chat={CHAT} agent={null} tasks={[makeTask({ id: 'a' }), makeTask({ id: 'b' })]} />,
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
expect(taskButton().getAttribute('data-task-state')).toBe('idle');
|
|
141
|
-
expect(taskButton().textContent).toBe('2');
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe('ChatHeader 角标直达', () => {
|
|
146
|
-
it('唯一失败任务:点角标直接开推理过程,不开弹层', () => {
|
|
147
|
-
const onInspectTask = vi.fn();
|
|
148
|
-
render(
|
|
149
|
-
<ChatHeader
|
|
150
|
-
chat={CHAT}
|
|
151
|
-
agent={null}
|
|
152
|
-
onInspectTask={onInspectTask}
|
|
153
|
-
tasks={[makeTask({ id: 'a', status: 'failed', task: '跑测试', error: '炸了' })]}
|
|
154
|
-
/>,
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
expect(taskButton().getAttribute('data-task-shortcut')).toBe('process');
|
|
158
|
-
expect(taskButton().getAttribute('title')).toContain('点击查看推理过程');
|
|
159
|
-
|
|
160
|
-
fireEvent.click(taskButton());
|
|
161
|
-
|
|
162
|
-
expect(onInspectTask).toHaveBeenCalledWith({
|
|
163
|
-
id: 'a',
|
|
164
|
-
chatId: 'chat_1',
|
|
165
|
-
title: '跑测试',
|
|
166
|
-
});
|
|
167
|
-
expect(document.querySelector('[data-task-panel-modal]')).toBeNull();
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('唯一待合并任务:点角标开弹层并展开该行', async () => {
|
|
171
|
-
const task = makeTask({
|
|
172
|
-
id: 'a',
|
|
173
|
-
status: 'completed',
|
|
174
|
-
worktreeState: 'pending',
|
|
175
|
-
worktreePath: '/tmp/wt',
|
|
176
|
-
worktreeBranch: 'steerable/a',
|
|
177
|
-
});
|
|
178
|
-
installBridge([task]);
|
|
179
|
-
render(<ChatHeader chat={CHAT} agent={null} onInspectTask={vi.fn()} tasks={[task]} />);
|
|
180
|
-
|
|
181
|
-
expect(taskButton().getAttribute('data-task-shortcut')).toBe('expand');
|
|
182
|
-
expect(taskButton().getAttribute('title')).toContain('点击处理 worktree');
|
|
183
|
-
|
|
184
|
-
fireEvent.click(taskButton());
|
|
185
|
-
|
|
186
|
-
// 展开态的标志:worktree 的「合并到主仓」按钮直接可见,不用再点一次。
|
|
187
|
-
await waitFor(() => expect(document.querySelector('[data-task-merge]')).not.toBeNull());
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('有任务在跑时不直达——先给列表', () => {
|
|
191
|
-
const onInspectTask = vi.fn();
|
|
192
|
-
render(
|
|
193
|
-
<ChatHeader
|
|
194
|
-
chat={CHAT}
|
|
195
|
-
agent={null}
|
|
196
|
-
onInspectTask={onInspectTask}
|
|
197
|
-
tasks={[
|
|
198
|
-
makeTask({ id: 'a', status: 'failed', error: '炸了' }),
|
|
199
|
-
makeTask({ id: 'b', status: 'running' }),
|
|
200
|
-
]}
|
|
201
|
-
/>,
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
expect(taskButton().getAttribute('data-task-shortcut')).toBeNull();
|
|
205
|
-
expect(taskButton().getAttribute('title')).not.toContain('点击');
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('多件待处理时不直达——用户得先挑', () => {
|
|
209
|
-
const onInspectTask = vi.fn();
|
|
210
|
-
installBridge([]);
|
|
211
|
-
render(
|
|
212
|
-
<ChatHeader
|
|
213
|
-
chat={CHAT}
|
|
214
|
-
agent={null}
|
|
215
|
-
onInspectTask={onInspectTask}
|
|
216
|
-
tasks={[
|
|
217
|
-
makeTask({ id: 'a', status: 'failed', error: '炸了' }),
|
|
218
|
-
makeTask({ id: 'b', status: 'failed', error: '也炸了' }),
|
|
219
|
-
]}
|
|
220
|
-
/>,
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
expect(taskButton().getAttribute('data-task-shortcut')).toBeNull();
|
|
224
|
-
|
|
225
|
-
fireEvent.click(taskButton());
|
|
226
|
-
|
|
227
|
-
expect(onInspectTask).not.toHaveBeenCalled();
|
|
228
|
-
expect(document.querySelector('[data-task-panel-modal]')).not.toBeNull();
|
|
229
|
-
});
|
|
230
|
-
});
|