Files
Inventory/scripts/test_frontend.cjs
2026-09-29 08:43:05 +02:00

59 lines
3.1 KiB
JavaScript

// npm install --prefix <temporary directory> jsdom@26
// node scripts/test_frontend.cjs <temporary directory>/node_modules/jsdom
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");
const {JSDOM} = require(process.argv[2] || "jsdom");
const source = fs.readFileSync(path.join(__dirname, "../inventory/static/inventory/js/app.js"), "utf8");
const pause = ms => new Promise(resolve => setTimeout(resolve, ms));
async function run() {
const dom = new JSDOM(`<!doctype html><body data-csrf="test">
<div id="toast-root"></div><div id="drawer-root"></div>
<form data-confirm="Remove employee?"><button>Remove</button></form>
<div id="device-table-container"><table><tbody><tr data-device-id="1" data-revision="7">
<td data-field="employee" data-input="select" data-value="23">Current employee</td>
</tr></tbody></table></div>
</body>`, {url: "https://inventory.test/", runScripts: "outside-only"});
try {
const {window} = dom, requests = [];
window.fetch = async (url, options) => {
requests.push({url, options});
return {ok: true, json: async () => options?.method === "POST"
? {value: 1, display: "Employee 1", revision: 8, updated_at: "2026-09-23T10:00:00Z"}
: {results: [{id: 1, label: "Employee 1"}]}};
};
window.eval(source);
const form = window.document.querySelector("form");
form.dataset.confirm = '<img src=x onerror="window.injected=true">';
form.dataset.confirmAction = "<b>Unsafe label</b>";
form.dispatchEvent(new window.Event("submit", {bubbles: true, cancelable: true}));
const box = window.document.querySelector(".confirm-box");
assert.equal(box.querySelector("img"), null);
assert.equal(box.querySelector("b"), null);
assert.equal(box.querySelector("p").textContent, form.dataset.confirm);
box.querySelector("button").click();
const cell = window.document.querySelector("td");
cell.dispatchEvent(new window.MouseEvent("dblclick", {bubbles: true}));
await pause(20);
assert.equal(cell.querySelector("select").value, "23", "Missing/inactive current value must be preserved");
cell.querySelector("select").dispatchEvent(new window.Event("blur"));
await pause(250);
assert.equal(requests.filter(item => item.options?.method === "POST").length, 0, "Unchanged field must not save on blur");
cell.dispatchEvent(new window.MouseEvent("dblclick", {bubbles: true}));
await pause(20);
const select = cell.querySelector("select");
select.value = "1";
select.dispatchEvent(new window.KeyboardEvent("keydown", {key: "Enter", bubbles: true}));
await pause(20);
const save = requests.find(item => item.options?.method === "POST");
assert.equal(JSON.parse(save.options.body).expected_revision, "7");
assert.equal(cell.closest("tr").dataset.revision, "8");
assert.equal(cell.textContent, "Employee 1");
console.log("Frontend regression checks passed: safe confirmation text, preserved selection, no-op blur, revision save.");
} finally { dom.window.close(); }
}
run().catch(error => { console.error(error); process.exitCode = 1; });