1import { afterEach, describe, expect, it, vi } from "vite-plus/test";
2
3import { measureComposerMultilinePrompt } from "./useComposerMultilinePrompt";
4
5function measure(input: { width: number; height: number; resting?: boolean; hidden?: boolean }) {
6 const editor = { clientWidth: input.hidden ? 0 : input.resting ? 400 : 500 };
7 const body = { clientWidth: 532, querySelector: () => editor };
8 vi.stubGlobal("getComputedStyle", (element: unknown) =>
9 element === body
10 ? { paddingLeft: "16px", paddingRight: "16px" }
11 : { lineHeight: input.resting ? "32px" : "22.75px" },
12 );
13 vi.stubGlobal("document", {
14 createRange: () => ({
15 selectNodeContents() {},
16 getBoundingClientRect: () => ({ width: input.width, height: input.height }),
17 }),
18 });
19 return measureComposerMultilinePrompt(body as unknown as HTMLElement);
20}
21
22afterEach(() => vi.unstubAllGlobals());
23
24describe("composer prompt line measurement", () => {
25 it("allows a single line even though the editor has a larger minimum height", () => {
26 expect(measure({ width: 500, height: 22.75 })).toBe(false);
27 });
28
29 it("keeps soft-wrapped lines expanded", () => {
30 expect(measure({ width: 500, height: 45.5 })).toBe(true);
31 });
32
33 it("recognizes a long restored draft while the resting row is unwrapped", () => {
34 expect(measure({ width: 700, height: 32, resting: true })).toBe(true);
35 });
36
37 it("uses the expanded width so inline actions cannot cause a collapse loop", () => {
38 expect(measure({ width: 450, height: 32, resting: true })).toBe(false);
39 expect(measure({ width: 500, height: 22.75 })).toBe(false);
40 });
41
42 it("allows collapse again after deleting the second line", () => {
43 expect(measure({ width: 500, height: 45.5 })).toBe(true);
44 expect(measure({ width: 500, height: 22.75 })).toBe(false);
45 });
46
47 it("retains the previous measurement when the editor is hidden", () => {
48 expect(measure({ width: 0, height: 0, hidden: true })).toBeNull();
49 });
50});