import { PROVIDER_SEND_TURN_MAX_INPUT_CHARS } from "@t3tools/contracts";import { describe, expect, it, vi } from "vite-plus/test"; import { submitComposerDraft } from "./composerSubmission"; describe("submitComposerDraft", () => { it("keeps an oversized draft editable and sends a corrected follow-up", () => { let draft = "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS + 1); let validationMessage: string | null = null; const dispatchedDrafts: string[] = []; const preventDefault = vi.fn(); const submit = () => { const result = submitComposerDraft({ prompt: draft, submissionTarget: "provider-turn", event: { preventDefault }, onSend: () => { dispatchedDrafts.push(draft); }, }); validationMessage = result.validationMessage; }; submit(); expect(dispatchedDrafts).toEqual([]); expect(draft).toHaveLength(PROVIDER_SEND_TURN_MAX_INPUT_CHARS + 1); expect(validationMessage).toBe( "Prompt is 1 character over the 120,000-character limit. Shorten or split it before sending.", ); expect(preventDefault).toHaveBeenCalledOnce(); draft = "Corrected prompt"; submit(); expect(dispatchedDrafts).toEqual(["Corrected prompt"]); expect(validationMessage).toBeNull(); }); it("allows a draft at the shared character limit through the normal send path", () => { const draft = "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS); const onSend = vi.fn(); const preventDefault = vi.fn(); const result = submitComposerDraft({ prompt: draft, submissionTarget: "provider-turn", event: { preventDefault }, onSend, }); expect(result).toEqual({ validationMessage: null, didDispatch: true }); expect(onSend).toHaveBeenCalledOnce(); expect(preventDefault).not.toHaveBeenCalled(); }); it("blocks when appended context pushes the provider input over the shared limit", () => { const draft = "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS); const onSend = vi.fn(); const result = submitComposerDraft({ prompt: draft, providerInput: `${draft}\n\nTerminal context`, submissionTarget: "provider-turn", event: undefined, onSend, }); expect(result).toEqual({ validationMessage: "Prompt is 18 characters over the 120,000-character limit. Shorten or split it before sending.", didDispatch: false, }); expect(draft).toHaveLength(PROVIDER_SEND_TURN_MAX_INPUT_CHARS); expect(onSend).not.toHaveBeenCalled(); const correctedResult = submitComposerDraft({ prompt: "Corrected prompt", providerInput: "Corrected prompt\n\nShort terminal context", submissionTarget: "provider-turn", event: undefined, onSend, }); expect(correctedResult).toEqual({ validationMessage: null, didDispatch: true }); expect(onSend).toHaveBeenCalledOnce(); }); it("does not finish submission when the send boundary rejects composed provider input", () => { const preventDefault = vi.fn(); const result = submitComposerDraft({ prompt: "Sendable raw draft", submissionTarget: "provider-turn", event: { preventDefault }, onSend: () => false, }); expect(result).toEqual({ validationMessage: null, didDispatch: false }); expect(preventDefault).toHaveBeenCalledOnce(); }); it("allows fully composed provider input at the shared character limit", () => { const onSend = vi.fn(); const result = submitComposerDraft({ prompt: "Short draft", providerInput: "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS), submissionTarget: "provider-turn", event: undefined, onSend, }); expect(result).toEqual({ validationMessage: null, didDispatch: true }); expect(onSend).toHaveBeenCalledOnce(); }); it("blocks a generated plan follow-up that exceeds the shared limit", () => { const onSend = vi.fn(); const result = submitComposerDraft({ prompt: "", providerInput: `PLEASE IMPLEMENT THIS PLAN:\n${"x".repeat( PROVIDER_SEND_TURN_MAX_INPUT_CHARS, )}`, submissionTarget: "provider-turn", event: undefined, onSend, }); expect(result.didDispatch).toBe(false); expect(result.validationMessage).toContain("over the 120,000-character limit"); expect(onSend).not.toHaveBeenCalled(); }); it("allows surrounding whitespace that the provider turn contract trims", () => { const draft = ` ${"x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS)} `; const onSend = vi.fn(); const preventDefault = vi.fn(); const result = submitComposerDraft({ prompt: draft, submissionTarget: "provider-turn", event: { preventDefault }, onSend, }); expect(result).toEqual({ validationMessage: null, didDispatch: true }); expect(onSend).toHaveBeenCalledOnce(); expect(preventDefault).not.toHaveBeenCalled(); }); it("dispatches pending user input answers on their separate response path", () => { const answer = "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS + 1); const onSend = vi.fn(); const preventDefault = vi.fn(); const result = submitComposerDraft({ prompt: answer, submissionTarget: "pending-user-input", event: { preventDefault }, onSend, }); expect(result).toEqual({ validationMessage: null, didDispatch: true }); expect(onSend).toHaveBeenCalledOnce(); expect(preventDefault).not.toHaveBeenCalled(); });});
import { PROVIDER_SEND_TURN_MAX_INPUT_CHARS } from "@t3tools/contracts";
import { describe, expect, it, vi } from "vite-plus/test";
import { submitComposerDraft } from "./composerSubmission";
describe("submitComposerDraft", () => {
it("keeps an oversized draft editable and sends a corrected follow-up", () => {
let draft = "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS + 1);
let validationMessage: string | null = null;
const dispatchedDrafts: string[] = [];
const preventDefault = vi.fn();
const submit = () => {
const result = submitComposerDraft({
prompt: draft,
submissionTarget: "provider-turn",
event: { preventDefault },
onSend: () => {
dispatchedDrafts.push(draft);
},
});
validationMessage = result.validationMessage;
};
submit();
expect(dispatchedDrafts).toEqual([]);
expect(draft).toHaveLength(PROVIDER_SEND_TURN_MAX_INPUT_CHARS + 1);
expect(validationMessage).toBe(
"Prompt is 1 character over the 120,000-character limit. Shorten or split it before sending.",
);
expect(preventDefault).toHaveBeenCalledOnce();
draft = "Corrected prompt";
submit();
expect(dispatchedDrafts).toEqual(["Corrected prompt"]);
expect(validationMessage).toBeNull();
});
it("allows a draft at the shared character limit through the normal send path", () => {
const draft = "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS);
const onSend = vi.fn();
const preventDefault = vi.fn();
const result = submitComposerDraft({
prompt: draft,
submissionTarget: "provider-turn",
event: { preventDefault },
onSend,
});
expect(result).toEqual({ validationMessage: null, didDispatch: true });
expect(onSend).toHaveBeenCalledOnce();
expect(preventDefault).not.toHaveBeenCalled();
});
it("blocks when appended context pushes the provider input over the shared limit", () => {
const draft = "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS);
const onSend = vi.fn();
const result = submitComposerDraft({
prompt: draft,
providerInput: `${draft}\n\nTerminal context`,
submissionTarget: "provider-turn",
event: undefined,
onSend,
});
expect(result).toEqual({
validationMessage:
"Prompt is 18 characters over the 120,000-character limit. Shorten or split it before sending.",
didDispatch: false,
});
expect(draft).toHaveLength(PROVIDER_SEND_TURN_MAX_INPUT_CHARS);
expect(onSend).not.toHaveBeenCalled();
const correctedResult = submitComposerDraft({
prompt: "Corrected prompt",
providerInput: "Corrected prompt\n\nShort terminal context",
submissionTarget: "provider-turn",
event: undefined,
onSend,
});
expect(correctedResult).toEqual({ validationMessage: null, didDispatch: true });
expect(onSend).toHaveBeenCalledOnce();
});
it("does not finish submission when the send boundary rejects composed provider input", () => {
const preventDefault = vi.fn();
const result = submitComposerDraft({
prompt: "Sendable raw draft",
submissionTarget: "provider-turn",
event: { preventDefault },
onSend: () => false,
});
expect(result).toEqual({ validationMessage: null, didDispatch: false });
expect(preventDefault).toHaveBeenCalledOnce();
});
it("allows fully composed provider input at the shared character limit", () => {
const onSend = vi.fn();
const result = submitComposerDraft({
prompt: "Short draft",
providerInput: "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS),
submissionTarget: "provider-turn",
event: undefined,
onSend,
});
expect(result).toEqual({ validationMessage: null, didDispatch: true });
expect(onSend).toHaveBeenCalledOnce();
});
it("blocks a generated plan follow-up that exceeds the shared limit", () => {
const onSend = vi.fn();
const result = submitComposerDraft({
prompt: "",
providerInput: `PLEASE IMPLEMENT THIS PLAN:\n${"x".repeat(
PROVIDER_SEND_TURN_MAX_INPUT_CHARS,
)}`,
submissionTarget: "provider-turn",
event: undefined,
onSend,
});
expect(result.didDispatch).toBe(false);
expect(result.validationMessage).toContain("over the 120,000-character limit");
expect(onSend).not.toHaveBeenCalled();
});
it("allows surrounding whitespace that the provider turn contract trims", () => {
const draft = ` ${"x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS)} `;
const onSend = vi.fn();
const preventDefault = vi.fn();
const result = submitComposerDraft({
prompt: draft,
submissionTarget: "provider-turn",
event: { preventDefault },
onSend,
});
expect(result).toEqual({ validationMessage: null, didDispatch: true });
expect(onSend).toHaveBeenCalledOnce();
expect(preventDefault).not.toHaveBeenCalled();
});
it("dispatches pending user input answers on their separate response path", () => {
const answer = "x".repeat(PROVIDER_SEND_TURN_MAX_INPUT_CHARS + 1);
const onSend = vi.fn();
const preventDefault = vi.fn();
const result = submitComposerDraft({
prompt: answer,
submissionTarget: "pending-user-input",
event: { preventDefault },
onSend,
});
expect(result).toEqual({ validationMessage: null, didDispatch: true });
expect(onSend).toHaveBeenCalledOnce();
expect(preventDefault).not.toHaveBeenCalled();
});
});