@lobehub/lobehub 2.0.0-next.271 → 2.0.0-next.272

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/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 2.0.0-next.272](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.271...v2.0.0-next.272)
6
+
7
+ <sup>Released on **2026-01-13**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Refresh sidebar after sendAsGroup and add E2E tests.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Refresh sidebar after sendAsGroup and add E2E tests, closes [#11450](https://github.com/lobehub/lobe-chat/issues/11450) ([8376a80](https://github.com/lobehub/lobe-chat/commit/8376a80))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ## [Version 2.0.0-next.271](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.270...v2.0.0-next.271)
6
31
 
7
32
  <sup>Released on **2026-01-12**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Refresh sidebar after sendAsGroup and add E2E tests."
6
+ ]
7
+ },
8
+ "date": "2026-01-13",
9
+ "version": "2.0.0-next.272"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "features": [
@@ -0,0 +1,34 @@
1
+ @journey @home @starter
2
+ Feature: Home 页面 Starter 快捷创建功能
3
+ 作为用户,我希望在 Home 页面可以通过 Starter 快捷创建 Agent 或 Group,返回首页后在侧边栏中看到它
4
+
5
+ Background:
6
+ Given 用户已登录系统
7
+
8
+ # ============================================
9
+ # 创建 Agent 后侧边栏刷新
10
+ # ============================================
11
+
12
+ @HOME-STARTER-AGENT-001 @P0
13
+ Scenario: 通过 Home 页面创建 Agent 后返回首页侧边栏应显示新创建的 Agent
14
+ Given 用户在 Home 页面
15
+ When 用户点击创建 Agent 按钮
16
+ And 用户在输入框中输入 "E2E Test Agent"
17
+ And 用户按 Enter 发送
18
+ Then 页面应该跳转到 Agent 的 profile 页面
19
+ When 用户返回 Home 页面
20
+ Then 新创建的 Agent 应该在侧边栏中显示
21
+
22
+ # ============================================
23
+ # 创建 Group 后侧边栏刷新
24
+ # ============================================
25
+
26
+ @HOME-STARTER-GROUP-001 @P0
27
+ Scenario: 通过 Home 页面创建 Group 后返回首页侧边栏应显示新创建的 Group
28
+ Given 用户在 Home 页面
29
+ When 用户点击创建 Group 按钮
30
+ And 用户在输入框中输入 "E2E Test Group"
31
+ And 用户按 Enter 发送
32
+ Then 页面应该跳转到 Group 的 profile 页面
33
+ When 用户返回 Home 页面
34
+ Then 新创建的 Group 应该在侧边栏中显示
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Home Starter Steps
3
+ *
4
+ * Step definitions for Home page Starter E2E tests
5
+ * - Create Agent from Home input
6
+ * - Create Group from Home input
7
+ * - Verify Agent/Group appears in sidebar after returning to Home
8
+ */
9
+ import { Given, Then, When } from '@cucumber/cucumber';
10
+ import { expect } from '@playwright/test';
11
+
12
+ import { llmMockManager, presetResponses } from '../../mocks/llm';
13
+ import { CustomWorld, WAIT_TIMEOUT } from '../../support/world';
14
+
15
+ // Store created IDs for verification
16
+ let createdAgentId: string | null = null;
17
+ let createdGroupId: string | null = null;
18
+
19
+ // ============================================
20
+ // Given Steps
21
+ // ============================================
22
+
23
+ Given('用户在 Home 页面', async function (this: CustomWorld) {
24
+ console.log(' 📍 Step: 设置 LLM mock...');
25
+ // Setup LLM mock before navigation (for agent/group builder message)
26
+ llmMockManager.setResponse('E2E Test Agent', presetResponses.greeting);
27
+ llmMockManager.setResponse('E2E Test Group', presetResponses.greeting);
28
+ await llmMockManager.setup(this.page);
29
+
30
+ console.log(' 📍 Step: 导航到 Home 页面...');
31
+ await this.page.goto('/');
32
+ await this.page.waitForLoadState('networkidle', { timeout: 15_000 });
33
+ await this.page.waitForTimeout(1000);
34
+
35
+ // Reset IDs for each test
36
+ createdAgentId = null;
37
+ createdGroupId = null;
38
+
39
+ console.log(' ✅ 已进入 Home 页面');
40
+ });
41
+
42
+ // ============================================
43
+ // When Steps
44
+ // ============================================
45
+
46
+ When('用户点击创建 Agent 按钮', async function (this: CustomWorld) {
47
+ console.log(' 📍 Step: 点击创建 Agent 按钮...');
48
+
49
+ // Find the "Create Agent" button by text (supports both English and Chinese)
50
+ const createAgentButton = this.page
51
+ .getByRole('button', { name: /create agent|创建智能体/i })
52
+ .first();
53
+
54
+ await expect(createAgentButton).toBeVisible({ timeout: WAIT_TIMEOUT });
55
+ await createAgentButton.click();
56
+ await this.page.waitForTimeout(500);
57
+
58
+ console.log(' ✅ 已点击创建 Agent 按钮');
59
+ });
60
+
61
+ When('用户点击创建 Group 按钮', async function (this: CustomWorld) {
62
+ console.log(' 📍 Step: 点击创建 Group 按钮...');
63
+
64
+ // Find the "Create Group" button by text (supports both English and Chinese)
65
+ const createGroupButton = this.page
66
+ .getByRole('button', { name: /create group|创建群组/i })
67
+ .first();
68
+
69
+ await expect(createGroupButton).toBeVisible({ timeout: WAIT_TIMEOUT });
70
+ await createGroupButton.click();
71
+ await this.page.waitForTimeout(500);
72
+
73
+ console.log(' ✅ 已点击创建 Group 按钮');
74
+ });
75
+
76
+ When('用户在输入框中输入 {string}', async function (this: CustomWorld, message: string) {
77
+ console.log(` 📍 Step: 在输入框中输入 "${message}"...`);
78
+
79
+ // The chat input is a contenteditable editor, need to click first then type
80
+ const chatInputContainer = this.page.locator('[data-testid="chat-input"]').first();
81
+
82
+ // If data-testid not found, try alternative selectors
83
+ let inputFound = false;
84
+ if ((await chatInputContainer.count()) > 0) {
85
+ await chatInputContainer.click();
86
+ inputFound = true;
87
+ } else {
88
+ // Try to find the editor by its contenteditable attribute
89
+ const editor = this.page.locator('[contenteditable="true"]').first();
90
+ if ((await editor.count()) > 0) {
91
+ await editor.click();
92
+ inputFound = true;
93
+ }
94
+ }
95
+
96
+ if (!inputFound) {
97
+ throw new Error('Could not find chat input');
98
+ }
99
+
100
+ await this.page.waitForTimeout(300);
101
+ await this.page.keyboard.type(message, { delay: 30 });
102
+
103
+ console.log(` ✅ 已输入 "${message}"`);
104
+ });
105
+
106
+ When('用户按 Enter 发送', async function (this: CustomWorld) {
107
+ console.log(' 📍 Step: 按 Enter 发送...');
108
+
109
+ // Listen for navigation to capture the agent/group ID
110
+ const navigationPromise = this.page.waitForURL(/\/(agent|group)\/.*\/profile/, {
111
+ timeout: 30_000,
112
+ });
113
+
114
+ await this.page.keyboard.press('Enter');
115
+
116
+ // Wait for navigation to profile page
117
+ await navigationPromise;
118
+ await this.page.waitForLoadState('networkidle', { timeout: 15_000 });
119
+
120
+ // Extract agent/group ID from URL
121
+ const currentUrl = this.page.url();
122
+
123
+ const agentMatch = currentUrl.match(/\/agent\/([^/]+)/);
124
+ if (agentMatch) {
125
+ createdAgentId = agentMatch[1];
126
+ console.log(` 📍 Created agent ID: ${createdAgentId}`);
127
+ }
128
+
129
+ const groupMatch = currentUrl.match(/\/group\/([^/]+)/);
130
+ if (groupMatch) {
131
+ createdGroupId = groupMatch[1];
132
+ console.log(` 📍 Created group ID: ${createdGroupId}`);
133
+ }
134
+
135
+ console.log(' ✅ 已发送消息');
136
+ });
137
+
138
+ When('用户返回 Home 页面', async function (this: CustomWorld) {
139
+ console.log(' 📍 Step: 返回 Home 页面...');
140
+
141
+ await this.page.goto('/');
142
+ await this.page.waitForLoadState('networkidle', { timeout: 15_000 });
143
+ await this.page.waitForTimeout(1000);
144
+
145
+ console.log(' ✅ 已返回 Home 页面');
146
+ });
147
+
148
+ // ============================================
149
+ // Then Steps
150
+ // ============================================
151
+
152
+ Then('页面应该跳转到 Agent 的 profile 页面', async function (this: CustomWorld) {
153
+ console.log(' 📍 Step: 验证页面跳转到 Agent profile 页面...');
154
+
155
+ // Check current URL matches /agent/{id}/profile pattern
156
+ const currentUrl = this.page.url();
157
+ expect(currentUrl).toMatch(/\/agent\/[^/]+\/profile/);
158
+
159
+ console.log(' ✅ 已跳转到 Agent profile 页面');
160
+ });
161
+
162
+ Then('页面应该跳转到 Group 的 profile 页面', async function (this: CustomWorld) {
163
+ console.log(' 📍 Step: 验证页面跳转到 Group profile 页面...');
164
+
165
+ // Check current URL matches /group/{id}/profile pattern
166
+ const currentUrl = this.page.url();
167
+ expect(currentUrl).toMatch(/\/group\/[^/]+\/profile/);
168
+
169
+ console.log(' ✅ 已跳转到 Group profile 页面');
170
+ });
171
+
172
+ Then('新创建的 Agent 应该在侧边栏中显示', async function (this: CustomWorld) {
173
+ console.log(' 📍 Step: 验证 Agent 在侧边栏中显示...');
174
+
175
+ // Wait for sidebar to be visible and data to load
176
+ await this.page.waitForTimeout(1500);
177
+
178
+ // Check if the agent appears in sidebar by its link (primary assertion)
179
+ // This proves that refreshAgentList() was called and the sidebar was updated
180
+ if (!createdAgentId) {
181
+ throw new Error('Agent ID was not captured during creation');
182
+ }
183
+
184
+ const agentLink = this.page.locator(`a[href="/agent/${createdAgentId}"]`).first();
185
+ await expect(agentLink).toBeVisible({ timeout: WAIT_TIMEOUT });
186
+ console.log(` ✅ 找到 Agent 链接: /agent/${createdAgentId}`);
187
+
188
+ // Get the aria-label or text content to verify it's the correct agent
189
+ const ariaLabel = await agentLink.getAttribute('aria-label');
190
+ console.log(` 📍 Agent aria-label: ${ariaLabel}`);
191
+
192
+ console.log(' ✅ Agent 已在侧边栏中显示');
193
+ });
194
+
195
+ Then('新创建的 Group 应该在侧边栏中显示', async function (this: CustomWorld) {
196
+ console.log(' 📍 Step: 验证 Group 在侧边栏中显示...');
197
+
198
+ // Wait for sidebar to be visible and data to load
199
+ await this.page.waitForTimeout(1500);
200
+
201
+ // Check if the group appears in sidebar by its link (primary assertion)
202
+ // This proves that refreshAgentList() was called and the sidebar was updated
203
+ if (!createdGroupId) {
204
+ throw new Error('Group ID was not captured during creation');
205
+ }
206
+
207
+ const groupLink = this.page.locator(`a[href="/group/${createdGroupId}"]`).first();
208
+ await expect(groupLink).toBeVisible({ timeout: WAIT_TIMEOUT });
209
+ console.log(` ✅ 找到 Group 链接: /group/${createdGroupId}`);
210
+
211
+ // Get the aria-label or text content to verify it's the correct group
212
+ const ariaLabel = await groupLink.getAttribute('aria-label');
213
+ console.log(` 📍 Group aria-label: ${ariaLabel}`);
214
+
215
+ console.log(' ✅ Group 已在侧边栏中显示');
216
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/lobehub",
3
- "version": "2.0.0-next.271",
3
+ "version": "2.0.0-next.272",
4
4
  "description": "LobeHub - an open-source,comprehensive AI Agent framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -119,13 +119,16 @@ export const createHomeInputSlice: StateCreator<
119
119
  const groupStore = getChatGroupStoreState();
120
120
  await groupStore.loadGroups();
121
121
 
122
- // 4. Navigate to Group profile page
122
+ // 4. Refresh sidebar agent list
123
+ get().refreshAgentList();
124
+
125
+ // 5. Navigate to Group profile page
123
126
  const { navigate } = get();
124
127
  if (navigate) {
125
128
  navigate(`/group/${group.id}/profile`);
126
129
  }
127
130
 
128
- // 5. Update groupAgentBuilder's model config and send initial message
131
+ // 6. Update groupAgentBuilder's model config and send initial message
129
132
  const groupAgentBuilderId = builtinAgentSelectors.groupAgentBuilderId(agentState);
130
133
 
131
134
  if (groupAgentBuilderId) {
@@ -141,7 +144,7 @@ export const createHomeInputSlice: StateCreator<
141
144
  });
142
145
  }
143
146
 
144
- // 6. Clear mode
147
+ // 7. Clear mode
145
148
  set({ inputActiveMode: null }, false, n('sendAsGroup/clearMode'));
146
149
 
147
150
  return group.id;