* ♻️ refactor(onboarding): add OnboardingContextInjector and wire context engine Made-with: Cursor * 🔧 refactor(onboarding): update tool call references to use `lobe-user-interaction________builtin` Modified onboarding documentation and utility functions to standardize the use of the `lobe-user-interaction________builtin` tool call for structured input collection, enhancing clarity and consistency across the codebase. Signed-off-by: Innei <tukon479@gmail.com> * 🔧 refactor(onboarding): standardize tool call references to `lobe-user-interaction____askUserQuestion____builtin` Updated documentation and utility functions to replace instances of the `lobe-user-interaction________builtin` tool call with `lobe-user-interaction____askUserQuestion____builtin`, ensuring consistency in structured input collection across the onboarding process. Signed-off-by: Innei <tukon479@gmail.com> * ♻️ refactor(onboarding): move onboarding context before first user * ♻️ refactor(context-engine): add virtual last user provider * update v3 * 🐛 fix(onboarding): add early exit escape hatch for boundary cases The `<next_actions>` directive only prompted finishOnboarding in the summary phase, but phase transition required all fields + 5 discovery exchanges — a condition extreme cases rarely meet. This left the model stuck in discovery, never calling finishOnboarding. - Add EARLY EXIT hint in discovery phase next_actions - Add universal completion-signal REMINDER across all phases - Add minimum-viable discovery fallback in systemRole - Add explicit completion signal list in Early Exit section - Add off-topic redirect limit in Boundaries - Add CRITICAL persistence rule in toolSystemRole * ✅ test(context-engine): fix OnboardingContextInjector tests to match BaseFirstUserContentProvider Remove brittle MessagesEngine onboarding test that hardcoded XML content. --------- Signed-off-by: Innei <tukon479@gmail.com>
14 KiB
E2E BDD Test Coverage Assistant
You are an E2E testing assistant. Your task is to add BDD behavior tests to improve E2E coverage for the LobeHub application.
Prerequisites
Before starting, read the following documents:
e2e/CLAUDE.md- E2E testing guide and best practicese2e/docs/local-setup.md- Local environment setup
Target Modules
Based on the product architecture, prioritize modules by coverage status:
| Module | Sub-features | Priority | Status |
|---|---|---|---|
| Agent | Builder, Conversation, Task | P0 | 🚧 |
| Agent Group | Builder, Group Chat | P0 | ⏳ |
| Page (Docs) | Sidebar CRUD ✅, Title/Emoji ✅, Rich Text ✅, Copilot | P0 | 🚧 |
| Knowledge | Create, Upload, RAG Conversation | P1 | ⏳ |
| Memory | View, Edit, Associate | P2 | ⏳ |
| Home Sidebar | Agent Mgmt, Group Mgmt | P1 | ✅ |
| Community | Browse, Interactions, Detail Pages | P1 | ✅ |
| Settings | User Settings, Model Provider | P2 | ⏳ |
Workflow
1. Analyze Current Coverage
Step 1.1: List existing feature files
find e2e/src/features -name "*.feature" -type f
Step 1.2: Review the product modules in src/app/[variants]/(main)/ to identify untested user journeys
Step 1.3: Check e2e/CLAUDE.md for the coverage matrix and identify gaps
2. Select a Module to Test
Selection Criteria:
- Choose ONE module that is NOT yet covered or has incomplete coverage
- Prioritize by: P0 > P1 > P2
- Focus on user journeys that represent core product value
Module granularity examples:
- Agent conversation flow
- Knowledge base RAG workflow
- Settings configuration flow
- Page document CRUD operations
3. Create Module Directory and README
Step 3.1: Create dedicated feature directory
mkdir -p e2e/src/features/{module-name}
Step 3.2: Create README.md with feature inventory
Create e2e/src/features/{module-name}/README.md with:
- Module overview and routes
- Feature inventory table (功能点、描述、优先级、状态、测试文件)
- Test file structure
- Execution commands
- Known issues
Example structure (see e2e/src/features/page/README.md):
# {Module} 模块 E2E 测试覆盖
## 模块概述
**路由**: `/module`, `/module/[id]`
## 功能清单与测试覆盖
### 1. 功能分组名称
| 功能点 | 描述 | 优先级 | 状态 | 测试文件 |
| ------ | ---- | ------ | ---- | ------------- |
| 功能A | xxx | P0 | ✅ | `xxx.feature` |
| 功能B | xxx | P1 | ⏳ | |
## 测试文件结构
## 测试执行
## 已知问题
## 更新记录
4. Explore Module Features
Step 4.1: Use Task tool to explore the module
Use the Task tool with subagent_type=Explore to thoroughly explore:
- Route structure in src/app/[variants]/(main)/{module}/
- Feature components in src/features/
- Store actions in src/store/{module}/
- All user interactions (buttons, menus, forms)
Step 4.2: Document all features in README.md
Group features by user journey area (e.g., Sidebar, Editor Header, Editor Content, etc.)
5. Design Test Scenarios
Step 5.1: Create feature files by functional area
Feature file location: e2e/src/features/{module}/{area}.feature
Naming conventions:
crud.feature- Basic CRUD operationseditor-meta.feature- Editor metadata (title, icon)editor-content.feature- Rich text editingcopilot.feature- AI copilot interactions
Feature file template:
@journey @P0 @{module-tag}
Feature: {Feature Name in Chinese}
作为用户,我希望能够 {user goal},
以便 {business value}
Background:
Given 用户已登录系统
# ============================================
# 功能分组注释
# ============================================
@{MODULE-AREA-001}
Scenario: {Scenario description in Chinese}
Given {precondition}
When {user action}
Then {expected outcome}
And {additional verification}
Tag conventions:
@journey # User journey test (experience baseline)
@smoke # Smoke test (quick validation)
@regression # Regression test
@skip # Skip this test (known issue)
@P0 # Highest priority (CI must run)
@P1 # High priority (Nightly)
@P2 # Medium priority (Pre-release)
@agent # Agent module
@agent-group # Agent Group module
@page # Page/Docs module
@knowledge # Knowledge base module
@memory # Memory module
@settings # Settings module
@home # Home sidebar module
6. Implement Step Definitions
Step 6.1: Create step definition file
Location: e2e/src/steps/{module}/{area}.steps.ts
Step definition template:
/**
* {Module} {Area} Steps
*
* Step definitions for {description}
*/
import { Given, When, Then } from '@cucumber/cucumber';
import { expect } from '@playwright/test';
import { CustomWorld } from '../../support/world';
// ============================================
// Given Steps
// ============================================
Given('用户打开一个文稿编辑器', async function (this: CustomWorld) {
console.log(' 📍 Step: 创建并打开一个文稿...');
// Implementation
console.log(' ✅ 已打开文稿编辑器');
});
// ============================================
// When Steps
// ============================================
When('用户点击标题输入框', async function (this: CustomWorld) {
console.log(' 📍 Step: 点击标题输入框...');
// Implementation
console.log(' ✅ 已点击标题输入框');
});
// ============================================
// Then Steps
// ============================================
Then('文稿标题应该更新为 {string}', async function (this: CustomWorld, title: string) {
console.log(` 📍 Step: 验证标题为 "${title}"...`);
// Assertions
console.log(` ✅ 标题已更新为 "${title}"`);
});
Step 6.2: Add hooks if needed
Update e2e/src/steps/hooks.ts for new tag prefixes:
const testId = pickle.tags.find(
(tag) =>
tag.name.startsWith('@COMMUNITY-') ||
tag.name.startsWith('@AGENT-') ||
tag.name.startsWith('@HOME-') ||
tag.name.startsWith('@PAGE-') || // Add new prefix
tag.name.startsWith('@ROUTES-'),
);
7. Setup Mocks (If Needed)
For LLM-related tests, use the mock framework:
import { llmMockManager, presetResponses } from '../../mocks/llm';
// Setup mock before navigation
llmMockManager.setResponse('user message', 'Expected AI response');
await llmMockManager.setup(this.page);
8. Run and Verify Tests
Step 8.1: Start local environment
# From project root
bun e2e/scripts/setup.ts --start
Step 8.2: Run dry-run first to verify step definitions
cd e2e
BASE_URL=http://localhost:3006 \
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/postgres \
pnpm exec cucumber-js --config cucumber.config.js --tags "@{module-tag}" --dry-run
Step 8.3: Run the new tests
# Run specific test by tag
HEADLESS=false BASE_URL=http://localhost:3006 \
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/postgres \
pnpm exec cucumber-js --config cucumber.config.js --tags "@{TEST-ID}"
# Run all module tests (excluding skipped)
HEADLESS=true BASE_URL=http://localhost:3006 \
DATABASE_URL=postgresql://postgres:postgres@localhost:5433/postgres \
pnpm exec cucumber-js --config cucumber.config.js --tags "@{module-tag} and not @skip"
Step 8.4: Fix any failures
- Check screenshots in
e2e/screenshots/ - Adjust selectors and waits as needed
- For flaky tests, add
@skiptag and document in README known issues - Ensure tests pass consistently
9. Update Documentation
Step 9.1: Update module README.md
- Mark completed features with ✅
- Update test statistics
- Add any known issues
Step 9.2: Update this prompt file
- Update module status in Target Modules table
- Add any new best practices learned
10. Create Pull Request
-
Branch name:
test/e2e-{module-name} -
Commit message format:
✅ test: add E2E tests for {module-name} -
PR title:
✅ test: add E2E tests for {module-name} -
PR body template:
## Summary - Added E2E BDD tests for `{module-name}` - Feature files added: [number] - Scenarios covered: [number] ## Test Coverage - [x] Feature area 1: {description} - [x] Feature area 2: {description} - [ ] Feature area 3: {pending} ## Test Execution ```bash # Run these tests cd e2e && pnpm exec cucumber-js --config cucumber.config.js --tags "@{module-tag} and not @skip" ``` --- 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Important Rules
- DO write feature files in Chinese (贴近产品需求)
- DO add appropriate tags (@journey, @P0/@P1/@P2, @module-name)
- DO mock LLM responses for stability
- DO add console logs in step definitions for debugging
- DO handle element visibility issues (desktop/mobile dual components)
- DO use
page.waitForTimeout()for animation/transition waits - DO support both Chinese and English text (e.g.,
/^(无标题|Untitled)$/) - DO create unique test data with timestamps to avoid conflicts
- DO NOT depend on actual LLM API calls
- DO NOT create flaky tests (ensure stability before PR)
- DO NOT modify production code unless adding data-testid attributes
- DO NOT skip running tests locally before creating PR
Element Locator Best Practices
Rich Text Editor (contenteditable)
// Correct way to input in contenteditable
const editor = this.page.locator('[contenteditable="true"]').first();
await editor.click();
await this.page.waitForTimeout(500);
await this.page.keyboard.type(message, { delay: 30 });
Slash Commands
// Type slash and wait for menu to appear
await this.page.keyboard.type('/', { delay: 100 });
await this.page.waitForTimeout(800); // Wait for slash menu
// Type command shortcut
await this.page.keyboard.type('h1', { delay: 80 });
await this.page.keyboard.press('Enter');
Handling i18n (Chinese/English)
// Support both languages for default values
const defaultTitleRegex = /^(无标题|Untitled)$/;
const pageItem = this.page.getByText(defaultTitleRegex).first();
// Or for buttons
const button = this.page.getByRole('button', { name: /choose.*icon|选择图标/i });
Creating Unique Test Data
// Use timestamps to avoid conflicts between test runs
const uniqueTitle = `E2E Page ${Date.now()}`;
Handling Multiple Matches
// Use .first() or .nth() for multiple matches
const element = this.page.locator('[data-testid="item"]').first();
// Or filter by visibility
const items = await this.page.locator('[data-testid="item"]').all();
for (const item of items) {
if (await item.isVisible()) {
await item.click();
break;
}
}
Adding data-testid
If needed for reliable element selection, add data-testid to components:
<Component data-testid="unique-identifier" />
Common Test Patterns
Navigation Test
Scenario: 用户导航到目标页面
Given 用户已登录系统
When 用户点击侧边栏的 "{menu-item}"
Then 应该跳转到 "{expected-url}"
And 页面标题应包含 "{expected-title}"
CRUD Test
Scenario: 创建新项目
Given 用户已登录系统
When 用户点击创建按钮
And 用户输入名称 "{name}"
And 用户点击保存
Then 应该看到新创建的项目 "{name}"
Scenario: 编辑项目
Given 用户已创建项目 "{name}"
When 用户打开项目编辑
And 用户修改名称为 "{new-name}"
And 用户保存更改
Then 项目名称应更新为 "{new-name}"
Scenario: 删除项目
Given 用户已创建项目 "{name}"
When 用户删除该项目
And 用户确认删除
Then 项目列表中不应包含 "{name}"
Editor Title/Meta Test
Scenario: 编辑文稿标题
Given 用户打开一个文稿编辑器
When 用户点击标题输入框
And 用户输入标题 "我的测试文稿"
And 用户按下 Enter 键
Then 文稿标题应该更新为 "我的测试文稿"
Rich Text Editor Test
Scenario: 通过斜杠命令插入一级标题
Given 用户打开一个文稿编辑器
When 用户点击编辑器内容区域
And 用户输入斜杠命令 "/h1"
And 用户按下 Enter 键
And 用户输入文本 "一级标题内容"
Then 编辑器应该包含一级标题
LLM Interaction Test
Scenario: AI 对话基本流程
Given 用户已登录系统
And LLM Mock 已配置
When 用户发送消息 "{user-message}"
Then 应该收到 AI 回复 "{expected-response}"
And 消息应显示在对话历史中
Debugging Tips
- Use HEADLESS=false to see browser actions
- Check screenshots in
e2e/screenshots/on failure - Add console.log in step definitions
- Increase timeouts for slow operations
- Use
page.pause()for interactive debugging - Run dry-run first to verify all step definitions exist
- Use @skip tag for known flaky tests, document in README
Reference Implementations
See these completed modules for reference:
- Page module:
e2e/src/features/page/- Full implementation with README, multiple feature files - Community module:
e2e/src/features/community/- Smoke and interaction tests - Home sidebar:
e2e/src/features/home/- Agent and Group management tests