Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions apps/web/src/components/ChatView.browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,45 @@ function createSnapshotWithLongProposedPlan(): OrchestrationReadModel {
};
}

function createSnapshotWithPendingUserInputAndPlan(): OrchestrationReadModel {
const snapshot = createSnapshotWithLongProposedPlan();
return {
...snapshot,
threads: snapshot.threads.map((thread) =>
thread.id === THREAD_ID
? Object.assign({}, thread, {
interactionMode: "plan",
activities: [
{
id: "activity-user-input-requested",
threadId: THREAD_ID,
turnId: null,
kind: "user-input.requested",
createdAt: isoAt(1_010),
order: 1,
payload: {
requestId: "request-browser-test",
questions: [
{
id: "question-browser-test",
header: "Need an answer",
question: "Pick an option or type a custom answer.",
options: [
{ label: "Yes", description: "Confirm." },
{ label: "No", description: "Decline." },
],
},
],
},
},
],
updatedAt: isoAt(1_010),
})
: thread,
),
};
}

function resolveWsRpc(body: WsRequestEnvelope["body"]): unknown {
const tag = body._tag;
if (tag === ORCHESTRATION_WS_METHODS.getSnapshot) {
Expand Down Expand Up @@ -531,6 +570,23 @@ async function waitForComposerEditor(): Promise<HTMLElement> {
);
}

function composerTextContent(): string {
return document.querySelector<HTMLElement>('[data-testid="composer-editor"]')?.textContent ?? "";
}

async function waitForPendingUserInputComposerEditor(expectedValue?: string): Promise<HTMLElement> {
return waitForElement(() => {
const editor = document.querySelector<HTMLElement>('[data-testid="composer-editor"]');
if (!editor) {
return null;
}
if (expectedValue !== undefined && editor.textContent !== expectedValue) {
return null;
}
return editor;
}, "Unable to find pending user-input composer editor.");
}

async function waitForInteractionModeButton(
expectedLabel: "Chat" | "Plan",
): Promise<HTMLButtonElement> {
Expand Down Expand Up @@ -1247,4 +1303,64 @@ describe("ChatView timeline estimator parity (full app)", () => {
await mounted.cleanup();
}
});

it("routes pending custom-answer typing through the composer while preserving the main draft", async () => {
const mounted = await mountChatView({
viewport: DEFAULT_VIEWPORT,
snapshot: createSnapshotWithPendingUserInputAndPlan(),
});

try {
const composerEditor = await waitForComposerEditor();
composerEditor.focus();
document.execCommand("insertText", false, "main composer draft");

await vi.waitFor(
() => {
expect(composerTextContent()).toContain("main composer draft");
},
{ timeout: 8_000, interval: 16 },
);

composerEditor.focus();
document.execCommand("selectAll", false);
document.execCommand("insertText", false, "custom follow-up answer");

await vi.waitFor(
() => {
expect(composerTextContent()).toBe("custom follow-up answer");
},
{ timeout: 8_000, interval: 16 },
);

wsRequests.length = 0;
composerEditor.dispatchEvent(new KeyboardEvent("keydown", { key: "2", bubbles: true }));
document.execCommand("insertText", false, "custom follow-up answer2");

await vi.waitFor(
() => {
expect(composerTextContent()).toBe("custom follow-up answer2");
expect(wsRequests.some((request) => request._tag === "thread.user-input.respond")).toBe(
false,
);
},
{ timeout: 8_000, interval: 16 },
);

const { syncServerReadModel } = useStore.getState();
syncServerReadModel(createSnapshotWithLongProposedPlan());

await vi.waitFor(
() => {
expect(document.body.textContent).toContain("Add feedback to refine the plan");
expect(composerTextContent()).toContain("main composer draft");
},
{ timeout: 8_000, interval: 16 },
);

await waitForPendingUserInputComposerEditor("main composer draft");
} finally {
await mounted.cleanup();
}
});
});
7 changes: 7 additions & 0 deletions apps/web/src/components/ChatView.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,10 @@ export function getCustomModelOptionsByProvider(settings: {
codex: getAppModelOptions("codex", settings.customCodexModels),
};
}

export function resolveComposerDraftText(options: {
prompt: string;
pendingCustomAnswer: string | null;
}): string {
return options.pendingCustomAnswer ?? options.prompt;
}
Loading