Merged PR 2429: Add public search API grouping facade slice
Add public search API grouping facade slice Related work items: #23754
This commit is contained in:
@@ -51,7 +51,7 @@ export const getBasicSearchPaged = (
|
|||||||
showNumberOfRecords
|
showNumberOfRecords
|
||||||
) => {
|
) => {
|
||||||
return getJson(
|
return getJson(
|
||||||
"/api/endpoint/getbasicsearchpaged_api?searchString=" +
|
"/api/search/basic-paged?searchString=" +
|
||||||
searchString +
|
searchString +
|
||||||
"&pageNumber=" +
|
"&pageNumber=" +
|
||||||
pageNumber +
|
pageNumber +
|
||||||
@@ -103,7 +103,7 @@ export const getBasicDNSSearchPaged = (
|
|||||||
export const getAdvancedSearch = (searchString) => {
|
export const getAdvancedSearch = (searchString) => {
|
||||||
return getJson(
|
return getJson(
|
||||||
BASE_URL +
|
BASE_URL +
|
||||||
"/api/endpoint/getadvancedsearch_api?searchstring=" +
|
"/api/search/advanced?searchstring=" +
|
||||||
JSON.stringify(searchString)
|
JSON.stringify(searchString)
|
||||||
).catch(logAndReturnEmptyValueErrorResponse);
|
).catch(logAndReturnEmptyValueErrorResponse);
|
||||||
};
|
};
|
||||||
@@ -116,7 +116,7 @@ export const getAdvancedSearchPaged = (
|
|||||||
showNumberOfRecords
|
showNumberOfRecords
|
||||||
) => {
|
) => {
|
||||||
return getJson(
|
return getJson(
|
||||||
"/api/endpoint/getadvancedsearchpaged_api?searchstring=" +
|
"/api/search/advanced-paged?searchstring=" +
|
||||||
JSON.stringify(searchString) +
|
JSON.stringify(searchString) +
|
||||||
"&pageNumber=" +
|
"&pageNumber=" +
|
||||||
pageNumber +
|
pageNumber +
|
||||||
@@ -186,12 +186,12 @@ export const getBasicSearchDetailsPaged = async (
|
|||||||
})
|
})
|
||||||
.join(" or ");
|
.join(" or ");
|
||||||
|
|
||||||
const url = `/api/endpoint/getbasicsearchdetailspaged_api?appealTypeName=${appealTypeName}&primaryIdAttribute=${primaryIdAttribute}&incidentID=${encodeURIComponent(
|
const groupedUrl = `/api/search/basic-details-paged?appealTypeName=${appealTypeName}&primaryIdAttribute=${primaryIdAttribute}&incidentID=${encodeURIComponent(
|
||||||
filter
|
filter
|
||||||
)}`;
|
)}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await getJson(url);
|
const data = await getJson(groupedUrl);
|
||||||
return data.value || [];
|
return data.value || [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
consoleLogger(error);
|
consoleLogger(error);
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ Pilot status update (2026-06-25):
|
|||||||
- active published-document metadata consumer (`actions/services/searchDirectService.js`) now targets grouped documents façade routes for details, paged details, and document-type reads
|
- active published-document metadata consumer (`actions/services/searchDirectService.js`) now targets grouped documents façade routes for details, paged details, and document-type reads
|
||||||
- published document download remains on the existing grouped runtime path `pages/api/documents/download/[id].js`
|
- published document download remains on the existing grouped runtime path `pages/api/documents/download/[id].js`
|
||||||
- legacy document metadata endpoint routes remain canonical and stable behind the additive façade wrappers
|
- legacy document metadata endpoint routes remain canonical and stable behind the additive façade wrappers
|
||||||
|
- third additive grouping façade vertical slice implemented for the active Public Search results journey under `pages/api/search/`
|
||||||
|
- active public search consumer (`actions/services/searchDirectService.js`) now targets grouped search façade routes for the proven in-scope public results path
|
||||||
|
- legacy public search endpoint routes remain canonical and stable behind the additive façade wrappers
|
||||||
- legacy watched-case endpoint routes remain canonical and stable
|
- legacy watched-case endpoint routes remain canonical and stable
|
||||||
- no old route removal or migration has occurred
|
- no old route removal or migration has occurred
|
||||||
- runtime behaviour is intended to remain unchanged because the grouped routes delegate to the existing handlers
|
- runtime behaviour is intended to remain unchanged because the grouped routes delegate to the existing handlers
|
||||||
@@ -218,6 +221,44 @@ It means:
|
|||||||
- download behaviour remains unchanged
|
- download behaviour remains unchanged
|
||||||
- no route migration, deletion, or contract change has occurred
|
- no route migration, deletion, or contract change has occurred
|
||||||
|
|
||||||
|
#### Additional vertical-slice proof point — Public Search
|
||||||
|
|
||||||
|
- The active Public Search journey now has a third complete additive grouping slice for the bounded public results path:
|
||||||
|
- grouped façade routes exist under `pages/api/search/` for the proven active service calls:
|
||||||
|
- `basic-paged`
|
||||||
|
- `advanced`
|
||||||
|
- `advanced-paged`
|
||||||
|
- `basic-details-paged`
|
||||||
|
- `actions/services/searchDirectService.js` targets those grouped routes for:
|
||||||
|
- `getBasicSearchPaged`
|
||||||
|
- `getAdvancedSearch`
|
||||||
|
- `getAdvancedSearchPaged`
|
||||||
|
- `getBasicSearchDetailsPaged`
|
||||||
|
- legacy endpoint handlers remain present and canonical behind the façade:
|
||||||
|
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||||
|
- `pages/api/endpoint/getadvancedsearch_api.js`
|
||||||
|
- `pages/api/endpoint/getadvancedsearchpaged_api.js`
|
||||||
|
- `pages/api/endpoint/getbasicsearchdetailspaged_api.js`
|
||||||
|
- runtime behaviour remains unchanged through direct delegation and unchanged service-layer query contracts
|
||||||
|
- The bounded public-search audit also established that:
|
||||||
|
- the active public results UI is driven through `components/search/searchresults.js`
|
||||||
|
- the common service layer is `actions/services/searchDirectService.js`
|
||||||
|
- paged basic results are active in the public results path
|
||||||
|
- advanced search is in scope because `pages/advancedsearchresults.js` actively uses the advanced bootstrap route and the shared results UI actively uses the advanced paged route
|
||||||
|
- paged search-detail expansion is in scope because the active results UI uses `getSearchDetailsPaged(...)`, which calls `getBasicSearchDetailsPaged(...)`
|
||||||
|
- the following routes remain outside this grouped adoption scope because they belong to adjacent or excluded journeys rather than the active bounded public results path:
|
||||||
|
- `getbasicsearch_api.js` (used for adjacent case-detail bootstrap and other non-results contexts)
|
||||||
|
- `getbasicsearchdetails_api.js` (used for adjacent case-detail/bootstrap detail expansion)
|
||||||
|
- address-search variants
|
||||||
|
- DNS search variants
|
||||||
|
- myportal search pages
|
||||||
|
- For this Public Search slice, a complete vertical grouping slice means:
|
||||||
|
- active public results journey calls have grouped façade routes
|
||||||
|
- the active common service layer uses those grouped routes
|
||||||
|
- legacy handlers remain present and canonical
|
||||||
|
- excluded adjacent search journeys remain unchanged
|
||||||
|
- no route migration, deletion, or contract change has occurred
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Stage 3 — Documentation Maturity
|
### Stage 3 — Documentation Maturity
|
||||||
|
|||||||
@@ -18,6 +18,92 @@ Follow-ups:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption
|
||||||
|
|
||||||
|
date: 2026-06-25
|
||||||
|
author: Cline
|
||||||
|
scope: `pages/api/search/basic-paged.js`, `pages/api/search/advanced.js`, `pages/api/search/advanced-paged.js`, `pages/api/search/basic-details-paged.js`, `actions/services/searchDirectService.js`, `tests/phase22/api-grouping-facade-search.test.cjs`, `context/api-grouping-adoption-roadmap.md`
|
||||||
|
type: change
|
||||||
|
rationale: Implement the next additive API grouping façade vertical slice by grouping the active Public Search results journey under `pages/api/search/` and adopting the grouped routes only in the existing common public-search service layer, while preserving legacy endpoint handlers unchanged and leaving adjacent/excluded search journeys alone.
|
||||||
|
impact: Runtime behaviour is intended to remain unchanged; grouped search façade routes now cover the proven active public-results path, `actions/services/searchDirectService.js` now targets those grouped routes for the bounded in-scope calls, legacy endpoint handlers remain canonical, and no migration, route deletion, query-parameter change, paging change, sorting change, payload change, or response-contract 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/portal-api-platform-assessment.md`
|
||||||
|
- `context/architecture.md`
|
||||||
|
- `memory-bank/change-log.md`
|
||||||
|
- Performed a bounded audit of the active Public Search journey across pages, components, services, and endpoint usage.
|
||||||
|
- Audit findings established that:
|
||||||
|
- the active public results UI is driven through `pages/searchresults.js` and `components/search/searchresults.js`
|
||||||
|
- the advanced results page `pages/advancedsearchresults.js` is part of the active public-search scope
|
||||||
|
- the common owning service layer is `actions/services/searchDirectService.js`
|
||||||
|
- `components/search/searchresults.js` actively uses:
|
||||||
|
- `getBasicSearchPaged`
|
||||||
|
- `getAdvancedSearchPaged`
|
||||||
|
- `getSearchDetailsPaged(...)`, which in turn uses `getBasicSearchDetailsPaged`
|
||||||
|
- `pages/advancedsearchresults.js` actively uses `getAdvancedSearch(...)` for bootstrap
|
||||||
|
- `getbasicsearch_api.js` and `getbasicsearchdetails_api.js` remain active in adjacent case-detail/bootstrap paths and therefore remained out of scope for this bounded public-results slice
|
||||||
|
- address-search routes, DNS routes, and myportal search pages were intentionally excluded as separate journeys
|
||||||
|
- Added grouped façade routes under `pages/api/search/` for the proven active public-results path only:
|
||||||
|
- `basic-paged.js`
|
||||||
|
- `advanced.js`
|
||||||
|
- `advanced-paged.js`
|
||||||
|
- `basic-details-paged.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 suitable common service layer in `actions/services/searchDirectService.js` to target grouped façade routes for:
|
||||||
|
- `getBasicSearchPaged`
|
||||||
|
- `getAdvancedSearch`
|
||||||
|
- `getAdvancedSearchPaged`
|
||||||
|
- `getBasicSearchDetailsPaged`
|
||||||
|
- Left the following unchanged:
|
||||||
|
- existing endpoint handlers
|
||||||
|
- query parameter names
|
||||||
|
- CRM queries
|
||||||
|
- paging behaviour
|
||||||
|
- sorting behaviour
|
||||||
|
- search semantics
|
||||||
|
- case-detail behaviour
|
||||||
|
- document behaviour
|
||||||
|
- excluded search journeys
|
||||||
|
- Added focused characterization coverage to prove:
|
||||||
|
- grouped search façade routes delegate correctly
|
||||||
|
- active service methods now target grouped search routes
|
||||||
|
- legacy endpoint handlers remain present
|
||||||
|
- adopted service methods no longer target the legacy endpoint URLs directly
|
||||||
|
- request-contract strings/parameter names remain unchanged
|
||||||
|
- excluded routes remain outside grouped adoption scope
|
||||||
|
- Updated the adoption roadmap to record the active Public Search grouping slice and its exact scope boundary.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
- Focused validation only intended for this slice:
|
||||||
|
- `npx eslint actions/services/searchDirectService.js pages/api/search/*.js tests/phase22/api-grouping-facade-search.test.cjs`
|
||||||
|
- `node tests/phase22/api-grouping-facade-search.test.cjs`
|
||||||
|
- No repository-wide validation run.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- The active Public Search journey is now a complete vertical grouping slice for the bounded public-results scope:
|
||||||
|
- grouped façade routes
|
||||||
|
- active service-layer adoption
|
||||||
|
- legacy canonical handlers retained
|
||||||
|
- unchanged behaviour
|
||||||
|
- Adjacent or excluded search journeys should remain separate future slices if needed:
|
||||||
|
- case-detail bootstrap search
|
||||||
|
- address search
|
||||||
|
- DNS search
|
||||||
|
- myportal search
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### CL-2026-06-25-API-GROUPING-FACADE-DOCUMENTS-VERTICAL-SLICE: active published-document metadata grouping and service adoption
|
### CL-2026-06-25-API-GROUPING-FACADE-DOCUMENTS-VERTICAL-SLICE: active published-document metadata grouping and service adoption
|
||||||
|
|
||||||
date: 2026-06-25
|
date: 2026-06-25
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyAdvancedSearchPagedHandler from "../endpoint/getadvancedsearchpaged_api";
|
||||||
|
|
||||||
|
export default async function advancedSearchPagedFacade(req, res) {
|
||||||
|
return legacyAdvancedSearchPagedHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyAdvancedSearchHandler from "../endpoint/getadvancedsearch_api";
|
||||||
|
|
||||||
|
export default async function advancedSearchFacade(req, res) {
|
||||||
|
return legacyAdvancedSearchHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyBasicSearchDetailsPagedHandler from "../endpoint/getbasicsearchdetailspaged_api";
|
||||||
|
|
||||||
|
export default async function basicSearchDetailsPagedFacade(req, res) {
|
||||||
|
return legacyBasicSearchDetailsPagedHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import legacyBasicSearchPagedHandler from "../endpoint/getbasicsearchpaged_api";
|
||||||
|
|
||||||
|
export default async function basicSearchPagedFacade(req, res) {
|
||||||
|
return legacyBasicSearchPagedHandler(req, res);
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
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("search basic-paged facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/search/basic-paged.js", {
|
||||||
|
legacyBasicSearchPagedHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ ok: true, route: "basic-paged" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
query: {
|
||||||
|
searchString: "cas",
|
||||||
|
pageNumber: "1",
|
||||||
|
orderby: "createdon",
|
||||||
|
fieldSort: "desc",
|
||||||
|
showNumberOfRecords: "10"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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("search advanced facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/search/advanced.js", {
|
||||||
|
legacyAdvancedSearchHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ ok: true, route: "advanced" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { method: "GET", query: { searchstring: '{"q":"cas"}' } };
|
||||||
|
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("search advanced-paged facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/search/advanced-paged.js", {
|
||||||
|
legacyAdvancedSearchPagedHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ ok: true, route: "advanced-paged" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
query: {
|
||||||
|
searchstring: '{"q":"cas"}',
|
||||||
|
pageNumber: "1",
|
||||||
|
orderby: "createdon",
|
||||||
|
fieldSort: "desc",
|
||||||
|
showNumberOfRecords: "10"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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("search basic-details-paged facade delegates to legacy handler", async () => {
|
||||||
|
const calls = [];
|
||||||
|
const mod = loadModule("pages/api/search/basic-details-paged.js", {
|
||||||
|
legacyBasicSearchDetailsPagedHandler: async (req, res) => {
|
||||||
|
calls.push({ req, res });
|
||||||
|
return res.status(200).json({ value: [{ ticketnumber: "CAS-1" }] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
query: {
|
||||||
|
appealTypeName: "pinswg_planningappeals78s",
|
||||||
|
primaryIdAttribute: "pinswg_planningappeals78id",
|
||||||
|
incidentID: "_pinswg_planningappeals78ids_value eq abc"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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 public search endpoint handlers remain present", async () => {
|
||||||
|
const legacyFiles = [
|
||||||
|
"pages/api/endpoint/getbasicsearchpaged_api.js",
|
||||||
|
"pages/api/endpoint/getadvancedsearch_api.js",
|
||||||
|
"pages/api/endpoint/getadvancedsearchpaged_api.js",
|
||||||
|
"pages/api/endpoint/getbasicsearchdetailspaged_api.js",
|
||||||
|
"pages/api/endpoint/getbasicsearch_api.js",
|
||||||
|
"pages/api/endpoint/getbasicsearchdetails_api.js"
|
||||||
|
];
|
||||||
|
|
||||||
|
legacyFiles.forEach((filePath) => {
|
||||||
|
assert.strictEqual(
|
||||||
|
fs.existsSync(path.join(rootDir, filePath)),
|
||||||
|
true,
|
||||||
|
filePath
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("searchDirectService active public search methods now target grouped search routes", async () => {
|
||||||
|
const source = read("actions/services/searchDirectService.js");
|
||||||
|
|
||||||
|
assert.match(source, /\/api\/search\/basic-paged\?/);
|
||||||
|
assert.match(source, /\/api\/search\/advanced\?/);
|
||||||
|
assert.match(source, /\/api\/search\/advanced-paged\?/);
|
||||||
|
assert.match(source, /\/api\/search\/basic-details-paged\?/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("searchDirectService adopted public search methods no longer target legacy endpoint URLs", async () => {
|
||||||
|
const source = read("actions/services/searchDirectService.js");
|
||||||
|
|
||||||
|
assert.doesNotMatch(source, /\/api\/endpoint\/getbasicsearchpaged_api/);
|
||||||
|
assert.doesNotMatch(source, /\/api\/endpoint\/getadvancedsearch_api/);
|
||||||
|
assert.doesNotMatch(source, /\/api\/endpoint\/getadvancedsearchpaged_api/);
|
||||||
|
assert.doesNotMatch(
|
||||||
|
source,
|
||||||
|
/\/api\/endpoint\/getbasicsearchdetailspaged_api/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("searchDirectService excluded routes remain outside grouped public search adoption scope", async () => {
|
||||||
|
const source = read("actions/services/searchDirectService.js");
|
||||||
|
|
||||||
|
assert.match(source, /\/api\/endpoint\/getbasicsearch_api/);
|
||||||
|
assert.match(source, /\/api\/endpoint\/getbasicsearchdetails_api/);
|
||||||
|
assert.match(
|
||||||
|
source,
|
||||||
|
/\/api\/endpoint\/getbasicsearch_by_address_paged_api/
|
||||||
|
);
|
||||||
|
assert.match(source, /\/api\/endpoint\/getbasicsearch_by_address_api/);
|
||||||
|
assert.match(source, /\/api\/endpoint\/getbasicdnssearch_api/);
|
||||||
|
assert.match(source, /\/api\/endpoint\/getbasicdnssearchpaged_api/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("searchDirectService public search request contract strings are preserved", async () => {
|
||||||
|
const source = read("actions/services/searchDirectService.js");
|
||||||
|
|
||||||
|
assert.match(source, /searchString=/);
|
||||||
|
assert.match(source, /searchstring=/);
|
||||||
|
assert.match(source, /pageNumber=/);
|
||||||
|
assert.match(source, /orderby=/);
|
||||||
|
assert.match(source, /fieldSort=/);
|
||||||
|
assert.match(source, /showNumberOfRecords=/);
|
||||||
|
assert.match(source, /appealTypeName=/);
|
||||||
|
assert.match(source, /primaryIdAttribute=/);
|
||||||
|
assert.match(source, /incidentID=/);
|
||||||
|
});
|
||||||
|
|
||||||
|
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