| 1 | // Run: tsx src/__tests__/history-catalog-body-hits.test.ts |
| 2 | // |
| 3 | // Body-only history search must remain visible when metadata session matches |
| 4 | // are empty, and load-more must advance the search cursor independently. |
| 5 | |
| 6 | import assert from "node:assert/strict"; |
| 7 | import { readFileSync } from "node:fs"; |
| 8 | import { dirname, join } from "node:path"; |
| 9 | import { fileURLToPath } from "node:url"; |
| 10 | |
| 11 | const root = join(dirname(fileURLToPath(import.meta.url)), ".."); |
| 12 | const panel = readFileSync(join(root, "components/HistoryPanel.tsx"), "utf8"); |
| 13 | const hook = readFileSync(join(root, "lib/useHistoryCatalog.ts"), "utf8"); |
| 14 | |
| 15 | assert.match(panel, /hasBodyHits/, "HistoryPanel must branch on body hits"); |
| 16 | assert.match(panel, /searchHits\.length > 0/, "HistoryPanel must keep body hits out of empty state"); |
| 17 | assert.match(panel, /!isTrash && \(\s*<label className="mem-search history-search"/, "history search input stays available without metadata sessions"); |
| 18 | assert.match(panel, /isTrash && sessions\.length > 0/, "trash search may still gate on session rows"); |
| 19 | assert.match(hook, /nextSearchCursor/, "useHistoryCatalog must track an independent body cursor"); |
| 20 | assert.match(hook, /append[\s\S]*bodyPage\.items/, "useHistoryCatalog must append body hits on load more"); |
| 21 | assert.match(hook, /!append \|\| searchCursor/, "load-more must not restart body search with an empty cursor"); |
| 22 | |
| 23 | console.log(" PASS history body-only hits contract"); |
| 24 |