Refactor breadcrumbs to use factory-based route renderers

This commit is contained in:
2026-04-09 12:08:36 +01:00
parent 5f6dde782a
commit 062cd23463
7 changed files with 813 additions and 382 deletions
+350 -165
View File
@@ -1,187 +1,372 @@
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const assert = require("assert");
const rootDir = path.resolve(__dirname, "..", "..");
const loadRouteStateModule = () => {
const filePath = path.join(rootDir, "lib", "routing", "routeState.js");
let source = fs.readFileSync(filePath, "utf8");
source = source.replace(/export const\s+/g, "const ");
source +=
"\nmodule.exports = { normalizeRouteStateQuery, resolveSearchResultsHref, resolveSearchBreadcrumbLabel, isDnsRoutePath, resolveCaseBreadcrumbState };\n";
const context = {
module: { exports: {} },
exports: {},
require
};
vm.runInNewContext(source, context, { filename: filePath });
return context.module.exports;
};
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
test("routing/routeState normalizes va/adv/ads flags and key", async () => {
const mod = loadRouteStateModule();
const helperModulePath = path.join(
__dirname,
"..",
"..",
"lib",
"routing",
"routeState.js"
);
const normalized = mod.normalizeRouteStateQuery({
va: "true",
adv: "false",
ads: "true",
key: "myCases"
const toPlain = (value) => JSON.parse(JSON.stringify(value));
const loadHelpers = () => {
const source = fs.readFileSync(helperModulePath, "utf8");
const transformedSource = `
${source
.replace(/export const /g, "const ")
.replace(/export function /g, "function ")}
module.exports = {
normalizeRouteStateQuery,
resolveSearchResultsHref,
resolveSearchBreadcrumbLabel,
isDnsRoutePath,
resolveCaseBreadcrumbState
};
`;
const sandbox = {
module: { exports: {} },
exports: {},
require,
__dirname: path.dirname(helperModulePath),
__filename: helperModulePath,
console
};
vm.runInNewContext(transformedSource, sandbox, {
filename: helperModulePath
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(normalized)), {
viewAll: true,
advanced: false,
address: true,
key: "myCases"
});
return sandbox.module.exports;
};
test("normalizeRouteStateQuery maps known boolean query flags", () => {
const { normalizeRouteStateQuery } = loadHelpers();
assert.deepStrictEqual(
toPlain(
normalizeRouteStateQuery({
va: "true",
adv: "false",
ads: "true",
key: "myCases"
})
),
{
viewAll: true,
advanced: false,
address: true,
key: "myCases"
}
);
});
test("routing/routeState resolves view-all href when va=true", async () => {
const mod = loadRouteStateModule();
test("resolveSearchResultsHref falls back to myportal when no flags and no search query exist for signed-in user", () => {
const { resolveSearchResultsHref } = loadHelpers();
const href = mod.resolveSearchResultsHref({
query: { va: "true", key: "myCases", adv: "true" },
hasSession: true
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(href)), {
pathname: "/myportal/viewall",
query: { key: "myCases" }
});
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: {},
hasSession: true,
fallbackToMyPortalWhenNoFlags: true
})
),
{
pathname: "/myportal"
}
);
});
test("routing/routeState resolves advanced/address/default result hrefs", async () => {
const mod = loadRouteStateModule();
test("resolveSearchResultsHref preserves public searchresults route when search query exists and no flags are set", () => {
const { resolveSearchResultsHref } = loadHelpers();
const advancedHref = mod.resolveSearchResultsHref({
query: { adv: "true", q: "abc" },
hasSession: false
});
const addressHref = mod.resolveSearchResultsHref({
query: { ads: "true", q: "abc" },
hasSession: true
});
const defaultHref = mod.resolveSearchResultsHref({
query: { q: "abc" },
hasSession: true
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(advancedHref)), {
pathname: "/advancedsearchresults",
query: { adv: "true", q: "abc" }
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(addressHref)), {
pathname: "/myportal/addresssearchresults",
query: { ads: "true", q: "abc" }
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(defaultHref)), {
pathname: "/myportal/searchresults",
query: { q: "abc" }
});
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: { q: "abc" },
hasSession: false,
fallbackToMyPortalWhenNoFlags: true
})
),
{
pathname: "/searchresults",
query: { q: "abc" }
}
);
});
test("routing/routeState supports fallback to /myportal when no route flags", async () => {
const mod = loadRouteStateModule();
test("resolveSearchResultsHref preserves myportal searchresults route when search query exists and no flags are set for signed-in user", () => {
const { resolveSearchResultsHref } = loadHelpers();
const href = mod.resolveSearchResultsHref({
query: { q: "abc" },
hasSession: false,
fallbackToMyPortalWhenNoFlags: true
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(href)), {
pathname: "/myportal"
});
});
test("routing/routeState supports DNS route override", async () => {
const mod = loadRouteStateModule();
const href = mod.resolveSearchResultsHref({
query: { adv: "true", q: "abc" },
hasSession: true,
isDnsRoute: true
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(href)), {
pathname: "/myportal/dnsapplications"
});
});
test("routing/routeState resolves breadcrumb label using key and route flags", async () => {
const mod = loadRouteStateModule();
const getViewAllLabel = (key) =>
({ myCases: "My Cases", watchedCases: "Watched Cases" })[key] || null;
const fromViewAll = mod.resolveSearchBreadcrumbLabel({
query: { va: "true", key: "myCases" },
getViewAllLabel,
advancedLabel: "Advanced",
addressLabel: "Address",
defaultLabel: "Search"
});
const fromAdvanced = mod.resolveSearchBreadcrumbLabel({
query: { adv: "true" },
getViewAllLabel,
advancedLabel: "Advanced",
addressLabel: "Address",
defaultLabel: "Search"
});
const fromDefault = mod.resolveSearchBreadcrumbLabel({
query: {},
getViewAllLabel,
advancedLabel: "Advanced",
addressLabel: "Address",
defaultLabel: "Search"
});
assert.strictEqual(fromViewAll, "My Cases");
assert.strictEqual(fromAdvanced, "Advanced");
assert.strictEqual(fromDefault, "Search");
});
test("routing/routeState identifies dns and myportal dns route prefixes", async () => {
const mod = loadRouteStateModule();
assert.strictEqual(mod.isDnsRoutePath("/dns"), true);
assert.strictEqual(mod.isDnsRoutePath("/dns/development"), true);
assert.strictEqual(mod.isDnsRoutePath("/myportal/dns"), true);
assert.strictEqual(mod.isDnsRoutePath("/myportal/dns/applications"), true);
assert.strictEqual(mod.isDnsRoutePath("/case/123"), false);
});
test("routing/routeState resolves bundled case breadcrumb state", async () => {
const mod = loadRouteStateModule();
const getViewAllLabel = (key) => ({ myCases: "My Cases" })[key] || null;
const state = mod.resolveCaseBreadcrumbState({
query: { va: "true", key: "myCases" },
hasSession: true,
getViewAllLabel,
advancedLabel: "Advanced",
addressLabel: "Address",
defaultLabel: "Search"
});
assert.deepStrictEqual(JSON.parse(JSON.stringify(state)), {
breadcrumbHref: {
pathname: "/myportal/viewall",
query: { key: "myCases" }
},
caseResultsHref: {
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: { q: "abc" },
hasSession: true,
fallbackToMyPortalWhenNoFlags: true
})
),
{
pathname: "/myportal/searchresults",
query: { va: "true", key: "myCases" }
},
breadcrumbLabel: "My Cases"
});
query: { q: "abc" }
}
);
});
test("resolveSearchResultsHref routes view-all requests to myportal viewall with key only", () => {
const { resolveSearchResultsHref } = loadHelpers();
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: {
va: "true",
key: "mySubmittedReps",
q: "ignored"
},
hasSession: true,
includeViewAll: true
})
),
{
pathname: "/myportal/viewall",
query: { key: "mySubmittedReps" }
}
);
});
test("resolveSearchResultsHref routes advanced requests to advancedsearchresults and preserves query", () => {
const { resolveSearchResultsHref } = loadHelpers();
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: { q: "abc", adv: "true", page: "2" },
hasSession: false
})
),
{
pathname: "/advancedsearchresults",
query: { q: "abc", adv: "true", page: "2" }
}
);
});
test("resolveSearchResultsHref routes address requests to addresssearchresults and preserves query", () => {
const { resolveSearchResultsHref } = loadHelpers();
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: { postcode: "CF10", ads: "true" },
hasSession: true
})
),
{
pathname: "/myportal/addresssearchresults",
query: { postcode: "CF10", ads: "true" }
}
);
});
test("resolveSearchResultsHref routes dns requests to dnsapplications", () => {
const { resolveSearchResultsHref } = loadHelpers();
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: { anything: "x" },
hasSession: true,
isDnsRoute: true
})
),
{
pathname: "/myportal/dnsapplications"
}
);
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: { anything: "x" },
hasSession: false,
isDnsRoute: true
})
),
{
pathname: "/dnsapplications"
}
);
});
test("resolveSearchResultsHref defaults to searchresults and preserves query when no special route applies", () => {
const { resolveSearchResultsHref } = loadHelpers();
assert.deepStrictEqual(
toPlain(
resolveSearchResultsHref({
query: { q: "abc", page: "3" },
hasSession: false
})
),
{
pathname: "/searchresults",
query: { q: "abc", page: "3" }
}
);
});
test("resolveSearchBreadcrumbLabel prioritizes view-all label, then keyed label, then advanced/address/default", () => {
const { resolveSearchBreadcrumbLabel } = loadHelpers();
const getViewAllLabel = (key) => {
const labels = {
myCases: "My cases",
watchedCases: "Watched cases"
};
return labels[key] || null;
};
assert.strictEqual(
resolveSearchBreadcrumbLabel({
query: { va: "true", key: "myCases" },
getViewAllLabel,
advancedLabel: "Advanced search results",
addressLabel: "Address search results",
defaultLabel: "Search results"
}),
"My cases"
);
assert.strictEqual(
resolveSearchBreadcrumbLabel({
query: { key: "watchedCases" },
getViewAllLabel,
advancedLabel: "Advanced search results",
addressLabel: "Address search results",
defaultLabel: "Search results"
}),
"Watched cases"
);
assert.strictEqual(
resolveSearchBreadcrumbLabel({
query: { adv: "true" },
getViewAllLabel,
advancedLabel: "Advanced search results",
addressLabel: "Address search results",
defaultLabel: "Search results"
}),
"Advanced search results"
);
assert.strictEqual(
resolveSearchBreadcrumbLabel({
query: { ads: "true" },
getViewAllLabel,
advancedLabel: "Advanced search results",
addressLabel: "Address search results",
defaultLabel: "Search results"
}),
"Address search results"
);
assert.strictEqual(
resolveSearchBreadcrumbLabel({
query: {},
getViewAllLabel,
advancedLabel: "Advanced search results",
addressLabel: "Address search results",
defaultLabel: "Search results"
}),
"Search results"
);
});
test("isDnsRoutePath correctly identifies dns route families", () => {
const { isDnsRoutePath } = loadHelpers();
assert.strictEqual(isDnsRoutePath("/dns"), true);
assert.strictEqual(isDnsRoutePath("/dns/applications"), true);
assert.strictEqual(isDnsRoutePath("/myportal/dns"), true);
assert.strictEqual(isDnsRoutePath("/myportal/dns/example"), true);
assert.strictEqual(isDnsRoutePath("/searchresults"), false);
assert.strictEqual(isDnsRoutePath("/myportal"), false);
assert.strictEqual(isDnsRoutePath("/myportal/case/[ticketnumber]"), false);
});
test("resolveCaseBreadcrumbState returns breadcrumb and case-results hrefs plus breadcrumb label", () => {
const { resolveCaseBreadcrumbState } = loadHelpers();
const getViewAllLabel = (key) =>
key === "mySubmittedReps" ? "Submitted representations" : null;
assert.deepStrictEqual(
toPlain(
resolveCaseBreadcrumbState({
query: { q: "cas-", adv: "false" },
hasSession: true,
getViewAllLabel,
advancedLabel: "Advanced search results",
addressLabel: "Address search results",
defaultLabel: "Search results"
})
),
{
breadcrumbHref: {
pathname: "/myportal/searchresults",
query: { q: "cas-", adv: "false" }
},
caseResultsHref: {
pathname: "/myportal/searchresults",
query: { q: "cas-", adv: "false" }
},
breadcrumbLabel: "Search results"
}
);
});
test("resolveCaseBreadcrumbState distinguishes view-all breadcrumb from case-results href", () => {
const { resolveCaseBreadcrumbState } = loadHelpers();
const getViewAllLabel = (key) =>
key === "mySubmittedReps" ? "Submitted representations" : null;
assert.deepStrictEqual(
toPlain(
resolveCaseBreadcrumbState({
query: { va: "true", key: "mySubmittedReps" },
hasSession: true,
getViewAllLabel,
advancedLabel: "Advanced search results",
addressLabel: "Address search results",
defaultLabel: "Search results"
})
),
{
breadcrumbHref: {
pathname: "/myportal/viewall",
query: { key: "mySubmittedReps" }
},
caseResultsHref: {
pathname: "/myportal/searchresults",
query: { va: "true", key: "mySubmittedReps" }
},
breadcrumbLabel: "Submitted representations"
}
);
});
const run = async () => {
@@ -193,7 +378,7 @@ const run = async () => {
}
console.log(
`Phase 22 route-state helper tests passed (${passed}/${tests.length}).`
`Phase 22 route-state-helper tests passed (${passed}/${tests.length}).`
);
};