按text22收束promotion证据

结论:完成 evidence-ready / inventory-ready 双层 promotion candidate 管理,release/SDK gate 已纳入 virtual HAL promotion summary,baseline 保持 28/28/131/0,Python remap、tool DB、external user-M hard block 继续 locked。
This commit is contained in:
2026-06-18 17:20:29 +08:00
parent 014081f1f9
commit 1c94ea4527
29 changed files with 8500 additions and 121 deletions

View File

@@ -84,6 +84,25 @@ import {
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness,
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan,
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange,
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls,
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState,
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomContract,
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomReadiness,
renderIniPanelShellWorkflowOverviewPromotionFamilyRowsState,
mountIniPanelShellWorkflowOverviewPromotionFamilyRowsState,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract,
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness,
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
@@ -744,6 +763,19 @@ assert.deepEqual(shellViewModel.shellWorkflowOverviewContract, {
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
"getWorkflowOverviewPromotionFamilyRowsRenderState",
"getWorkflowOverviewPromotionFamilySourceFilterViewModel",
"getWorkflowOverviewPromotionFamilySourceFilterDomContract",
"getWorkflowOverviewPromotionFamilySourceFilterDomReadiness",
"getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan",
"handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange",
"bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls",
"renderWorkflowOverviewPromotionFamilySourceFilterState",
"mountWorkflowOverviewPromotionFamilySourceFilterState",
"getWorkflowOverviewPromotionFamilyRowsDomContract",
"getWorkflowOverviewPromotionFamilyRowsDomReadiness",
"renderWorkflowOverviewPromotionFamilyRowsState",
"mountWorkflowOverviewPromotionFamilyRowsState",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
@@ -1044,7 +1076,27 @@ const mountRowsNode = {
const mountNode = { dataset: {} };
const mountStatusNode = { textContent: "" };
const mountDocument = {
createElement: (tagName) => ({ tagName, dataset: {}, textContent: "" }),
createElement: (tagName) => ({
tagName,
dataset: {},
textContent: "",
children: [],
listeners: {},
append(...nodes) {
this.children.push(...nodes);
},
addEventListener(eventName, handler) {
this.listeners[eventName] = handler;
},
removeEventListener(eventName, handler) {
if (this.listeners[eventName] === handler) {
delete this.listeners[eventName];
}
},
dispatchEvent(eventName) {
return this.listeners[eventName]?.();
},
}),
};
assert.deepEqual(
createIniPanelShellSessionHandoffMountDomReadiness({
@@ -1366,9 +1418,34 @@ assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.s
const releaseReadinessMotionControllerMatrix = createVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
});
const releaseReadinessPromotionCandidates = createVirtualHalSimConfigPromotionCandidateReport({
manifestEntries: [
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
],
motionControllerMatrix: releaseReadinessMotionControllerMatrix,
});
const releaseReadinessMacroLoadFixtures = createVirtualHalSimConfigMacroLoadFixtureReport({
manifestEntries: [
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
],
});
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalSimConfigPromotionCandidates: releaseReadinessPromotionCandidates,
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
}));
const browserDiagnosticsVirtualHalState = createVirtualHalState();
@@ -1379,29 +1456,24 @@ const browserDiagnosticsArtifact = {
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(browserDiagnosticsVirtualHalState),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: browserDiagnosticsVirtualHalState }),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport({
manifestEntries: [
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
],
motionControllerMatrix: releaseReadinessMotionControllerMatrix,
}),
virtualHalSimConfigMacroLoadFixtures: createVirtualHalSimConfigMacroLoadFixtureReport({
manifestEntries: [
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone.ngc",
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
],
}),
virtualHalSimConfigPromotionCandidates: releaseReadinessPromotionCandidates,
virtualHalPromotionCandidateSummary: {
apiName: "real-browser-simulation-promotion-candidate-summary",
summaryVersion: 1,
phase: "ready",
ready: true,
candidateCount: 8,
readyCandidateCount: 8,
familyCount: 3,
sourceFileCount: 17,
preferredCandidateId: "qtdragon-multi-joint-on-abort",
preferredIniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
preferredGcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
blockedCandidateIds: [],
explicitBrowserDiagnosticsCount: 8,
inventoryBaseline: "executed=28 passed=28 skipped=131 unexpected_fail=0",
},
virtualHalSimConfigMacroLoadFixtures: releaseReadinessMacroLoadFixtures,
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: browserDiagnosticsVirtualHalState }),
virtualHalMotionControllerMatrix: releaseReadinessMotionControllerMatrix,
};
@@ -1725,7 +1797,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
rowsNode: releaseArtifactRowsNode,
});
assert.equal(releaseArtifactRenderResult.rendered, true);
assert.equal(releaseArtifactRenderResult.rowCount, 11);
assert.equal(releaseArtifactRenderResult.rowCount, 17);
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
@@ -1735,6 +1807,10 @@ assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-execution-summary"),
true,
);
assert.equal(
releaseArtifactRenderResult.rowIds.includes("virtual-hal-promotion-families"),
true,
);
assert.equal(
releaseArtifactRenderResult.rowIds.includes("gate-action-plan"),
true,
@@ -1752,7 +1828,7 @@ assert.equal(
true,
);
assert.equal(releaseArtifactMountResult.ready, true);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 11);
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 17);
assert.deepEqual(
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
["artifact-json"],
@@ -1811,11 +1887,18 @@ assert.equal(
);
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 11);
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 17);
const releaseArtifactUrlWorkflowSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
releaseArtifactUrlWorkflow,
);
const expectedPromotionFamilySummary =
"qtdragon-on-abort: 6/6 ready; vismach-remap-sims: 1/1 ready; rose-engine-rcone-demo: 1/1 ready";
const expectedPromotionFamilyRows = [
["virtual-hal-promotion-family-qtdragon-on-abort", "6/6 ready; sources=12; diagnostics=ready"],
["virtual-hal-promotion-family-vismach-remap-sims", "1/1 ready; sources=3; diagnostics=ready"],
["virtual-hal-promotion-family-rose-engine-rcone-demo", "1/1 ready; sources=2; diagnostics=ready"],
];
assert.equal(
releaseArtifactUrlWorkflowSummary.statusLine,
"Ready: Artifact URL workflow complete",
@@ -1836,6 +1919,583 @@ assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "browser-diagnostics")?.value,
"ready",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-summary")?.value,
"ready",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate")?.value,
"qtdragon-multi-joint-on-abort",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-ini")?.value,
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-gcode")?.value,
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-count")?.value,
"8/8 ready",
);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-candidate-baseline")?.value,
"executed=28 passed=28 skipped=131 unexpected_fail=0",
);
const promotionCandidateDrilldownViewModel =
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownViewModel(
releaseArtifactUrlWorkflowSummary,
{ diagnosticsArtifact: releaseArtifactUrlWorkflow.diagnosticsArtifact },
);
const promotionCandidateDrilldownRenderState =
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownRenderState(
promotionCandidateDrilldownViewModel,
);
assert.equal(promotionCandidateDrilldownViewModel.ready, true);
assert.equal(
promotionCandidateDrilldownViewModel.statusLine,
"Ready: qtdragon-multi-joint-on-abort source-derived browser evidence ready",
);
assert.equal(promotionCandidateDrilldownViewModel.candidateId, "qtdragon-multi-joint-on-abort");
assert.equal(promotionCandidateDrilldownViewModel.blockedFamilyExcluded, true);
assert.equal(promotionCandidateDrilldownViewModel.nodeInventoryReady, true);
assert.equal(promotionCandidateDrilldownViewModel.browserEvidenceReady, true);
assert.equal(promotionCandidateDrilldownViewModel.virtualHalReportsReady, true);
assert.deepEqual(promotionCandidateDrilldownViewModel.requiredReports, [
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]);
assert.equal(
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "ini")?.value,
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
);
assert.equal(
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "gcode")?.value,
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
);
assert.equal(
promotionCandidateDrilldownViewModel.rows.find(({ id }) => id === "blocked-family-exclusion")?.value,
"passed",
);
assert.equal(promotionCandidateDrilldownViewModel.sourceFileCount, 2);
assert.deepEqual(
promotionCandidateDrilldownViewModel.sourceRows.map(({ value, sourceKind, manifestBacked }) => [
value,
sourceKind,
manifestBacked,
]),
[
["linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini", "ini", true],
["linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc", "gcode", true],
],
);
assert.equal(promotionCandidateDrilldownRenderState.ready, true);
assert.equal(promotionCandidateDrilldownRenderState.rowCount, 11);
assert.equal(promotionCandidateDrilldownRenderState.sourceFileCount, 2);
assert.equal(
createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomContract().selectors.mount,
"[data-workflow-overview-promotion-candidate-drilldown]",
);
assert.equal(createIniPanelShellWorkflowOverviewPromotionCandidateDrilldownDomReadiness().ready, false);
const promotionCandidateDrilldownMountNode = mountDocument.createElement("section");
promotionCandidateDrilldownMountNode.dataset.workflowOverviewPromotionCandidateDrilldown = "";
const promotionCandidateDrilldownStatusNode = mountDocument.createElement("p");
promotionCandidateDrilldownStatusNode.dataset.workflowOverviewPromotionCandidateDrilldownStatus = "";
const promotionCandidateDrilldownRowsNode = {
children: [],
dataset: {},
append(...nodes) {
this.children.push(...nodes);
},
replaceChildren(...nodes) {
this.children = [...nodes];
},
};
promotionCandidateDrilldownRowsNode.dataset.workflowOverviewPromotionCandidateDrilldownRows = "";
const promotionCandidateDrilldownRenderResult =
renderIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState(
promotionCandidateDrilldownRenderState,
{
documentRef: mountDocument,
mountNode: promotionCandidateDrilldownMountNode,
statusNode: promotionCandidateDrilldownStatusNode,
rowsNode: promotionCandidateDrilldownRowsNode,
},
);
const promotionCandidateDrilldownMountResult =
mountIniPanelShellWorkflowOverviewPromotionCandidateDrilldownState({
renderState: promotionCandidateDrilldownRenderState,
documentRef: mountDocument,
mountNode: promotionCandidateDrilldownMountNode,
statusNode: promotionCandidateDrilldownStatusNode,
rowsNode: promotionCandidateDrilldownRowsNode,
});
assert.equal(promotionCandidateDrilldownRenderResult.rendered, true);
assert.equal(promotionCandidateDrilldownRenderResult.rowCount, 11);
assert.deepEqual(promotionCandidateDrilldownRenderResult.dataset, {
handoffPhase: "ready",
handoffReady: "true",
handoffScope: "workflow-overview-promotion-candidate-drilldown",
candidateId: "qtdragon-multi-joint-on-abort",
});
assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionCandidateDrilldownValue === "blocked-family-exclusion" &&
node.textContent === "passed"
), true);
assert.equal(promotionCandidateDrilldownRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionCandidateDrilldownSourceKind === "gcode" &&
node.dataset?.workflowOverviewPromotionCandidateDrilldownManifestBacked === "true" &&
node.textContent === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"
), true);
assert.equal(promotionCandidateDrilldownMountResult.ready, true);
assert.equal(promotionCandidateDrilldownMountResult.renderResult.sourceFileCount, 2);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "virtual-hal-promotion-families")?.value,
expectedPromotionFamilySummary,
);
assert.deepEqual(
releaseArtifactUrlWorkflowSummary.rows
.filter(({ id }) => id.startsWith("virtual-hal-promotion-family-"))
.map(({ id, value }) => [id, value]),
expectedPromotionFamilyRows,
);
const promotionFamilyRowsRenderState =
createIniPanelShellWorkflowOverviewPromotionFamilyRowsRenderState(
releaseArtifactUrlWorkflowSummary,
);
const promotionFamilySourceFilterViewModel =
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(
promotionFamilyRowsRenderState,
);
const promotionFamilyRemapSourceFilterViewModel =
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(
promotionFamilyRowsRenderState,
{ sourceKind: "remap-subroutine" },
);
const promotionFamilyQtDragonGcodeFilterViewModel =
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterViewModel(
promotionFamilyRowsRenderState,
{
familyId: "virtual-hal-promotion-family-qtdragon-on-abort",
sourceKind: "gcode",
},
);
assert.equal(promotionFamilyRowsRenderState.ready, true);
assert.equal(promotionFamilyRowsRenderState.statusLine, "Ready: 3 promotion families ready");
assert.equal(promotionFamilyRowsRenderState.rows.length, 3);
assert.equal(promotionFamilyRowsRenderState.sourceFileCount, 17);
assert.equal(promotionFamilyRowsRenderState.sourceRows.length, 17);
assert.equal(
promotionFamilyRowsRenderState.rows.find(({ id }) => id === "virtual-hal-promotion-family-qtdragon-on-abort")
?.sourceKindSummary,
"ini=6 gcode=6",
);
assert.deepEqual(
promotionFamilyRowsRenderState.rows.find(({ id }) => id === "virtual-hal-promotion-family-vismach-remap-sims")
?.sourceKindCounts,
{ ini: 1, gcode: 1, "remap-subroutine": 1 },
);
assert.equal(
promotionFamilyRowsRenderState.rows.find(({ id }) => id === "virtual-hal-promotion-family-rose-engine-rcone-demo")
?.sourceKindSummary,
"ini=1 gcode=1",
);
assert.equal(promotionFamilySourceFilterViewModel.ready, true);
assert.equal(promotionFamilySourceFilterViewModel.statusLine, "Ready: 17/17 source files selected");
assert.equal(promotionFamilySourceFilterViewModel.filteredSourceFileCount, 17);
assert.equal(promotionFamilySourceFilterViewModel.totalSourceFileCount, 17);
assert.deepEqual(
promotionFamilySourceFilterViewModel.familyFilters.map(({ id, count, selected }) => [id, count, selected]),
[
["all", 17, true],
["virtual-hal-promotion-family-qtdragon-on-abort", 12, false],
["virtual-hal-promotion-family-vismach-remap-sims", 3, false],
["virtual-hal-promotion-family-rose-engine-rcone-demo", 2, false],
],
);
assert.deepEqual(
promotionFamilySourceFilterViewModel.sourceKindFilters.map(({ id, count, selected }) => [id, count, selected]),
[
["all", 17, true],
["ini", 8, false],
["gcode", 8, false],
["remap-subroutine", 1, false],
],
);
assert.equal(promotionFamilyRemapSourceFilterViewModel.selectedSourceKind, "remap-subroutine");
assert.equal(promotionFamilyRemapSourceFilterViewModel.statusLine, "Ready: 1/17 source files selected");
assert.equal(promotionFamilyRemapSourceFilterViewModel.familyRows.length, 1);
assert.equal(
promotionFamilyRemapSourceFilterViewModel.familyRows[0]?.id,
"virtual-hal-promotion-family-vismach-remap-sims",
);
assert.equal(
promotionFamilyRemapSourceFilterViewModel.sourceRows[0]?.value,
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc",
);
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.selectedFamilyId, "virtual-hal-promotion-family-qtdragon-on-abort");
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.selectedSourceKind, "gcode");
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.statusLine, "Ready: 6/17 source files selected");
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.scopedSourceFileCount, 12);
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.filteredSourceFileCount, 6);
assert.equal(promotionFamilyQtDragonGcodeFilterViewModel.familyRows.length, 1);
assert.equal(
promotionFamilyQtDragonGcodeFilterViewModel.familyRows[0]?.sourceKindSummary,
"gcode=6",
);
assert.deepEqual(
promotionFamilyQtDragonGcodeFilterViewModel.sourceKindFilters.map(({ id, count, selected }) => [id, count, selected]),
[
["all", 12, false],
["ini", 6, false],
["gcode", 6, true],
],
);
assert.equal(
promotionFamilyQtDragonGcodeFilterViewModel.sourceRows.every(({ familyId, sourceKind }) =>
familyId === "virtual-hal-promotion-family-qtdragon-on-abort" &&
sourceKind === "gcode"
),
true,
);
assert.equal(
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract().selectors.mount,
"[data-workflow-overview-promotion-family-source-filter]",
);
assert.equal(createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness().ready, false);
const promotionFamilySourceFilterMountNode = mountDocument.createElement("section");
promotionFamilySourceFilterMountNode.dataset.workflowOverviewPromotionFamilySourceFilter = "";
const promotionFamilySourceFilterStatusNode = mountDocument.createElement("p");
promotionFamilySourceFilterStatusNode.dataset.workflowOverviewPromotionFamilySourceFilterStatus = "";
const promotionFamilySourceFilterControlsNode = {
...mountDocument.createElement("div"),
children: [],
replaceChildren() {
this.children = [];
},
append(...nodes) {
this.children.push(...nodes);
},
};
promotionFamilySourceFilterControlsNode.dataset.workflowOverviewPromotionFamilySourceFilterControls = "";
const promotionFamilySourceFilterRowsNode = {
...mountDocument.createElement("dl"),
children: [],
replaceChildren() {
this.children = [];
},
append(...nodes) {
this.children.push(...nodes);
},
};
promotionFamilySourceFilterRowsNode.dataset.workflowOverviewPromotionFamilySourceFilterRows = "";
const promotionFamilySourceFilterRenderResult =
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState(
promotionFamilyQtDragonGcodeFilterViewModel,
{
documentRef: mountDocument,
mountNode: promotionFamilySourceFilterMountNode,
statusNode: promotionFamilySourceFilterStatusNode,
controlsNode: promotionFamilySourceFilterControlsNode,
rowsNode: promotionFamilySourceFilterRowsNode,
},
);
const promotionFamilySourceFilterMountResult =
mountIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState({
viewModel: promotionFamilyQtDragonGcodeFilterViewModel,
documentRef: mountDocument,
mountNode: promotionFamilySourceFilterMountNode,
statusNode: promotionFamilySourceFilterStatusNode,
controlsNode: promotionFamilySourceFilterControlsNode,
rowsNode: promotionFamilySourceFilterRowsNode,
});
assert.equal(promotionFamilySourceFilterRenderResult.rendered, true);
assert.equal(promotionFamilySourceFilterRenderResult.statusLine, "Ready: 6/17 source files selected");
assert.equal(promotionFamilySourceFilterRenderResult.controlCount, 2);
assert.equal(promotionFamilySourceFilterRenderResult.filterRowCount, 7);
assert.equal(promotionFamilySourceFilterRenderResult.sourceFileCount, 6);
assert.equal(promotionFamilySourceFilterRenderResult.rowCount, 13);
assert.deepEqual(promotionFamilySourceFilterRenderResult.dataset, {
handoffPhase: "ready",
handoffReady: "true",
handoffScope: "workflow-overview-promotion-family-source-filter",
selectedFamilyId: "virtual-hal-promotion-family-qtdragon-on-abort",
selectedSourceKind: "gcode",
});
assert.equal(promotionFamilySourceFilterControlsNode.children.length, 4);
assert.equal(
promotionFamilySourceFilterControlsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterControl === "family" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterControlValue ===
"virtual-hal-promotion-family-qtdragon-on-abort" &&
node.textContent === "Family"
),
true,
);
assert.equal(
promotionFamilySourceFilterControlsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "source-kind" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterValue === "gcode" &&
node.value === "gcode" &&
node.children.some((option) =>
option.value === "gcode" &&
option.selected === true &&
option.dataset?.workflowOverviewPromotionFamilySourceFilterCount === "6"
)
),
true,
);
assert.equal(
promotionFamilySourceFilterRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterValue ===
"virtual-hal-promotion-family-qtdragon-on-abort" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "family-filter" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelected === "true" &&
node.textContent === "virtual-hal-promotion-family-qtdragon-on-abort: 12 selected"
),
true,
);
assert.equal(
promotionFamilySourceFilterRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterValue === "gcode" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "source-kind-filter" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelected === "true" &&
node.textContent === "gcode: 6 selected"
),
true,
);
assert.equal(
promotionFamilySourceFilterRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "source-row" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterFamily ===
"virtual-hal-promotion-family-qtdragon-on-abort" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterSourceKind === "gcode" &&
node.textContent === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"
),
true,
);
assert.equal(promotionFamilySourceFilterMountResult.ready, true);
assert.equal(promotionFamilySourceFilterMountResult.renderResult.rowCount, 13);
const promotionFamilySourceFilterSelectionActionPlan =
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan(
promotionFamilyRowsRenderState,
{
familyId: "virtual-hal-promotion-family-rose-engine-rcone-demo",
sourceKind: "all",
previousViewModel: promotionFamilyQtDragonGcodeFilterViewModel,
},
);
assert.equal(promotionFamilySourceFilterSelectionActionPlan.ready, true);
assert.equal(promotionFamilySourceFilterSelectionActionPlan.changed, true);
assert.equal(
promotionFamilySourceFilterSelectionActionPlan.statusLine,
"Ready: source filter changed to virtual-hal-promotion-family-rose-engine-rcone-demo / all",
);
assert.equal(
promotionFamilySourceFilterSelectionActionPlan.nextViewModel.statusLine,
"Ready: 2/17 source files selected",
);
assert.equal(promotionFamilySourceFilterSelectionActionPlan.nextViewModel.filteredSourceFileCount, 2);
assert.equal(promotionFamilySourceFilterSelectionActionPlan.nextViewModel.scopedSourceFileCount, 2);
const sourceFilterFamilySelect = promotionFamilySourceFilterControlsNode.children.find((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "family"
);
const sourceFilterKindSelect = promotionFamilySourceFilterControlsNode.children.find((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "source-kind"
);
sourceFilterFamilySelect.value = "virtual-hal-promotion-family-rose-engine-rcone-demo";
sourceFilterKindSelect.value = "all";
const promotionFamilySourceFilterSelectionChange =
handleIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionChange({
renderState: promotionFamilyRowsRenderState,
viewModel: promotionFamilyQtDragonGcodeFilterViewModel,
documentRef: mountDocument,
mountNode: promotionFamilySourceFilterMountNode,
statusNode: promotionFamilySourceFilterStatusNode,
controlsNode: promotionFamilySourceFilterControlsNode,
rowsNode: promotionFamilySourceFilterRowsNode,
render: true,
});
assert.equal(promotionFamilySourceFilterSelectionChange.ready, true);
assert.equal(
promotionFamilySourceFilterSelectionChange.selectedFamilyId,
"virtual-hal-promotion-family-rose-engine-rcone-demo",
);
assert.equal(promotionFamilySourceFilterSelectionChange.selectedSourceKind, "all");
assert.equal(promotionFamilySourceFilterSelectionChange.actionPlan.changed, true);
assert.equal(promotionFamilySourceFilterSelectionChange.viewModel.filteredSourceFileCount, 2);
assert.equal(promotionFamilySourceFilterSelectionChange.renderResult.sourceFileCount, 2);
assert.equal(promotionFamilySourceFilterSelectionChange.renderResult.dataset.selectedSourceKind, "all");
assert.equal(
promotionFamilySourceFilterRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterType === "source-row" &&
node.dataset?.workflowOverviewPromotionFamilySourceFilterFamily ===
"virtual-hal-promotion-family-rose-engine-rcone-demo" &&
node.textContent === "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc"
),
true,
);
renderIniPanelShellWorkflowOverviewPromotionFamilySourceFilterState(
promotionFamilyQtDragonGcodeFilterViewModel,
{
documentRef: mountDocument,
mountNode: promotionFamilySourceFilterMountNode,
statusNode: promotionFamilySourceFilterStatusNode,
controlsNode: promotionFamilySourceFilterControlsNode,
rowsNode: promotionFamilySourceFilterRowsNode,
},
);
let promotionFamilySourceFilterBindingLastChange = null;
const promotionFamilySourceFilterSelectionBinding =
bindIniPanelShellWorkflowOverviewPromotionFamilySourceFilterSelectionControls({
renderState: promotionFamilyRowsRenderState,
viewModel: promotionFamilyQtDragonGcodeFilterViewModel,
documentRef: mountDocument,
mountNode: promotionFamilySourceFilterMountNode,
statusNode: promotionFamilySourceFilterStatusNode,
controlsNode: promotionFamilySourceFilterControlsNode,
rowsNode: promotionFamilySourceFilterRowsNode,
render: true,
onChange: (changeResult) => {
promotionFamilySourceFilterBindingLastChange = changeResult;
},
});
assert.equal(promotionFamilySourceFilterSelectionBinding.ready, true);
assert.equal(promotionFamilySourceFilterSelectionBinding.boundControlCount, 2);
assert.deepEqual(promotionFamilySourceFilterSelectionBinding.controlIds, ["family", "source-kind"]);
const boundSourceFilterFamilySelect = promotionFamilySourceFilterControlsNode.children.find((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "family"
);
const boundSourceFilterKindSelect = promotionFamilySourceFilterControlsNode.children.find((node) =>
node.dataset?.workflowOverviewPromotionFamilySourceFilterSelect === "source-kind"
);
boundSourceFilterFamilySelect.value = "virtual-hal-promotion-family-rose-engine-rcone-demo";
boundSourceFilterKindSelect.value = "all";
const promotionFamilySourceFilterBoundChange = boundSourceFilterFamilySelect.dispatchEvent("change");
assert.equal(promotionFamilySourceFilterBoundChange.ready, true);
assert.equal(
promotionFamilySourceFilterBoundChange.selectedFamilyId,
"virtual-hal-promotion-family-rose-engine-rcone-demo",
);
assert.equal(promotionFamilySourceFilterBoundChange.renderResult.sourceFileCount, 2);
assert.equal(
promotionFamilySourceFilterBindingLastChange.selectedFamilyId,
"virtual-hal-promotion-family-rose-engine-rcone-demo",
);
assert.equal(promotionFamilySourceFilterSelectionBinding.lastChange.selectedSourceKind, "all");
assert.deepEqual(promotionFamilySourceFilterSelectionBinding.unbind(), { unbound: 2 });
assert.equal(
promotionFamilyRowsRenderState.sourceRows.some(({ value }) =>
value === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"
),
true,
);
assert.equal(
promotionFamilyRowsRenderState.sourceRows.some(({ sourceKind, manifestBacked, status, value }) =>
sourceKind === "ini" &&
manifestBacked === true &&
status === "manifest-backed" &&
value === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"
),
true,
);
assert.equal(
promotionFamilyRowsRenderState.sourceRows.some(({ sourceKind, manifestBacked, status, value }) =>
sourceKind === "gcode" &&
manifestBacked === true &&
status === "manifest-backed" &&
value === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc"
),
true,
);
assert.equal(
promotionFamilyRowsRenderState.sourceRows.some(({ value }) =>
value === "linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
),
true,
);
assert.equal(
promotionFamilyRowsRenderState.sourceRows.some(({ sourceKind, manifestBacked, status, value }) =>
sourceKind === "remap-subroutine" &&
manifestBacked === true &&
status === "manifest-backed" &&
value === "linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc"
),
true,
);
assert.equal(
promotionFamilyRowsRenderState.sourceRows.some(({ value }) =>
value === "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc"
),
true,
);
assert.equal(
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomContract().selectors.mount,
"[data-workflow-overview-promotion-family-rows]",
);
assert.equal(createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomReadiness().ready, false);
const promotionFamilyRowsMountNode = mountDocument.createElement("section");
promotionFamilyRowsMountNode.dataset.workflowOverviewPromotionFamilyRows = "";
const promotionFamilyRowsStatusNode = mountDocument.createElement("p");
promotionFamilyRowsStatusNode.dataset.workflowOverviewPromotionFamilyRowsStatus = "";
const promotionFamilyRowsNode = {
...mountDocument.createElement("dl"),
children: [],
replaceChildren() {
this.children = [];
},
append(...nodes) {
this.children.push(...nodes);
},
};
promotionFamilyRowsNode.dataset.workflowOverviewPromotionFamilyRowList = "";
const promotionFamilyRowsRenderResult =
renderIniPanelShellWorkflowOverviewPromotionFamilyRowsState(
promotionFamilyRowsRenderState,
{
documentRef: mountDocument,
mountNode: promotionFamilyRowsMountNode,
statusNode: promotionFamilyRowsStatusNode,
rowsNode: promotionFamilyRowsNode,
},
);
const promotionFamilyRowsMountResult =
mountIniPanelShellWorkflowOverviewPromotionFamilyRowsState({
renderState: promotionFamilyRowsRenderState,
documentRef: mountDocument,
mountNode: promotionFamilyRowsMountNode,
statusNode: promotionFamilyRowsStatusNode,
rowsNode: promotionFamilyRowsNode,
});
assert.equal(promotionFamilyRowsRenderResult.rendered, true);
assert.equal(promotionFamilyRowsRenderResult.rowCount, 20);
assert.deepEqual(promotionFamilyRowsRenderResult.rowIds, expectedPromotionFamilyRows.map(([id]) => id));
assert.equal(promotionFamilyRowsRenderResult.sourceFileCount, 17);
assert.equal(
promotionFamilyRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilyValue ===
"virtual-hal-promotion-family-qtdragon-on-abort" &&
node.dataset?.workflowOverviewPromotionFamilySourceKindCounts === "ini=6 gcode=6" &&
node.textContent === "6/6 ready; sources=12; diagnostics=ready; ini=6 gcode=6"
),
true,
);
assert.equal(
promotionFamilyRowsNode.children.some((node) =>
node.dataset?.workflowOverviewPromotionFamilyValue ===
"virtual-hal-promotion-family-qtdragon-on-abort-source-1" &&
node.dataset?.workflowOverviewPromotionFamilySourceKind === "ini" &&
node.dataset?.workflowOverviewPromotionFamilyManifestBacked === "true" &&
node.dataset?.workflowOverviewPromotionFamilySourceStatus === "manifest-backed" &&
node.textContent === "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini"
),
true,
);
assert.equal(promotionFamilyRowsMountResult.ready, true);
assert.equal(promotionFamilyRowsMountResult.renderResult.rowCount, 20);
assert.equal(
releaseArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "saved-session-diagnostics")?.value,
"ready",
@@ -1905,7 +2565,7 @@ const releaseArtifactUrlWorkflowMountResult =
rowsNode: releaseArtifactUrlWorkflowRowsNode,
});
assert.equal(releaseArtifactUrlWorkflowRenderResult.rendered, true);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 12);
assert.equal(releaseArtifactUrlWorkflowRenderResult.rowCount, 22);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("browser-diagnostics"),
true,
@@ -1914,12 +2574,16 @@ assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("saved-session-diagnostics"),
true,
);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("virtual-hal-promotion-families"),
true,
);
assert.equal(
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"),
true,
);
assert.equal(releaseArtifactUrlWorkflowMountResult.ready, true);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 12);
assert.equal(releaseArtifactUrlWorkflowMountResult.renderResult.rowCount, 22);
const releaseArtifactUrlWorkflowMissingDom = await loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl({
artifactUrl: "/project-release-readiness.json",
fetchRef: async () => ({
@@ -1978,6 +2642,39 @@ const workflowOverviewApi = {
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel: () => releaseArtifactUrlWorkflowSummary,
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan: () => releaseArtifactUrlWorkflow.actionPlan,
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState: () => releaseArtifactUrlWorkflowRenderState,
getWorkflowOverviewPromotionFamilyRowsRenderState: () => promotionFamilyRowsRenderState,
getWorkflowOverviewPromotionFamilySourceFilterViewModel: () => promotionFamilySourceFilterViewModel,
getWorkflowOverviewPromotionFamilySourceFilterDomContract:
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomContract,
getWorkflowOverviewPromotionFamilySourceFilterDomReadiness: () => (
createIniPanelShellWorkflowOverviewPromotionFamilySourceFilterDomReadiness({
documentRef: mountDocument,
mountNode: promotionFamilySourceFilterMountNode,
statusNode: promotionFamilySourceFilterStatusNode,
controlsNode: promotionFamilySourceFilterControlsNode,
rowsNode: promotionFamilySourceFilterRowsNode,
})
),
getWorkflowOverviewPromotionFamilySourceFilterSelectionActionPlan: () =>
promotionFamilySourceFilterSelectionActionPlan,
handleWorkflowOverviewPromotionFamilySourceFilterSelectionChange: () =>
promotionFamilySourceFilterSelectionChange,
bindWorkflowOverviewPromotionFamilySourceFilterSelectionControls: () =>
promotionFamilySourceFilterSelectionBinding,
renderWorkflowOverviewPromotionFamilySourceFilterState: () => promotionFamilySourceFilterRenderResult,
mountWorkflowOverviewPromotionFamilySourceFilterState: () => promotionFamilySourceFilterMountResult,
getWorkflowOverviewPromotionFamilyRowsDomContract:
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomContract,
getWorkflowOverviewPromotionFamilyRowsDomReadiness: () => (
createIniPanelShellWorkflowOverviewPromotionFamilyRowsDomReadiness({
documentRef: mountDocument,
mountNode: promotionFamilyRowsMountNode,
statusNode: promotionFamilyRowsStatusNode,
rowsNode: promotionFamilyRowsNode,
})
),
renderWorkflowOverviewPromotionFamilyRowsState: () => promotionFamilyRowsRenderResult,
mountWorkflowOverviewPromotionFamilyRowsState: () => promotionFamilyRowsMountResult,
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract:
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness: () => (