import path from "node:path";
import type { IncomingMessage, ServerResponse } from "node:http";
import { expect, test } from "@playwright/test";
import { createServer, type ViteDevServer } from "../../ui/node_modules/vite/dist/node/index.js";
import { answerResumeQuestionThroughUi } from "./question-resume-ui.js";
import { chatQuestionPresentation } from "./chat-flow.js";
let server: ViteDevServer;
let origin: string;
const uiRoot = path.resolve(import.meta.dirname, "../../ui");
const probe = path.join(uiRoot, "src/__question_resume_probe.tsx");
test.beforeAll(async () => {
server = await createServer({
root: uiRoot, configFile: false,
resolve: { alias: { "@": path.join(uiRoot, "src") } },
oxc: { jsx: { runtime: "automatic" } },
server: { host: "127.0.0.1", port: 0 },
plugins: [{
name: "question-resume-controls",
resolveId(id: string) { if (id === "/src/__question_resume_probe.tsx") return probe; },
load(id: string) {
if (id !== probe) return;
return `import React from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { QuestionForm } from "/src/components/task-chat/QuestionForm.tsx";
const root = createRoot(document.getElementById("root"));
const client = new QueryClient();
window.showQuestion = questionSet => root.render(
{ window.submitted = response; }} />
);`;
},
configureServer(vite: ViteDevServer) {
vite.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {
if (req.url !== "/") return next();
res.setHeader("Content-Type", "text/html");
res.end('
');
});
},
}],
});
await server.listen();
const address = server.httpServer!.address();
if (!address || typeof address === "string") throw new Error("Missing test server address");
origin = `http://127.0.0.1:${address.port}`;
});
test.afterAll(async () => { await server?.close(); });
for (const mode of ["text", "single_select"] as const) {
test(`submits an exact typed reference through the real ${mode} QuestionForm control`, async ({ page }) => {
const field = { id: "reference", prompt: "What reference should I include?", required: true, answerMode: mode,
...(mode === "single_select" ? { options: [{ id: "none", label: "No reference" }], customAnswer: { enabled: true, label: "Write reference" } } : {}) };
const card = { payload: { questionSet: { schema: "paperclip.question_set.v1", questions: [field] } } };
await page.goto(origin);
await page.waitForFunction(() => typeof (window as any).showQuestion === "function");
await page.evaluate(set => (window as any).showQuestion(set), card.payload.questionSet);
if (mode === "single_select") await page.getByRole("radio", { name: "No reference", exact: true }).click();
await answerResumeQuestionThroughUi(page, card, { text: "Use AMBERwritten.", allowProviderCustomAnswer: true });
expect(await page.evaluate(() => (window as any).submitted)).toEqual({
schema: "paperclip.question_response.v1", answers: { reference: mode === "text"
? { text: "Use AMBERwritten." } : { selectedOptionIds: [], customText: "Use AMBERwritten." } },
});
if (mode === "single_select") await expect(page.getByRole("radio", { name: "No reference", exact: true })).toHaveAttribute("aria-checked", "false");
});
}
for (const invalid of ["missing", "disabled", "legacy", "strict"] as const) {
test(`does not infer a written-answer capability from ${invalid} metadata`, async ({ page }) => {
const field = { id: "reference", prompt: "Reference?", answerMode: "single_select", options: [{ id: "none", label: "No reference" }],
...(invalid === "missing" ? {} : { customAnswer: { enabled: invalid !== "disabled" } }) };
const card = { payload: invalid === "legacy" ? { questions: [{ ...field, options: [{ id: "none", label: "No reference", freeText: true }] }] }
: { questionSet: { schema: "paperclip.question_set.v1", questions: [field] } } };
await page.goto(origin);
await page.waitForFunction(() => typeof (window as any).showQuestion === "function");
const set = invalid === "legacy" ? chatQuestionPresentation(card.payload as any) : card.payload.questionSet;
await page.evaluate(set => (window as any).showQuestion(set), set);
await expect(page.getByRole("radio", { name: "No reference", exact: true })).toBeVisible();
await expect(page.getByRole("radio", { name: "Other", exact: true })).toHaveCount(invalid === "strict" ? 1 : 0);
await expect(answerResumeQuestionThroughUi(page, card, { text: "Use AMBERwritten.", ...(invalid === "strict" ? {} : { allowProviderCustomAnswer: true }) })).rejects.toThrow();
expect(await page.evaluate(() => (window as any).submitted)).toBeUndefined();
});
}