test(advisor): E2E for the Clarity-Gate chain (Playwright, stubbed endpoint)
Per the "always write E2E" rule: drives the floating advisor widget end-to-end against a stubbed /api/sdk/compliance-advisor/chat with contract fixtures — clarify (L1 + context chips), answer ([n] citation + evidence pane), and clarify->pick-context->scoped-answer. No backend needed (route interception). Runs on CI/macmini (Next app on :3002); validated here via tsc + `playwright --list` (3 tests discovered). check-loc 0. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* E2E: Compliance Advisor — Clarity Gate (v3 contract)
|
||||
*
|
||||
* Drives the floating advisor widget end-to-end against a stubbed /api/sdk/compliance-advisor/chat
|
||||
* (contract fixtures), so the whole FE chain is exercised without the RAG/LLM backend:
|
||||
* - underspecified question -> clarify mode (L1 general answer + domain context chips)
|
||||
* - specific question -> answer mode (markdown + [n] citation coupling + evidence pane)
|
||||
* - clarify -> pick a context -> scoped answer
|
||||
* Runs on CI / macmini (needs the Next app on :3002).
|
||||
*/
|
||||
|
||||
import { test, expect } from '../fixtures/sdk-fixtures'
|
||||
|
||||
const CHAT_ROUTE = '**/api/sdk/compliance-advisor/chat'
|
||||
const openAdvisor = 'Compliance Advisor oeffnen'
|
||||
const inputPlaceholder = 'Frage eingeben...'
|
||||
|
||||
const CLARIFY = {
|
||||
mode: 'clarify',
|
||||
question: 'Was ist PDCA?',
|
||||
clarity: {
|
||||
is_underspecified: true,
|
||||
concentration: 0.3,
|
||||
suggested_contexts: [
|
||||
{ id: 'datenschutz', label: 'Datenschutz' },
|
||||
{ id: 'cyber', label: 'Cybersecurity' },
|
||||
],
|
||||
},
|
||||
general_answer: 'PDCA steht für **Plan-Do-Check-Act**.',
|
||||
answer: null,
|
||||
evidence: [],
|
||||
citations: [],
|
||||
visual_evidence: [],
|
||||
footnotes: [],
|
||||
}
|
||||
|
||||
const ANSWER = {
|
||||
mode: 'answer',
|
||||
question: 'CRA Meldefrist',
|
||||
clarity: { is_underspecified: false, dominant_context: 'cyber', concentration: 0.88 },
|
||||
answer: 'Die Meldung erfolgt unverzüglich [1].',
|
||||
evidence: [
|
||||
{ evidence_id: 'e1', document: 'CRA', section: 'Art. 14', paragraph: 'Abs. 1', snippet: 'unverzüglich melden' },
|
||||
],
|
||||
citations: [
|
||||
{ citation_id: 'c1', number: 1, evidence_id: 'e1', document: 'CRA', section: 'Art. 14', paragraph: 'Abs. 1' },
|
||||
],
|
||||
visual_evidence: [],
|
||||
footnotes: [],
|
||||
}
|
||||
|
||||
async function ask(page: import('@playwright/test').Page, question: string) {
|
||||
await page.getByRole('button', { name: openAdvisor }).click()
|
||||
const input = page.getByPlaceholder(inputPlaceholder)
|
||||
await input.fill(question)
|
||||
await input.press('Enter')
|
||||
}
|
||||
|
||||
test.describe('Compliance Advisor — Clarity Gate', () => {
|
||||
test('underspecified question -> clarify (L1 definition + context chips, no evidence)', async ({ sdkPage }) => {
|
||||
await sdkPage.route(CHAT_ROUTE, (r) =>
|
||||
r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(CLARIFY) }),
|
||||
)
|
||||
await ask(sdkPage, 'Was ist PDCA?')
|
||||
|
||||
await expect(sdkPage.getByText('Allgemeine Definition')).toBeVisible()
|
||||
await expect(sdkPage.getByText('Plan-Do-Check-Act')).toBeVisible()
|
||||
await expect(sdkPage.getByRole('button', { name: 'Datenschutz' })).toBeVisible()
|
||||
await expect(sdkPage.getByRole('button', { name: 'Cybersecurity' })).toBeVisible()
|
||||
})
|
||||
|
||||
test('specific question -> answer with [n] citation + evidence pane', async ({ sdkPage }) => {
|
||||
await sdkPage.route(CHAT_ROUTE, (r) =>
|
||||
r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(ANSWER) }),
|
||||
)
|
||||
await ask(sdkPage, 'CRA Meldefrist')
|
||||
|
||||
await expect(sdkPage.getByText(/unverzüglich/)).toBeVisible()
|
||||
await expect(sdkPage.getByTitle('Beleg 1 anzeigen')).toBeVisible()
|
||||
await expect(sdkPage.getByText('Cyber Resilience Act (CRA)')).toBeVisible()
|
||||
})
|
||||
|
||||
test('clarify -> pick a context -> scoped answer', async ({ sdkPage }) => {
|
||||
let calls = 0
|
||||
await sdkPage.route(CHAT_ROUTE, (r) => {
|
||||
calls += 1
|
||||
r.fulfill({
|
||||
status: 200,
|
||||
contentType: 'application/json',
|
||||
body: JSON.stringify(calls === 1 ? CLARIFY : ANSWER),
|
||||
})
|
||||
})
|
||||
await ask(sdkPage, 'Was ist PDCA?')
|
||||
await sdkPage.getByRole('button', { name: 'Datenschutz' }).click()
|
||||
|
||||
await expect(sdkPage.getByText(/unverzüglich/)).toBeVisible()
|
||||
await expect(sdkPage.getByTitle('Beleg 1 anzeigen')).toBeVisible()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user