1import { afterEach, describe, expect, it, vi } from "vite-plus/test";
2
3import {
4 resolveRestingComposerControlsLayout,
5 resolveRestingComposerControlsNaturalWidth,
6} from "../composerFooterLayout";
7import { measureRestingComposerControls } from "./restingComposerControlsMeasurement";
8
9function measurePicker(input: { clientWidth: number; flexGrow: string; maxWidth?: string }) {
10 const label = { clientWidth: input.clientWidth, scrollWidth: 160 };
11 const picker = {
12 getBoundingClientRect: () => ({ width: 52 }),
13 querySelector: () => label,
14 };
15 const controls = {
16 querySelector: (selector: string) => {
17 if (selector === "[data-chat-provider-model-picker]") return picker;
18 if (selector === "[data-resting-controls-overflow]") {
19 return { getBoundingClientRect: () => ({ width: 24 }) };
20 }
21 return null;
22 },
23 querySelectorAll: () => [
24 {
25 dataset: {},
26 querySelectorAll: () => [],
27 getBoundingClientRect: () => ({ width: 140 }),
28 },
29 ],
30 };
31 vi.stubGlobal("getComputedStyle", (element: unknown) => {
32 if (element === label) return { flexGrow: input.flexGrow };
33 if (element === picker) return { minWidth: "52px", maxWidth: input.maxWidth ?? "none" };
34 return { columnGap: "4px" };
35 });
36 return measureRestingComposerControls(controls as unknown as HTMLElement)!;
37}
38
39afterEach(() => vi.unstubAllGlobals());
40
41describe("measureRestingComposerControls", () => {
42 it("keeps controls inline when the model label is deliberately collapsed", () => {
43 const measurement = measurePicker({ clientWidth: 0, flexGrow: "0" });
44
45 expect(measurement.naturalFixedWidth).toBe(52);
46 expect(resolveRestingComposerControlsNaturalWidth(measurement)).toBe(196);
47 expect(resolveRestingComposerControlsLayout({ ...measurement, hostWidth: 200 })).toEqual({
48 hiddenCount: 0,
49 iconOnlyCount: 0,
50 visible: true,
51 });
52 });
53
54 it("recovers truncated text while the model label is flexible", () => {
55 const measurement = measurePicker({ clientWidth: 20, flexGrow: "1" });
56
57 expect(measurement.naturalFixedWidth).toBe(192);
58 expect(resolveRestingComposerControlsLayout({ ...measurement, hostWidth: 200 })).toEqual({
59 hiddenCount: 1,
60 iconOnlyCount: 1,
61 visible: true,
62 });
63 });
64
65 it("recovers flexible text squeezed to zero instead of mistaking it for collapsed text", () => {
66 const measurement = measurePicker({ clientWidth: 0, flexGrow: "1" });
67
68 expect(measurement.naturalFixedWidth).toBe(212);
69 expect(resolveRestingComposerControlsLayout({ ...measurement, hostWidth: 200 })).toEqual({
70 hiddenCount: 1,
71 iconOnlyCount: 1,
72 visible: true,
73 });
74 });
75
76 it("still caps recovered text at the model picker's maximum width", () => {
77 expect(
78 measurePicker({ clientWidth: 0, flexGrow: "1", maxWidth: "180px" }).naturalFixedWidth,
79 ).toBe(180);
80 });
81});