import React, { act, useCallback, useState } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, describe, expect, it } from "vitest";
import { AdaptiveTextInput } from "./adaptive-text-input";
interface MountedInput {
root: Root;
container: HTMLDivElement;
textarea: HTMLTextAreaElement;
}
const mountedInputs: MountedInput[] = [];
function SchedulePromptHarness({ changes }: { changes: string[] }) {
const [prompt, setPrompt] = useState("");
const handleChangeText = useCallback(
(text: string) => {
changes.push(text);
setPrompt(text);
},
[changes],
);
return (
);
}
function mountSchedulePrompt(changes: string[]): MountedInput {
const container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => root.render());
const textarea = container.querySelector("textarea");
if (!textarea) {
throw new Error("Schedule prompt did not render a textarea");
}
const mounted = { root, container, textarea };
mountedInputs.push(mounted);
return mounted;
}
function typeFromIme(textarea: HTMLTextAreaElement, text: string): void {
const valueSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value")?.set;
if (!valueSetter) {
throw new Error("HTML textarea value setter is unavailable");
}
valueSetter.call(textarea, text);
textarea.dispatchEvent(new InputEvent("input", { bubbles: true, data: text }));
}
afterEach(() => {
for (const mounted of mountedInputs.splice(0)) {
act(() => mounted.root.unmount());
mounted.container.remove();
}
});
describe("AdaptiveTextInput web IME composition", () => {
it("keeps the candidate editing-surface owned until composition commits", () => {
const changes: string[] = [];
const { textarea } = mountSchedulePrompt(changes);
act(() => {
textarea.dispatchEvent(new CompositionEvent("compositionstart", { bubbles: true }));
typeFromIme(textarea, "n");
});
expect(textarea.value).toBe("n");
expect(changes).toEqual([]);
act(() => {
textarea.value = "你";
textarea.dispatchEvent(new CompositionEvent("compositionend", { bubbles: true }));
});
expect(textarea.value).toBe("你");
expect(changes).toEqual(["你"]);
});
});