59 lines
3.1 KiB
JavaScript
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; });
|