1import { describe, expect, it } from "vite-plus/test";
2
3import {
4 REMOVE_QUEUED_MESSAGE_ACCESSIBILITY_LABEL,
5 buildCancelQueuedRunCommand,
6 resolveQueueDragBeforeRunId,
7 resolveThreadQueueRowControls,
8 resolveQueueDropBeforeRunId,
9} from "./threadQueueControlPresentation";
10import { threadDragGapOffset } from "./threadDragGap";
11
12describe("threadQueueControlPresentation", () => {
13 it("preserves queue reorder and steer controls with removal", () => {
14 const controls = resolveThreadQueueRowControls({
15 busy: false,
16 canPromoteToSteer: true,
17 canReorder: true,
18 index: 1,
19 queuedCount: 3,
20 text: "Please review the follow-up change.",
21 });
22
23 expect(controls.displayText).toBe("Please review the follow-up change.");
24 expect(controls.canMoveUp).toBe(true);
25 expect(controls.canMoveDown).toBe(true);
26 expect(controls.canSteer).toBe(true);
27 expect(controls.canDismiss).toBe(true);
28 expect(controls.dismissAccessibilityLabel).toBe(REMOVE_QUEUED_MESSAGE_ACCESSIBILITY_LABEL);
29 });
30
31 it("disables edge reorder controls and busy dismissal", () => {
32 const first = resolveThreadQueueRowControls({
33 busy: false,
34 canPromoteToSteer: false,
35 canReorder: true,
36 index: 0,
37 queuedCount: 2,
38 text: "First",
39 });
40 const busy = resolveThreadQueueRowControls({
41 busy: true,
42 canPromoteToSteer: true,
43 canReorder: true,
44 index: 0,
45 queuedCount: 1,
46 text: "Queued message",
47 });
48
49 expect(first.canMoveUp).toBe(false);
50 expect(first.canMoveDown).toBe(true);
51 expect(first.canSteer).toBe(false);
52 expect(busy.canDismiss).toBe(false);
53 expect(busy.canMoveUp).toBe(false);
54 expect(busy.canSteer).toBe(false);
55 });
56
57 it("keeps the row already open in the composer from being reopened or steered", () => {
58 const editing = resolveThreadQueueRowControls({
59 busy: false,
60 canPromoteToSteer: true,
61 canReorder: true,
62 index: 1,
63 isEditing: true,
64 queuedCount: 3,
65 text: "Being edited",
66 });
67
68 expect(editing.isEditing).toBe(true);
69 expect(editing.canEdit).toBe(false);
70 expect(editing.canSteer).toBe(false);
71 // Reordering and removing a message stay available while it is edited.
72 expect(editing.canMoveUp).toBe(true);
73 expect(editing.canDismiss).toBe(true);
74 });
75
76 it("builds cancelQueuedRun command arguments for removal", () => {
77 expect(
78 buildCancelQueuedRunCommand({
79 environmentId: "environment:test" as never,
80 runId: "run:queued" as never,
81 threadId: "thread:test" as never,
82 }),
83 ).toEqual({
84 environmentId: "environment:test",
85 input: {
86 runId: "run:queued",
87 threadId: "thread:test",
88 },
89 });
90 });
91});
92
93describe("queue drag insertion", () => {
94 const rows = [
95 { id: "first" as never, y: 0, height: 80 },
96 { id: "second" as never, y: 80, height: 140 },
97 { id: "third" as never, y: 220, height: 80 },
98 ];
99
100 it("moves between variable-height rows and to either end", () => {
101 expect(resolveQueueDropBeforeRunId(rows, rows[0]!.id, 140)).toBe("third");
102 expect(resolveQueueDropBeforeRunId(rows, rows[0]!.id, 300)).toBeNull();
103 expect(resolveQueueDropBeforeRunId(rows, rows[2]!.id, -300)).toBe("first");
104 });
105
106 it("opens the destination gap while the dragged row crosses other rows", () => {
107 const offsets = (runId: (typeof rows)[number]["id"], translation: number) => {
108 const before = resolveQueueDragBeforeRunId(rows, runId, translation);
109 if (before === undefined) return;
110 const source = rows.find((row) => row.id === runId)!;
111 const last = rows.at(-1)!;
112 const insertion =
113 before === null ? last.y + last.height : rows.find((row) => row.id === before)!.y;
114 return rows.map((row) => threadDragGapOffset(row.y, source.y, source.height, insertion));
115 };
116
117 expect(resolveQueueDragBeforeRunId(rows, rows[0]!.id, 0)).toBe(rows[1]!.id);
118 expect(offsets(rows[0]!.id, 140)).toEqual([0, -80, 0]);
119 expect(offsets(rows[0]!.id, 300)).toEqual([0, -80, -80]);
120 expect(offsets(rows[2]!.id, -300)).toEqual([80, 80, 0]);
121 expect(offsets(rows[1]!.id, 0)).toEqual([0, 0, 0]);
122 });
123
124 it("does not send a reorder for an unchanged or unmeasured drop", () => {
125 expect(resolveQueueDropBeforeRunId(rows, rows[1]!.id, 0)).toBeUndefined();
126 expect(resolveQueueDropBeforeRunId(rows, rows[2]!.id, 20)).toBeUndefined();
127 expect(resolveQueueDropBeforeRunId([{ id: rows[0]!.id }], rows[0]!.id, 10)).toBeUndefined();
128 expect(resolveQueueDropBeforeRunId(rows, "missing" as never, 100)).toBeUndefined();
129 });
130});