| 1 | import assert from "node:assert/strict"; |
| 2 | import { JSDOM } from "jsdom"; |
| 3 | import React, { act } from "react"; |
| 4 | import type { SessionMeta } from "../lib/types"; |
| 5 | |
| 6 | const dom = new JSDOM('<html><body><div id="root"></div></body></html>', { url: "http://localhost/", pretendToBeVisual: true }); |
| 7 | Object.assign(globalThis, { |
| 8 | window: dom.window, document: dom.window.document, Node: dom.window.Node, |
| 9 | Element: dom.window.Element, HTMLElement: dom.window.HTMLElement, |
| 10 | Event: dom.window.Event, MouseEvent: dom.window.MouseEvent, |
| 11 | IS_REACT_ACT_ENVIRONMENT: true, |
| 12 | }); |
| 13 | Object.defineProperty(globalThis, "navigator", { value: dom.window.navigator, configurable: true }); |
| 14 | const { createRoot } = await import("react-dom/client"); |
| 15 | const { HistoryPanel } = await import("../components/HistoryPanel"); |
| 16 | const { LocaleProvider } = await import("../lib/i18n"); |
| 17 | const root = createRoot(document.getElementById("root")!); |
| 18 | const renamed: string[] = []; |
| 19 | const session: SessionMeta = { |
| 20 | path: "/sessions/one.jsonl", title: "Old name", preview: "Old name", |
| 21 | turns: 1, createdAt: Date.now(), lastActivityAt: Date.now(), modTime: Date.now(), |
| 22 | current: false, open: false, |
| 23 | }; |
| 24 | |
| 25 | try { |
| 26 | await act(async () => root.render(<LocaleProvider><HistoryPanel |
| 27 | sessions={[session]} running={false} onResume={() => {}} onPreview={async () => []} |
| 28 | onDelete={() => {}} onRename={(_session, title) => renamed.push(title)} onClose={() => {}} |
| 29 | /></LocaleProvider>)); |
| 30 | const row = document.querySelector(".hist-item"); |
| 31 | assert.ok(row); |
| 32 | await act(async () => row.dispatchEvent(new dom.window.MouseEvent("contextmenu", { bubbles: true, clientX: 30, clientY: 30 }))); |
| 33 | const rename = [...document.querySelectorAll<HTMLButtonElement>('[role="menuitem"]')].find(button => button.textContent?.includes("Rename")); |
| 34 | assert.ok(rename, "history menu offers rename"); |
| 35 | await act(async () => rename.click()); |
| 36 | const input = document.querySelector<HTMLInputElement>(".hist-item__rename"); |
| 37 | assert.ok(input, "history rename opens an input"); |
| 38 | const composingEnter = new dom.window.KeyboardEvent("keydown", { key: "Enter", isComposing: true, bubbles: true }); |
| 39 | const safariEnter = new dom.window.KeyboardEvent("keydown", { key: "Enter", bubbles: true }); |
| 40 | Object.defineProperty(safariEnter, "keyCode", { value: 229 }); |
| 41 | await act(async () => { input.dispatchEvent(composingEnter); input.dispatchEvent(safariEnter); }); |
| 42 | assert.equal(document.querySelector(".hist-item__rename"), input, "IME Enter keeps history rename open"); |
| 43 | assert.deepEqual(renamed, [], "IME Enter does not persist an unfinished name"); |
| 44 | await act(async () => input.dispatchEvent(new dom.window.KeyboardEvent("keydown", { key: "Enter", bubbles: true }))); |
| 45 | assert.deepEqual(renamed, ["Old name"], "ordinary Enter commits the name once"); |
| 46 | assert.equal(document.querySelector(".hist-item__rename"), null); |
| 47 | console.log("history session rename: IME and ordinary Enter passed"); |
| 48 | } finally { |
| 49 | await act(async () => root.unmount()); |
| 50 | dom.window.close(); |
| 51 | } |
| 52 |