Release govoplan-search v0.1.20: centralize filtering and refresh behavior
Module Package Release / publish-packages (push) Successful in 11s
Module Package Release / publish-packages (push) Successful in 11s
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import test from "node:test";
|
||||
|
||||
const requireCore = createRequire(new URL("../../../govoplan-core/webui/package.json", import.meta.url));
|
||||
const ts = requireCore("typescript");
|
||||
|
||||
function loadTypeScript(relativePath, dependencies = {}) {
|
||||
const source = readFileSync(new URL(relativePath, import.meta.url), "utf8");
|
||||
const { outputText } = ts.transpileModule(source, { compilerOptions: {
|
||||
target: ts.ScriptTarget.ES2020, module: ts.ModuleKind.CommonJS,
|
||||
} });
|
||||
const module = { exports: {} };
|
||||
new Function("require", "module", "exports", outputText)((id) => {
|
||||
if (id in dependencies) return dependencies[id];
|
||||
throw new Error(`Unexpected runtime dependency: ${id}`);
|
||||
}, module, module.exports);
|
||||
return module.exports;
|
||||
}
|
||||
|
||||
const { readSearchFilter, writeSearchFilter, effectiveSearchFilters, searchFilterOptions } = loadTypeScript("../src/components/searchFilters.ts");
|
||||
|
||||
test("legacy missing filters remain all; explicit none wins over repeated values", () => {
|
||||
assert.equal(readSearchFilter(new URLSearchParams("q=permit"), "module"), null);
|
||||
assert.deepEqual(readSearchFilter(new URLSearchParams("module=files&module=cases&module=files"), "module"), ["cases", "files"]);
|
||||
assert.deepEqual(readSearchFilter(new URLSearchParams("module=cases&module_none=1"), "module"), []);
|
||||
assert.deepEqual(readSearchFilter(new URLSearchParams("resource_type=case&resource_type_none=1"), "resource_type"), []);
|
||||
});
|
||||
|
||||
test("filter URL roundtrips preserve query, context, language and the other filter", () => {
|
||||
const original = new URLSearchParams("q=permit&context=cases.current&language=german&resource_type=case&other=keep");
|
||||
const selected = writeSearchFilter(original, "module", ["files", "cases", "files"]);
|
||||
assert.deepEqual(selected.getAll("module"), ["cases", "files"]);
|
||||
assert.equal(original.has("module"), false);
|
||||
const none = writeSearchFilter(selected, "module", []);
|
||||
assert.equal(none.get("module_none"), "1");
|
||||
assert.equal(none.has("module"), false);
|
||||
assert.deepEqual(readSearchFilter(none, "module"), []);
|
||||
const all = writeSearchFilter(none, "module", null);
|
||||
assert.equal(all.toString(), original.toString());
|
||||
assert.equal(readSearchFilter(all, "module"), null);
|
||||
});
|
||||
|
||||
test("global all, none and subset selections keep distinct effective requests", () => {
|
||||
assert.deepEqual(effectiveSearchFilters(null, null), { modules: undefined, resourceTypes: undefined, matchNone: false });
|
||||
assert.equal(effectiveSearchFilters([], null).matchNone, true);
|
||||
assert.equal(effectiveSearchFilters(null, []).matchNone, true);
|
||||
assert.deepEqual(effectiveSearchFilters(["cases", "files"], ["case", "file"]), {
|
||||
modules: ["cases", "files"], resourceTypes: ["case", "file"], matchNone: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("context bounds never broaden an empty resource-type intersection", () => {
|
||||
const context = { id: "files.context", moduleId: "files", label: "Files", pathPrefixes: ["/files"], resourceTypes: ["file", "folder"] };
|
||||
assert.deepEqual(effectiveSearchFilters(null, null, context), { modules: ["files"], resourceTypes: ["file", "folder"], matchNone: false });
|
||||
assert.deepEqual(effectiveSearchFilters(["cases"], ["case", "file"], context), { modules: ["files"], resourceTypes: ["file"], matchNone: false });
|
||||
assert.deepEqual(effectiveSearchFilters(null, ["case"], context), { modules: ["files"], resourceTypes: [], matchNone: true });
|
||||
assert.equal(effectiveSearchFilters(null, [], context).matchNone, true);
|
||||
// The hidden global module selection must not override the current context.
|
||||
assert.equal(effectiveSearchFilters([], null, context).matchNone, false);
|
||||
assert.equal(effectiveSearchFilters(null, null, { ...context, resourceTypes: [] }).matchNone, false);
|
||||
});
|
||||
|
||||
const catalogue = [
|
||||
{ provider_id: "files.source", module_id: "files", resource_type: "file", label: "Files", order: 1 },
|
||||
{ provider_id: "files.source", module_id: "files", resource_type: "folder", label: "Folders", order: 2 },
|
||||
{ provider_id: "cases.source", module_id: "cases", resource_type: "case", label: "Cases", order: 3 },
|
||||
];
|
||||
const observed = [{ module_id: "mail", resource_type: "message" }];
|
||||
const labels = new Map([["files", "Dateien"], ["cases", "Fälle"]]);
|
||||
|
||||
test("filter choices combine catalogue, observed values and removable stale selections", () => {
|
||||
const all = searchFilterOptions(catalogue, observed, labels, ["files", "retired-source"], ["unknown-type"]);
|
||||
assert.deepEqual(new Set(all.modules.map((item) => item.value)), new Set(["files", "cases", "mail", "retired-source"]));
|
||||
assert.equal(all.modules.find((item) => item.value === "files").label, "Dateien");
|
||||
assert.deepEqual(new Set(all.resourceTypes.map((item) => item.value)), new Set(["file", "folder", "unknown-type"]));
|
||||
assert.deepEqual(searchFilterOptions(catalogue, observed, labels, [], null).resourceTypes, []);
|
||||
const context = { id: "files.context", moduleId: "files", label: "Files", pathPrefixes: ["/files"], resourceTypes: ["file"] };
|
||||
assert.deepEqual(searchFilterOptions(catalogue, observed, labels, null, null, context).resourceTypes, [{ value: "file", label: "Files" }]);
|
||||
});
|
||||
|
||||
test("explicit none is a frontend-only empty response; legacy API empty arrays still request all", async () => {
|
||||
const paths = [], calls = [];
|
||||
const response = { query: "permit", results: [], diagnostics: [], next_cursor: null, has_more: false };
|
||||
const { search } = loadTypeScript("../src/api/search.ts", { "@govoplan/core-webui": {
|
||||
apiPath: (path, parameters) => { paths.push({ path, parameters }); return path; },
|
||||
apiFetch: async (...args) => { calls.push(args); return response; },
|
||||
} });
|
||||
const settings = { apiBaseUrl: "", apiKey: "", accessToken: "" };
|
||||
assert.deepEqual(await search(settings, { query: "permit", matchNone: true, modules: [] }), response);
|
||||
assert.equal(calls.length, 0);
|
||||
assert.equal(paths.length, 0);
|
||||
await search(settings, { query: "permit", modules: [], resourceTypes: [] });
|
||||
assert.equal(calls.length, 1);
|
||||
assert.deepEqual(paths[0].parameters.module, []);
|
||||
assert.deepEqual(paths[0].parameters.resource_type, []);
|
||||
assert.equal("matchNone" in paths[0].parameters, false);
|
||||
await search(settings, { query: "permit", modules: ["cases", "files"], resourceTypes: ["case", "file"] });
|
||||
assert.deepEqual(paths[1].parameters.module, ["cases", "files"]);
|
||||
assert.deepEqual(paths[1].parameters.resource_type, ["case", "file"]);
|
||||
});
|
||||
Reference in New Issue
Block a user