上传真实浏览器仿真推进

结论:新增 LinuxCNC WASM 驱动的真实浏览器仿真页面,接入 browser smoke、release gate、SDK 与项目交付文档,并完成当前推进批次的验收记录。
This commit is contained in:
2026-06-16 20:59:21 +08:00
parent 19eabdf06e
commit 24e1fc18b1
39 changed files with 7563 additions and 59 deletions

View File

@@ -0,0 +1,51 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import {
createProjectBatchAcceptanceReportJsonWorkflow,
createProjectBatchAcceptanceReportValidationActionPlan,
createProjectBatchAcceptanceReportValidationSummaryViewModel,
createProjectBatchAcceptanceReportValidation,
} from "../../../runtime/sdk/src/index.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "../../..");
const artifactPath = process.argv[2] ?? resolve(root, "build/project-batch-acceptance.json");
const artifactJson = readFileSync(artifactPath, "utf8");
const jsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({ reportJson: artifactJson });
const validation = createProjectBatchAcceptanceReportValidation(jsonWorkflow.report);
const validationSummary = createProjectBatchAcceptanceReportValidationSummaryViewModel(validation);
const validationActionPlan = createProjectBatchAcceptanceReportValidationActionPlan(validation);
assert.equal(jsonWorkflow.apiName, "project-batch-acceptance-report-json-workflow");
assert.equal(jsonWorkflow.ready, true);
assert.equal(jsonWorkflow.phase, "ready");
assert.equal(jsonWorkflow.parsed, true);
assert.equal(jsonWorkflow.parseError, null);
assert.deepEqual(jsonWorkflow.missing, []);
assert.equal(validation.apiName, "project-batch-acceptance-report-validation");
assert.equal(validation.ready, true);
assert.equal(validation.phase, "ready");
assert.deepEqual(validation.missing, []);
assert.equal(validation.batchId, "project-batch-acceptance-artifact");
assert.equal(jsonWorkflow.report.apiName, "project-batch-acceptance-report");
assert.equal(jsonWorkflow.report.reportVersion, 1);
assert.equal(jsonWorkflow.report.ready, true);
assert.equal(jsonWorkflow.report.capabilityMatrix.ready, true);
assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "gate"]);
assert.equal(jsonWorkflow.report.workflow.ready, true);
assert.equal(jsonWorkflow.report.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
assert.equal(jsonWorkflow.report.actionPlan.nextCommand, null);
assert.equal(jsonWorkflow.report.checklist.passedCount, 3);
assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "3/3 passed");
assert.equal(validationSummary.apiName, "project-batch-acceptance-report-validation-summary-view-model");
assert.equal(validationSummary.statusLine, "Ready: Batch acceptance report evidence complete");
assert.equal(validationSummary.rows.find(({ id }) => id === "validation")?.value, "ready");
assert.equal(validationActionPlan.apiName, "project-batch-acceptance-report-validation-action-plan");
assert.equal(validationActionPlan.ready, true);
assert.equal(validationActionPlan.nextCommand, null);
assert.equal(validationActionPlan.shellScript, "# Project batch acceptance report validation is already ready.\n");
console.log("project_batch_acceptance_artifact_node_smoke=ok");

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
node "$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_artifact.mjs" "$@"

View File

