mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-27 13:29:15 +07:00
🐛 fix: refresh sidebar after sendAsGroup and add E2E tests (#11450)
* fix actions * ✅ test: add E2E test for sendAsGroup sidebar refresh Add E2E test to verify that after creating a Group from the Home page input, the sidebar correctly displays the newly created Group when returning to the Home page. This test validates the fix for LOBE-3083 where the sidebar wasn't refreshing after creating a Group via sendAsGroup. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ✅ test: rename sendGroup to starter and add Agent test - Rename sendGroup.feature/steps to starter.feature/steps - Add E2E test for sendAsAgent sidebar refresh - Both Agent and Group creation now have E2E test coverage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
34
e2e/src/features/home/starter.feature
Normal file
34
e2e/src/features/home/starter.feature
Normal file
@@ -0,0 +1,34 @@
|
||||
@journey @home @starter
|
||||
Feature: Home 页面 Starter 快捷创建功能
|
||||
作为用户,我希望在 Home 页面可以通过 Starter 快捷创建 Agent 或 Group,返回首页后在侧边栏中看到它
|
||||
|
||||
Background:
|
||||
Given 用户已登录系统
|
||||
|
||||
# ============================================
|
||||
# 创建 Agent 后侧边栏刷新
|
||||
# ============================================
|
||||
|
||||
@HOME-STARTER-AGENT-001 @P0
|
||||
Scenario: 通过 Home 页面创建 Agent 后返回首页侧边栏应显示新创建的 Agent
|
||||
Given 用户在 Home 页面
|
||||
When 用户点击创建 Agent 按钮
|
||||
And 用户在输入框中输入 "E2E Test Agent"
|
||||
And 用户按 Enter 发送
|
||||
Then 页面应该跳转到 Agent 的 profile 页面
|
||||
When 用户返回 Home 页面
|
||||
Then 新创建的 Agent 应该在侧边栏中显示
|
||||
|
||||
# ============================================
|
||||
# 创建 Group 后侧边栏刷新
|
||||
# ============================================
|
||||
|
||||
@HOME-STARTER-GROUP-001 @P0
|
||||
Scenario: 通过 Home 页面创建 Group 后返回首页侧边栏应显示新创建的 Group
|
||||
Given 用户在 Home 页面
|
||||
When 用户点击创建 Group 按钮
|
||||
And 用户在输入框中输入 "E2E Test Group"
|
||||
And 用户按 Enter 发送
|
||||
Then 页面应该跳转到 Group 的 profile 页面
|
||||
When 用户返回 Home 页面
|
||||
Then 新创建的 Group 应该在侧边栏中显示
|
||||
216
e2e/src/steps/home/starter.steps.ts
Normal file
216
e2e/src/steps/home/starter.steps.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* Home Starter Steps
|
||||
*
|
||||
* Step definitions for Home page Starter E2E tests
|
||||
* - Create Agent from Home input
|
||||
* - Create Group from Home input
|
||||
* - Verify Agent/Group appears in sidebar after returning to Home
|
||||
*/
|
||||
import { Given, Then, When } from '@cucumber/cucumber';
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import { llmMockManager, presetResponses } from '../../mocks/llm';
|
||||
import { CustomWorld, WAIT_TIMEOUT } from '../../support/world';
|
||||
|
||||
// Store created IDs for verification
|
||||
let createdAgentId: string | null = null;
|
||||
let createdGroupId: string | null = null;
|
||||
|
||||
// ============================================
|
||||
// Given Steps
|
||||
// ============================================
|
||||
|
||||
Given('用户在 Home 页面', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 设置 LLM mock...');
|
||||
// Setup LLM mock before navigation (for agent/group builder message)
|
||||
llmMockManager.setResponse('E2E Test Agent', presetResponses.greeting);
|
||||
llmMockManager.setResponse('E2E Test Group', presetResponses.greeting);
|
||||
await llmMockManager.setup(this.page);
|
||||
|
||||
console.log(' 📍 Step: 导航到 Home 页面...');
|
||||
await this.page.goto('/');
|
||||
await this.page.waitForLoadState('networkidle', { timeout: 15_000 });
|
||||
await this.page.waitForTimeout(1000);
|
||||
|
||||
// Reset IDs for each test
|
||||
createdAgentId = null;
|
||||
createdGroupId = null;
|
||||
|
||||
console.log(' ✅ 已进入 Home 页面');
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// When Steps
|
||||
// ============================================
|
||||
|
||||
When('用户点击创建 Agent 按钮', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 点击创建 Agent 按钮...');
|
||||
|
||||
// Find the "Create Agent" button by text (supports both English and Chinese)
|
||||
const createAgentButton = this.page
|
||||
.getByRole('button', { name: /create agent|创建智能体/i })
|
||||
.first();
|
||||
|
||||
await expect(createAgentButton).toBeVisible({ timeout: WAIT_TIMEOUT });
|
||||
await createAgentButton.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
|
||||
console.log(' ✅ 已点击创建 Agent 按钮');
|
||||
});
|
||||
|
||||
When('用户点击创建 Group 按钮', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 点击创建 Group 按钮...');
|
||||
|
||||
// Find the "Create Group" button by text (supports both English and Chinese)
|
||||
const createGroupButton = this.page
|
||||
.getByRole('button', { name: /create group|创建群组/i })
|
||||
.first();
|
||||
|
||||
await expect(createGroupButton).toBeVisible({ timeout: WAIT_TIMEOUT });
|
||||
await createGroupButton.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
|
||||
console.log(' ✅ 已点击创建 Group 按钮');
|
||||
});
|
||||
|
||||
When('用户在输入框中输入 {string}', async function (this: CustomWorld, message: string) {
|
||||
console.log(` 📍 Step: 在输入框中输入 "${message}"...`);
|
||||
|
||||
// The chat input is a contenteditable editor, need to click first then type
|
||||
const chatInputContainer = this.page.locator('[data-testid="chat-input"]').first();
|
||||
|
||||
// If data-testid not found, try alternative selectors
|
||||
let inputFound = false;
|
||||
if ((await chatInputContainer.count()) > 0) {
|
||||
await chatInputContainer.click();
|
||||
inputFound = true;
|
||||
} else {
|
||||
// Try to find the editor by its contenteditable attribute
|
||||
const editor = this.page.locator('[contenteditable="true"]').first();
|
||||
if ((await editor.count()) > 0) {
|
||||
await editor.click();
|
||||
inputFound = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inputFound) {
|
||||
throw new Error('Could not find chat input');
|
||||
}
|
||||
|
||||
await this.page.waitForTimeout(300);
|
||||
await this.page.keyboard.type(message, { delay: 30 });
|
||||
|
||||
console.log(` ✅ 已输入 "${message}"`);
|
||||
});
|
||||
|
||||
When('用户按 Enter 发送', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 按 Enter 发送...');
|
||||
|
||||
// Listen for navigation to capture the agent/group ID
|
||||
const navigationPromise = this.page.waitForURL(/\/(agent|group)\/.*\/profile/, {
|
||||
timeout: 30_000,
|
||||
});
|
||||
|
||||
await this.page.keyboard.press('Enter');
|
||||
|
||||
// Wait for navigation to profile page
|
||||
await navigationPromise;
|
||||
await this.page.waitForLoadState('networkidle', { timeout: 15_000 });
|
||||
|
||||
// Extract agent/group ID from URL
|
||||
const currentUrl = this.page.url();
|
||||
|
||||
const agentMatch = currentUrl.match(/\/agent\/([^/]+)/);
|
||||
if (agentMatch) {
|
||||
createdAgentId = agentMatch[1];
|
||||
console.log(` 📍 Created agent ID: ${createdAgentId}`);
|
||||
}
|
||||
|
||||
const groupMatch = currentUrl.match(/\/group\/([^/]+)/);
|
||||
if (groupMatch) {
|
||||
createdGroupId = groupMatch[1];
|
||||
console.log(` 📍 Created group ID: ${createdGroupId}`);
|
||||
}
|
||||
|
||||
console.log(' ✅ 已发送消息');
|
||||
});
|
||||
|
||||
When('用户返回 Home 页面', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 返回 Home 页面...');
|
||||
|
||||
await this.page.goto('/');
|
||||
await this.page.waitForLoadState('networkidle', { timeout: 15_000 });
|
||||
await this.page.waitForTimeout(1000);
|
||||
|
||||
console.log(' ✅ 已返回 Home 页面');
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// Then Steps
|
||||
// ============================================
|
||||
|
||||
Then('页面应该跳转到 Agent 的 profile 页面', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 验证页面跳转到 Agent profile 页面...');
|
||||
|
||||
// Check current URL matches /agent/{id}/profile pattern
|
||||
const currentUrl = this.page.url();
|
||||
expect(currentUrl).toMatch(/\/agent\/[^/]+\/profile/);
|
||||
|
||||
console.log(' ✅ 已跳转到 Agent profile 页面');
|
||||
});
|
||||
|
||||
Then('页面应该跳转到 Group 的 profile 页面', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 验证页面跳转到 Group profile 页面...');
|
||||
|
||||
// Check current URL matches /group/{id}/profile pattern
|
||||
const currentUrl = this.page.url();
|
||||
expect(currentUrl).toMatch(/\/group\/[^/]+\/profile/);
|
||||
|
||||
console.log(' ✅ 已跳转到 Group profile 页面');
|
||||
});
|
||||
|
||||
Then('新创建的 Agent 应该在侧边栏中显示', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 验证 Agent 在侧边栏中显示...');
|
||||
|
||||
// Wait for sidebar to be visible and data to load
|
||||
await this.page.waitForTimeout(1500);
|
||||
|
||||
// Check if the agent appears in sidebar by its link (primary assertion)
|
||||
// This proves that refreshAgentList() was called and the sidebar was updated
|
||||
if (!createdAgentId) {
|
||||
throw new Error('Agent ID was not captured during creation');
|
||||
}
|
||||
|
||||
const agentLink = this.page.locator(`a[href="/agent/${createdAgentId}"]`).first();
|
||||
await expect(agentLink).toBeVisible({ timeout: WAIT_TIMEOUT });
|
||||
console.log(` ✅ 找到 Agent 链接: /agent/${createdAgentId}`);
|
||||
|
||||
// Get the aria-label or text content to verify it's the correct agent
|
||||
const ariaLabel = await agentLink.getAttribute('aria-label');
|
||||
console.log(` 📍 Agent aria-label: ${ariaLabel}`);
|
||||
|
||||
console.log(' ✅ Agent 已在侧边栏中显示');
|
||||
});
|
||||
|
||||
Then('新创建的 Group 应该在侧边栏中显示', async function (this: CustomWorld) {
|
||||
console.log(' 📍 Step: 验证 Group 在侧边栏中显示...');
|
||||
|
||||
// Wait for sidebar to be visible and data to load
|
||||
await this.page.waitForTimeout(1500);
|
||||
|
||||
// Check if the group appears in sidebar by its link (primary assertion)
|
||||
// This proves that refreshAgentList() was called and the sidebar was updated
|
||||
if (!createdGroupId) {
|
||||
throw new Error('Group ID was not captured during creation');
|
||||
}
|
||||
|
||||
const groupLink = this.page.locator(`a[href="/group/${createdGroupId}"]`).first();
|
||||
await expect(groupLink).toBeVisible({ timeout: WAIT_TIMEOUT });
|
||||
console.log(` ✅ 找到 Group 链接: /group/${createdGroupId}`);
|
||||
|
||||
// Get the aria-label or text content to verify it's the correct group
|
||||
const ariaLabel = await groupLink.getAttribute('aria-label');
|
||||
console.log(` 📍 Group aria-label: ${ariaLabel}`);
|
||||
|
||||
console.log(' ✅ Group 已在侧边栏中显示');
|
||||
});
|
||||
@@ -119,13 +119,16 @@ export const createHomeInputSlice: StateCreator<
|
||||
const groupStore = getChatGroupStoreState();
|
||||
await groupStore.loadGroups();
|
||||
|
||||
// 4. Navigate to Group profile page
|
||||
// 4. Refresh sidebar agent list
|
||||
get().refreshAgentList();
|
||||
|
||||
// 5. Navigate to Group profile page
|
||||
const { navigate } = get();
|
||||
if (navigate) {
|
||||
navigate(`/group/${group.id}/profile`);
|
||||
}
|
||||
|
||||
// 5. Update groupAgentBuilder's model config and send initial message
|
||||
// 6. Update groupAgentBuilder's model config and send initial message
|
||||
const groupAgentBuilderId = builtinAgentSelectors.groupAgentBuilderId(agentState);
|
||||
|
||||
if (groupAgentBuilderId) {
|
||||
@@ -141,7 +144,7 @@ export const createHomeInputSlice: StateCreator<
|
||||
});
|
||||
}
|
||||
|
||||
// 6. Clear mode
|
||||
// 7. Clear mode
|
||||
set({ inputActiveMode: null }, false, n('sendAsGroup/clearMode'));
|
||||
|
||||
return group.id;
|
||||
|
||||
Reference in New Issue
Block a user