add vitest harness with parser and getRequiredDocs unit tests

This commit is contained in:
Khalim Conn-Kowlessar 2026-05-05 11:26:02 +00:00
parent 35d0af4474
commit 486427f73d
5 changed files with 1455 additions and 2 deletions

1297
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,8 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "vitest run",
"test:watch": "vitest",
"test:e2e:open": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"",
"test:e2e:run": "cypress run",
"migration:generate": "drizzle-kit generate",
@ -91,6 +93,7 @@
"drizzle-kit": "^0.31.5",
"eslint": "^8.57.1",
"prettier": "^3.6.2",
"start-server-and-test": "^2.0.0"
"start-server-and-test": "^2.0.0",
"vitest": "^2.1.9"
}
}

View file

@ -0,0 +1,75 @@
import { describe, expect, it } from "vitest";
import {
BASE_DOCS,
MEASURE_DOC_REQUIREMENTS,
MEASURE_NAMES,
getRequiredDocs,
} from "./measureDocumentRequirements";
describe("MEASURE_NAMES catalogue", () => {
it("includes every measure keyed in MEASURE_DOC_REQUIREMENTS", () => {
for (const name of Object.keys(MEASURE_DOC_REQUIREMENTS)) {
expect(MEASURE_NAMES).toContain(name);
}
});
it("includes the names called out in the trailing comment as base-only", () => {
const baseOnly = [
"CWI",
"EWI",
"IWI",
"Flat roof",
"RIR",
"UFI",
"HW",
"Windows",
"Ext. doors",
"TRVs",
"Heating controls",
"New boiler",
"HHRSH",
"Battery",
"LEL",
"Listed building",
"Removal 2nd heating",
"Others",
];
for (const name of baseOnly) {
expect(MEASURE_NAMES).toContain(name);
}
});
});
describe("getRequiredDocs", () => {
it("returns the explicit doc list for a known measure", () => {
const docs = getRequiredDocs("ASHP");
// ASHP gets BASE + MCS_EXTRA + commissioning_records
expect(docs).toEqual([
...BASE_DOCS,
"mcs_compliance_certificate",
"commissioning_records",
]);
});
it("returns Solar PV's specific docs (with G98 notification)", () => {
const docs = getRequiredDocs("Solar PV");
expect(docs).toContain("g98_notification");
expect(docs).toContain("mcs_compliance_certificate");
});
it("returns BASE_DOCS for a measure that only requires the baseline", () => {
const docs = getRequiredDocs("CWI");
expect(docs).toEqual([...BASE_DOCS]);
});
it("returns BASE_DOCS for an unknown measure name", () => {
const docs = getRequiredDocs("Definitely Not A Real Measure");
expect(docs).toEqual([...BASE_DOCS]);
});
it("returns a fresh array (mutating the result must not affect BASE_DOCS)", () => {
const docs = getRequiredDocs("Unknown");
docs.push("mutation");
expect(BASE_DOCS).not.toContain("mutation");
});
});

View file

@ -0,0 +1,63 @@
import { describe, expect, it } from "vitest";
import { parseMeasures } from "./parseMeasures";
describe("parseMeasures", () => {
it("returns [] for null", () => {
expect(parseMeasures(null)).toEqual([]);
});
it("returns [] for undefined", () => {
expect(parseMeasures(undefined)).toEqual([]);
});
it("returns [] for empty string", () => {
expect(parseMeasures("")).toEqual([]);
});
it("returns [] for whitespace-only string", () => {
expect(parseMeasures(" ")).toEqual([]);
expect(parseMeasures("\t\n ")).toEqual([]);
});
it("splits semicolon-separated HubSpot output and trims each entry", () => {
expect(parseMeasures("ASHP;Solar PV;Loft insulation")).toEqual([
"ASHP",
"Solar PV",
"Loft insulation",
]);
});
it("trims whitespace around semicolon-separated entries", () => {
expect(parseMeasures(" ASHP ; Solar PV ;Loft insulation ")).toEqual([
"ASHP",
"Solar PV",
"Loft insulation",
]);
});
it("splits legacy comma-separated input and trims", () => {
expect(parseMeasures("ASHP, Solar PV, Loft insulation")).toEqual([
"ASHP",
"Solar PV",
"Loft insulation",
]);
});
it("tolerates a mix of semicolons and commas", () => {
expect(parseMeasures("ASHP; Solar PV, Loft insulation")).toEqual([
"ASHP",
"Solar PV",
"Loft insulation",
]);
});
it("drops empty entries from trailing or duplicated separators", () => {
expect(parseMeasures("ASHP;;Solar PV;")).toEqual(["ASHP", "Solar PV"]);
expect(parseMeasures(",ASHP,,Solar PV,")).toEqual(["ASHP", "Solar PV"]);
});
it("returns a single entry when the input contains no separator", () => {
expect(parseMeasures("ASHP")).toEqual(["ASHP"]);
expect(parseMeasures(" ASHP ")).toEqual(["ASHP"]);
});
});

17
vitest.config.ts Normal file
View file

@ -0,0 +1,17 @@
import { defineConfig } from "vitest/config";
import path from "node:path";
export default defineConfig({
test: {
environment: "node",
include: ["src/**/*.test.ts", "src/**/*.test.tsx"],
// Cypress lives under /cypress and uses its own runner; exclude it so the
// two harnesses do not collide.
exclude: ["node_modules", ".next", "cypress"],
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});