docs: 交付文档、维护指引与端到端回归
补齐 README、开发计划、维护交接与 E2E 用例,执行最终验收。
This commit is contained in:
1 parent
18732801b5
commit
6cd904d460
216 files changed
+37038
No files matched your search
@@ -0,0 +1,107 @@
|
||||
import { test, expect, expectUnchanged, publishWorkflow, AI_REPLY, type WorkflowGraph } from '@e2e'
|
||||
|
||||
// A published workflow runs instead of the model: scripted steps, no AI calls.
|
||||
|
||||
test.describe('workflows', () => {
|
||||
test('a one-message workflow greets once on load and answers each message once, without the AI @workflows', async ({ agent, adminApi, widget, llm }) => {
|
||||
await publishWorkflow(adminApi, agent.id, {
|
||||
nodes: [{ id: 'msg-1', node_type: 'message', name: 'Greeting', config: { message_text: 'Hello from our workflow.' } }],
|
||||
})
|
||||
|
||||
await widget.embed()
|
||||
await widget.open()
|
||||
await expect(widget.agentMessages).toHaveText(['Hello from our workflow.'], AI_REPLY)
|
||||
|
||||
await widget.send('hi')
|
||||
await expect(widget.agentMessages).toHaveText(['Hello from our workflow.', 'Hello from our workflow.'], AI_REPLY)
|
||||
await expectUnchanged('agent messages', () => widget.agentMessages.allTextContents())
|
||||
expect(await llm.requests(agent)).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('a workflow asks a question and branches on the answer @workflows', async ({ agent, adminApi, widget }) => {
|
||||
await publishWorkflow(adminApi, agent.id, {
|
||||
nodes: [
|
||||
{ id: 'ask', node_type: 'user_input', name: 'Ask Plan', config: { prompt_message: 'Which plan are you on? (pro/basic)', confirmation_message: 'Thanks — {{ask_plan_input}} it is.' } },
|
||||
{ id: 'is-pro', node_type: 'condition', name: 'Is Pro', config: { condition_expression: '{{ask_plan_input}} === pro' } },
|
||||
{ id: 'pro', node_type: 'message', name: 'Pro', config: { message_text: 'Pro customers get priority support.' } },
|
||||
{ id: 'basic', node_type: 'message', name: 'Basic', config: { message_text: 'Basic customers can upgrade any time.' } },
|
||||
],
|
||||
connections: [
|
||||
{ id: 'c1', source_node_id: 'ask', target_node_id: 'is-pro' },
|
||||
{ id: 'c2', source_node_id: 'is-pro', target_node_id: 'pro', label: 'true' },
|
||||
{ id: 'c3', source_node_id: 'is-pro', target_node_id: 'basic', label: 'false' },
|
||||
],
|
||||
})
|
||||
|
||||
await widget.embed()
|
||||
await widget.open()
|
||||
await expect(widget.lastAgentMessage()).toHaveText('Which plan are you on? (pro/basic)', AI_REPLY)
|
||||
|
||||
await widget.send('pro')
|
||||
await expect(widget.lastAgentMessage()).toHaveText('Pro customers get priority support.', AI_REPLY)
|
||||
await expect(widget.agentMessages).toContainText(['Thanks — pro it is.'])
|
||||
await expect(widget.agentMessages.filter({ hasText: 'Basic customers' })).toHaveCount(0)
|
||||
})
|
||||
|
||||
// Plan AND seats, built the way the dashboard's condition builder saves it.
|
||||
function planAndSeats(wired: Array<'true' | 'false'> = ['true', 'false']): WorkflowGraph {
|
||||
return {
|
||||
nodes: [
|
||||
{ id: 'ask-plan', node_type: 'user_input', name: 'Ask Plan', config: { prompt_message: 'Which plan are you on?' } },
|
||||
{ id: 'ask-seats', node_type: 'user_input', name: 'Ask Seats', config: { prompt_message: 'How many seats?' } },
|
||||
{
|
||||
id: 'big-pro', node_type: 'condition', name: 'Big Pro',
|
||||
config: {
|
||||
condition_groups: [{
|
||||
id: 'g1', logicalOperator: 'AND', rules: [
|
||||
{ id: 'r1', variable: 'ask_plan_input', operator: '===', value: 'pro' },
|
||||
{ id: 'r2', variable: 'ask_seats_input', operator: '>', value: '3' },
|
||||
],
|
||||
}],
|
||||
condition_expression: '({{ask_plan_input}} === "pro" AND {{ask_seats_input}} > 3)',
|
||||
},
|
||||
},
|
||||
{ id: 'priority', node_type: 'message', name: 'Priority', config: { message_text: 'You get a dedicated account manager.' } },
|
||||
{ id: 'standard', node_type: 'message', name: 'Standard', config: { message_text: 'Our team will get back to you.' } },
|
||||
],
|
||||
connections: [
|
||||
{ id: 'c1', source_node_id: 'ask-plan', target_node_id: 'ask-seats' },
|
||||
{ id: 'c2', source_node_id: 'ask-seats', target_node_id: 'big-pro' },
|
||||
...wired.map((branch) => ({ id: `c-${branch}`, source_node_id: 'big-pro', target_node_id: branch === 'true' ? 'priority' : 'standard', label: branch })),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
const answers = [
|
||||
{ plan: 'pro', seats: '5', reply: 'You get a dedicated account manager.', why: 'both rules hold' },
|
||||
{ plan: 'pro', seats: '2', reply: 'Our team will get back to you.', why: 'one rule fails' },
|
||||
{ plan: 'basic" OR "a" === "a', seats: '5', reply: 'Our team will get back to you.', why: 'an answer that looks like a condition is just text' },
|
||||
{ plan: 'pro', seats: 'lots', reply: 'Our team will get back to you.', why: 'a non-number is never "greater than"' },
|
||||
]
|
||||
for (const { plan, seats, reply, why } of answers) {
|
||||
test(`a combined condition routes on both answers: ${why} @workflows`, async ({ agent, adminApi, widget }) => {
|
||||
await publishWorkflow(adminApi, agent.id, planAndSeats())
|
||||
await widget.embed()
|
||||
await widget.open()
|
||||
await expect(widget.lastAgentMessage()).toHaveText('Which plan are you on?', AI_REPLY)
|
||||
await widget.send(plan)
|
||||
await expect(widget.lastAgentMessage()).toHaveText('How many seats?', AI_REPLY)
|
||||
await widget.send(seats)
|
||||
await expect(widget.lastAgentMessage()).toHaveText(reply, AI_REPLY)
|
||||
})
|
||||
}
|
||||
|
||||
test('a condition with no branch for its result stops instead of taking the other one @workflows', async ({ agent, adminApi, widget, llm }) => {
|
||||
await publishWorkflow(adminApi, agent.id, planAndSeats(['true']))
|
||||
await widget.embed()
|
||||
await widget.open()
|
||||
await expect(widget.lastAgentMessage()).toHaveText('Which plan are you on?', AI_REPLY)
|
||||
await widget.send('basic')
|
||||
await expect(widget.lastAgentMessage()).toHaveText('How many seats?', AI_REPLY)
|
||||
await widget.send('1')
|
||||
|
||||
await expectUnchanged('agent messages', () => widget.agentMessages.allTextContents())
|
||||
await expect(widget.agentMessages.filter({ hasText: 'dedicated account manager' })).toHaveCount(0)
|
||||
expect(await llm.requests(agent)).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user