char-slop/berry

multipurpose self-modifiable agentic harness

git clone https://git.t4t.associates/char-slop/berry

Charlotte Somtests: cut test sprawl1c0a335

main
6.0 KiB150 linesraw
1import { assertEquals } from "@std/assert";
2import { press, withDom } from "@berry/util/dom.ts";
3import { conversationName } from "./conversation-name.ts";
4
5Deno.test("sidebar names edit inline, save deliberately, and retain failed edits", async (t) => {
6  await withDom(async (window) => {
7    const row = document.createElement("div");
8    const link = document.createElement("a");
9    link.href = "#/conversation";
10    const label = document.createElement("span");
11    link.append(label);
12    const saved: (string | null)[] = [];
13    let save = (_title: string | null) => Promise.resolve();
14    const name = conversationName(link, label, (title) => {
15      saved.push(title);
16      return save(title);
17    });
18    row.append(name.editor, link);
19    document.body.append(row);
20    const start = () =>
21      link.dispatchEvent(
22        new window.MouseEvent("dblclick", { cancelable: true }) as unknown as MouseEvent,
23      );
24    const key = (value: string) => !press(name.editor, value, { bubbles: false });
25    const settle = () => window.happyDOM.waitUntilComplete();
26    try {
27      await t.step("double-click opens an input holding the shown name", () => {
28        name.update(undefined, "first message preview");
29        assertEquals(label.textContent, "first message preview");
30        assertEquals(name.editor.hidden, true);
31        assertEquals(start(), false);
32        assertEquals(link.hidden, true);
33        assertEquals(name.editor.hidden, false);
34        assertEquals(name.editor.value, "first message preview");
35        assertEquals(document.activeElement, name.editor);
36      });
37      await t.step("Escape cancels without saving and restores keyboard focus", async () => {
38        name.editor.value = "cancel this";
39        key("Escape");
40        await settle();
41        assertEquals(saved, []);
42        assertEquals(link.hidden, false);
43        assertEquals(name.editor.hidden, true);
44        assertEquals(label.textContent, "first message preview");
45        assertEquals(document.activeElement, link);
46      });
47      await t.step(
48        "Enter trims and saves once even though restoring focus blurs the input",
49        async () => {
50          start();
51          name.editor.value = "  renamed conversation  ";
52          key("Enter");
53          await settle();
54          assertEquals(saved, ["renamed conversation"]);
55          assertEquals(label.textContent, "renamed conversation");
56          assertEquals(name.editor.hidden, true);
57          assertEquals(link.hidden, false);
58        },
59      );
60      await t.step("blur saves changes without stealing focus from another control", async () => {
61        start();
62        name.editor.value = "saved on blur";
63        const elsewhere = document.createElement("input");
64        document.body.append(elsewhere);
65        elsewhere.focus();
66        await settle();
67        assertEquals(saved, ["renamed conversation", "saved on blur"]);
68        assertEquals(document.activeElement, elsewhere);
69        assertEquals(label.textContent, "saved on blur");
70      });
71      await t.step(
72        "leaving the generated preview unchanged does not create a custom name",
73        async () => {
74          name.update(undefined, "automatic preview");
75          start();
76          key("Enter");
77          await settle();
78          assertEquals(saved.length, 2);
79          name.update(undefined, "updated automatic preview");
80          assertEquals(label.textContent, "updated automatic preview");
81        },
82      );
83      await t.step("clearing a custom name returns to the message preview", async () => {
84        name.update("custom", "automatic preview");
85        start();
86        name.editor.value = "  ";
87        key("Enter");
88        await settle();
89        assertEquals(saved.at(-1), null);
90        assertEquals(label.textContent, "automatic preview");
91      });
92      await t.step("live sidebar updates do not overwrite an unfinished edit", async () => {
93        name.update("before", "preview");
94        start();
95        name.editor.value = "my unfinished name";
96        name.update("renamed on another device", "changed preview");
97        assertEquals(name.editor.value, "my unfinished name");
98        key("Escape");
99        await settle();
100        assertEquals(label.textContent, "renamed on another device");
101        assertEquals(saved.length, 3);
102      });
103      await t.step("failed saves keep the input editable and can be retried", async () => {
104        save = () => Promise.reject(new Error("offline"));
105        start();
106        name.editor.value = "retry this";
107        key("Enter");
108        await settle();
109        assertEquals(name.editor.hidden, false);
110        assertEquals(name.editor.value, "retry this");
111        assertEquals(name.editor.readOnly, false);
112        assertEquals(name.editor.getAttribute("aria-invalid"), "true");
113        assertEquals(name.editor.title, "offline");
114        save = () => Promise.resolve();
115        key("Enter");
116        await settle();
117        assertEquals(name.editor.hidden, true);
118        assertEquals(label.textContent, "retry this");
119      });
120      await t.step("F2 provides a keyboard equivalent for double-click", async () => {
121        link.dispatchEvent(
122          new window.KeyboardEvent("keydown", { key: "F2" }) as unknown as KeyboardEvent,
123        );
124        assertEquals(name.editor.hidden, false);
125        assertEquals(document.activeElement, name.editor);
126        key("Escape");
127        await settle();
128      });
129      await t.step(
130        "a pending save cannot overwrite a newer name received from another device",
131        async () => {
132          const pending = Promise.withResolvers<void>();
133          save = () => pending.promise;
134          start();
135          name.editor.value = "earlier request";
136          key("Enter");
137          const count = saved.length;
138          key("Enter");
139          assertEquals(saved.length, count);
140          name.update("newer remote name", "preview");
141          pending.resolve();
142          await settle();
143          assertEquals(label.textContent, "newer remote name");
144        },
145      );
146    } finally {
147      name.dispose();
148    }
149  });
150});