Merged PR 2431: Add case details API grouping facade
Add case details API grouping facade Related work items: #23754
This commit is contained in:
@@ -5,7 +5,7 @@ import { getJson, requestJson } from "../clients/endpointClient";
|
|||||||
import { buildFileQuery, withBaseUrl } from "../clients/fileRouteBuilder";
|
import { buildFileQuery, withBaseUrl } from "../clients/fileRouteBuilder";
|
||||||
|
|
||||||
export const getCaseMessage = (searchString) => {
|
export const getCaseMessage = (searchString) => {
|
||||||
const route = buildFileQuery("/api/endpoint/getcasemessage_api", {
|
const route = buildFileQuery("/api/cases/get-case-message", {
|
||||||
id: searchString
|
id: searchString
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ export const getCaseMessage = (searchString) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getIncidentbyID = (searchString) => {
|
export const getIncidentbyID = (searchString) => {
|
||||||
const route = buildFileQuery("/api/endpoint/getincidentbyid_api", {
|
const route = buildFileQuery("/api/cases/get-incident-by-id", {
|
||||||
searchString
|
searchString
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export const getPartSavedAppeal = (searchString) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getSIPSEvents = async (caseid) => {
|
export const getSIPSEvents = async (caseid) => {
|
||||||
const route = buildFileQuery("/api/endpoint/getsipsevents_api", {
|
const route = buildFileQuery("/api/cases/get-sips-events", {
|
||||||
caseid
|
caseid
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ export const getSIPSEvents = async (caseid) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getSIPSMedia = async (caseid) => {
|
export const getSIPSMedia = async (caseid) => {
|
||||||
const route = buildFileQuery("/api/endpoint/getsipsmedia_api", {
|
const route = buildFileQuery("/api/cases/get-sips-media", {
|
||||||
caseid
|
caseid
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ export const getSearchDocumentDetailsPaged = (
|
|||||||
|
|
||||||
export const getLinkedCases = (parentIncidentid) => {
|
export const getLinkedCases = (parentIncidentid) => {
|
||||||
return getJson(
|
return getJson(
|
||||||
"/api/endpoint/getlinkedcases_api?parentincidentid=" + parentIncidentid
|
"/api/cases/get-linked-cases?parentincidentid=" + parentIncidentid
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
consoleLogger(error);
|
consoleLogger(error);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -318,6 +318,59 @@ UI journey
|
|||||||
- grouping would require contract change
|
- grouping would require contract change
|
||||||
- In these cases, documentation may still classify the area conceptually, but additive façade rollout should wait for a safer bounded slice.
|
- In these cases, documentation may still classify the area conceptually, but additive façade rollout should wait for a safer bounded slice.
|
||||||
|
|
||||||
|
#### Additional vertical-slice proof point — Case Details read journey
|
||||||
|
|
||||||
|
- The active Case Details read journey now has a fourth additive grouping slice for the proven case-specific read family only:
|
||||||
|
- grouped façade routes exist under `pages/api/cases/` for the proven active reads:
|
||||||
|
- `get-incident-by-id`
|
||||||
|
- `get-case-message`
|
||||||
|
- `get-linked-cases`
|
||||||
|
- `get-sips-events`
|
||||||
|
- `get-sips-media`
|
||||||
|
- the existing service-layer adoption is split across the current owning read services:
|
||||||
|
- `actions/services/caseDirectService.js` now targets grouped cases routes for:
|
||||||
|
- `getIncidentbyID`
|
||||||
|
- `getCaseMessage`
|
||||||
|
- `getSIPSEvents`
|
||||||
|
- `getSIPSMedia`
|
||||||
|
- `actions/services/searchDirectService.js` now targets grouped cases routes for:
|
||||||
|
- `getLinkedCases`
|
||||||
|
- legacy endpoint handlers remain present and canonical behind the façade:
|
||||||
|
- `pages/api/endpoint/getincidentbyid_api.js`
|
||||||
|
- `pages/api/endpoint/getcasemessage_api.js`
|
||||||
|
- `pages/api/endpoint/getlinkedcases_api.js`
|
||||||
|
- `pages/api/endpoint/getsipsevents_api.js`
|
||||||
|
- `pages/api/endpoint/getsipsmedia_api.js`
|
||||||
|
- The bounded audit established that the true active Case Details read journey is narrower than the full case page bootstrap:
|
||||||
|
- public ticketnumber case pages (`pages/case/[ticketnumber].js`) still bootstrap primarily through the search family:
|
||||||
|
- `getBasicSearch(...)`
|
||||||
|
- `getSearchDetails(...)`
|
||||||
|
- incident-id case pages (`pages/case/id/[incident].js` and `pages/myportal/case/id/[incident].js`) use the case-specific incident-id route
|
||||||
|
- linked cases are actively loaded inside `components/case/summary.js` as a case-details read concern
|
||||||
|
- SIPS events/media are active case-page enrichments for the relevant case type
|
||||||
|
- documents remain a separate active journey owned by the documents slice through `components/case/documents.js` and `actions/services/searchDirectService.js`
|
||||||
|
- The following routes were intentionally left outside this grouped adoption scope:
|
||||||
|
- `getbasicsearch_api.js`
|
||||||
|
- `getbasicsearchdetails_api.js`
|
||||||
|
- `getbasicsearchdetailspaged_api.js`
|
||||||
|
- `getsearchdocumentdetails_api.js`
|
||||||
|
- `getsearchdocumentdetailspaged_api.js`
|
||||||
|
- `getsearchdocumentTypes_api.js`
|
||||||
|
- `getcase_api.js`
|
||||||
|
- `getcasebyid_api.js`
|
||||||
|
- `getportalmoduledetails_api.js`
|
||||||
|
- Exclusion reasoning:
|
||||||
|
- search bootstrap/detail-expansion routes remain part of Public Search or search-to-case bootstrap rather than the narrowed case-specific read family
|
||||||
|
- published document metadata remains owned by the documents slice
|
||||||
|
- `getcase_api.js` and `getcasebyid_api.js` are not proven active in the current case-page read path and remain adjacent/support routes
|
||||||
|
- `getportalmoduledetails_api.js` is actively used in myportal aggregation/detail enrichment rather than the bounded public/myportal case-details page read slice itself
|
||||||
|
- For this Case Details slice, a complete vertical grouping slice means:
|
||||||
|
- the proven case-specific read family has grouped façade routes
|
||||||
|
- current owning service calls for that family target the grouped routes
|
||||||
|
- adjacent search bootstrap and documents routes remain unchanged
|
||||||
|
- legacy endpoint handlers remain canonical
|
||||||
|
- no route migration, deletion, or contract change has occurred
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Stage 3 — Documentation Maturity
|
### Stage 3 — Documentation Maturity
|
||||||
|
|||||||
@@ -20,6 +20,86 @@ Follow-ups:
|
|||||||
|
|
||||||
### CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption
|
### CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption
|
||||||
|
|
||||||
|
### CL-2026-06-25-API-GROUPING-FACADE-CASE-DETAILS-VERTICAL-SLICE: bounded case-details read grouping and service adoption
|
||||||
|
|
||||||
|
date: 2026-06-25
|
||||||
|
author: Cline
|
||||||
|
scope: `pages/api/cases/get-incident-by-id.js`, `pages/api/cases/get-case-message.js`, `pages/api/cases/get-linked-cases.js`, `pages/api/cases/get-sips-events.js`, `pages/api/cases/get-sips-media.js`, `actions/services/caseDirectService.js`, `actions/services/searchDirectService.js`, `tests/phase22/api-grouping-facade-case-details.test.cjs`, `context/api-grouping-adoption-roadmap.md`
|
||||||
|
type: change
|
||||||
|
rationale: Implement the next additive API grouping façade vertical slice for the bounded Case Details read journey by grouping only the proven case-specific read family under `pages/api/cases/` and adopting those grouped routes in the existing owning service layers without changing bootstrap/search/documents behavior.
|
||||||
|
impact: Runtime behaviour is intended to remain unchanged; grouped cases façade routes now cover the proven incident-id, case-message, linked-cases, and SIPS enrichment reads, legacy endpoint handlers remain canonical, and no route migration, deletion, contract change, document behaviour change, search behaviour change, auth change, storage change, or submission/orchestration change has occurred.
|
||||||
|
status: completed
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
|
||||||
|
- Confirmed the required context was read before implementation:
|
||||||
|
- `context/api-grouping-plan.md`
|
||||||
|
- `context/api-grouping-adoption-roadmap.md`
|
||||||
|
- `context/api-route-map.md`
|
||||||
|
- `context/journey-architecture-map.md`
|
||||||
|
- `context/architecture.md`
|
||||||
|
- `memory-bank/change-log.md`
|
||||||
|
- Performed a bounded audit of the active Case Details read journey across pages, components, services, and endpoint usage.
|
||||||
|
- Audit findings established that:
|
||||||
|
- `pages/case/[ticketnumber].js` and `pages/myportal/case/[ticketnumber].js` still bootstrap primarily through the Public Search family using `getBasicSearch(...)` and `getSearchDetails(...)`
|
||||||
|
- `pages/case/id/[incident].js` and `pages/myportal/case/id/[incident].js` actively use the case-specific incident-id read route through `getIncidentbyID(...)`
|
||||||
|
- `getCaseMessage(...)` is actively used in case-page loading for banner/message content
|
||||||
|
- `getLinkedCases(...)` is actively used inside `components/case/summary.js` as a case-details read concern
|
||||||
|
- `getSIPSEvents(...)` and `getSIPSMedia(...)` are active case-page enrichments for relevant SIPS cases
|
||||||
|
- `components/case/documents.js` and document metadata routes remain part of the documents slice, not this case-details slice
|
||||||
|
- `getcase_api.js`, `getcasebyid_api.js`, and `getportalmoduledetails_api.js` are adjacent/support reads but were not proven to belong to the bounded active case-page read path for this slice
|
||||||
|
- Added grouped façade routes under `pages/api/cases/` for the proven case-specific read family only:
|
||||||
|
- `get-incident-by-id.js`
|
||||||
|
- `get-case-message.js`
|
||||||
|
- `get-linked-cases.js`
|
||||||
|
- `get-sips-events.js`
|
||||||
|
- `get-sips-media.js`
|
||||||
|
- Implemented each façade as the smallest safe compatibility wrapper:
|
||||||
|
- import the existing legacy endpoint handler
|
||||||
|
- delegate `req` and `res` directly to that existing handler
|
||||||
|
- Updated only the existing owning service call sites needed for the bounded slice:
|
||||||
|
- `actions/services/caseDirectService.js`
|
||||||
|
- `getIncidentbyID`
|
||||||
|
- `getCaseMessage`
|
||||||
|
- `getSIPSEvents`
|
||||||
|
- `getSIPSMedia`
|
||||||
|
- `actions/services/searchDirectService.js`
|
||||||
|
- `getLinkedCases`
|
||||||
|
- Left the following unchanged:
|
||||||
|
- legacy endpoint handlers
|
||||||
|
- search bootstrap routes and search detail expansion routes
|
||||||
|
- document metadata/download routes
|
||||||
|
- CRM queries
|
||||||
|
- transforms
|
||||||
|
- query parameter names
|
||||||
|
- response contracts
|
||||||
|
- status codes
|
||||||
|
- auth/session behavior
|
||||||
|
- storage/queue/submission behavior
|
||||||
|
- Added focused characterization coverage to prove:
|
||||||
|
- grouped cases façade routes delegate correctly
|
||||||
|
- legacy endpoint handlers remain present
|
||||||
|
- adopted service methods now target grouped cases routes
|
||||||
|
- request-contract cues remain unchanged
|
||||||
|
- adjacent search/documents/support routes remain outside grouped adoption scope
|
||||||
|
- Updated the adoption roadmap to record the audited scope boundary, included routes, excluded routes, service adoption status, and behaviour-preservation notes for the Case Details read slice.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
- Focused validation only intended for this slice:
|
||||||
|
- `npx eslint pages/api/cases/*.js actions/services/caseDirectService.js actions/services/searchDirectService.js tests/phase22/api-grouping-facade-case-details.test.cjs context/api-grouping-adoption-roadmap.md memory-bank/change-log.md`
|
||||||
|
- `node tests/phase22/api-grouping-facade-case-details.test.cjs`
|
||||||
|
- No repository-wide validation run.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- The bounded Case Details read family is now a complete façade grouping slice only for the proven case-specific read routes adopted here.
|
||||||
|
- Adjacent case bootstrap, document, and myportal aggregation/detail-enrichment routes should remain separate future slices if needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption
|
||||||
|
|
||||||
### CL-2026-06-25-API-GROUPING-FACADE-ROLLOUT-CHECKPOINT: proven rollout pattern and guardrails checkpoint
|
### CL-2026-06-25-API-GROUPING-FACADE-ROLLOUT-CHECKPOINT: proven rollout pattern and guardrails checkpoint
|
||||||
|
|
||||||
date: 2026-06-25
|
date: 2026-06-25
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyGetCaseMessageHandler from "../endpoint/getcasemessage_api";
|
||||||
|
|
||||||
|
export default async function getCaseMessageFacade(req, res) {
|
||||||
|
return legacyGetCaseMessageHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyGetIncidentByIdHandler from "../endpoint/getincidentbyid_api";
|
||||||
|
|
||||||
|
export default async function getIncidentByIdFacade(req, res) {
|
||||||
|
return legacyGetIncidentByIdHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyGetLinkedCasesHandler from "../endpoint/getlinkedcases_api";
|
||||||
|
|
||||||
|
export default async function getLinkedCasesFacade(req, res) {
|
||||||
|
return legacyGetLinkedCasesHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyGetSipsEventsHandler from "../endpoint/getsipsevents_api";
|
||||||
|
|
||||||
|
export default async function getSipsEventsFacade(req, res) {
|
||||||
|
return legacyGetSipsEventsHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyGetSipsMediaHandler from "../endpoint/getsipsmedia_api";
|
||||||
|
|
||||||
|
export default async function getSipsMediaFacade(req, res) {
|
||||||
|
return legacyGetSipsMediaHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const { loadModule, createRes } = require("../phase21/_shared.cjs");
|
||||||
|
|
||||||
|
const rootDir = path.resolve(__dirname, "..", "..");
|
||||||
|
|
||||||
|
const tests = [];
|
||||||
|
const test = (name, fn) => tests.push({ name, fn });
|
||||||
|
|
||||||
|
const read = (relativePath) =>
|
||||||
|
fs.readFileSync(path.join(rootDir, relativePath), "utf8");
|
||||||
|
|
||||||
|
test("cases get-incident-by-id facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/cases/get-incident-by-id.js", {
|
||||||
|
legacyGetIncidentByIdHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ ok: true, route: "incident-by-id" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { method: "GET", query: { searchString: "abc-123" } };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(calls.length, 1);
|
||||||
|
assert.strictEqual(calls[0].req, req);
|
||||||
|
assert.strictEqual(calls[0].res, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("cases get-case-message facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/cases/get-case-message.js", {
|
||||||
|
legacyGetCaseMessageHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ ok: true, route: "case-message" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { method: "GET", query: { id: "case-1" } };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(calls.length, 1);
|
||||||
|
assert.strictEqual(calls[0].req, req);
|
||||||
|
assert.strictEqual(calls[0].res, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("cases get-linked-cases facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/cases/get-linked-cases.js", {
|
||||||
|
legacyGetLinkedCasesHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ ok: true, route: "linked-cases" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { method: "GET", query: { parentincidentid: "parent-1" } };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(calls.length, 1);
|
||||||
|
assert.strictEqual(calls[0].req, req);
|
||||||
|
assert.strictEqual(calls[0].res, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("cases get-sips-events facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/cases/get-sips-events.js", {
|
||||||
|
legacyGetSipsEventsHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ ok: true, route: "sips-events" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { method: "GET", query: { caseid: "sips-1" } };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(calls.length, 1);
|
||||||
|
assert.strictEqual(calls[0].req, req);
|
||||||
|
assert.strictEqual(calls[0].res, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("cases get-sips-media facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/cases/get-sips-media.js", {
|
||||||
|
legacyGetSipsMediaHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ ok: true, route: "sips-media" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { method: "GET", query: { caseid: "sips-2" } };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(calls.length, 1);
|
||||||
|
assert.strictEqual(calls[0].req, req);
|
||||||
|
assert.strictEqual(calls[0].res, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("legacy case-details endpoint handlers remain present", async () => {
|
||||||
|
const legacyFiles = [
|
||||||
|
"pages/api/endpoint/getincidentbyid_api.js",
|
||||||
|
"pages/api/endpoint/getcasemessage_api.js",
|
||||||
|
"pages/api/endpoint/getlinkedcases_api.js",
|
||||||
|
"pages/api/endpoint/getsipsevents_api.js",
|
||||||
|
"pages/api/endpoint/getsipsmedia_api.js",
|
||||||
|
"pages/api/endpoint/getbasicsearch_api.js",
|
||||||
|
"pages/api/endpoint/getbasicsearchdetails_api.js",
|
||||||
|
"pages/api/endpoint/getsearchdocumentdetails_api.js"
|
||||||
|
];
|
||||||
|
|
||||||
|
legacyFiles.forEach((filePath) => {
|
||||||
|
assert.strictEqual(
|
||||||
|
fs.existsSync(path.join(rootDir, filePath)),
|
||||||
|
true,
|
||||||
|
filePath
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("caseDirectService adopted case-details reads now target grouped cases routes", async () => {
|
||||||
|
const source = read("actions/services/caseDirectService.js");
|
||||||
|
|
||||||
|
assert.match(source, /\/api\/cases\/get-incident-by-id/);
|
||||||
|
assert.match(source, /\/api\/cases\/get-case-message/);
|
||||||
|
assert.match(source, /\/api\/cases\/get-sips-events/);
|
||||||
|
assert.match(source, /\/api\/cases\/get-sips-media/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("searchDirectService linked-cases read now targets grouped cases route", async () => {
|
||||||
|
const source = read("actions/services/searchDirectService.js");
|
||||||
|
|
||||||
|
assert.match(source, /\/api\/cases\/get-linked-cases\?/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("case-details service contracts remain unchanged", async () => {
|
||||||
|
const caseSource = read("actions/services/caseDirectService.js");
|
||||||
|
const searchSource = read("actions/services/searchDirectService.js");
|
||||||
|
|
||||||
|
assert.match(caseSource, /id: searchString/);
|
||||||
|
assert.match(caseSource, /searchString/);
|
||||||
|
assert.match(caseSource, /caseid/);
|
||||||
|
assert.match(searchSource, /parentincidentid=/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("adjacent search and documents routes remain outside case-details adoption scope", async () => {
|
||||||
|
const caseSource = read("actions/services/caseDirectService.js");
|
||||||
|
const searchSource = read("actions/services/searchDirectService.js");
|
||||||
|
|
||||||
|
assert.match(searchSource, /\/api\/endpoint\/getbasicsearch_api/);
|
||||||
|
assert.match(searchSource, /\/api\/endpoint\/getbasicsearchdetails_api/);
|
||||||
|
assert.match(
|
||||||
|
searchSource,
|
||||||
|
/\/api\/documents\/get-search-document-details\?/
|
||||||
|
);
|
||||||
|
assert.doesNotMatch(caseSource, /\/api\/cases\/get-case"/);
|
||||||
|
assert.doesNotMatch(caseSource, /\/api\/cases\/get-case-by-id/);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
let failures = 0;
|
||||||
|
|
||||||
|
for (const { name, fn } of tests) {
|
||||||
|
try {
|
||||||
|
await fn();
|
||||||
|
console.log(`✓ ${name}`);
|
||||||
|
} catch (error) {
|
||||||
|
failures += 1;
|
||||||
|
console.error(`✗ ${name}`);
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures > 0) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
Reference in New Issue
Block a user