const fs = require("fs"); const path = require("path"); const vm = require("vm"); const assert = require("assert"); const rootDir = path.resolve(__dirname, "..", ".."); const loadGetLifecycleStageStatusModule = () => { const filePath = path.join( rootDir, "lib", "domain", "case-lifecycle", "getLifecycleStageStatus.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace( /export function\s+getLifecycleStageStatus/, "function getLifecycleStageStatus" ); source += "\nmodule.exports = { getLifecycleStageStatus };\n"; const context = { module: { exports: {} }, exports: {}, require }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const loadClosedCaseStatusModule = () => { const filePath = path.join( rootDir, "lib", "domain", "case-lifecycle", "isClosedCaseStatus.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace( /export function\s+isClosedCaseStatus/, "function isClosedCaseStatus" ); source += "\nmodule.exports = { isClosedCaseStatus };\n"; const context = { module: { exports: {} }, exports: {}, require }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const loadGetLifecycleStageIndexModule = () => { const filePath = path.join( rootDir, "lib", "domain", "case-lifecycle", "getLifecycleStageIndex.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, ""); source = source.replace( /export function\s+getLifecycleStageIndex/, "function getLifecycleStageIndex" ); source += "\nmodule.exports = { getLifecycleStageIndex };\n"; const context = { module: { exports: {} }, exports: {}, require, isClosedCaseStatus: closedCaseStatusModule.isClosedCaseStatus }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const loadMapAppealTypeModule = () => { const filePath = path.join( rootDir, "lib", "domain", "case-lifecycle", "mapAppealType.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/export const\s+/g, "const "); source = source.replace( /export function\s+mapAppealType/, "function mapAppealType" ); source += ` module.exports = { caseTypeAliases, caseTypeKeyByAppealTypeId, normaliseCaseType, mapAppealType }; `; const context = { module: { exports: {} }, exports: {}, require }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const loadMapSpecialistProcessStageTypeModule = () => { const filePath = path.join( rootDir, "lib", "domain", "case-lifecycle", "mapSpecialistProcessStageType.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/export const\s+/g, "const "); source = source.replace( /export function\s+mapSpecialistProcessStageType/, "function mapSpecialistProcessStageType" ); source += ` module.exports = { specialistProcessStageTypeByCaseType, mapSpecialistProcessStageType }; `; const context = { module: { exports: {} }, exports: {}, require }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const loadGetStageCaseTypeKeyModule = () => { const filePath = path.join( rootDir, "lib", "domain", "case-lifecycle", "getStageCaseTypeKey.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, ""); source = source.replace( /export function\s+getStageCaseTypeKey/, "function getStageCaseTypeKey" ); source += "\nmodule.exports = { getStageCaseTypeKey };\n"; const context = { module: { exports: {} }, exports: {}, require, caseTypeAliases: mapAppealTypeModule.caseTypeAliases, caseTypeKeyByAppealTypeId: mapAppealTypeModule.caseTypeKeyByAppealTypeId, mapAppealType: mapAppealTypeModule.mapAppealType, normaliseCaseType: mapAppealTypeModule.normaliseCaseType, mapSpecialistProcessStageType: mapSpecialistProcessStageTypeModule.mapSpecialistProcessStageType }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const loadStageCatalogueModule = () => { const filePath = path.join( rootDir, "components", "case", "summary", "utils", "caseStagesByAppealType.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, ""); source = source.replace(/export const\s+/g, "const "); source += "\nmodule.exports = { stagesByCaseType };\n"; const context = { module: { exports: {} }, exports: {}, require, getStageCaseTypeKey: getStageCaseTypeKeyModule.getStageCaseTypeKey, getStagesForCaseTypeKey: () => [], isClosedCaseStatus: closedCaseStatusModule.isClosedCaseStatus, getLifecycleStageIndex: getLifecycleStageIndexModule.getLifecycleStageIndex, getLifecycleStageStatus: getLifecycleStageStatusModule.getLifecycleStageStatus }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const loadGetStagesForCaseTypeKeyModule = () => { const filePath = path.join( rootDir, "lib", "domain", "case-lifecycle", "getStagesForCaseTypeKey.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, ""); source = source.replace( /export function\s+getStagesForCaseTypeKey/, "function getStagesForCaseTypeKey" ); source += "\nmodule.exports = { getStagesForCaseTypeKey };\n"; const context = { module: { exports: {} }, exports: {}, require: (modulePath) => { if ( modulePath === "../../../components/case/summary/utils/caseStagesByAppealType" ) { return stageCatalogueModule; } return require(modulePath); } }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const loadStageHelperModule = () => { const filePath = path.join( rootDir, "components", "case", "summary", "utils", "caseStagesByAppealType.js" ); let source = fs.readFileSync(filePath, "utf8"); source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, ""); source = source.replace(/export const\s+/g, "const "); source += "\nmodule.exports = { getStagesForAppealType, stagesByCaseType };\n"; const context = { module: { exports: {} }, exports: {}, require, getStageCaseTypeKey: getStageCaseTypeKeyModule.getStageCaseTypeKey, getStagesForCaseTypeKey: getStagesForCaseTypeKeyModule.getStagesForCaseTypeKey, isClosedCaseStatus: closedCaseStatusModule.isClosedCaseStatus, getLifecycleStageIndex: getLifecycleStageIndexModule.getLifecycleStageIndex, getLifecycleStageStatus: getLifecycleStageStatusModule.getLifecycleStageStatus }; vm.runInNewContext(source, context, { filename: filePath }); return context.module.exports; }; const tests = []; const test = (name, fn) => tests.push({ name, fn }); const normalizeForAssertion = (value) => JSON.parse(JSON.stringify(value ?? null)); const closedCaseStatusModule = loadClosedCaseStatusModule(); const getLifecycleStageStatusModule = loadGetLifecycleStageStatusModule(); const getLifecycleStageIndexModule = loadGetLifecycleStageIndexModule(); const mapAppealTypeModule = loadMapAppealTypeModule(); const mapSpecialistProcessStageTypeModule = loadMapSpecialistProcessStageTypeModule(); const getStageCaseTypeKeyModule = loadGetStageCaseTypeKeyModule(); const stageCatalogueModule = loadStageCatalogueModule(); const getStagesForCaseTypeKeyModule = loadGetStagesForCaseTypeKeyModule(); const stageHelperModule = loadStageHelperModule(); const { getLifecycleStageStatus } = getLifecycleStageStatusModule; const { getLifecycleStageIndex } = getLifecycleStageIndexModule; const { getStagesForAppealType, stagesByCaseType } = stageHelperModule; test("returns complete when index is less than current index", () => { assert.strictEqual(getLifecycleStageStatus(2, 5), "complete"); }); test("returns in-progress when index matches current index", () => { assert.strictEqual(getLifecycleStageStatus(5, 5), "in-progress"); }); test("returns not-started when index is greater than current index", () => { assert.strictEqual(getLifecycleStageStatus(6, 5), "not-started"); }); test("preserves current all-not-started behaviour when currentIndex is -1", () => { assert.strictEqual(getLifecycleStageStatus(0, -1), "not-started"); assert.strictEqual(getLifecycleStageStatus(4, -1), "not-started"); assert.strictEqual(getLifecycleStageStatus(-3, -1), "not-started"); }); test("preserves current comparison behaviour for unknown and negative indexes", () => { assert.strictEqual(getLifecycleStageStatus(-2, 3), "complete"); assert.strictEqual(getLifecycleStageStatus(-2, -2), "in-progress"); assert.strictEqual(getLifecycleStageStatus(3, -2), "not-started"); }); test("preserves current null and undefined coercion behaviour", () => { assert.strictEqual(getLifecycleStageStatus(null, 2), "complete"); assert.strictEqual(getLifecycleStageStatus(2, null), "not-started"); assert.strictEqual(getLifecycleStageStatus(null, null), "in-progress"); assert.strictEqual(getLifecycleStageStatus(undefined, 2), "not-started"); assert.strictEqual(getLifecycleStageStatus(2, undefined), "not-started"); assert.strictEqual( getLifecycleStageStatus(undefined, undefined), "in-progress" ); }); test("integration parity preserves lifecycle outputs after status extraction", () => { const scenarios = [ [846040000, 846040018, undefined], [846040011, 846040006, undefined], [846040004, 846040021, undefined], [846040010, 846040049, undefined], [846040015, 846040032, 846040000], [846040000, 846040059, undefined], [846040000, 999999999, undefined] ]; const baselineGetStagesForAppealType = ( caseType, currentStageId, specialistProcess ) => { const stages = normalizeForAssertion( getStagesForAppealType(caseType, currentStageId, specialistProcess) ).map(({ status, ...stage }) => stage); const currentIndex = getLifecycleStageIndex(stages, currentStageId); return stages.map((stage, index) => ({ ...stage, status: currentIndex === -1 ? "not-started" : index < currentIndex ? "complete" : index === currentIndex ? "in-progress" : "not-started" })); }; for (const [caseType, currentStageId, specialistProcess] of scenarios) { assert.deepStrictEqual( normalizeForAssertion( getStagesForAppealType( caseType, currentStageId, specialistProcess ) ), normalizeForAssertion( baselineGetStagesForAppealType( caseType, currentStageId, specialistProcess ) ) ); } }); const run = async () => { let passed = 0; for (const currentTest of tests) { await currentTest.fn(); passed += 1; } console.log( `Phase 22 case-lifecycle-stage-status tests passed (${passed}/${tests.length}).` ); }; module.exports = run; if (require.main === module) { run().catch((error) => { console.error(error); process.exit(1); }); }