| 1 | import assert from "node:assert/strict"; |
| 2 | import { act } from "react"; |
| 3 | import { createTranscriptHarness } from "./transcript-dom-harness"; |
| 4 | import type { Item } from "../lib/useController"; |
| 5 | |
| 6 | const page: Item[] = []; |
| 7 | for (let turn = 1; turn <= 4; turn += 1) { |
| 8 | page.push( |
| 9 | { kind: "user", id: `u${turn}`, text: `prompt ${turn}`, checkpointTurn: turn }, |
| 10 | { kind: "assistant", id: `a${turn}`, text: `answer ${turn}`, reasoning: "", streaming: false }, |
| 11 | ); |
| 12 | } |
| 13 | const harness = await createTranscriptHarness({ deterministic: true }); |
| 14 | try { |
| 15 | let loads = 0; |
| 16 | const window = { hasNewerHistory: true, historyStartTurn: 0, historyEndTurn: 4, totalTurns: 20, geometrySessionKey: "newer-auto" }; |
| 17 | const props = { ...window, onLoadNewerHistory: async () => { loads += 1; return "loaded" as const; } }; |
| 18 | await harness.render(page, props); |
| 19 | await harness.settle(); |
| 20 | assert.equal(loads, 1, "a reader sitting at the bottom of a window with newer history requests the next page once"); |
| 21 | await harness.render([...page], props); |
| 22 | await harness.settle(); |
| 23 | assert.equal(loads, 1, "a republished window does not request another page"); |
| 24 | |
| 25 | await harness.render(page, { ...props, hasNewerHistory: false }); |
| 26 | await harness.settle(); |
| 27 | assert.equal(loads, 1, "nothing is requested once the window reaches the latest turn"); |
| 28 | |
| 29 | let blocked = 0; |
| 30 | const guarded = { ...window, geometrySessionKey: "newer-auto-selected", onLoadNewerHistory: async () => { blocked += 1; return "loaded" as const; } }; |
| 31 | await harness.render(page, { ...guarded, hasNewerHistory: false }); |
| 32 | await harness.settle(); |
| 33 | const selected = harness.container.querySelector<HTMLElement>('[data-chat-anchor-key="a2"] .md p')!; |
| 34 | const range = harness.dom.window.document.createRange(); |
| 35 | range.selectNodeContents(selected.firstChild!); |
| 36 | const selection = harness.dom.window.getSelection()!; |
| 37 | selection.removeAllRanges(); |
| 38 | selection.addRange(range); |
| 39 | await harness.render(page, guarded); |
| 40 | await harness.settle(); |
| 41 | assert.equal(blocked, 0, "a native selection in the transcript blocks the automatic request"); |
| 42 | |
| 43 | let failed = 0; |
| 44 | selection.removeAllRanges(); |
| 45 | const failing = { ...window, geometrySessionKey: "newer-auto-failed", onLoadNewerHistory: async () => { failed += 1; return "empty" as const; } }; |
| 46 | await harness.render(page, failing); |
| 47 | await harness.settle(); |
| 48 | await harness.render([...page], failing); |
| 49 | await harness.settle(); |
| 50 | assert.equal(failed, 1, "a page that did not load is not retried without the reader leaving and returning to the bottom"); |
| 51 | |
| 52 | const manual = harness.container.querySelector<HTMLButtonElement>(".chat-history-newer .btn")!; |
| 53 | await act(async () => manual.click()); |
| 54 | assert.equal(failed, 2, "the manual button still requests a page"); |
| 55 | console.log("chat newer auto-load: bottom request, no repeat, selection guard and manual button passed"); |
| 56 | } finally { await harness.unmount(); await harness.close(); } |
| 57 |