import { expect, test } from "bun:test"; import { createElement } from "react"; import { renderToStaticMarkup } from "react-dom/server"; import { forwardStage, ResearchOfferCard, shouldShowResearchActionError } from "./research-offer"; import { deferred } from "./research-offer.test-fixtures"; import { researchBriefKey } from "./research-brief-editor"; import type { ConversationPlan } from "@chopin/protocol"; import type { OfferLinkView } from "./research-offer"; import type { ResearchRequestStore } from "../research-requests"; import { researchDraftHarness } from "../../../server/src/conversation-plan/research-draft.test-fixtures"; // Exact archive446a9779a937fa5be7cd3eb52fd7f3023d691ed2, apps/web/src/chat/research-offer.test.ts. test("Resume appears only for a verified unresolved link and linked work hides stale errors", () => { let offer: ConversationPlan.ResearchOffer = { id: "offer-1", needId: "need-1", contextId: "context-1", brief: "Research the hosting options", status: "accepted", source: { messageId: "source-1", author: { kind: "member", handle: "ana" }, quote: "Research hosting", start: 0, end: 16, }, }; let store = { subscribe: () => () => {}, get: () => undefined, retain: () => () => {}, } as unknown as ResearchRequestStore; let render = (link?: OfferLinkView, canAct = true) => renderToStaticMarkup(createElement(ResearchOfferCard, { offer, controls: { links: link ? { [offer.id]: link } : {}, busy: new Set(), errors: { [offer.id]: "Old Resume error" }, canAct, canCheckLink: true, store, onAction: () => {}, onRetryLink: () => {}, }, })); for (let link of [undefined, { status: "checking" }, { status: "error" }] as const) { expect(render(link)).not.toContain(">Resume"); } // A pending link usually links within moments; Resume waits until it stalls. expect(render({ status: "pending" })).not.toContain(">Resume"); expect(render({ status: "unlinked", researchRequestId: "request-1" })).toContain( ">Resume", ); let linked = render({ status: "linked", researchRequestId: "request-1" }); expect(linked).not.toContain(">Resume"); expect(linked).not.toContain("Old Resume error"); let failedViewer = render({ status: "error", exhausted: true }, false); expect(failedViewer).toContain(">Retry link check"); expect(failedViewer).not.toContain(">Resume"); expect(render({ status: "error" }, false)).not.toContain(">Retry link check"); }); test("a deferred Resume failure cannot create an error after the link becomes linked", async () => { let reply = deferred(); let link: OfferLinkView["status"] = "pending"; let showError = false; let completed = reply.promise.catch(() => { showError = shouldShowResearchActionError("resume", "accepted", link); }); link = "linked"; reply.reject(new Error("old Resume failed")); await completed; expect(showError).toBe(false); expect(shouldShowResearchActionError("resume", "accepted", "unlinked")).toBe(true); expect(shouldShowResearchActionError("research", "accepted", undefined)).toBe(false); }); test("a failed refinement with a usable brief offers a quiet writer-only retry", () => { let offer = researchDraftHarness().offer(); offer.workflow!.preparation = "failed"; let render = (canAct: boolean, brief = offer.brief) => renderToStaticMarkup(createElement(ResearchOfferCard, { offer: { ...offer, brief }, controls: { links: {}, busy: new Set(), errors: {}, canAct, canCheckLink: false, store: {} as ResearchRequestStore, onAction() {}, onRetryLink() {}, }, })); expect(render(true)).toContain('aria-label="Retry brief refinement"'); expect(render(true)).not.toContain(">Retry refinement"); expect(render(false)).not.toContain("Retry brief refinement"); expect(render(true, "")).toContain("Brief refinement failed."); expect(render(true, "")).toContain(">Retry refinement"); expect(render(false, "")).not.toContain(">Retry refinement"); }); test("the brief editor finishes on Escape and starts on Command or Control Enter", () => { let key = ( key: string, extra: { metaKey?: boolean; ctrlKey?: boolean; isComposing?: boolean } = {}, ) => researchBriefKey({ key, metaKey: false, ctrlKey: false, isComposing: false, ...extra }); expect(key("Escape")).toBe("done"); expect(key("Enter", { metaKey: true })).toBe("start"); expect(key("Enter", { ctrlKey: true })).toBe("start"); expect(key("Enter")).toBeUndefined(); expect(key("Escape", { isComposing: true })).toBeUndefined(); }); test("an active request never shows an earlier stage, but terminal and retried stages do", () => { expect(forwardStage(undefined, "searching")).toBe("searching"); expect(forwardStage("searching", "queued")).toBe("searching"); expect(forwardStage("searching", "analyzing")).toBe("analyzing"); expect(forwardStage("writing", "failed")).toBe("failed"); expect(forwardStage("failed", "queued")).toBe("queued"); });