test(e2e): repoint document-chat spec to the Coach hand-off

The bespoke document-chat drawer was removed with Design A, so the old e2e
asserting the drawer overlay no longer applies. The spec now pins the
documents-side contract that replaced it: with a provider available the Coach
affordance hands off to the document-scoped Coach (/coach?doc=<id>), and with
no provider the AI area shows only the settings pointer and no Coach entry.

The Coach-side scope banner (the Document badge, "Chatting about", and the
not-indexed hint) moves to a coach-conversation component test, replacing the
drawer's not-indexed/render coverage; the fenced-send guarantee stays pinned by
the coach-send-target unit test.
This commit is contained in:
Marc-André Bombeck committed 2026-07-16 11:24:22 +02:00
1 parent 5b24ca0fa1
commit 5440ac9070
2 files changed
+131 -339

No files matched your search

@@ -0,0 +1,100 @@
import { describe, it, expect, vi } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
// The page surface reads `useRouter` (Conversations routes via the app router).
const pushMock = vi.fn();
vi.mock("next/navigation", () => ({
useRouter: () => ({ push: pushMock }),
}));
import { I18nProvider } from "@/lib/i18n/context";
import { queryKeys } from "@/lib/query-keys";
import { CoachConversation } from "../coach-conversation";
/**
* v1.28.51 (Documents R3, Design A) — the doc-scope banner.
*
* When the Coach is scoped to a document (the `/coach?doc=<id>` hand-off from
* the vault, before any thread exists), the shared `<CoachConversation>` surface
* paints a "Document" badge + "Chatting about: <title>" and, when the document
* is not yet content-indexed, the calm "read it with AI first" hint. On a normal
* health thread the banner is absent. Replaces the deleted drawer's not-indexed /
* scope coverage at the component level; the fenced-send guarantee is pinned by
* `coach-send-target.test.ts`.
*
* `pendingDocumentId` seeds from the `initialDocumentId` prop via a `useState`
* initializer, so the banner resolves in a single static-markup pass. The
* document detail (`/api/documents/inbound/<id>`) is seeded into the cache so the
* title + indexed status render without a network call.
*/
const DOC_ID = "e2edocscopedoc0000000001";
const TITLE = "Chest X-ray report";
const NOT_INDEXED_HINT =
"Read this document with AI first in the vault so the Coach can answer about it.";
function makeClient(hasContentIndex: boolean): QueryClient {
const client = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
// A fresh doc scope has no thread yet — keep the rail empty so the surface
// stays on the new-chat hero and the banner is the thing under test.
client.setQueryData(queryKeys.coachConversations(), {
conversations: [],
nextCursor: null,
});
client.setQueryData(queryKeys.coachAboutMeQuestions(), { questions: [] });
client.setQueryData(queryKeys.inboundDocument(DOC_ID), {
id: DOC_ID,
title: TITLE,
filename: "xray.pdf",
hasContentIndex,
});
return client;
}
function render(node: React.ReactNode, client: QueryClient): string {
return renderToStaticMarkup(
<I18nProvider initialLocale="en">
<QueryClientProvider client={client}>{node}</QueryClientProvider>
</I18nProvider>,
);
}
describe("<CoachConversation> doc-scope banner (R3)", () => {
it("shows the badge + 'Chatting about' title for an indexed document scope", () => {
const html = render(
<CoachConversation surface="page" initialDocumentId={DOC_ID} />,
makeClient(true),
);
expect(html).toContain('data-slot="coach-doc-scope"');
expect(html).toContain("Document");
expect(html).toContain(`Chatting about: ${TITLE}`);
// Indexed → no "read it first" hint.
expect(html).not.toContain(NOT_INDEXED_HINT);
});
it("shows the calm not-indexed hint when the document has no content index", () => {
const html = render(
<CoachConversation surface="page" initialDocumentId={DOC_ID} />,
makeClient(false),
);
expect(html).toContain('data-slot="coach-doc-scope"');
expect(html).toContain(NOT_INDEXED_HINT);
});
it("paints no banner on a normal health thread (no document scope)", () => {
const client = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
client.setQueryData(queryKeys.coachConversations(), {
conversations: [],
nextCursor: null,
});
client.setQueryData(queryKeys.coachAboutMeQuestions(), { questions: [] });
const html = render(<CoachConversation surface="page" />, client);
expect(html).not.toContain('data-slot="coach-doc-scope"');
});
});