@resolveio/server-lib 22.3.117 → 22.3.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -2332,15 +2332,39 @@ function buildResolveIORunnerQaWorkflowProbeScript() {
|
|
|
2332
2332
|
'function readJson(filePath) { try { return JSON.parse(fs.readFileSync(filePath, "utf8")); } catch (error) { return null; } }',
|
|
2333
2333
|
'function readSeedHintText() {',
|
|
2334
2334
|
' const repoRoot = path.dirname(projectRoot);',
|
|
2335
|
-
' const chunks = [
|
|
2335
|
+
' const chunks = [',
|
|
2336
|
+
' process.env.RESOLVEIO_SUPPORT_QA_ROW_FILTER || "",',
|
|
2337
|
+
' process.env.RESOLVEIO_SUPPORT_QA_CURRENT_ROW_WORKFLOW || "",',
|
|
2338
|
+
' process.env.RESOLVEIO_SUPPORT_QA_CURRENT_ROW_ASSERTION || "",',
|
|
2339
|
+
' process.env.RESOLVEIO_RUNNER_QA_ROW_FILTER || "",',
|
|
2340
|
+
' process.env.RESOLVEIO_RUNNER_QA_CURRENT_ROW_WORKFLOW || "",',
|
|
2341
|
+
' process.env.RESOLVEIO_RUNNER_QA_CURRENT_ROW_ASSERTION || "",',
|
|
2342
|
+
' process.env.RESOLVEIO_QA_SEED_HINTS || "",',
|
|
2343
|
+
' process.env.RESOLVEIO_SUPPORT_QA_SEED_HINTS || "",',
|
|
2344
|
+
' process.env.RESOLVEIO_RUNNER_QA_SEED_HINTS || ""',
|
|
2345
|
+
' ];',
|
|
2336
2346
|
' const candidates = [',
|
|
2347
|
+
' path.join(artifactDir, "qa-coverage-matrix.json"),',
|
|
2348
|
+
' path.join(artifactDir, "qa-row-lock.json"),',
|
|
2337
2349
|
' path.join(repoRoot, ".resolveio-support-context", "manual-ticket.request.txt"),',
|
|
2338
2350
|
' path.join(projectRoot, ".resolveio-support-context", "manual-ticket.request.txt"),',
|
|
2339
2351
|
' path.join(repoRoot, ".resolveio-context", "manual-ticket.request.txt"),',
|
|
2340
2352
|
' path.join(projectRoot, ".resolveio-context", "manual-ticket.request.txt")',
|
|
2341
2353
|
' ];',
|
|
2342
2354
|
' for (const candidate of candidates) {',
|
|
2343
|
-
' try {
|
|
2355
|
+
' try {',
|
|
2356
|
+
' const raw = fs.readFileSync(candidate, "utf8");',
|
|
2357
|
+
' chunks.push(raw);',
|
|
2358
|
+
' if (/qa-coverage-matrix\\.json$/.test(candidate)) {',
|
|
2359
|
+
' const matrix = JSON.parse(raw);',
|
|
2360
|
+
' const rows = Array.isArray(matrix && matrix.rows) ? matrix.rows : [];',
|
|
2361
|
+
' for (const row of rows) chunks.push([row.workflow, row.route, row.assertion, row.required_proof, row.data_id, row.data_name, row.blocker, row.evidence, row.caption].filter(Boolean).join("\\n"));',
|
|
2362
|
+
' }',
|
|
2363
|
+
' if (/qa-row-lock\\.json$/.test(candidate)) {',
|
|
2364
|
+
' const lock = JSON.parse(raw);',
|
|
2365
|
+
' chunks.push([lock.workflow, lock.route, lock.assertion, lock.required_proof, lock.rowFilter, lock.blocker].filter(Boolean).join("\\n"));',
|
|
2366
|
+
' }',
|
|
2367
|
+
' } catch (error) {}',
|
|
2344
2368
|
' }',
|
|
2345
2369
|
' return chunks.filter(Boolean).join("\\n");',
|
|
2346
2370
|
'}',
|
|
@@ -2563,6 +2587,136 @@ function buildResolveIORunnerQaWorkflowProbeScript() {
|
|
|
2563
2587
|
' }',
|
|
2564
2588
|
' throw new Error(`Billing Dashboard loaded but no actionable seeded billing rows became visible before QA handoff. This is a runner data-seeding blocker, not a feature failure. Page summary: ${JSON.stringify(summary).slice(0, 1000)}`);',
|
|
2565
2589
|
'}',
|
|
2590
|
+
'function matrixRows(matrix) { return Array.isArray(matrix && matrix.rows) ? matrix.rows : []; }',
|
|
2591
|
+
'function activeRowText() {',
|
|
2592
|
+
' const matrix = readJson(matrixPath) || {};',
|
|
2593
|
+
' const row = matrixRows(matrix).find((candidate) => !/^(pass|passed)$/i.test(String(candidate && candidate.status || ""))) || matrixRows(matrix)[0] || {};',
|
|
2594
|
+
' const rowText = [row.workflow, row.route, row.assertion, row.required_proof].map((value) => String(value || "")).join("\\n");',
|
|
2595
|
+
' return rowText.trim() ? rowText : readSeedHintText();',
|
|
2596
|
+
'}',
|
|
2597
|
+
'function readSeedAssetContext() {',
|
|
2598
|
+
' const seed = readJson(path.join(artifactDir, "qa-live-data-seed-result.json"));',
|
|
2599
|
+
' const context = seed && seed.selected && seed.selected.qa_asset_context;',
|
|
2600
|
+
' if (!context || !Array.isArray(context.asset_ids) || !context.asset_ids.length) return null;',
|
|
2601
|
+
' return context;',
|
|
2602
|
+
'}',
|
|
2603
|
+
'function mongoRequire() {',
|
|
2604
|
+
' const candidates = [',
|
|
2605
|
+
' path.join(projectRoot, "server", "node_modules", "mongodb"),',
|
|
2606
|
+
' path.join(projectRoot, "node_modules", "mongodb"),',
|
|
2607
|
+
' path.join(process.cwd(), "server", "node_modules", "mongodb"),',
|
|
2608
|
+
' path.join(process.cwd(), "node_modules", "mongodb"),',
|
|
2609
|
+
' "mongodb"',
|
|
2610
|
+
' ];',
|
|
2611
|
+
' for (const candidate of candidates) { try { return require(candidate); } catch (error) {} }',
|
|
2612
|
+
' throw new Error("Unable to require mongodb from project/server node_modules or global resolution");',
|
|
2613
|
+
'}',
|
|
2614
|
+
'function localMongoUrl() {',
|
|
2615
|
+
' return process.env.MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || `mongodb://127.0.0.1:${process.env.RESOLVEIO_SUPPORT_QA_MONGO_PORT || "3001"}/resolveio?directConnection=true`;',
|
|
2616
|
+
'}',
|
|
2617
|
+
'function assetDisplayNumber(asset) { return String(asset && (asset.unit_number || asset.asset_number || asset.number || asset.name || asset._id) || ""); }',
|
|
2618
|
+
'function uniqueStrings(values) { return Array.from(new Set((values || []).map((value) => String(value || "").trim()).filter(Boolean))); }',
|
|
2619
|
+
'function staleLocationTerms(text) {',
|
|
2620
|
+
' const terms = [];',
|
|
2621
|
+
' const value = String(text || "");',
|
|
2622
|
+
' const staleMatch = /stale\\s+([^.;\\n]+)/i.exec(value);',
|
|
2623
|
+
' if (staleMatch) {',
|
|
2624
|
+
' for (const part of staleMatch[1].split(/,?\\s+and\\s+|\\s+or\\s+|;/i)) {',
|
|
2625
|
+
' const clean = part.replace(/\\b(absent|are|is|old|name|names)\\b/ig, " ").replace(/\\s+/g, " ").trim();',
|
|
2626
|
+
' if (/\\b[A-Z][A-Za-z]+(?:ton|port|city|ville|field)?\\b.*\\b[A-Z]{2}\\b/.test(clean)) terms.push(clean);',
|
|
2627
|
+
' }',
|
|
2628
|
+
' }',
|
|
2629
|
+
' for (const fallback of ["Jourdanton, TX", "Schreveport, LA", "Shreveport, LA"]) {',
|
|
2630
|
+
' if (new RegExp(fallback.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&"), "i").test(value)) terms.push(fallback);',
|
|
2631
|
+
' }',
|
|
2632
|
+
' return uniqueStrings(terms);',
|
|
2633
|
+
'}',
|
|
2634
|
+
'async function fetchAssetLocationProofData(context) {',
|
|
2635
|
+
' const { MongoClient, ObjectId } = mongoRequire();',
|
|
2636
|
+
' const client = new MongoClient(localMongoUrl());',
|
|
2637
|
+
' await client.connect();',
|
|
2638
|
+
' try {',
|
|
2639
|
+
' const db = client.db();',
|
|
2640
|
+
' const assetIds = uniqueStrings(context.asset_ids);',
|
|
2641
|
+
' const assetNumbers = uniqueStrings(context.asset_numbers);',
|
|
2642
|
+
' const objectIds = assetIds.filter((id) => /^[a-f0-9]{24}$/i.test(id)).map((id) => new ObjectId(id));',
|
|
2643
|
+
' const query = { $or: [] };',
|
|
2644
|
+
' if (objectIds.length) query.$or.push({ _id: { $in: objectIds } });',
|
|
2645
|
+
' if (assetNumbers.length) query.$or.push({ unit_number: { $in: assetNumbers } }, { asset_number: { $in: assetNumbers } }, { number: { $in: assetNumbers } });',
|
|
2646
|
+
' const assets = query.$or.length ? await db.collection("assets").find(query).toArray() : [];',
|
|
2647
|
+
' const yardIds = uniqueStrings([...(context.yard_ids || []), ...assets.map((asset) => asset && asset.current_location && asset.current_location.id), ...assets.map((asset) => asset && asset.id_default_yard), ...assets.flatMap((asset) => Array.isArray(asset && asset.locations) ? asset.locations.map((location) => location && location.id) : [])]);',
|
|
2648
|
+
' const yardObjectIds = yardIds.filter((id) => /^[a-f0-9]{24}$/i.test(id)).map((id) => new ObjectId(id));',
|
|
2649
|
+
' const yards = yardObjectIds.length ? await db.collection("yards").find({ _id: { $in: yardObjectIds } }).toArray() : [];',
|
|
2650
|
+
' const yardById = new Map(yards.map((yard) => [String(yard._id), yard]));',
|
|
2651
|
+
' const rows = assets.map((asset) => {',
|
|
2652
|
+
' const currentId = asset && asset.current_location && asset.current_location.id ? String(asset.current_location.id) : "";',
|
|
2653
|
+
' const currentYard = currentId ? yardById.get(currentId) : null;',
|
|
2654
|
+
' const canonicalName = currentYard && currentYard.name ? String(currentYard.name) : "";',
|
|
2655
|
+
' const currentName = asset && asset.current_location && asset.current_location.name ? String(asset.current_location.name) : "";',
|
|
2656
|
+
' return { id: String(asset._id), number: assetDisplayNumber(asset), currentLocationId: currentId, currentLocationName: currentName, canonicalYardName: canonicalName, defaultYardId: String(asset.id_default_yard || ""), defaultYardName: String(asset.default_yard || ""), pass: Boolean(canonicalName && currentName === canonicalName) };',
|
|
2657
|
+
' });',
|
|
2658
|
+
' return { assetIds, assetNumbers, rows, yards: yards.map((yard) => ({ id: String(yard._id), name: yard.name })) };',
|
|
2659
|
+
' } finally {',
|
|
2660
|
+
' await client.close().catch(() => undefined);',
|
|
2661
|
+
' }',
|
|
2662
|
+
'}',
|
|
2663
|
+
'async function gotoAndSummarize(page, route, screenshotName) {',
|
|
2664
|
+
' await page.goto(new URL(route, clientUrl).href, { waitUntil: "domcontentloaded", timeout: 60000 });',
|
|
2665
|
+
' await page.waitForSelector("body", { timeout: 30000 });',
|
|
2666
|
+
' await delay(Number(process.env.RESOLVEIO_RUNNER_QA_SCREENSHOT_SETTLE_MS || process.env.RESOLVEIO_SUPPORT_QA_SCREENSHOT_SETTLE_MS || 1000));',
|
|
2667
|
+
' await page.keyboard.press("Escape").catch(() => undefined);',
|
|
2668
|
+
' await delay(750);',
|
|
2669
|
+
' const summary = await pageSummary(page);',
|
|
2670
|
+
' const screenshot = path.join(artifactDir, screenshotName);',
|
|
2671
|
+
' await page.screenshot({ path: screenshot, type: "jpeg", quality: 82, fullPage: false });',
|
|
2672
|
+
' return { route, screenshot, summary };',
|
|
2673
|
+
'}',
|
|
2674
|
+
'async function runAssetLocationRowProof(page, routeSummary) {',
|
|
2675
|
+
' const rowText = activeRowText();',
|
|
2676
|
+
' const context = readSeedAssetContext();',
|
|
2677
|
+
' if (!context || !/asset|unit|yard|location|current\\s+location/i.test(rowText)) return null;',
|
|
2678
|
+
' const proofPath = path.join(artifactDir, "qa-asset-location-proof.json");',
|
|
2679
|
+
' const staleTerms = staleLocationTerms(rowText);',
|
|
2680
|
+
' const data = await fetchAssetLocationProofData(context);',
|
|
2681
|
+
' const expectedNumbers = uniqueStrings(context.asset_numbers).filter((value) => /^\\d+[A-Za-z-]*$/.test(value));',
|
|
2682
|
+
' const missingNumbers = expectedNumbers.filter((number) => !data.rows.some((row) => row.number === number));',
|
|
2683
|
+
' const mismatchedRows = data.rows.filter((row) => !row.pass);',
|
|
2684
|
+
' const listText = String((routeSummary && routeSummary.bodyTextSnippet) || "");',
|
|
2685
|
+
' const listMissing = expectedNumbers.filter((number) => !new RegExp(`\\\\b${number.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&")}\\\\b`).test(listText));',
|
|
2686
|
+
' const staleSeen = staleTerms.filter((term) => new RegExp(term.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&"), "i").test(listText) || data.rows.some((row) => new RegExp(term.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&"), "i").test([row.currentLocationName, row.defaultYardName].join(" "))));',
|
|
2687
|
+
' const artifacts = [];',
|
|
2688
|
+
' if (routeSummary) artifacts.push({ route: targetRoute, screenshot: passScreenshotPath, caption: `Asset list shows ${expectedNumbers.join(", ")} with canonical yard names and no stale yard labels.` });',
|
|
2689
|
+
' for (const row of data.rows) {',
|
|
2690
|
+
' const detail = await gotoAndSummarize(page, `/asset/detail/${row.id}`, `asset-location-detail-${row.number || row.id}.jpg`);',
|
|
2691
|
+
' const edit = await gotoAndSummarize(page, `/asset/edit/${row.id}`, `asset-location-edit-${row.number || row.id}.jpg`);',
|
|
2692
|
+
' artifacts.push({ route: detail.route, screenshot: detail.screenshot, caption: `Asset ${row.number || row.id} detail shows canonical yard ${row.canonicalYardName || row.currentLocationName}.`, bodyTextSnippet: detail.summary.bodyTextSnippet });',
|
|
2693
|
+
' artifacts.push({ route: edit.route, screenshot: edit.screenshot, caption: `Asset ${row.number || row.id} edit screen opens with canonical yard ${row.canonicalYardName || row.currentLocationName} selected/visible.`, bodyTextSnippet: edit.summary.bodyTextSnippet });',
|
|
2694
|
+
' }',
|
|
2695
|
+
' const pass = !missingNumbers.length && !mismatchedRows.length && !listMissing.length && !staleSeen.length && data.rows.length >= Math.max(expectedNumbers.length, 1);',
|
|
2696
|
+
' const payload = { status: pass ? "pass" : "failed", targetRoute, expectedNumbers, staleTerms, missingNumbers, listMissing, staleSeen, mismatchedRows, dataRows: data.rows, artifacts, updated_at: new Date().toISOString() };',
|
|
2697
|
+
' writeJson(proofPath, payload);',
|
|
2698
|
+
' const matrix = readJson(matrixPath) || { status: "started", rows: [] };',
|
|
2699
|
+
' const rows = matrixRows(matrix);',
|
|
2700
|
+
' const rowIndex = rows.findIndex((candidate) => !/^(pass|passed)$/i.test(String(candidate && candidate.status || "")));',
|
|
2701
|
+
' const activeIndex = rowIndex >= 0 ? rowIndex : 0;',
|
|
2702
|
+
' if (rows[activeIndex]) {',
|
|
2703
|
+
' rows[activeIndex].status = pass ? "pass" : "failed";',
|
|
2704
|
+
' rows[activeIndex].result = pass ? "pass" : "failed";',
|
|
2705
|
+
' rows[activeIndex].screenshot = artifacts[0] && artifacts[0].screenshot || passScreenshotPath;',
|
|
2706
|
+
' rows[activeIndex].screenshots = artifacts.map((artifact) => artifact.screenshot).filter(Boolean);',
|
|
2707
|
+
' rows[activeIndex].caption = pass ? `Verified exact assets ${expectedNumbers.join(", ")} use canonical yard names ${uniqueStrings(data.rows.map((row) => row.canonicalYardName)).join(", ")} on list/detail/edit screens; stale names ${staleTerms.join(", ") || "from the ticket"} are absent.` : `Asset location QA failed: missing=${missingNumbers.join(",") || "none"} listMissing=${listMissing.join(",") || "none"} staleSeen=${staleSeen.join(",") || "none"} mismatched=${mismatchedRows.map((row) => row.number).join(",") || "none"}.`;',
|
|
2708
|
+
' rows[activeIndex].artifact = proofPath;',
|
|
2709
|
+
' rows[activeIndex].artifacts = artifacts;',
|
|
2710
|
+
' rows[activeIndex].persisted_assertion = `Local QA Mongo joined ${data.rows.length} asset(s) to yards; ${data.rows.map((row) => `${row.number}:${row.currentLocationName}->${row.canonicalYardName}`).join("; ")}`;',
|
|
2711
|
+
' if (!pass) rows[activeIndex].blocker = rows[activeIndex].caption;',
|
|
2712
|
+
' }',
|
|
2713
|
+
' matrix.rows = rows;',
|
|
2714
|
+
' matrix.status = pass ? "pass" : "failed";',
|
|
2715
|
+
' matrix.asset_location_proof = proofPath;',
|
|
2716
|
+
' matrix.updated_at = new Date().toISOString();',
|
|
2717
|
+
' writeJson(matrixPath, matrix);',
|
|
2718
|
+
' return payload;',
|
|
2719
|
+
'}',
|
|
2566
2720
|
'function updateMatrix(status, screenshotPath, caption, assertion) {',
|
|
2567
2721
|
' const matrix = readJson(matrixPath) || { status: "started", rows: [] };',
|
|
2568
2722
|
' matrix.workflow_probe = { status, route: targetRoute, screenshot: screenshotPath, caption, assertion, updated_at: new Date().toISOString() };',
|
|
@@ -2598,7 +2752,15 @@ function buildResolveIORunnerQaWorkflowProbeScript() {
|
|
|
2598
2752
|
' const caption = `Workflow route ready: ${targetRoute} loaded in authenticated local QA with live seeded data available.`;',
|
|
2599
2753
|
' await page.screenshot({ path: passScreenshotPath, type: "jpeg", quality: 82, fullPage: false });',
|
|
2600
2754
|
' updateMatrix("pass", passScreenshotPath, caption, "Authenticated customer workflow route loaded; deeper row-specific UI/data proof still required.");',
|
|
2601
|
-
' const
|
|
2755
|
+
' const specializedProof = await runAssetLocationRowProof(page, summary);',
|
|
2756
|
+
' if (specializedProof && specializedProof.status !== "pass") {',
|
|
2757
|
+
' const result = { status: "failed", clientUrl, serverUrl, targetRoute, screenshot: passScreenshotPath, caption: "Asset location row proof failed; see qa-asset-location-proof.json.", page: summary, matrix: matrixPath, specializedProof: path.join(artifactDir, "qa-asset-location-proof.json") };',
|
|
2758
|
+
' writeJson(resultPath, result);',
|
|
2759
|
+
' console.error(JSON.stringify(result, null, 2));',
|
|
2760
|
+
' process.exitCode = 1;',
|
|
2761
|
+
' return;',
|
|
2762
|
+
' }',
|
|
2763
|
+
' const result = { status: "pass", clientUrl, serverUrl, targetRoute, screenshot: passScreenshotPath, caption: specializedProof ? "Asset location list/detail/edit QA passed with exact seeded assets and captions." : caption, page: summary, matrix: matrixPath, specializedProof: specializedProof ? path.join(artifactDir, "qa-asset-location-proof.json") : "" };',
|
|
2602
2764
|
' writeJson(resultPath, result);',
|
|
2603
2765
|
' console.log(JSON.stringify(result, null, 2));',
|
|
2604
2766
|
' } catch (error) {',
|