@@ -0,0 +1,507 @@
import assert from "node:assert/strict";
import {
createProjectBatchAcceptanceActionPlan,
createProjectBatchAcceptanceCapabilityMatrix,
createProjectBatchAcceptanceChecklist,
createProjectBatchAcceptanceReport,
createProjectBatchAcceptanceReportJsonWorkflow,
createProjectBatchAcceptanceReportUrlWorkflowActionPlan,
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel,
createProjectBatchAcceptanceReportValidationActionPlan,
createProjectBatchAcceptanceReportValidationSummaryViewModel,
createProjectBatchAcceptanceReportValidation,
createProjectBatchAcceptanceSummaryViewModel,
createProjectBatchAcceptanceWorkflow,
loadProjectBatchAcceptanceReportUrlWorkflow,
} from "../../../runtime/sdk/src/index.js";
const readyCapabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({
capabilities: [
{
id: "callable-api",
type: "api",
label: "Callable API",
evidence: "createProjectBatchAcceptanceCapabilityMatrix",
},
{
id: "browser-smoke",
type: "browser",
label: "Browser smoke",
evidence: "browser smoke output",
},
{
id: "docs-only-note",
type: "docs",
label: "Docs-only note",
},
],
});
assert.equal(readyCapabilityMatrix.apiName, "project-batch-acceptance-capability-matrix");
assert.equal(readyCapabilityMatrix.matrixVersion, 1);
assert.equal(readyCapabilityMatrix.phase, "ready");
assert.equal(readyCapabilityMatrix.ready, true);
assert.deepEqual(readyCapabilityMatrix.acceptedCapabilityTypes, ["api", "workflow", "gate", "browser"]);
assert.deepEqual(readyCapabilityMatrix.capabilityTypes, ["api", "browser"]);
assert.equal(readyCapabilityMatrix.capabilityCount, 2);
assert.equal(readyCapabilityMatrix.acceptedCount, 2);
assert.equal(readyCapabilityMatrix.rejectedCount, 1);
assert.deepEqual(readyCapabilityMatrix.countsByType, {
api: 1,
workflow: 0,
gate: 0,
browser: 1,
});
assert.equal(readyCapabilityMatrix.rejectedCapabilities[0].type, "docs");
assert.deepEqual(readyCapabilityMatrix.missing, []);
assert.deepEqual(
readyCapabilityMatrix.rows.map(({ id, value }) => [id, value]),
[
["accepted-capabilities", "2"],
["accepted-types", "api, browser"],
["rejected-capabilities", "1"],
["missing", "none"],
],
);
const blockedCapabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({
capabilities: [
{
id: "docs-only-note",
type: "docs",
label: "Docs-only note",
},
],
});
assert.equal(blockedCapabilityMatrix.phase, "blocked");
assert.equal(blockedCapabilityMatrix.ready, false);
assert.equal(blockedCapabilityMatrix.acceptedCount, 0);
assert.equal(blockedCapabilityMatrix.rejectedCount, 1);
assert.deepEqual(blockedCapabilityMatrix.missing, ["batch-capability"]);
const readyWorkflow = createProjectBatchAcceptanceWorkflow({
batchId: "batch-acceptance-workflow",
capabilities: [
{
id: "project-batch-acceptance-workflow-api",
type: "api",
label: "Project batch acceptance workflow API",
evidence: "createProjectBatchAcceptanceWorkflow",
},
{
id: "project-batch-acceptance-node-gate",
type: "gate",
label: "Project batch acceptance Node gate",
evidence: "project_batch_acceptance_workflow_node_smoke=ok",
command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
},
],
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
});
assert.equal(readyWorkflow.apiName, "project-batch-acceptance-workflow");
assert.equal(readyWorkflow.workflowVersion, 1);
assert.equal(readyWorkflow.phase, "ready");
assert.equal(readyWorkflow.ready, true);
assert.equal(readyWorkflow.batchId, "batch-acceptance-workflow");
assert.deepEqual(readyWorkflow.acceptedCapabilityTypes, ["api", "workflow", "gate", "browser"]);
assert.deepEqual(readyWorkflow.capabilityTypes, ["api", "gate"]);
assert.equal(readyWorkflow.capabilityCount, 2);
assert.equal(readyWorkflow.capabilityMatrix.apiName, "project-batch-acceptance-capability-matrix");
assert.equal(readyWorkflow.capabilityMatrix.ready, true);
assert.deepEqual(readyWorkflow.missing, []);
assert.equal(
readyWorkflow.gateResultMatrix.rows.find(({ id }) => id === "project-batch-acceptance")?.passed,
true,
);
assert.deepEqual(
readyWorkflow.rows.map(({ id, value }) => [id, value]),
[
["batch", "batch-acceptance-workflow"],
["capabilities", "api, gate"],
["project-batch-acceptance", "passed"],
["missing", "none"],
],
);
const readySummary = createProjectBatchAcceptanceSummaryViewModel(readyWorkflow);
assert.equal(readySummary.apiName, "project-batch-acceptance-summary-view-model");
assert.equal(readySummary.phase, "ready");
assert.equal(readySummary.statusLine, "Ready: Batch acceptance evidence complete");
assert.equal(readySummary.rows.find(({ id }) => id === "capability-count")?.value, "2");
const readyActionPlan = createProjectBatchAcceptanceActionPlan(readyWorkflow);
assert.equal(readyActionPlan.apiName, "project-batch-acceptance-action-plan");
assert.equal(readyActionPlan.phase, "ready");
assert.equal(readyActionPlan.ready, true);
assert.equal(readyActionPlan.commandCount, 0);
assert.equal(readyActionPlan.nextCommand, null);
assert.equal(readyActionPlan.shellScript, "# Project batch acceptance workflow is already ready.\n");
const readyChecklist = createProjectBatchAcceptanceChecklist({
workflow: readyWorkflow,
actionPlan: readyActionPlan,
});
assert.equal(readyChecklist.apiName, "project-batch-acceptance-checklist");
assert.equal(readyChecklist.checklistVersion, 1);
assert.equal(readyChecklist.phase, "ready");
assert.equal(readyChecklist.ready, true);
assert.equal(readyChecklist.batchId, "batch-acceptance-workflow");
assert.equal(readyChecklist.passedCount, 3);
assert.equal(readyChecklist.blockedCount, 0);
assert.deepEqual(
readyChecklist.rows.map(({ id, value }) => [id, value]),
[
["accepted-capability", "pass: api, gate"],
["batch-acceptance-gate", "pass: passed"],
["next-action", "pass: none"],
],
);
const readyReport = createProjectBatchAcceptanceReport({
batchId: "batch-acceptance-workflow",
capabilities: readyWorkflow.capabilities,
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
});
assert.equal(readyReport.apiName, "project-batch-acceptance-report");
assert.equal(readyReport.reportVersion, 1);
assert.equal(readyReport.phase, "ready");
assert.equal(readyReport.ready, true);
assert.equal(readyReport.batchId, "batch-acceptance-workflow");
assert.deepEqual(readyReport.missing, []);
assert.equal(readyReport.capabilityMatrix.apiName, "project-batch-acceptance-capability-matrix");
assert.equal(readyReport.workflow.apiName, "project-batch-acceptance-workflow");
assert.equal(readyReport.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
assert.equal(readyReport.actionPlan.nextCommand, null);
assert.equal(readyReport.checklist.passedCount, 3);
assert.deepEqual(
readyReport.rows.map(({ id, value }) => [id, value]),
[
["summary", "Ready: Batch acceptance evidence complete"],
["capabilities", "api, gate"],
["checklist", "3/3 passed"],
["next-command", "none"],
],
);
const readyReportValidation = createProjectBatchAcceptanceReportValidation(readyReport);
const readyReportValidationSummary = createProjectBatchAcceptanceReportValidationSummaryViewModel(
readyReportValidation,
);
const readyReportValidationActionPlan = createProjectBatchAcceptanceReportValidationActionPlan(
readyReportValidation,
);
assert.equal(readyReportValidation.apiName, "project-batch-acceptance-report-validation");
assert.equal(readyReportValidation.validationVersion, 1);
assert.equal(readyReportValidation.phase, "ready");
assert.equal(readyReportValidation.ready, true);
assert.deepEqual(readyReportValidation.missing, []);
assert.equal(readyReportValidation.batchId, "batch-acceptance-workflow");
assert.deepEqual(
readyReportValidation.rows.map(({ id, value }) => [id, value]),
[
["report", "project-batch-acceptance-report"],
["capability-matrix", "ready"],
["workflow", "ready"],
["checklist", "ready"],
["missing", "none"],
],
);
assert.equal(
readyReportValidationSummary.statusLine,
"Ready: Batch acceptance report evidence complete",
);
assert.deepEqual(
readyReportValidationSummary.rows.map(({ id, value }) => [id, value]),
[
["report", "project-batch-acceptance-report"],
["batch", "batch-acceptance-workflow"],
["validation", "ready"],
["missing", "none"],
],
);
assert.deepEqual(readyReportValidationActionPlan, {
apiName: "project-batch-acceptance-report-validation-action-plan",
planVersion: 1,
phase: "ready",
ready: true,
missing: [],
missingCount: 0,
commandCount: 0,
nextActionId: null,
nextCommand: null,
commands: [],
shellScript: "# Project batch acceptance report validation is already ready.\n",
rows: [
["report-validation", "Report validation", "ready"],
["missing", "Missing report evidence", "none"],
["next-command", "Next command", "none"],
].map(([id, label, value]) => ({ id, label, value })),
});
const readyReportJsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({
reportJson: JSON.stringify(readyReport),
});
assert.equal(readyReportJsonWorkflow.apiName, "project-batch-acceptance-report-json-workflow");
assert.equal(readyReportJsonWorkflow.workflowVersion, 1);
assert.equal(readyReportJsonWorkflow.phase, "ready");
assert.equal(readyReportJsonWorkflow.ready, true);
assert.equal(readyReportJsonWorkflow.parsed, true);
assert.equal(readyReportJsonWorkflow.parseError, null);
assert.equal(readyReportJsonWorkflow.validation.ready, true);
assert.deepEqual(readyReportJsonWorkflow.missing, []);
let fetchedReportUrl = null;
const readyReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({
reportUrl: "/project-batch-acceptance.json",
fetchRef: async (url) => {
fetchedReportUrl = url;
return {
ok: true,
status: 200,
text: async () => JSON.stringify(readyReport),
};
},
});
const readyReportUrlSummary = createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel(
readyReportUrlWorkflow,
);
const readyReportUrlActionPlan = createProjectBatchAcceptanceReportUrlWorkflowActionPlan(
readyReportUrlWorkflow,
);
assert.equal(fetchedReportUrl, "/project-batch-acceptance.json");
assert.equal(readyReportUrlWorkflow.apiName, "project-batch-acceptance-report-url-workflow");
assert.equal(readyReportUrlWorkflow.workflowVersion, 1);
assert.equal(readyReportUrlWorkflow.phase, "ready");
assert.equal(readyReportUrlWorkflow.ready, true);
assert.equal(readyReportUrlWorkflow.fetched, true);
assert.equal(readyReportUrlWorkflow.httpStatus, 200);
assert.equal(readyReportUrlWorkflow.fetchError, null);
assert.equal(readyReportUrlWorkflow.jsonWorkflow.ready, true);
assert.equal(readyReportUrlWorkflow.validation.ready, true);
assert.deepEqual(readyReportUrlWorkflow.missing, []);
assert.equal(
readyReportUrlSummary.statusLine,
"Ready: Batch report URL workflow complete",
);
assert.deepEqual(
readyReportUrlSummary.rows.map(({ id, value }) => [id, value]),
[
["report-url", "/project-batch-acceptance.json"],
["fetch", "ready (200)"],
["json-workflow", "ready"],
["report-validation", "ready"],
["missing", "none"],
],
);
assert.deepEqual(readyReportUrlActionPlan, {
apiName: "project-batch-acceptance-report-url-workflow-action-plan",
planVersion: 1,
phase: "ready",
ready: true,
missing: [],
actionCount: 0,
commandCount: 0,
nextActionId: null,
nextCommand: null,
actions: [],
commands: [],
shellScript: "# Project batch acceptance report URL workflow is already ready.\n",
rows: [],
});
const blockedWorkflow = createProjectBatchAcceptanceWorkflow({
batchId: "docs-only-batch",
capabilities: [
{
id: "docs-only-note",
type: "docs",
label: "Docs-only note",
},
],
});
assert.equal(blockedWorkflow.phase, "blocked");
assert.equal(blockedWorkflow.ready, false);
assert.deepEqual(blockedWorkflow.capabilityTypes, []);
assert.equal(blockedWorkflow.rejectedCapabilities[0].type, "docs");
assert.deepEqual(blockedWorkflow.missing, ["batch-capability", "project-batch-acceptance"]);
const blockedSummary = createProjectBatchAcceptanceSummaryViewModel(blockedWorkflow);
assert.equal(blockedSummary.statusLine, "Blocked: batch-capability, project-batch-acceptance");
const blockedActionPlan = createProjectBatchAcceptanceActionPlan(blockedWorkflow);
assert.equal(blockedActionPlan.phase, "blocked");
assert.equal(blockedActionPlan.ready, false);
assert.equal(blockedActionPlan.commandCount, 1);
assert.equal(blockedActionPlan.nextActionId, "project-batch-acceptance");
assert.equal(
blockedActionPlan.nextCommand,
"wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
);
assert.match(blockedActionPlan.shellScript, /verify_project_batch_acceptance_workflow\.sh/);
const blockedChecklist = createProjectBatchAcceptanceChecklist({
workflow: blockedWorkflow,
actionPlan: blockedActionPlan,
});
assert.equal(blockedChecklist.phase, "blocked");
assert.equal(blockedChecklist.ready, false);
assert.equal(blockedChecklist.passedCount, 0);
assert.equal(blockedChecklist.blockedCount, 3);
assert.deepEqual(
blockedChecklist.items.map(({ id, status }) => [id, status]),
[
["accepted-capability", "blocked"],
["batch-acceptance-gate", "blocked"],
["next-action", "blocked"],
],
);
const blockedReport = createProjectBatchAcceptanceReport({
batchId: "docs-only-batch",
capabilities: blockedWorkflow.rejectedCapabilities,
});
assert.equal(blockedReport.phase, "blocked");
assert.equal(blockedReport.ready, false);
assert.deepEqual(blockedReport.missing, ["batch-capability", "project-batch-acceptance"]);
assert.equal(blockedReport.checklist.blockedCount, 3);
assert.equal(
blockedReport.rows.find(({ id }) => id === "next-command")?.value,
"wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
);
const blockedReportValidation = createProjectBatchAcceptanceReportValidation(blockedReport);
const blockedReportValidationSummary = createProjectBatchAcceptanceReportValidationSummaryViewModel(
blockedReportValidation,
);
const blockedReportValidationActionPlan = createProjectBatchAcceptanceReportValidationActionPlan(
blockedReportValidation,
);
assert.equal(blockedReportValidation.phase, "blocked");
assert.equal(blockedReportValidation.ready, false);
assert.deepEqual(blockedReportValidation.missing, [
"phase",
"ready",
"missing",
"capabilityMatrix",
"workflow",
"summaryViewModel",
"actionPlan",
"checklist",
"rows.checklist",
]);
assert.match(blockedReportValidationSummary.statusLine, /^Blocked: phase, ready, missing/);
assert.equal(blockedReportValidationSummary.rows.find(({ id }) => id === "validation")?.value, "blocked");
assert.equal(blockedReportValidationActionPlan.phase, "blocked");
assert.equal(blockedReportValidationActionPlan.ready, false);
assert.equal(blockedReportValidationActionPlan.commandCount, 1);
assert.equal(
blockedReportValidationActionPlan.nextCommand,
"wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
);
assert.equal(
blockedReportValidationActionPlan.commands[0].expectedOutput,
"project_batch_acceptance_artifact_node_smoke=ok",
);
const invalidReportJsonWorkflow = createProjectBatchAcceptanceReportJsonWorkflow({
reportJson: "{not-json",
});
assert.equal(invalidReportJsonWorkflow.phase, "blocked");
assert.equal(invalidReportJsonWorkflow.ready, false);
assert.equal(invalidReportJsonWorkflow.parsed, false);
assert.match(invalidReportJsonWorkflow.parseError, /JSON/);
assert.equal(invalidReportJsonWorkflow.missing[0], "report-json");
const missingInputReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({
reportUrl: "",
fetchRef: null,
});
const missingInputReportUrlActionPlan = createProjectBatchAcceptanceReportUrlWorkflowActionPlan(
missingInputReportUrlWorkflow,
);
assert.equal(missingInputReportUrlWorkflow.ready, false);
assert.equal(missingInputReportUrlWorkflow.fetched, false);
assert.deepEqual(missingInputReportUrlWorkflow.missing.slice(0, 2), ["report-url", "fetch"]);
assert.match(
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel(
missingInputReportUrlWorkflow,
).statusLine,
/^Blocked: report-url, fetch/,
);
assert.equal(missingInputReportUrlActionPlan.actionCount, 3);
assert.equal(missingInputReportUrlActionPlan.commandCount, 1);
assert.equal(missingInputReportUrlActionPlan.nextActionId, "provide-report-url");
assert.equal(
missingInputReportUrlActionPlan.nextCommand,
"wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
);
assert.deepEqual(
missingInputReportUrlActionPlan.actions.slice(0, 2).map(({ id, kind, command }) => [id, kind, command]),
[
["provide-report-url", "input", null],
["provide-fetch", "input", null],
],
);
const httpFailureReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({
reportUrl: "/missing-batch-report.json",
fetchRef: async () => ({
ok: false,
status: 404,
text: async () => "",
}),
});
const httpFailureReportUrlActionPlan = createProjectBatchAcceptanceReportUrlWorkflowActionPlan(
httpFailureReportUrlWorkflow,
);
assert.equal(httpFailureReportUrlWorkflow.ready, false);
assert.equal(httpFailureReportUrlWorkflow.fetched, false);
assert.equal(httpFailureReportUrlWorkflow.httpStatus, 404);
assert.match(httpFailureReportUrlWorkflow.fetchError, /404/);
assert.equal(httpFailureReportUrlWorkflow.missing[0], "report-fetch");
assert.match(
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel(
httpFailureReportUrlWorkflow,
).statusLine,
/^Blocked: report-fetch/,
);
assert.equal(httpFailureReportUrlActionPlan.nextActionId, "verify-report-url-fetch");
assert.equal(httpFailureReportUrlActionPlan.commands.length, 1);
const thrownFetchReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({
reportUrl: "/throws-batch-report.json",
fetchRef: async () => {
throw new Error("batch report network unavailable");
},
});
assert.equal(thrownFetchReportUrlWorkflow.ready, false);
assert.equal(thrownFetchReportUrlWorkflow.fetched, false);
assert.equal(thrownFetchReportUrlWorkflow.httpStatus, null);
assert.equal(thrownFetchReportUrlWorkflow.fetchError, "batch report network unavailable");
assert.equal(thrownFetchReportUrlWorkflow.missing[0], "report-fetch");
assert.equal(
createProjectBatchAcceptanceReportUrlWorkflowActionPlan(
thrownFetchReportUrlWorkflow,
).nextActionId,
"verify-report-url-fetch",
);
const capabilityOnlyWorkflow = createProjectBatchAcceptanceWorkflow({
capabilities: [
{
id: "callable-helper",
type: "api",
label: "Callable helper",
},
],
});
assert.deepEqual(capabilityOnlyWorkflow.missing, ["project-batch-acceptance"]);
assert.equal(
createProjectBatchAcceptanceActionPlan(capabilityOnlyWorkflow).nextCommand,
"wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
);
console.log("project_batch_acceptance_workflow_node_smoke=ok");

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
ARTIFACT_PATH="${1:-$ROOT_DIR/build/project-batch-acceptance.json}"
node "$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_workflow.mjs"
node "$ROOT_DIR/tests/sdk/node/write_project_batch_acceptance_artifact.mjs" "$ARTIFACT_PATH"
"$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_artifact.sh" "$ARTIFACT_PATH"

View File

@@ -20,7 +20,7 @@ const manifest = createProjectReleaseGateManifest();
assert.equal(manifest.apiName, "project-release-gate-manifest");
assert.equal(manifest.manifestVersion, 1);
assert.equal(manifest.gateCount, 11);
assert.equal(manifest.gateCount, 12);
assert.deepEqual(manifest.gateIds, [
"diff-check",
"vendor-sync",
@@ -30,6 +30,7 @@ assert.deepEqual(manifest.gateIds, [
"ini-panel-browser",
"opfs-session-browser",
"release-artifact-url-browser",
"project-batch-acceptance",
"ui-node-smokes",
"host-smokes",
"project-release-gate",
@@ -45,6 +46,7 @@ assert.deepEqual(
"browser_ini_shell_integration_workflow_smoke=ok",
"browser_opfs_session_workflow_smoke=ok",
"browser_release_artifact_url_workflow_smoke=ok",
"project_batch_acceptance_workflow_node_smoke=ok",
"ui_node_smokes=ok",
"host_wasm_opfs_browser_smokes=ok",
"project_release_gate=ok",
@@ -58,6 +60,10 @@ assert.equal(
manifest.gates.find(({ id }) => id === "opfs-session-browser")?.command,
"SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
);
assert.equal(
manifest.gates.find(({ id }) => id === "project-batch-acceptance")?.command,
"wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
);
const partialMatrix = createProjectReleaseGateResultMatrix({
gateResults: {
@@ -72,15 +78,16 @@ assert.equal(partialMatrix.apiName, "project-release-gate-result-matrix");
assert.equal(partialMatrix.matrixVersion, 1);
assert.equal(partialMatrix.phase, "waiting");
assert.equal(partialMatrix.ready, false);
assert.equal(partialMatrix.gateCount, 11);
assert.equal(partialMatrix.gateCount, 12);
assert.equal(partialMatrix.passedCount, 3);
assert.equal(partialMatrix.unknownCount, 8);
assert.equal(partialMatrix.unknownCount, 9);
assert.deepEqual(partialMatrix.missingGateIds, [
"standalone-cnc-semantics",
"interp-wasm",
"sim-config-inventory",
"ini-panel-browser",
"opfs-session-browser",
"project-batch-acceptance",
"ui-node-smokes",
"host-smokes",
"project-release-gate",
@@ -107,9 +114,9 @@ assert.equal(partialExecutionManifest.apiName, "project-release-gate-execution-m
assert.equal(partialExecutionManifest.manifestVersion, 1);
assert.equal(partialExecutionManifest.phase, "waiting");
assert.equal(partialExecutionManifest.ready, false);
assert.equal(partialExecutionManifest.gateCount, 11);
assert.equal(partialExecutionManifest.gateCount, 12);
assert.equal(partialExecutionManifest.passedCount, 3);
assert.equal(partialExecutionManifest.unknownCount, 8);
assert.equal(partialExecutionManifest.unknownCount, 9);
assert.deepEqual(partialExecutionManifest.missingGateIds, partialMatrix.missingGateIds);
assert.deepEqual(
partialExecutionManifest.rows.filter(({ passed }) => passed).map(({ id, evidence }) => [id, evidence]),
@@ -132,25 +139,25 @@ assert.equal(partialExecutionSummary.apiName, "project-release-gate-execution-su
assert.equal(partialExecutionSummary.viewModelVersion, 1);
assert.equal(partialExecutionSummary.phase, "waiting");
assert.equal(partialExecutionSummary.ready, false);
assert.equal(partialExecutionSummary.statusLine, "Waiting: standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, ui-node-smokes, host-smokes, project-release-gate");
assert.equal(partialExecutionSummary.gateCount, 11);
assert.equal(partialExecutionSummary.statusLine, "Waiting: standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, project-batch-acceptance, ui-node-smokes, host-smokes, project-release-gate");
assert.equal(partialExecutionSummary.gateCount, 12);
assert.equal(partialExecutionSummary.passedCount, 3);
assert.equal(partialExecutionSummary.unknownCount, 8);
assert.equal(partialExecutionSummary.unknownCount, 9);
assert.equal(partialExecutionSummary.nextGateId, "standalone-cnc-semantics");
assert.deepEqual(partialExecutionSummary.evidenceCounts, {
"explicit-result": 1,
"expected-output": 2,
missing: 8,
missing: 9,
});
assert.deepEqual(
partialExecutionSummary.rows.map(({ id, value }) => [id, value]),
[
["gate-results", "3/11 passed"],
["unknown-gates", "8"],
["gate-results", "3/12 passed"],
["unknown-gates", "9"],
["next-gate", "standalone-cnc-semantics"],
["expected-output-evidence", "2"],
["explicit-result-evidence", "1"],
["missing", "standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, ui-node-smokes, host-smokes, project-release-gate"],
["missing", "standalone-cnc-semantics, interp-wasm, sim-config-inventory, ini-panel-browser, opfs-session-browser, project-batch-acceptance, ui-node-smokes, host-smokes, project-release-gate"],
],
);
assert.equal(partialExecutionSummary.gateRows.length, manifest.gateCount);
@@ -164,9 +171,9 @@ assert.equal(partialActionPlan.apiName, "project-release-gate-action-plan");
assert.equal(partialActionPlan.planVersion, 1);
assert.equal(partialActionPlan.phase, "waiting");
assert.equal(partialActionPlan.ready, false);
assert.equal(partialActionPlan.gateCount, 11);
assert.equal(partialActionPlan.gateCount, 12);
assert.equal(partialActionPlan.completedCount, 3);
assert.equal(partialActionPlan.pendingCount, 8);
assert.equal(partialActionPlan.pendingCount, 9);
assert.deepEqual(partialActionPlan.completedGateIds, [
"diff-check",
"vendor-sync",
@@ -178,13 +185,14 @@ assert.deepEqual(partialActionPlan.pendingGateIds, [
"sim-config-inventory",
"ini-panel-browser",
"opfs-session-browser",
"project-batch-acceptance",
"ui-node-smokes",
"host-smokes",
"project-release-gate",
]);
assert.equal(partialActionPlan.nextGateId, "standalone-cnc-semantics");
assert.equal(partialActionPlan.nextCommand, "wasm-port/tools/verify_no_standalone_cnc_semantics.sh");
assert.equal(partialActionPlan.commands.length, 8);
assert.equal(partialActionPlan.commands.length, 9);
assert.equal(partialActionPlan.commands[0].step, 1);
assert.equal(partialActionPlan.commands[0].gateId, "standalone-cnc-semantics");
assert.equal(partialActionPlan.rows[0].id, "standalone-cnc-semantics");
@@ -200,7 +208,7 @@ assert.equal(report.gateExecutionManifest.ready, true);
assert.equal(report.gateExecutionManifest.passedCount, manifest.gateCount);
assert.equal(report.gateExecutionSummaryViewModel.apiName, "project-release-gate-execution-summary-view-model");
assert.equal(report.gateExecutionSummaryViewModel.statusLine, "Ready: All release gate evidence present");
assert.equal(report.gateExecutionSummaryViewModel.rows[0].value, "11/11 passed");
assert.equal(report.gateExecutionSummaryViewModel.rows[0].value, "12/12 passed");
assert.equal(report.gateExecutionSummaryViewModel.nextGateId, null);
assert.equal(report.gateResultMatrix.ready, true);
assert.equal(report.gateResultMatrix.passedCount, manifest.gateCount);
@@ -223,7 +231,7 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
{
id: "gate-results",
label: "Release gates",
value: "11/11 passed",
value: "12/12 passed",
},
{
id: "unknown-gates",
@@ -289,7 +297,7 @@ assert.deepEqual(
artifactValidationSummary.rows.map(({ id, value }) => [id, value]),
[
["artifact", "project-release-readiness-report"],
["gate-count", "11/11"],
["gate-count", "12/12"],
["gate-manifest", "ready"],
["gate-execution-manifest", "ready"],
["gate-execution-summary", "ready"],

View File

@@ -25,8 +25,12 @@ import {
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
createLinuxCncIniSdk,
createLinuxCncInterpSdk,
@@ -34,6 +38,18 @@ import {
createMachineSessionPersistenceDisplayViewModel,
createMachineSessionPersistenceRenderState,
createMachineSessionPersistenceSummary,
createProjectBatchAcceptanceActionPlan,
createProjectBatchAcceptanceCapabilityMatrix,
createProjectBatchAcceptanceChecklist,
createProjectBatchAcceptanceReport,
createProjectBatchAcceptanceReportJsonWorkflow,
createProjectBatchAcceptanceReportUrlWorkflowActionPlan,
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel,
createProjectBatchAcceptanceReportValidationActionPlan,
createProjectBatchAcceptanceReportValidationSummaryViewModel,
createProjectBatchAcceptanceReportValidation,
createProjectBatchAcceptanceSummaryViewModel,
createProjectBatchAcceptanceWorkflow,
createProjectReleaseGateActionPlan,
createProjectReleaseGateExecutionManifest,
createProjectReleaseGateExecutionSummaryViewModel,
@@ -60,6 +76,30 @@ import {
isSupportedIniPanelLaunchApiManifest,
isSupportedIniPanelShellIntegrationManifest,
loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl,
loadProjectBatchAcceptanceReportUrlWorkflow,
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
loadProjectReleaseReadinessArtifactUrlWorkflow,
loadMachineSessionFromOpfs,
loadMachineSessionSnapshot,
@@ -97,6 +137,30 @@ const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md
const requiredExports = [
["createLinuxCncIniSdk", createLinuxCncIniSdk],
["createLinuxCncInterpSdk", createLinuxCncInterpSdk],
["createProjectBatchAcceptanceActionPlan", createProjectBatchAcceptanceActionPlan],
["createProjectBatchAcceptanceCapabilityMatrix", createProjectBatchAcceptanceCapabilityMatrix],
["createProjectBatchAcceptanceChecklist", createProjectBatchAcceptanceChecklist],
["createProjectBatchAcceptanceReport", createProjectBatchAcceptanceReport],
["createProjectBatchAcceptanceReportJsonWorkflow", createProjectBatchAcceptanceReportJsonWorkflow],
[
"createProjectBatchAcceptanceReportUrlWorkflowActionPlan",
createProjectBatchAcceptanceReportUrlWorkflowActionPlan,
],
[
"createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel",
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel,
],
[
"createProjectBatchAcceptanceReportValidationActionPlan",
createProjectBatchAcceptanceReportValidationActionPlan,
],
[
"createProjectBatchAcceptanceReportValidationSummaryViewModel",
createProjectBatchAcceptanceReportValidationSummaryViewModel,
],
["createProjectBatchAcceptanceReportValidation", createProjectBatchAcceptanceReportValidation],
["createProjectBatchAcceptanceSummaryViewModel", createProjectBatchAcceptanceSummaryViewModel],
["createProjectBatchAcceptanceWorkflow", createProjectBatchAcceptanceWorkflow],
["createProjectReleaseGateActionPlan", createProjectReleaseGateActionPlan],
["createProjectReleaseGateExecutionManifest", createProjectReleaseGateExecutionManifest],
["createProjectReleaseGateExecutionSummaryViewModel", createProjectReleaseGateExecutionSummaryViewModel],
@@ -127,10 +191,103 @@ const requiredExports = [
"validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson",
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson,
],
[
"runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow",
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness,
],
[
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
],
[
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
],
[
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
],
[
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness,
],
[
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState",
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
],
[
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState",
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness,
],
[
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState",
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
],
[
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState",
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
],
[
"loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl",
loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl,
],
["loadProjectBatchAcceptanceReportUrlWorkflow", loadProjectBatchAcceptanceReportUrlWorkflow],
["loadProjectReleaseReadinessArtifactUrlWorkflow", loadProjectReleaseReadinessArtifactUrlWorkflow],
["planIniFileContextStaging", planIniFileContextStaging],
["planSimConfigStaging", planSimConfigStaging],
@@ -194,10 +351,26 @@ const requiredExports = [
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel,
],
[
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel",
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
@@ -246,6 +419,106 @@ assert.equal(normalizeOpfsPath("linuxcnc/machines/sdk-mill/tool.tbl"), "linuxcnc
assert.equal(gcodeFilenameFromProgramPath("linuxcnc/gcode/demo.ngc"), "demo.ngc");
const releaseReadinessWaiting = createProjectReleaseReadinessReport();
const batchCapabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({
capabilities: [
{
id: "sdk-surface-api",
type: "api",
label: "SDK surface API",
},
],
});
assert.equal(batchCapabilityMatrix.ready, true);
assert.equal(batchCapabilityMatrix.countsByType.api, 1);
const batchAcceptanceWorkflow = createProjectBatchAcceptanceWorkflow({
batchId: "sdk-surface-batch",
capabilities: [
{
id: "sdk-surface-api",
type: "api",
label: "SDK surface API",
},
],
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
});
assert.equal(batchAcceptanceWorkflow.ready, true);
assert.equal(batchAcceptanceWorkflow.capabilityTypes[0], "api");
assert.equal(
createProjectBatchAcceptanceSummaryViewModel(batchAcceptanceWorkflow).statusLine,
"Ready: Batch acceptance evidence complete",
);
assert.equal(
createProjectBatchAcceptanceActionPlan(batchAcceptanceWorkflow).shellScript,
"# Project batch acceptance workflow is already ready.\n",
);
assert.equal(
createProjectBatchAcceptanceChecklist({ workflow: batchAcceptanceWorkflow }).rows[0].value,
"pass: api",
);
assert.equal(
createProjectBatchAcceptanceReport({
batchId: "sdk-surface-batch",
capabilities: batchAcceptanceWorkflow.capabilities,
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
}).rows.find(({ id }) => id === "checklist")?.value,
"3/3 passed",
);
const batchAcceptanceReport = createProjectBatchAcceptanceReport({
batchId: "sdk-surface-batch",
capabilities: batchAcceptanceWorkflow.capabilities,
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
});
assert.equal(createProjectBatchAcceptanceReportValidation(batchAcceptanceReport).ready, true);
assert.equal(
createProjectBatchAcceptanceReportValidationSummaryViewModel(
createProjectBatchAcceptanceReportValidation(batchAcceptanceReport),
).statusLine,
"Ready: Batch acceptance report evidence complete",
);
assert.equal(
createProjectBatchAcceptanceReportValidationActionPlan(
createProjectBatchAcceptanceReportValidation(batchAcceptanceReport),
).shellScript,
"# Project batch acceptance report validation is already ready.\n",
);
assert.equal(
createProjectBatchAcceptanceReportJsonWorkflow({
reportJson: JSON.stringify(batchAcceptanceReport),
}).ready,
true,
);
const batchAcceptanceReportUrlWorkflow = await loadProjectBatchAcceptanceReportUrlWorkflow({
reportUrl: "/project-batch-acceptance.json",
fetchRef: async () => ({
ok: true,
status: 200,
text: async () => JSON.stringify(batchAcceptanceReport),
}),
});
assert.equal(batchAcceptanceReportUrlWorkflow.ready, true);
assert.equal(
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel(
batchAcceptanceReportUrlWorkflow,
).statusLine,
"Ready: Batch report URL workflow complete",
);
assert.equal(
createProjectBatchAcceptanceReportUrlWorkflowActionPlan(
batchAcceptanceReportUrlWorkflow,
).shellScript,
"# Project batch acceptance report URL workflow is already ready.\n",
);
assert.equal(
(await loadProjectBatchAcceptanceReportUrlWorkflow({
reportUrl: "/missing-project-batch-acceptance.json",
fetchRef: async () => ({ ok: false, status: 404, text: async () => "" }),
})).missing[0],
"report-fetch",
);
assert.equal(
createProjectBatchAcceptanceActionPlan(createProjectBatchAcceptanceWorkflow()).nextCommand,
"wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
);
assert.equal(releaseReadinessWaiting.apiName, "project-release-readiness-report");
assert.equal(releaseReadinessWaiting.ready, false);
assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate"]);
@@ -446,6 +719,44 @@ assert.equal(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(JSON.stringify(releaseReadinessArtifact)).ready,
true,
);
assert.equal(
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(JSON.stringify(releaseReadinessArtifact)),
).shellScript,
"# Project release readiness artifact validation is already ready.\n",
);
assert.equal(
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({
artifactJson: JSON.stringify(releaseReadinessArtifact),
}).statusLine,
"Ready: Artifact JSON workflow complete",
);
assert.equal(
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel(
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({
artifactJson: JSON.stringify(releaseReadinessArtifact),
}),
).statusLine,
"Ready: Artifact JSON workflow complete",
);
assert.equal(
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan(
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({
artifactJson: JSON.stringify(releaseReadinessArtifact),
}),
).nextCommand,
null,
);
assert.equal(
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState(
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel(
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({
artifactJson: JSON.stringify(releaseReadinessArtifact),
}),
),
).dataset.handoffScope,
"workflow-overview-release-readiness-artifact-json-workflow",
);
const releaseReadinessArtifactUrlWorkflow = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({
@@ -465,6 +776,12 @@ assert.equal(
).statusLine,
"Ready: Artifact URL workflow complete",
);
assert.equal(
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(
releaseReadinessArtifactUrlWorkflow,
).shellScript,
"# Project release readiness artifact URL workflow is already ready.\n",
);
assert.equal(
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState(
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel(

View File

@@ -6,3 +6,4 @@ ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
node "$ROOT_DIR/tests/sdk/node/verify_sdk_surface.mjs"
"$ROOT_DIR/tests/sdk/node/verify_project_release_gate_manifest.sh"
"$ROOT_DIR/tests/sdk/node/verify_project_release_artifact_url_workflow.sh"
"$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_workflow.sh"

View File

@@ -0,0 +1,36 @@
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { createProjectBatchAcceptanceReport } from "../../../runtime/sdk/src/index.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const root = resolve(__dirname, "../../..");
const outputPath = process.argv[2] ?? resolve(root, "build/project-batch-acceptance.json");
const report = createProjectBatchAcceptanceReport({
batchId: "project-batch-acceptance-artifact",
capabilities: [
{
id: "project-batch-acceptance-report-artifact",
type: "workflow",
label: "Project batch acceptance report artifact",
evidence: "project_batch_acceptance_artifact_node_smoke=ok",
command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
},
{
id: "project-batch-acceptance-workflow-gate",
type: "gate",
label: "Project batch acceptance workflow gate",
evidence: "project_batch_acceptance_workflow_node_smoke=ok",
command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
},
],
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
});
mkdirSync(dirname(outputPath), { recursive: true });
writeFileSync(`${outputPath}.tmp`, `${JSON.stringify(report, null, 2)}\n`);
renameSync(`${outputPath}.tmp`, outputPath);
console.log(`project_batch_acceptance_artifact=${outputPath}`);