123 lines
4.2 KiB
JavaScript
123 lines
4.2 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* ObsiGate - JSDOM tests for the semantic search toggle (ROADMAP #70).
|
|
*
|
|
* Verifies the "Recherche sémantique" toggle wiring in frontend/js/search.js:
|
|
* - the toggle is disabled until the backend reports semantic availability
|
|
* - clicking it flips state.semanticSearch and the active class
|
|
* - clicking while unavailable is a no-op
|
|
*
|
|
* Usage: node tests/frontend/semantic-search.test.mjs
|
|
*/
|
|
|
|
import { strict as assert } from "node:assert";
|
|
import { JSDOM } from "jsdom";
|
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
import path from "node:path";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const REPO_ROOT = path.resolve(__dirname, "..", "..");
|
|
|
|
// ── JSDOM bootstrap ────────────────────────────────────────────────────────
|
|
const dom = new JSDOM(
|
|
`<!DOCTYPE html><html><body>
|
|
<input id="search-input" type="text" />
|
|
<select id="vault-filter"><option value="all">all</option></select>
|
|
<div id="results-bar" style="display:none">
|
|
<span id="rb-count">0/0</span>
|
|
<button id="rb-prev"></button>
|
|
<button id="rb-next"></button>
|
|
<button id="rb-case"></button>
|
|
<button id="rb-word"></button>
|
|
<button id="rb-regex"></button>
|
|
<button id="rb-semantic" disabled></button>
|
|
</div>
|
|
<button id="search-filter-btn"></button>
|
|
<button id="search-clear-btn"></button>
|
|
<div id="search-filter-row" style="display:none">
|
|
<input id="search-include-input" />
|
|
<input id="search-exclude-input" />
|
|
</div>
|
|
<div id="content-area"></div>
|
|
<div id="search-chips"></div>
|
|
</body></html>`,
|
|
{ url: "https://example.com/", pretendToBeVisual: true },
|
|
);
|
|
|
|
const w = dom.window;
|
|
globalThis.window = w;
|
|
globalThis.document = w.document;
|
|
globalThis.HTMLElement = w.HTMLElement;
|
|
globalThis.Element = w.Element;
|
|
globalThis.Node = w.Node;
|
|
globalThis.Event = w.Event;
|
|
globalThis.CustomEvent = w.CustomEvent;
|
|
globalThis.KeyboardEvent = w.KeyboardEvent;
|
|
globalThis.localStorage = w.localStorage;
|
|
Object.defineProperty(globalThis, "navigator", {
|
|
value: w.navigator,
|
|
configurable: true,
|
|
writable: true,
|
|
});
|
|
globalThis.MutationObserver = w.MutationObserver;
|
|
globalThis.getComputedStyle = w.getComputedStyle.bind(w);
|
|
globalThis.requestAnimationFrame = (cb) => setTimeout(cb, 0);
|
|
globalThis.cancelAnimationFrame = (id) => clearTimeout(id);
|
|
globalThis.fetch = async () => ({ ok: true, json: async () => ({}) });
|
|
|
|
const stateMod = await import(
|
|
pathToFileURL(path.join(REPO_ROOT, "frontend", "js", "state.js")).href
|
|
);
|
|
const { state } = stateMod;
|
|
const searchMod = await import(
|
|
pathToFileURL(path.join(REPO_ROOT, "frontend", "js", "search.js")).href
|
|
);
|
|
|
|
// ── Test harness ───────────────────────────────────────────────────────────
|
|
let testCount = 0;
|
|
let passCount = 0;
|
|
|
|
async function test(name, fn) {
|
|
testCount++;
|
|
try {
|
|
await fn();
|
|
console.log(` \u2713 ${name}`);
|
|
passCount++;
|
|
} catch (e) {
|
|
console.log(` \u2717 ${name}`);
|
|
console.log(` ${e.message}`);
|
|
}
|
|
}
|
|
|
|
searchMod.initSearch();
|
|
const semanticBtn = document.getElementById("rb-semantic");
|
|
|
|
await test("semantic toggle is disabled until availability is known", () => {
|
|
assert.equal(semanticBtn.disabled, true);
|
|
});
|
|
|
|
await test("clicking while unavailable does not toggle", () => {
|
|
semanticBtn.click();
|
|
assert.equal(state.semanticSearch, false);
|
|
});
|
|
|
|
await test("clicking while available toggles state and active class", () => {
|
|
state.semanticAvailable = true;
|
|
semanticBtn.disabled = false;
|
|
semanticBtn.click();
|
|
assert.equal(state.semanticSearch, true);
|
|
assert.ok(semanticBtn.classList.contains("active"));
|
|
semanticBtn.click();
|
|
assert.equal(state.semanticSearch, false);
|
|
assert.ok(!semanticBtn.classList.contains("active"));
|
|
});
|
|
|
|
await test("semantic state keys exist", () => {
|
|
assert.equal(state.semanticAvailable, true);
|
|
assert.equal(typeof state.semanticSearch, "boolean");
|
|
});
|
|
|
|
console.log(`\n${passCount}/${testCount} tests passed`);
|
|
if (passCount !== testCount) process.exit(1);
|