Compare commits
90 Commits
01d168e247
...
24e1fc18b1
| Author | SHA1 | Date | |
|---|---|---|---|
| 24e1fc18b1 | |||
| 19eabdf06e | |||
| 35bc25714d | |||
| 741afc5103 | |||
| 2f6e8480bf | |||
| e11c29533f | |||
| b7ec759645 | |||
| bee05005b5 | |||
| 9015de1961 | |||
| d789920d7f | |||
| 640f1e4438 | |||
| e00c7b038b | |||
| 4970d156c1 | |||
| 61779cad55 | |||
| 82bb02de15 | |||
| ce2c145374 | |||
| 9abca15d60 | |||
| 1d383b475c | |||
| 7f937bd56c | |||
| 343ad0acd6 | |||
| 6c087499fd | |||
| 904f906f86 | |||
| 56175e29cc | |||
| b63669588c | |||
| 23f337834b | |||
| 7177a29fc6 | |||
| f4f4b3c744 | |||
| ae3adf2d13 | |||
| d3ee5d49ff | |||
| 65e95327b3 | |||
| 2615e5bc57 | |||
| ce3d08ef61 | |||
| eb50e6b455 | |||
| 68ff2fb5d6 | |||
| 03db34485e | |||
| 8e37c69bc4 | |||
| d076763ef4 | |||
| efbcf8b9bf | |||
| 5918a3c573 | |||
| 67a4a69698 | |||
| d16003b67a | |||
| 98ee77f086 | |||
| f3d8955fd7 | |||
| b771ee4141 | |||
| 7a5aa23dbc | |||
| 543609cbe7 | |||
| 683f86c33c | |||
| 861c854309 | |||
| ac77de3c13 | |||
| b51ff3415a | |||
| 1a863fa0b4 | |||
| fc9e6a3514 | |||
| 83df80d00f | |||
| 75f71c2563 | |||
| 6856721695 | |||
| 3cfd4b371e | |||
| f70fc09770 | |||
| 4fdbadd60c | |||
| 7095690869 | |||
| 766f630dfb | |||
| b61e7e980d | |||
| 6b6629ce73 | |||
| e1e0c7f6d2 | |||
| 1da6180387 | |||
| 4c02f33d57 | |||
| 859d01356b | |||
| c3d9b90de1 | |||
| 298aa5e403 | |||
| 94b44c0e73 | |||
| 22ca6ced3f | |||
| 54f8551c50 | |||
| 1375e0ddd5 | |||
| df0773b431 | |||
| f548f24fb8 | |||
| 1d06fd4cd4 | |||
| bb8ac55905 | |||
| e1029ede0f | |||
| f1d10fc317 | |||
| 542b139477 | |||
| c92021fa82 | |||
| 2a7485c14d | |||
| b1f23e6641 | |||
| 5c951ab9bc | |||
| 01817f6493 | |||
| b31b9a2f8a | |||
| ee665c47be | |||
| a663c8dbca | |||
| 1f9a8c33b0 | |||
| 7b17603601 | |||
| b3f131fa5f |
281
PROJECT_COMPLETION_TRACKER.md
Normal file
281
PROJECT_COMPLETION_TRACKER.md
Normal file
@@ -0,0 +1,281 @@
|
||||
# Project Completion Tracker
|
||||
|
||||
Last updated: 2026-06-16 CST
|
||||
|
||||
This file tracks the overall completion state of the LinuxCNC WASM/browser port.
|
||||
Use it for project-level status and acceptance tracking. Use `text15.txt` for
|
||||
turn-by-turn continuation notes and next-batch execution records.
|
||||
|
||||
## Current Status
|
||||
|
||||
- Active continuation file: `text15.txt`
|
||||
- Latest completed batch: real browser simulation page MVP
|
||||
- Latest relevant commit: use `git log -1 --oneline` after each committed batch
|
||||
- Working tree at tracker creation: clean
|
||||
- Required policy: LinuxCNC remains the only CNC semantic source
|
||||
- First priority: real UI/browser CNC simulation page; see `wasm-port/docs/real-browser-simulation-priority.md`
|
||||
- Current scope status: complete after final clean-tree release validation
|
||||
|
||||
## Completion Definition
|
||||
|
||||
The project is complete for the current scope when all of the following are true:
|
||||
|
||||
- Browser UI workflows are usable and validated:
|
||||
- real browser CNC simulation page
|
||||
- INI panel
|
||||
- control page
|
||||
- workflow overview
|
||||
- external shell handoff
|
||||
- OPFS/session persistence
|
||||
- SDK/API surface exposes stable helpers for external callers.
|
||||
- Sim config coverage has clear pass/skip/promotion state.
|
||||
- Host/runtime blocked families are explicitly documented and not falsely promoted.
|
||||
- All required gates pass.
|
||||
- Documentation explains what is supported, blocked, and how to verify it.
|
||||
- `text15.txt` contains the current continuation record and the working tree is clean.
|
||||
|
||||
## Area Status
|
||||
|
||||
| Area | Status | Notes |
|
||||
| --- | --- | --- |
|
||||
| LinuxCNC semantic boundary | Stable | JS/browser remains glue, staging, OPFS, WASM/browser boundary, docs, and tests only. |
|
||||
| Real browser simulation priority | Active first priority | Future batches should prioritize the real UI/browser CNC simulation page, with visible machine/session loading, G-code program state, LinuxCNC-backed interpreter execution, machine readiness/status panels, toolpath/preview rendering from LinuxCNC-produced output or validated runtime events, and browser smoke coverage. First entry point: `wasm-port/runtime/ui/simulation/index.html`; gate: `wasm-port/tests/browser/verify_real_simulation_browser.sh`. See `wasm-port/docs/real-browser-simulation-priority.md`. |
|
||||
| Real browser simulation page | MVP active | `runtime/ui/simulation/index.html` loads in Chromium, runs a representative G-code program through LinuxCNC interpreter WASM, renders program lines, final axis readout, canonical event table, raw canonical output, and SVG toolpath preview. |
|
||||
| Vendor/source guard | Stable | `verify_vendor_sync.sh` and standalone semantic guard are required every batch. |
|
||||
| Interpreter WASM smoke | Stable | `interp_wasm_node_smoke=ok`. |
|
||||
| Sim config inventory | Stable with skips | `executed=28`, `passed=28`, `skipped=131`, `unexpected_fail=0`; `docs/sim-configs-coverage-handoff.md` records the release-gate baseline. |
|
||||
| Native nc_files baseline | Stable | Last recorded Layer 1: `total 107`, `pass 101`, `expected_fail 6`, `unexpected_fail 0`. |
|
||||
| Native sim configs baseline | Stable | Last recorded Layer 2: `total 159`, `pass 151`, `expected_fail 8`, `unexpected_fail 0`. |
|
||||
| OPFS/session persistence | Stable for current scope | Browser smoke passes, SDK re-exports OPFS/session helpers including `readMachineSessionReadiness()` and `createMachineSessionPersistenceSummary()`, the UI exposes `getMachineSessionPersistenceSummary()`, and `docs/opfs-session-persistence.md` defines the project-level release gate. |
|
||||
| SDK/API surface | Stable for current scope | SDK re-exports core, OPFS/session readiness/load helpers, sim-config staging, project release gate manifest/result matrix/action-plan/readiness summary view-model, project batch acceptance capability matrix/workflow/summary/action-plan/checklist/report validation summary/action-plan, JSON workflow, URL workflow helpers, and artifact writer/validator gate, project release readiness report/artifact JSON workflow/artifact URL workflow summary/action-plan/artifact validation summary/action-plan plus URL loading workflow, INI panel entry/launcher/control-page/shell/workflow overview helpers including release artifact JSON and URL workflow summary/action-plan/render-state DOM mount helpers, validation action-plan render/mount helpers, URL workflow action-plan, and gate execution summary render/mount helpers, the session readiness workflow report, and the shell API surface inventory with dedicated Node smokes. |
|
||||
| Browser/UI workflow | Stable for current scope | Workflow overview embedding mount DOM closure is complete, and INI panel session workflows plus read-only control/shell handoff expose, render, mount, fetch, validate, summarize, action-plan, and report OPFS/session readiness, OPFS/session persistence summary, release readiness artifact JSON/URL workflows, validation/rendering/URL workflow summaries/action-plans/gate execution summaries, and API surface inventory before loading or reading WASM session state. |
|
||||
| Workflow overview embedding | Stable for current scope | Report/display/render, DOM contract/readiness/renderer, mount result/display/render, mount DOM contract/readiness/renderer/wrapper exist. |
|
||||
| Host/runtime boundary proof | Stable blocked state | Host smoke passes; runtime families remain blocked and `docs/host-runtime-boundary-handoff.md` records host readiness, promotion blockers, dispatch, and evidence gates. |
|
||||
| Documentation/release gate | Stable for current scope | `docs/project-release-handoff.md` links README, panel entry, SDK, OPFS/session, sim-config, host/runtime, source reuse, drift, tracker acceptance paths, and the machine-readable release readiness report/artifact. |
|
||||
| Project release gate wrapper | Stable | `tests/host/verify_project_release_gate.sh` runs the documented minimum release validation commands including the release artifact URL browser workflow gate, SDK URL workflow gate, and project batch acceptance workflow/artifact gate, writes `build/project-release-readiness.json` and `build/project-batch-acceptance.json`, verifies embedded gate manifest/result matrix/action-plan evidence plus batch acceptance report evidence through SDK validation workflows, and ends with `project_release_gate=ok`. |
|
||||
|
||||
## Blocked Runtime Families
|
||||
|
||||
Do not promote these without real LinuxCNC-owned runtime proof and the required
|
||||
native -> Node/WASM -> browser/host validation sequence:
|
||||
|
||||
- `L4-USER-M-PROCESS`
|
||||
- `L4-TOOL-DB`
|
||||
- `L4-PYTHON-REMAP`
|
||||
|
||||
Do not run these unless the host environment actually provides the needed
|
||||
LinuxCNC runtime:
|
||||
|
||||
```bash
|
||||
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||
```
|
||||
|
||||
## Remaining Work
|
||||
|
||||
Execute these in order unless a later user request explicitly changes priority.
|
||||
|
||||
### 0. Real Browser CNC Simulation Page
|
||||
|
||||
Current first priority:
|
||||
|
||||
- Build a real UI/browser CNC simulation page as quickly as possible.
|
||||
- First MVP exists at `wasm-port/runtime/ui/simulation/index.html`.
|
||||
- The page must be visible and operator-oriented, not another release dashboard.
|
||||
- It should use OPFS/session persistence, staged G-code, and existing
|
||||
LinuxCNC-backed WASM SDK execution.
|
||||
- It should expose machine/session readiness, active program state, run status,
|
||||
and a toolpath/preview surface based on LinuxCNC-produced interpreter output,
|
||||
canonical events, or validated runtime summaries.
|
||||
- Add browser smoke coverage proving the page loads, renders nonempty runtime
|
||||
state, and runs a representative LinuxCNC-backed flow.
|
||||
- Do not implement G-code, tool, parameter, kinematics, remap, planner, or
|
||||
canonical motion semantics in JavaScript.
|
||||
|
||||
Constraint document:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh
|
||||
```
|
||||
|
||||
### 1. Browser/UI Workflow Closure
|
||||
|
||||
Current next task:
|
||||
|
||||
- Current scope is complete for the established release baseline. Continue future
|
||||
work from `text15.txt`, and keep each batch tied to a callable API, verifiable
|
||||
workflow, executable gate, or real UI/browser capability.
|
||||
|
||||
Then complete the closure:
|
||||
|
||||
- Mount render-state DOM renderer is complete.
|
||||
- Non-throwing mount workflow wrapper is complete.
|
||||
- Expose each helper through:
|
||||
- `createIniPanelLaunchApiManifest().methods`
|
||||
- `createIniPanelShellViewModel()`
|
||||
- `window.linuxCncIniPanelLaunchApi`
|
||||
- `window.linuxCncIniPanelWorkflowOverviewApi`
|
||||
- Cover each helper in:
|
||||
- UI Node smoke
|
||||
- launch API manifest test
|
||||
- shell integration manifest test
|
||||
- entry docs test
|
||||
- workflow overview browser smoke
|
||||
- launch browser smoke
|
||||
- shell integration browser smoke
|
||||
- `docs/panel-entry.md`
|
||||
|
||||
Completion criteria:
|
||||
|
||||
- External shell can render workflow overview embedding mount state with a
|
||||
custom DOM contract.
|
||||
- Missing DOM returns structured blocked results where applicable.
|
||||
- No control buttons or CNC semantic parsing are added.
|
||||
|
||||
### 2. SDK/API Surface Consolidation
|
||||
|
||||
Work items:
|
||||
|
||||
- Audit `runtime/sdk/src/index.js`. Done.
|
||||
- Export stable helpers that external callers should use. Done for core, OPFS/session, sim-config staging, project release readiness, INI panel entry/launcher/control-page/shell/workflow overview helpers.
|
||||
- Add or update SDK smoke for export shape and manifest compatibility. Done via `tests/sdk/node/verify_sdk_surface.sh`.
|
||||
- Update `runtime/sdk/README.md` with usage examples and unsupported areas. Done for the current SDK surface.
|
||||
|
||||
Completion criteria:
|
||||
|
||||
- SDK exports match documented browser/UI helper surface.
|
||||
- SDK tests pass in Node.
|
||||
- No CNC semantics are implemented in JS.
|
||||
|
||||
### 3. OPFS/Session Persistence Summary
|
||||
|
||||
Work items:
|
||||
|
||||
- Add a project-level OPFS/session workflow summary helper or report. Done as `docs/opfs-session-persistence.md`.
|
||||
- Include machine files, session snapshot, readonly control status, and handoff
|
||||
readiness. Done through linked UI docs plus browser and host gate references.
|
||||
- Add browser smoke coverage for save -> restore -> readonly status -> shell
|
||||
handoff. Existing browser smoke remains the gate for this workflow.
|
||||
- Document OPFS scope and failure modes. Done.
|
||||
- Add docs smoke for the project-level handoff. Done via `tests/docs/node/verify_opfs_session_docs.sh`.
|
||||
|
||||
Completion criteria:
|
||||
|
||||
- `browser_ini_opfs_smoke=ok` remains stable.
|
||||
- External callers can inspect persistence readiness and failures.
|
||||
- `opfs_session_docs_node_smoke=ok` is included in the host aggregate gate.
|
||||
|
||||
### 4. Sim Config Coverage Promotion Matrix
|
||||
|
||||
Work items:
|
||||
|
||||
- Keep inventory `unexpected_fail=0`.
|
||||
- Make skip reasons machine-readable and documented:
|
||||
- `ASSET_ONLY`
|
||||
- `L4_PYTHON_REMAP`
|
||||
- `L4_TOOL_DB`
|
||||
- `L4_USER_M_PROCESS`
|
||||
- `NON_MAIN_CLASS`
|
||||
- `UPSTREAM_DEMO`
|
||||
- Promote only configs with LinuxCNC-owned proof.
|
||||
- Add short release handoff for current inventory baseline. Done via `docs/sim-configs-coverage-handoff.md`.
|
||||
- Add docs smoke for inventory baseline and skip reason map. Done via `tests/docs/node/verify_sim_configs_coverage_docs.sh`.
|
||||
|
||||
Completion criteria:
|
||||
|
||||
- No skipped config lacks a documented reason.
|
||||
- No blocked runtime family is falsely promoted.
|
||||
- `sim_configs_coverage_docs_node_smoke=ok` is included in the host aggregate gate.
|
||||
|
||||
### 5. Host/Runtime Boundary Reports
|
||||
|
||||
Work items:
|
||||
|
||||
- Consolidate runtime family proof reports. Done via `docs/host-runtime-boundary-handoff.md`.
|
||||
- Keep missing host runtime as blocked. Done; current status remains `host_blocked_for_all_opt_in_native_probes`.
|
||||
- If real runtime becomes available, validate in native -> Node/WASM -> browser/host order.
|
||||
- Add docs smoke for host/runtime boundary handoff. Done via `tests/docs/node/verify_host_runtime_boundary_docs.sh`.
|
||||
|
||||
Completion criteria:
|
||||
|
||||
- `verify_host_smokes.sh` remains stable.
|
||||
- Boundaries are documented and machine-readable.
|
||||
- `host_runtime_boundary_docs_node_smoke=ok` is included in the host aggregate gate.
|
||||
|
||||
### 6. Documentation/Release Gate
|
||||
|
||||
Work items:
|
||||
|
||||
- Update `README.md`. Done.
|
||||
- Update `docs/panel-entry.md`. Done.
|
||||
- Update `docs/source-reuse-map.md`. Done.
|
||||
- Update `docs/drift-report.md`. Done.
|
||||
- Add or refresh final acceptance checklist. Done via
|
||||
`docs/project-release-handoff.md`.
|
||||
- Add a machine-readable release readiness SDK report for CI/external callers.
|
||||
Done via `createProjectReleaseReadinessReport()`.
|
||||
- Add docs smoke for project release handoff. Done via
|
||||
`tests/docs/node/verify_project_release_handoff_docs.sh`.
|
||||
|
||||
Completion criteria:
|
||||
|
||||
- A new developer can run validation from README/docs without reading old
|
||||
`text1` through `text14`.
|
||||
- `project_release_handoff_docs_node_smoke=ok` is included in the host
|
||||
aggregate gate.
|
||||
|
||||
## Required Gates
|
||||
|
||||
Run these for each implementation batch:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh
|
||||
wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh
|
||||
wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
Or run the same current-scope minimum gate through:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/host/verify_project_release_gate.sh
|
||||
```
|
||||
|
||||
Run this if browser interpreter behavior is touched:
|
||||
|
||||
```bash
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_interp_browser.sh
|
||||
```
|
||||
|
||||
Run these for native/source/staging or promotion work:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_nc_files.sh
|
||||
wasm-port/tests/native/verify_sim_configs.sh
|
||||
```
|
||||
|
||||
## Final Acceptance Checklist
|
||||
|
||||
- [x] Working tree is clean.
|
||||
- [x] Latest continuation state has moved to `text15.txt`.
|
||||
- [x] `git diff --check` passes.
|
||||
- [x] Vendor sync guard passes.
|
||||
- [x] Standalone CNC semantics guard passes.
|
||||
- [x] Interpreter WASM Node smoke passes.
|
||||
- [x] Sim config WASM inventory has `unexpected_fail=0`.
|
||||
- [x] UI Node smoke passes.
|
||||
- [x] Browser INI panel smoke passes.
|
||||
- [x] Host aggregate smoke passes.
|
||||
- [x] Project release gate wrapper passes.
|
||||
- [x] Runtime blocked families remain documented.
|
||||
- [x] Project release handoff docs smoke passes.
|
||||
- [x] README/docs explain supported workflows and blocked items.
|
||||
1947
text12.txt
1947
text12.txt
File diff suppressed because it is too large
Load Diff
2040
text13.txt
Normal file
2040
text13.txt
Normal file
File diff suppressed because it is too large
Load Diff
4224
text14.txt
Normal file
4224
text14.txt
Normal file
File diff suppressed because it is too large
Load Diff
1632
text15.txt
Normal file
1632
text15.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,59 @@
|
||||
|
||||
This directory is the standalone workspace for the WASM-based simulation port.
|
||||
|
||||
## Current Release Handoff
|
||||
|
||||
Start from `docs/project-release-handoff.md` for the current project-level
|
||||
handoff. It links the browser/UI workflow surface, SDK surface,
|
||||
OPFS/session persistence scope, sim-config coverage baseline, blocked
|
||||
host/runtime families, source reuse rules, drift rules, the real browser
|
||||
simulation priority constraint, and acceptance tracker.
|
||||
|
||||
The first priority for subsequent work is documented in
|
||||
`docs/real-browser-simulation-priority.md`: move quickly toward a real browser
|
||||
CNC simulation page with visible machine/session loading, G-code program state,
|
||||
LinuxCNC-backed interpreter execution, and browser smoke coverage. This does
|
||||
not relax the LinuxCNC semantic boundary; browser code still must not implement
|
||||
G-code, tool, parameter, kinematics, remap, or planner semantics.
|
||||
The first real simulation entry point is `runtime/ui/simulation/index.html`,
|
||||
validated by `tests/browser/verify_real_simulation_browser.sh`.
|
||||
|
||||
The project release gate for the current scope is:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/host/verify_project_release_gate.sh
|
||||
```
|
||||
|
||||
It executes these minimum release validation commands:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
Expected handoff smoke outputs include:
|
||||
|
||||
```text
|
||||
project_release_handoff_docs_node_smoke=ok
|
||||
opfs_session_docs_node_smoke=ok
|
||||
sim_configs_coverage_docs_node_smoke=ok
|
||||
host_runtime_boundary_docs_node_smoke=ok
|
||||
real_browser_simulation_priority_docs_node_smoke=ok
|
||||
sdk_surface_node_smoke=ok
|
||||
host_wasm_opfs_browser_smokes=ok
|
||||
project_release_gate=ok
|
||||
```
|
||||
|
||||
Browser/UI entry points are documented in `docs/panel-entry.md`. Stable SDK
|
||||
imports are documented in `runtime/sdk/README.md` and should come from
|
||||
`runtime/sdk/src/index.js`.
|
||||
|
||||
Rules:
|
||||
|
||||
1. `linuxcnc/` is treated as the upstream source tree.
|
||||
@@ -130,5 +183,16 @@ Directory intent:
|
||||
- `tests/fixtures/`: stable simulation inputs shared by native and browser tests.
|
||||
- `build/` and `dist/`: generated outputs only; never hand-edited.
|
||||
|
||||
Project-level OPFS/session persistence and release-gate handoff is documented in
|
||||
`docs/opfs-session-persistence.md`.
|
||||
The real browser CNC simulation page priority is documented in
|
||||
`docs/real-browser-simulation-priority.md`.
|
||||
Sim-config inventory coverage and release-gate handoff is documented in
|
||||
`docs/sim-configs-coverage-handoff.md`.
|
||||
Host/runtime boundary release-gate handoff is documented in
|
||||
`docs/host-runtime-boundary-handoff.md`.
|
||||
Project-level release acceptance and next-maintainer handoff is documented in
|
||||
`docs/project-release-handoff.md`.
|
||||
|
||||
The presence of this directory means the port is managed independently of the
|
||||
native LinuxCNC tree even though LinuxCNC remains the semantic source of truth.
|
||||
|
||||
@@ -6,6 +6,10 @@ As of the current upstream baseline, no byte-level drift is allowed between
|
||||
files listed in `tools/source-manifest.txt` and the matching files under
|
||||
`../linuxcnc/`.
|
||||
|
||||
The project-level release handoff in `docs/project-release-handoff.md` includes
|
||||
this drift report in the final acceptance path alongside source reuse,
|
||||
OPFS/session, sim-config coverage, host/runtime boundary, SDK, and UI docs.
|
||||
|
||||
The enforced upstream baseline is:
|
||||
|
||||
```text
|
||||
|
||||
146
wasm-port/docs/host-runtime-boundary-handoff.md
Normal file
146
wasm-port/docs/host-runtime-boundary-handoff.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Host/runtime boundary release handoff
|
||||
|
||||
This document is the short release-gate handoff for blocked LinuxCNC runtime
|
||||
families in the WASM/browser port. It summarizes current host readiness,
|
||||
promotion blockers, opt-in probe dispatch state, native evidence acceptance, and
|
||||
the rule that blocked runtime families remain blocked on this host.
|
||||
|
||||
Detailed references:
|
||||
|
||||
- `docs/full-process-boundary-design.md`
|
||||
- `docs/sim-configs-coverage-matrix.md`
|
||||
- `docs/sim-configs-coverage-handoff.md`
|
||||
- `tests/wasm/node/verify_sim_configs_inventory_wasm.mjs`
|
||||
|
||||
## Current blocked runtime families
|
||||
|
||||
The current hard blocked families are:
|
||||
|
||||
- `L4-USER-M-PROCESS`
|
||||
- `L4-TOOL-DB`
|
||||
- `L4-PYTHON-REMAP`
|
||||
|
||||
Current host readiness is summarized by:
|
||||
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-native-evidence-acceptance-gate.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-skip-evidence-rollup.tsv`
|
||||
- `build/wasm/sim-configs-inventory/blocked-runtime-promotion-lock.tsv`
|
||||
|
||||
Current rollup state:
|
||||
|
||||
```text
|
||||
host_readiness_status=host_blocked_for_all_opt_in_native_probes
|
||||
family_count=3
|
||||
host_ready_family_count=0
|
||||
host_blocked_family_count=3
|
||||
blocked_families=L4-PYTHON-REMAP,L4-TOOL-DB,L4-USER-M-PROCESS
|
||||
missing_host_requirements=halcmd,halrun,linuxcnc,milltask
|
||||
ready_opt_in_command_count=0
|
||||
execution_enabled=0
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
## Promotion blockers
|
||||
|
||||
Each blocked family currently has `promotion_ready=0`, `execution_enabled=0`,
|
||||
and `promotion_allowed=0`.
|
||||
|
||||
The current blocker keys are:
|
||||
|
||||
```text
|
||||
host_runtime_requirements_missing
|
||||
native_runtime_probe_not_passed
|
||||
native_pass_evidence_not_ready
|
||||
node_inventory_gate_not_complete
|
||||
browser_smoke_gate_not_complete
|
||||
promotion_lock_active
|
||||
manual_lock_update_required
|
||||
```
|
||||
|
||||
The next unblock action is to provide the missing host runtime requirements for
|
||||
the relevant family. Providing host commands is not enough to promote a row; it
|
||||
only allows the next opt-in probe gate to be considered.
|
||||
|
||||
## Opt-in probe dispatch
|
||||
|
||||
The current dispatch rollup is:
|
||||
|
||||
```text
|
||||
dispatch_status=dispatch_blocked_for_all_opt_in_native_probes
|
||||
probe_count=3
|
||||
dispatch_allowed_count=0
|
||||
dispatch_blocked_count=3
|
||||
dispatch_actions=skip_missing_host_requirements
|
||||
execution_enabled=0
|
||||
promotion_allowed=0
|
||||
```
|
||||
|
||||
These commands remain blocked on this host:
|
||||
|
||||
```bash
|
||||
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||
```
|
||||
|
||||
Do not run them unless the generated preflight, family readiness, dispatch
|
||||
plan, and dispatch rollup all permit the specific family.
|
||||
|
||||
## Native evidence acceptance
|
||||
|
||||
`runtime-boundary-native-evidence-acceptance-gate.tsv` currently records:
|
||||
|
||||
- `current_probe_status=skipped_missing_host_runtime`
|
||||
- `native_evidence_status=pending_until_native_pass`
|
||||
- `skip_evidence_status=skip_valid_until_host_requirements_available`
|
||||
- `node_gate_status=blocked_until_native_pass_evidence`
|
||||
- `browser_gate_status=blocked_until_node_gate_complete`
|
||||
- `native_evidence_gate=blocked_until_host_requirements_available`
|
||||
- `evidence_acceptance_allowed=0`
|
||||
|
||||
A skipped opt-in probe is not a native pass. It only proves that this host is
|
||||
correctly blocked until the required LinuxCNC runtime commands are available.
|
||||
|
||||
## Release gate
|
||||
|
||||
Use this gate when touching blocked runtime family reports, host readiness, or
|
||||
promotion policy:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/docs/node/verify_host_runtime_boundary_docs.sh
|
||||
wasm-port/tests/docs/node/verify_sim_configs_coverage_docs.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
The host aggregate must include:
|
||||
|
||||
```text
|
||||
sim_configs_wasm_node_inventory=ok
|
||||
host_runtime_boundary_docs_node_smoke=ok
|
||||
sim_configs_coverage_docs_node_smoke=ok
|
||||
host_wasm_opfs_browser_smokes=ok
|
||||
```
|
||||
|
||||
## Promotion rule
|
||||
|
||||
No blocked family may be promoted until all of these are true:
|
||||
|
||||
1. Host readiness allows the family-specific opt-in probe.
|
||||
2. The explicit opt-in probe passes natively with LinuxCNC-owned runtime
|
||||
behavior.
|
||||
3. Native evidence acceptance allows the pass evidence.
|
||||
4. Node/WASM inventory proof passes after native evidence.
|
||||
5. Browser/host proof passes after Node/WASM proof.
|
||||
6. `verify_no_standalone_cnc_semantics.sh` still passes.
|
||||
7. `blocked-runtime-promotion-lock.tsv`, docs, and tracker records are updated
|
||||
deliberately.
|
||||
|
||||
Until then, `L4-USER-M-PROCESS`, `L4-TOOL-DB`, and `L4-PYTHON-REMAP` remain
|
||||
blocked, inventory-only runtime families.
|
||||
180
wasm-port/docs/opfs-session-persistence.md
Normal file
180
wasm-port/docs/opfs-session-persistence.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# OPFS/session persistence and release gate
|
||||
|
||||
This document is the project-level handoff for OPFS/session persistence in the
|
||||
LinuxCNC WASM/browser port. It links the stable SDK entrypoint, browser smoke,
|
||||
session snapshot helpers, machine-file helpers, host gate commands, and blocked
|
||||
runtime family policy in one place.
|
||||
|
||||
## Supported surface
|
||||
|
||||
Stable external imports come from `runtime/sdk/src/index.js`.
|
||||
|
||||
OPFS text/file helpers:
|
||||
|
||||
- `getOpfsRoot()`
|
||||
- `saveTextFile()`
|
||||
- `loadTextFile()`
|
||||
- `machineIniPath()`
|
||||
- `toolTablePath()`
|
||||
- `parameterFilePath()`
|
||||
- `gcodeProgramPath()`
|
||||
- `sessionSnapshotPath()`
|
||||
- `defaultMachinePaths()`
|
||||
- `normalizeOpfsPath()`
|
||||
|
||||
Session snapshot helpers:
|
||||
|
||||
- `createMachineSessionSnapshotPayload()`
|
||||
- `createSessionSnapshot()`
|
||||
- `saveSessionSnapshot()`
|
||||
- `saveMachineSessionSnapshot()`
|
||||
- `loadSessionSnapshot()`
|
||||
- `loadMachineSessionSnapshot()`
|
||||
- `validateSessionSnapshot()`
|
||||
- `readMachineSessionReadiness()`
|
||||
|
||||
Machine file and program helpers:
|
||||
|
||||
- `machineFilePaths()`
|
||||
- `saveMachineTextFiles()`
|
||||
- `loadMachineTextFiles()`
|
||||
- `saveGcodeProgram()`
|
||||
- `loadGcodeProgram()`
|
||||
- `gcodeFilenameFromProgramPath()`
|
||||
|
||||
LinuxCNC-backed OPFS bridges:
|
||||
|
||||
- `restoreMachineParametersFromOpfs()`
|
||||
- `saveMachineParametersToOpfs()`
|
||||
- `loadMachineToolTableFromOpfs()`
|
||||
- `saveMachineToolTableToOpfs()`
|
||||
- `loadMachineSessionFromOpfs()`
|
||||
|
||||
Browser UI/session helpers are documented in:
|
||||
|
||||
- `docs/ui-session-summary-helpers.md`
|
||||
- `docs/ui-machine-file-helpers.md`
|
||||
- `docs/ui-run-summary-helpers.md`
|
||||
- `docs/ui-panel-state-summary.md`
|
||||
- `docs/panel-entry.md`
|
||||
|
||||
## Persistence workflow
|
||||
|
||||
The browser/UI persistence workflow is host-side only:
|
||||
|
||||
1. The UI saves machine INI, parameter, and tool-table text under
|
||||
`linuxcnc/machines/<machine-id>/`.
|
||||
2. Optional G-code programs are saved under `linuxcnc/gcode/`.
|
||||
3. A session snapshot is saved under `linuxcnc/sessions/<session-id>/`.
|
||||
4. `loadMachineSessionFromOpfs()` loads the persisted INI text, copies it into
|
||||
the interpreter WASM filesystem, and uses the LinuxCNC-backed INI SDK to read
|
||||
`[RS274NGC]PARAMETER_FILE`, `[EMCIO]TOOL_TABLE`, and
|
||||
`[EMCIO]RANDOM_TOOLCHANGER` when explicit host options do not override them.
|
||||
5. Parameter and tool-table bridge helpers copy OPFS text into the interpreter
|
||||
WASM filesystem and then call LinuxCNC-backed interpreter SDK methods such as
|
||||
`restoreParameters()`, `saveParameters()`, `loadToolTable()`, and
|
||||
`saveToolTable()`.
|
||||
|
||||
External callers can run `readMachineSessionReadiness()` before step 4 to get a
|
||||
structured `ready`/`blocked` report for persisted INI, parameter, tool-table,
|
||||
optional G-code, and optional session snapshot files. This helper only checks
|
||||
host file availability and snapshot envelope validity.
|
||||
|
||||
This workflow stores host files and stages them into Emscripten FS. It does not
|
||||
implement G-code, tool-table, parameter-file, planner, remap, or kinematics
|
||||
semantics in JavaScript.
|
||||
|
||||
## Verified browser workflows
|
||||
|
||||
The aggregate browser gate is:
|
||||
|
||||
```bash
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
```
|
||||
|
||||
The dedicated OPFS/session browser workflow gate is:
|
||||
|
||||
```bash
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh
|
||||
```
|
||||
|
||||
Required output:
|
||||
|
||||
```text
|
||||
browser_ini_opfs_smoke=ok
|
||||
browser_opfs_session_workflow_smoke=ok
|
||||
browser_ini_control_page_smoke=ok
|
||||
browser_ini_launch_smoke=ok
|
||||
browser_ini_workflow_overview_smoke=ok
|
||||
browser_ini_shell_integration_workflow_smoke=ok
|
||||
```
|
||||
|
||||
`browser_ini_opfs_smoke=ok` covers real browser OPFS/session persistence,
|
||||
including INI text persistence, session snapshots, machine-file helpers,
|
||||
G-code program helpers, and the INI panel frame workflow.
|
||||
`browser_opfs_session_workflow_smoke=ok` isolates the save machine files ->
|
||||
save G-code -> save session snapshot -> readiness -> load session into WASM ->
|
||||
persistence summary workflow in a standalone browser gate.
|
||||
|
||||
## Release gate
|
||||
|
||||
Run the project-level gate before claiming a release-ready OPFS/session or SDK
|
||||
surface:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/opfs/node/verify_file_service.sh
|
||||
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
The host aggregate must include:
|
||||
|
||||
```text
|
||||
opfs_file_service_node_smoke=ok
|
||||
sdk_surface_node_smoke=ok
|
||||
ui_node_smokes=ok
|
||||
browser_ini_opfs_smoke=ok
|
||||
browser_opfs_session_workflow_smoke=ok
|
||||
browser_ini_shell_integration_workflow_smoke=ok
|
||||
host_wasm_opfs_browser_smokes=ok
|
||||
```
|
||||
|
||||
## Blocked runtime families
|
||||
|
||||
OPFS/session persistence does not unblock full LinuxCNC runtime families. These
|
||||
remain blocked until a LinuxCNC-owned runtime proof exists and native,
|
||||
Node/WASM, browser, and host gates all pass in that order:
|
||||
|
||||
- `L4-USER-M-PROCESS`
|
||||
- `L4-TOOL-DB`
|
||||
- `L4-PYTHON-REMAP`
|
||||
|
||||
Do not run the opt-in runtime probes on a host that lacks the required LinuxCNC
|
||||
runtime processes:
|
||||
|
||||
```bash
|
||||
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||
```
|
||||
|
||||
## Failure modes
|
||||
|
||||
- Missing OPFS support returns an OPFS availability error from `getOpfsRoot()`.
|
||||
- Invalid OPFS paths are rejected before storage access.
|
||||
- Invalid snapshot format, version, session id, payload, or machine id is
|
||||
rejected by snapshot validation helpers.
|
||||
- `readMachineSessionReadiness()` reports missing files or invalid snapshots as
|
||||
structured blocked checks before callers load a session into WASM.
|
||||
- Missing INI, parameter, or tool-table OPFS files remain host persistence
|
||||
errors. JavaScript does not synthesize LinuxCNC machine state.
|
||||
- Tool, parameter, remap, planner, and kinematics behavior remains owned by
|
||||
vendored LinuxCNC source and the C/WASM ABI.
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
This page is the short entry map for the INI panel UI shell.
|
||||
|
||||
For the project-level acceptance path that references this UI surface, start
|
||||
with `docs/project-release-handoff.md`. Stable SDK imports for external callers
|
||||
are documented in `runtime/sdk/README.md` and should come from
|
||||
`runtime/sdk/src/index.js`. The workflow overview embedding helpers below are
|
||||
the read-only external-shell path for embedding and mounting overview state.
|
||||
|
||||
## Shell Module
|
||||
|
||||
Use the shell-facing module when an outer UI needs entry links or a read-only
|
||||
@@ -10,24 +16,94 @@ control view:
|
||||
```js
|
||||
import {
|
||||
INI_PANEL_ENTRIES,
|
||||
createControlPageBrowserApiManifest,
|
||||
createControlPageBrowserApiMethods,
|
||||
createControlPageBrowserApiSchema,
|
||||
createIniPanelLaunchApiManifest,
|
||||
createIniPanelLaunchApiSchema,
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createIniPanelShellControlPageContract,
|
||||
createIniPanelShellIntegrationManifest,
|
||||
createIniPanelShellIntegrationReadiness,
|
||||
createIniPanelShellIntegrationSchema,
|
||||
createIniPanelShellIntegrationWorkflowPlan,
|
||||
createIniPanelShellSessionHandoffActionPlan,
|
||||
createIniPanelShellSessionHandoffChecklist,
|
||||
createIniPanelShellSessionHandoffCompatibilityReport,
|
||||
createIniPanelShellSessionHandoffDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackage,
|
||||
createIniPanelShellSessionHandoffPackageDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackageMountDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackageMountRenderState,
|
||||
createIniPanelShellSessionHandoffPackageMountState,
|
||||
createIniPanelShellSessionHandoffMountDomContract,
|
||||
createIniPanelShellSessionHandoffMountDomReadiness,
|
||||
mountIniPanelShellSessionHandoff,
|
||||
mountIniPanelShellSessionHandoffFromControlPage,
|
||||
createIniPanelShellSessionHandoffWorkflowSummary,
|
||||
createIniPanelShellSessionReadinessWorkflowReport,
|
||||
createIniPanelShellApiSurfaceInventory,
|
||||
createIniPanelShellSessionHandoffWorkflowDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffWorkflowRenderState,
|
||||
createIniPanelShellSessionHandoffWorkflowDomContract,
|
||||
createIniPanelShellSessionHandoffWorkflowDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewContract,
|
||||
createIniPanelShellWorkflowOverviewReadiness,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingReport,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingRenderState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomReadiness,
|
||||
mountIniPanelShellWorkflowOverviewEmbedding,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingState,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountRenderState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson,
|
||||
loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
renderIniPanelShellSessionHandoffWorkflowState,
|
||||
mountIniPanelShellSessionHandoffWorkflow,
|
||||
renderIniPanelShellSessionHandoffMountState,
|
||||
createIniPanelShellSessionHandoffPackageReport,
|
||||
createIniPanelShellSessionHandoffPackageRenderState,
|
||||
createIniPanelShellSessionHandoffPackageSchema,
|
||||
createIniPanelShellSessionHandoffRenderState,
|
||||
createIniPanelShellSessionHandoffSnapshot,
|
||||
createIniPanelShellSessionHandoffSummary,
|
||||
createIniPanelShellSessionHandoffSummarySchema,
|
||||
createIniPanelShellViewModel,
|
||||
getIniPanelEntryByHref,
|
||||
getVisibleIniPanelEntries,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createMachineSessionControlPageController,
|
||||
createMachineSessionReadonlyStatus,
|
||||
createMachineSessionReadonlyStatusApiManifest,
|
||||
createMachineSessionReadinessFieldState,
|
||||
machineSessionReadinessLogLines,
|
||||
readMachineSessionReadinessWorkflow,
|
||||
readMachineSessionReadonlyStatus,
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
createControlPageSessionLoadState,
|
||||
refreshMachineSessionControlPage,
|
||||
controlPageStatusText,
|
||||
renderMachineSessionControlView,
|
||||
isSupportedIniPanelControlPageApiManifest,
|
||||
isSupportedIniPanelLaunchApiManifest,
|
||||
isSupportedIniPanelShellIntegrationManifest,
|
||||
isSupportedIniPanelShellSessionHandoffPackage,
|
||||
isSupportedIniPanelShellSessionHandoffSummary,
|
||||
} from "../runtime/ui/ini-panel/ui-shell.js";
|
||||
```
|
||||
|
||||
@@ -42,6 +118,7 @@ and the control-view renderer. It does not implement CNC behavior.
|
||||
| Launch | `runtime/ui/ini-panel/launch.html` | Read-only entry page for available panel surfaces. |
|
||||
| Edit and run | `runtime/ui/ini-panel/index.html` | Editable INI panel and existing run workflow surface. |
|
||||
| Read-only control view | `runtime/ui/ini-panel/control-page.html` | Read-only control view backed by the panel API. |
|
||||
| Workflow overview | `runtime/ui/ini-panel/workflow-overview.html` | Read-only shell handoff workflow overview surface. |
|
||||
|
||||
## Visible Entries
|
||||
|
||||
@@ -51,6 +128,7 @@ The visible launch entries come from `getVisibleIniPanelEntries()`:
|
||||
| --- | --- | --- |
|
||||
| `edit-run` | `./index.html` | Open edit and run panel |
|
||||
| `control-view` | `./control-page.html` | Open read-only control view |
|
||||
| `workflow-overview` | `./workflow-overview.html` | Open shell handoff overview |
|
||||
|
||||
Launcher pages can render links from `createIniPanelLauncherLinkViewModel()`
|
||||
without reading manifest field names directly. Each item has `entryId`, `href`,
|
||||
@@ -61,10 +139,101 @@ read-only model with `pages`, `launcherLinks`, and `controlPage` helpers for
|
||||
mounting the existing control-page controller, refresh workflow, and renderer.
|
||||
Use `controlPage.readStatus` or `readMachineSessionReadonlyStatus()` when an
|
||||
outer shell needs one stable read of `stateBundle`, `workflowStatus`,
|
||||
`overview`, `controlView`, `session`, `runReadiness`, and `run`.
|
||||
`overview`, `controlView`, `sessionReadiness`, `session`, `runReadiness`, and
|
||||
`run`.
|
||||
Use `createMachineSessionReadonlyStatusApiManifest()` or
|
||||
`controlPage.readonlyStatusApiManifest` when you only need the API shape and
|
||||
method names.
|
||||
Use `readMachineSessionReadinessWorkflow()` when an outer shell or panel flow
|
||||
needs a read-only `ready`/`blocked` readiness report before loading a machine
|
||||
session into WASM. `createMachineSessionReadinessFieldState()` and
|
||||
`machineSessionReadinessLogLines()` turn that report into stable fields and
|
||||
status text without parsing G-code or executing LinuxCNC behavior.
|
||||
Use `createMachineSessionPersistenceSummary()` or
|
||||
`linuxCncIniPanelApi.getMachineSessionPersistenceSummary()` when a browser
|
||||
panel or outer shell needs one read-only OPFS/session persistence summary with
|
||||
machine-file, session snapshot, session readiness, session load, readonly
|
||||
status, and handoff readiness rows. The launch page exposes the same helper as
|
||||
`linuxCncIniPanelLaunchApi.getMachineSessionPersistenceSummary()`, and the
|
||||
workflow overview page exposes it as
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewPersistenceSummary()`.
|
||||
Use `validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.validateWorkflowOverviewReleaseReadinessArtifactJson()`
|
||||
when an outer shell needs to validate a project release readiness artifact JSON
|
||||
through the same read-only workflow overview surface. It parses the artifact,
|
||||
returns stable validation rows, and does not execute release gates or CNC
|
||||
behavior.
|
||||
Use `runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()`
|
||||
when the shell already has artifact JSON and needs parse, validation, summary,
|
||||
action-plan, and gate execution summary output in one workflow result.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()`
|
||||
when the shell needs the artifact's release gate execution summary rows.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`, or
|
||||
`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()` when the
|
||||
shell needs to render those per-gate rows into caller-owned DOM.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()`
|
||||
when the shell needs stable status text and rows for that JSON workflow.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()`
|
||||
when the shell needs the JSON workflow next-command plan without inspecting
|
||||
internal workflow fields.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`, or
|
||||
`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()` when the
|
||||
shell needs to render that JSON workflow state into caller-owned DOM.
|
||||
Use `loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.loadWorkflowOverviewReleaseReadinessArtifactUrl()`
|
||||
when the shell should fetch the artifact JSON URL, validate it, and optionally
|
||||
mount the render-state into caller-owned DOM without executing gates. The URL
|
||||
workflow result includes `validationSummaryViewModel` so callers can show
|
||||
stable validation status and rows from the same fetch.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()`
|
||||
when the shell needs one stable status line and dashboard rows for the whole
|
||||
artifact URL fetch/validate/render/mount workflow.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`, or
|
||||
`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()` when the
|
||||
shell needs to render that URL workflow state into caller-owned DOM.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()`
|
||||
when the shell needs the ordered next-action/next-command plan for that URL
|
||||
workflow without executing release gates.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel()`
|
||||
when the shell needs stable artifact validation status text and dashboard rows
|
||||
without interpreting validation flags itself.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()` or
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()`
|
||||
when direct artifact JSON validation needs its own ordered next-command plan
|
||||
without executing release gates.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`, or
|
||||
`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()` when the
|
||||
shell needs to render that validation action plan into caller-owned DOM.
|
||||
Use `createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState()`, or
|
||||
`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState()` when that
|
||||
validation needs to be rendered into caller-owned DOM. The workflow overview
|
||||
page exposes equivalent methods with the `getWorkflowOverview...`,
|
||||
`renderWorkflowOverview...`, and `mountWorkflowOverview...` prefixes.
|
||||
Use `createMachineSessionPersistenceDisplayViewModel()` and
|
||||
`createMachineSessionPersistenceRenderState()` when that summary needs stable
|
||||
display text, rows, status line, and dataset fields for caller-owned DOM.
|
||||
Use `createIniPanelShellControlPageContract()` or `controlPage.contract` when
|
||||
an outer shell needs a fast contract check for `createController`,
|
||||
`loadSessionState`, `readStatus`, `refresh`, `renderView`, the readonly status
|
||||
@@ -78,6 +247,14 @@ smokes can verify the same API shape before reading the embedded panel state.
|
||||
Use `linuxCncIniPanelControlPageApi.getControlPageApiMethods()` when a browser
|
||||
outer page needs the exact exposed method list before calling the read-only
|
||||
control page API.
|
||||
Use `createControlPageBrowserApiSchema()`,
|
||||
`createControlPageBrowserApiManifest()`, or
|
||||
`linuxCncIniPanelControlPageApi.getControlPageApiSchema()` and
|
||||
`linuxCncIniPanelControlPageApi.getControlPageApiManifest()` when a browser
|
||||
outer page needs a versioned control-page API description before reading the
|
||||
embedded panel. Use `isSupportedIniPanelControlPageApiManifest()` or
|
||||
`linuxCncIniPanelControlPageApi.isSupportedControlPageApiManifest()` for a
|
||||
read-only compatibility predicate before consuming that manifest.
|
||||
Use `linuxCncIniPanelControlPageApi.readControlPageStatus()` when a browser
|
||||
outer page needs the same readonly status snapshot from the real embedded
|
||||
control page after save/load session workflows.
|
||||
@@ -96,6 +273,273 @@ fields.
|
||||
Use `isSupportedIniPanelLaunchApiManifest()` or
|
||||
`linuxCncIniPanelLaunchApi.isSupportedLaunchApiManifest()` for a read-only
|
||||
compatibility predicate before using a launch manifest.
|
||||
Use `createIniPanelShellIntegrationSchema()`,
|
||||
`createIniPanelShellIntegrationManifest()`,
|
||||
`linuxCncIniPanelLaunchApi.getShellIntegrationSchema()`, or
|
||||
`linuxCncIniPanelLaunchApi.getShellIntegrationManifest()` when an outer shell
|
||||
needs one versioned read of pages, launcher links, launch API metadata,
|
||||
control-page API metadata, OPFS/session persistence capabilities, and current
|
||||
compatibility status. Use `isSupportedIniPanelShellIntegrationManifest()` when
|
||||
the outer shell needs to check that integration manifest before consuming it.
|
||||
Use `createIniPanelShellIntegrationReadiness()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellIntegrationReadiness()` when the outer shell
|
||||
needs a read-only `ready`/`blocked` status, failed check names, API names, and
|
||||
counts for pages, methods, and persistence capabilities before mounting any
|
||||
embedded control page.
|
||||
Use `createIniPanelShellIntegrationWorkflowPlan()` when the outer shell needs a
|
||||
Node-testable read-only plan for the control-page target, required launch and
|
||||
control-page API methods, readiness gate, and readonly status handoff fields
|
||||
before opening a browser frame.
|
||||
Use `createIniPanelShellSessionHandoffSummary()` when the outer shell needs one
|
||||
read-only display summary of readiness, target page, persistence capabilities,
|
||||
readonly status fields, handoff methods, and blocking reasons. Browser shells
|
||||
can read the same summary through
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffSummary()`.
|
||||
Use `createIniPanelShellSessionHandoffSummarySchema()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffSummarySchema()` for the
|
||||
versioned summary shape, and `isSupportedIniPanelShellSessionHandoffSummary()`
|
||||
or `linuxCncIniPanelLaunchApi.isSupportedShellSessionHandoffSummary()` before
|
||||
consuming a summary from a browser shell.
|
||||
Use `createIniPanelShellSessionHandoffCompatibilityReport()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffCompatibilityReport()` when an
|
||||
outer shell needs display-ready diagnostics for schema support, missing fields,
|
||||
api/version mismatches, and current blocking reasons before consuming the
|
||||
handoff summary.
|
||||
Use `createIniPanelShellSessionHandoffDisplayViewModel()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffDisplayViewModel()` when an
|
||||
outer shell needs the same diagnostics as stable read-only label/value rows for
|
||||
DOM display.
|
||||
Use `createIniPanelShellSessionHandoffSnapshot()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffSnapshot()` when an outer shell
|
||||
needs one read-only API read that includes readiness, handoff summary,
|
||||
compatibility report, and display view-model together.
|
||||
Use `createIniPanelShellSessionHandoffRenderState()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffRenderState()` when an outer
|
||||
shell needs the snapshot compressed into launch-side DOM state with a status
|
||||
line, stable rows, and `[data-handoff-shell]` phase/readiness flags. The launch
|
||||
page renders `[data-handoff-status]` and `[data-handoff-rows]` from this helper
|
||||
without adding control actions.
|
||||
Use `createIniPanelShellSessionHandoffActionPlan()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffActionPlan()` when an outer
|
||||
shell needs a read-only next-step description derived from the same snapshot:
|
||||
target page, required readonly methods, blocking reasons, display rows, and
|
||||
ready/blocked next step ids. The action plan describes what an outer shell can
|
||||
do next; it does not execute actions or add controls.
|
||||
Use `createIniPanelShellSessionHandoffChecklist()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffChecklist()` when an outer
|
||||
shell needs the same handoff state as stable health rows for display: render
|
||||
state, target page, load-session method, read-status method, blocking reasons,
|
||||
display rows, and next step ids. The checklist is read-only and does not
|
||||
execute the action plan.
|
||||
Use `createIniPanelShellSessionHandoffPackage()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffPackage()` when an outer shell
|
||||
needs one browser API read containing the launch/control method manifests,
|
||||
snapshot, render-state, action-plan, and checklist. The package is a read-only
|
||||
handoff artifact and does not execute action-plan steps.
|
||||
Use `createIniPanelShellSessionHandoffPackageSchema()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffPackageSchema()` for the
|
||||
versioned package shape, and `isSupportedIniPanelShellSessionHandoffPackage()`
|
||||
or `linuxCncIniPanelLaunchApi.isSupportedShellSessionHandoffPackage()` before
|
||||
consuming a package from a browser shell.
|
||||
Use `createIniPanelShellSessionHandoffPackageReport()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffPackageReport()` when an outer
|
||||
shell needs display-ready diagnostics for package schema support, missing
|
||||
package fields, missing manifest fields, api/version mismatches, and checklist
|
||||
blocking reasons.
|
||||
Use `createIniPanelShellSessionHandoffPackageDisplayViewModel()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffPackageDisplayViewModel()`
|
||||
when an outer shell needs the package report compressed into stable read-only
|
||||
label/value rows for DOM display.
|
||||
Use `createIniPanelShellSessionHandoffPackageRenderState()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffPackageRenderState()` when an
|
||||
outer shell needs the package display view-model compressed into launch-side
|
||||
DOM state. The launch page renders `[data-handoff-status]` and
|
||||
`[data-handoff-rows]` from the package render-state so the first page shows
|
||||
package-level schema/report status.
|
||||
Use `createIniPanelShellSessionHandoffPackageMountState()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffPackageMountState()` when an
|
||||
outer shell needs one read-only mount-state that combines package render-state,
|
||||
the control-page target, required readonly methods, and the current
|
||||
control-page session readiness snapshot.
|
||||
Use `createIniPanelShellSessionHandoffPackageMountDisplayViewModel()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffPackageMountDisplayViewModel()`
|
||||
when an outer shell needs mount-state checks as stable read-only label/value
|
||||
rows. Use `createIniPanelShellSessionHandoffPackageMountRenderState()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffPackageMountRenderState()`
|
||||
when those rows need a status line and `[data-handoff-shell]` style dataset
|
||||
flags for DOM rendering. Use
|
||||
`createIniPanelShellSessionHandoffMountDomContract()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffMountDomContract()` when an
|
||||
outer shell needs the required mount/status/rows selectors, mount dataset keys,
|
||||
row dataset keys, and render-result fields before rendering into shell-owned
|
||||
DOM. Use `createIniPanelShellSessionHandoffMountDomReadiness()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffMountDomReadiness()` to check
|
||||
that the required document and nodes exist before calling the renderer. Use
|
||||
`mountIniPanelShellSessionHandoff()` or
|
||||
`linuxCncIniPanelLaunchApi.mountShellSessionHandoff()` when an outer shell
|
||||
needs one browser-side workflow result containing the contract, DOM readiness,
|
||||
mount render-state, renderer result, and any mount error as data. Use
|
||||
`mountIniPanelShellSessionHandoffFromControlPage()` or
|
||||
`linuxCncIniPanelLaunchApi.mountShellSessionHandoffFromControlPage()` when an
|
||||
outer shell has a control-page API or already-observed session-load state and
|
||||
needs one read-only result containing the derived mount-state, mount display
|
||||
view-model, mount render-state, and DOM mount workflow result. Use
|
||||
`readIniPanelShellSessionHandoffStatusFromControlPage()` or
|
||||
`linuxCncIniPanelLaunchApi.readShellSessionHandoffStatusFromControlPage()`
|
||||
when an outer shell needs to read the control-page readonly status and verify
|
||||
the expected handoff status fields as a structured ready/blocked result. Use
|
||||
`createIniPanelShellSessionHandoffWorkflowSummary()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffWorkflowSummary()` when an
|
||||
outer shell needs one stable end-to-end summary that combines mount workflow,
|
||||
control-page mount workflow, readonly status result, machine-session
|
||||
ready/blocked state, and current blocking reasons into display rows. Use
|
||||
`createIniPanelShellSessionReadinessWorkflowReport()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionReadinessWorkflowReport()` when an
|
||||
outer shell needs a machine-readable report for just the session readiness
|
||||
phase, missing reasons, and stable rows. The workflow overview page exposes
|
||||
the same report through
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewSessionReadinessReport()`.
|
||||
Use `createIniPanelShellApiSurfaceInventory()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellApiSurfaceInventory()` when an outer shell
|
||||
needs a machine-readable inventory of launch, control-page, workflow overview,
|
||||
persistence capabilities, session readiness report rows, and persistence
|
||||
summary rows. The workflow overview page exposes the same inventory through
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewApiSurfaceInventory()`.
|
||||
When provided to `createIniPanelShellSessionHandoffWorkflowSummary()`, the
|
||||
inventory is also rendered as the read-only `api-surface-inventory` workflow
|
||||
summary row for browser overview pages and external shells. The same workflow
|
||||
summary accepts `persistenceSummary` from
|
||||
`createMachineSessionPersistenceSummary()` and renders it as the
|
||||
`persistence-summary` row, so a shell overview can show API coverage and
|
||||
OPFS/session persistence state together.
|
||||
Use
|
||||
`createIniPanelShellSessionHandoffWorkflowDisplayViewModel()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffWorkflowDisplayViewModel()`
|
||||
when an outer shell needs that end-to-end summary compressed into stable
|
||||
`title`, `statusText`, `detailText`, and rows for direct display. Use
|
||||
`createIniPanelShellSessionHandoffWorkflowRenderState()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffWorkflowRenderState()` when an
|
||||
outer shell needs that workflow display-model compressed into stable
|
||||
`dataset`, `statusLine`, and rows for direct DOM rendering. Use
|
||||
`createIniPanelShellSessionHandoffWorkflowDomContract()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffWorkflowDomContract()` when an
|
||||
outer shell needs the required workflow mount/status/rows selectors, mount
|
||||
dataset keys, and row dataset keys before rendering workflow overview DOM. Use
|
||||
`createIniPanelShellSessionHandoffWorkflowDomReadiness()` or
|
||||
`linuxCncIniPanelLaunchApi.getShellSessionHandoffWorkflowDomReadiness()` to
|
||||
check that those workflow DOM nodes exist before rendering. Use
|
||||
`renderIniPanelShellSessionHandoffWorkflowState()` or
|
||||
`linuxCncIniPanelLaunchApi.renderShellSessionHandoffWorkflowState()` when an
|
||||
outer shell needs the shared read-only DOM renderer for workflow overview rows.
|
||||
Use `mountIniPanelShellSessionHandoffWorkflow()` or
|
||||
`linuxCncIniPanelLaunchApi.mountShellSessionHandoffWorkflow()` when an outer
|
||||
shell needs one structured workflow DOM mount result containing contract,
|
||||
readiness, render-state, render result, and any mount error as data. Use
|
||||
`createIniPanelShellWorkflowOverviewContract()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewContract()` when an outer shell
|
||||
needs the `workflow-overview.html` page href, browser API namespace, and method
|
||||
names before embedding that page. Use
|
||||
`createIniPanelShellWorkflowOverviewReadiness()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewReadiness()` to check whether a
|
||||
loaded workflow overview iframe exposes the expected read-only API. Use
|
||||
`mountIniPanelShellWorkflowOverviewEmbedding()` or
|
||||
`linuxCncIniPanelLaunchApi.mountWorkflowOverviewEmbedding()` when an outer
|
||||
shell needs one structured embedding result containing contract readiness,
|
||||
render-state, workflow overview mount result, and any mount error as data. The
|
||||
workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.mountWorkflowOverviewEmbedding()` for the
|
||||
same read-only embedding path from inside the iframe. Use
|
||||
`createIniPanelShellWorkflowOverviewEmbeddingReport()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingReport()` when an outer
|
||||
shell needs display-ready diagnostics for overview API readiness, missing
|
||||
methods, embedding readiness, mount phase, render row count, and embedding
|
||||
errors. The workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingReport()` for
|
||||
the same report shape. Use
|
||||
`createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingDisplayViewModel()` when
|
||||
an outer shell needs that report compressed into stable `title`, `statusText`,
|
||||
`detailText`, and read-only label/value rows for direct rendering. The workflow
|
||||
overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingDisplayViewModel()`.
|
||||
Use `createIniPanelShellWorkflowOverviewEmbeddingRenderState()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingRenderState()` when an
|
||||
outer shell needs that display view-model compressed into stable `statusLine`,
|
||||
`dataset`, and rows for a shared DOM renderer. The workflow overview page also
|
||||
exposes `linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingRenderState()`.
|
||||
Use `createIniPanelShellWorkflowOverviewEmbeddingDomContract()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingDomContract()` when an
|
||||
outer shell needs the required embedding report mount/status/rows selectors,
|
||||
mount dataset keys, row dataset keys, and render-result fields before adding a
|
||||
DOM renderer. The workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingDomContract()`.
|
||||
Use `createIniPanelShellWorkflowOverviewEmbeddingDomReadiness()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingDomReadiness()` to check
|
||||
that the required document and embedding report DOM nodes exist before calling
|
||||
a renderer. The workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingDomReadiness()`.
|
||||
Use `renderIniPanelShellWorkflowOverviewEmbeddingState()` or
|
||||
`linuxCncIniPanelLaunchApi.renderWorkflowOverviewEmbeddingState()` when an
|
||||
outer shell needs to render an embedding report render-state into existing
|
||||
mount/status/rows nodes and receive a structured read-only render result. The
|
||||
workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.renderWorkflowOverviewEmbeddingState()`.
|
||||
Use `mountIniPanelShellWorkflowOverviewEmbeddingState()` or
|
||||
`linuxCncIniPanelLaunchApi.mountWorkflowOverviewEmbeddingState()` when an outer
|
||||
shell needs a non-throwing workflow result that combines embedding DOM
|
||||
readiness, render-state, render result, and any render error as structured
|
||||
data. The workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.mountWorkflowOverviewEmbeddingState()`.
|
||||
Use `createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingMountDisplayViewModel()`
|
||||
when an outer shell needs that mount workflow result compressed into stable
|
||||
`title`, `statusText`, `detailText`, and read-only label/value rows. The
|
||||
workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingMountDisplayViewModel()`.
|
||||
Use `createIniPanelShellWorkflowOverviewEmbeddingMountRenderState()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingMountRenderState()` when
|
||||
an outer shell needs that mount display view-model compressed into stable
|
||||
`statusLine`, `dataset`, and rows for a shared DOM renderer. The workflow
|
||||
overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingMountRenderState()`.
|
||||
Use `createIniPanelShellWorkflowOverviewEmbeddingMountDomContract()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingMountDomContract()` when
|
||||
an outer shell needs the required mount/status/rows selectors, mount dataset
|
||||
keys, row dataset keys, and render-result fields before rendering the mount
|
||||
render-state. The workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingMountDomContract()`.
|
||||
Use `createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness()` or
|
||||
`linuxCncIniPanelLaunchApi.getWorkflowOverviewEmbeddingMountDomReadiness()` to
|
||||
check that the required document and mount render-state DOM nodes exist before
|
||||
calling a renderer. The workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.getWorkflowOverviewEmbeddingMountDomReadiness()`.
|
||||
Use `renderIniPanelShellWorkflowOverviewEmbeddingMountState()` or
|
||||
`linuxCncIniPanelLaunchApi.renderWorkflowOverviewEmbeddingMountState()` when an
|
||||
outer shell needs to render a mount render-state into existing mount/status/rows
|
||||
nodes and receive a structured read-only render result. The workflow overview
|
||||
page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.renderWorkflowOverviewEmbeddingMountState()`.
|
||||
Use `mountIniPanelShellWorkflowOverviewEmbeddingMountState()` or
|
||||
`linuxCncIniPanelLaunchApi.mountWorkflowOverviewEmbeddingMountState()` when an
|
||||
outer shell needs a non-throwing mount workflow result for the mount render-state
|
||||
DOM. The workflow overview page also exposes
|
||||
`linuxCncIniPanelWorkflowOverviewApi.mountWorkflowOverviewEmbeddingMountState()`.
|
||||
Use
|
||||
`renderIniPanelShellSessionHandoffMountState()` or
|
||||
`linuxCncIniPanelLaunchApi.renderShellSessionHandoffMountState()` when an outer
|
||||
shell needs the shared read-only DOM renderer for those rows. The launch page
|
||||
renders the default waiting mount state into `[data-handoff-mount]`,
|
||||
`[data-handoff-mount-status]`, and `[data-handoff-mount-rows]` without opening
|
||||
the control page. The launch page also renders a default workflow overview into
|
||||
`[data-handoff-workflow]`, `[data-handoff-workflow-status]`, and
|
||||
`[data-handoff-workflow-rows]`.
|
||||
The browser workflow smoke
|
||||
`tests/browser/ini_panel_shell_integration_workflow_smoke.html` verifies the
|
||||
same external shell handoff: read integration manifest, pass readiness gate,
|
||||
open the control page target, mount waiting and ready mount-state into an
|
||||
external shell-owned `[data-external-shell-mount]` DOM region through the shared
|
||||
mount workflows, then read and validate readonly machine-session status through
|
||||
the shared status workflow.
|
||||
|
||||
The control-page refresh workflow lives in
|
||||
`runtime/ui/ini-panel/control-page-refresh-workflow.js`. Use
|
||||
@@ -108,7 +552,8 @@ state-shape helper used by the workflow.
|
||||
The control-page session workflow lives in
|
||||
`runtime/ui/ini-panel/control-page-session-workflow.js`. Use
|
||||
`loadMachineSessionForControlPageWorkflow()` when an outer shell needs a
|
||||
read-only readiness snapshot for the embedded panel API and first control view.
|
||||
read-only readiness snapshot for the embedded panel API, machine-session
|
||||
ready/blocked state, and first control view.
|
||||
`createControlPageSessionLoadState()` is the stable state-shape helper for
|
||||
`waiting-panel`, `waiting-api`, `waiting-session`, `ready`, and `error` phases.
|
||||
|
||||
@@ -117,3 +562,7 @@ read-only readiness snapshot for the embedded panel API and first control view.
|
||||
The entry shell only links pages and renders already-observed panel state. It
|
||||
does not parse G-code, infer canonical events, or implement LinuxCNC-owned
|
||||
tool, parameter, planner, or kinematics behavior.
|
||||
|
||||
Release validation for this surface is covered by `verify_ui_node_smokes.sh`,
|
||||
`verify_ini_panel_browser.sh`, and the project handoff docs smoke
|
||||
`verify_project_release_handoff_docs.sh`.
|
||||
|
||||
314
wasm-port/docs/project-release-handoff.md
Normal file
314
wasm-port/docs/project-release-handoff.md
Normal file
@@ -0,0 +1,314 @@
|
||||
# Project release handoff
|
||||
|
||||
This is the short project-level handoff for the current LinuxCNC WASM/browser
|
||||
port scope. It points a new maintainer to the supported workflow surfaces, the
|
||||
required release gates, and the boundaries that must remain blocked until real
|
||||
LinuxCNC-owned runtime proof exists.
|
||||
|
||||
## Start here
|
||||
|
||||
Use these documents as the current handoff set:
|
||||
|
||||
- `README.md` for the workspace layout and release validation commands.
|
||||
- `docs/panel-entry.md` for browser/UI entry points and shell handoff helpers.
|
||||
- `runtime/sdk/README.md` for stable SDK imports from `runtime/sdk/src/index.js`.
|
||||
- `docs/opfs-session-persistence.md` for OPFS/session persistence scope.
|
||||
- `docs/real-browser-simulation-priority.md` for the standing first priority:
|
||||
build the real browser CNC simulation page before lower-value metadata work.
|
||||
- `docs/sim-configs-coverage-handoff.md` for sim-config inventory status.
|
||||
- `docs/host-runtime-boundary-handoff.md` for blocked runtime families.
|
||||
- `docs/source-reuse-map.md` and `docs/drift-report.md` for LinuxCNC source
|
||||
reuse and non-drift rules.
|
||||
- `../PROJECT_COMPLETION_TRACKER.md` for project-level acceptance tracking.
|
||||
|
||||
Old turn logs `text1.txt` through `text14.txt` are historical context. Current
|
||||
continuation records are in `../text15.txt`.
|
||||
|
||||
## Supported current workflows
|
||||
|
||||
## First Priority Constraint
|
||||
|
||||
Subsequent work must prioritize a real browser CNC simulation page. The page
|
||||
should converge on visible machine/session loading, G-code program state,
|
||||
LinuxCNC-backed interpreter execution, machine readiness/status panels, and a
|
||||
toolpath/preview area backed by LinuxCNC-produced output or validated runtime
|
||||
events. The authoritative constraint is
|
||||
`docs/real-browser-simulation-priority.md`.
|
||||
Its executable docs gate is
|
||||
`verify_real_browser_simulation_priority_docs.sh`, which is included in the
|
||||
host smoke and project release gate and ends with
|
||||
`real_browser_simulation_priority_docs_node_smoke=ok`.
|
||||
The first implementation entry point is `runtime/ui/simulation/index.html`,
|
||||
validated by `verify_real_simulation_browser.sh`, which ends with
|
||||
`browser_real_simulation_page_smoke=ok`.
|
||||
|
||||
This priority does not permit JavaScript-owned CNC semantics. G-code, tool,
|
||||
parameter, kinematics, remap, planner, and canonical motion behavior must remain
|
||||
owned by vendored LinuxCNC source and existing LinuxCNC-backed WASM boundaries.
|
||||
|
||||
- INI panel launch, edit/run page, read-only control page, workflow overview,
|
||||
and external shell handoff are exposed from `runtime/ui/ini-panel/`.
|
||||
- External shells should use the read-only helpers documented in
|
||||
`docs/panel-entry.md`, including workflow overview embedding mount DOM
|
||||
contract/readiness/renderer/wrapper helpers.
|
||||
- SDK callers should import stable helpers from `runtime/sdk/src/index.js`,
|
||||
including `createIniPanelShellApiSurfaceInventory()` for API discovery and
|
||||
`createIniPanelShellSessionReadinessWorkflowReport()` when they need
|
||||
machine-readable session readiness phase and missing reasons. The workflow
|
||||
overview exposes the API surface inventory as a read-only summary row.
|
||||
- CI dashboards and external SDK callers can use
|
||||
`createProjectReleaseGateManifest()` to discover required gate commands and
|
||||
expected smoke outputs, `createProjectReleaseGateResultMatrix()` to map
|
||||
observed outputs onto passed/unknown gate rows,
|
||||
`createProjectReleaseGateActionPlan()` to derive pending gate commands, the
|
||||
next command, display rows, and a shell script from that matrix,
|
||||
`createProjectReleaseGateExecutionManifest()` to package each gate command,
|
||||
expected output, observed-output evidence, and passed/unknown status,
|
||||
`createProjectReleaseGateExecutionSummaryViewModel()` to expose dashboard
|
||||
status text, evidence counts, next missing gate, and normalized per-gate rows,
|
||||
and
|
||||
`createProjectReleaseReadinessReport()` for a machine-readable release
|
||||
readiness report covering the same manifest, execution manifest, result
|
||||
matrix, action plan, sim-config inventory baseline, and blocked runtime
|
||||
families.
|
||||
`createProjectReleaseReadinessSummaryViewModel()` turns that report into
|
||||
stable status text and rows for dashboards. The report helper is evidence
|
||||
driven and only reports `ready: true` when the caller supplies release gate
|
||||
evidence such as `project_release_gate=ok`. The project release gate writes
|
||||
the same report to `build/project-release-readiness.json` and validates it
|
||||
with `verify_project_release_readiness_artifact.sh`.
|
||||
External tools can load that artifact with
|
||||
`parseProjectReleaseReadinessArtifactJson()` and validate it with
|
||||
`createProjectReleaseReadinessArtifactValidation()`. Artifact validation
|
||||
requires the embedded gate manifest, execution manifest, result matrix, and
|
||||
action plan to be ready, so stale release JSON cannot pass the executable
|
||||
artifact gate.
|
||||
`createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns the
|
||||
validation into stable dashboard rows without reinterpreting artifact fields.
|
||||
`createProjectReleaseReadinessArtifactValidationActionPlan()` turns a blocked
|
||||
artifact validation into the project release gate and artifact validation
|
||||
commands to run next, plus the missing evidence rows.
|
||||
`createProjectReleaseReadinessArtifactJsonWorkflow()` is the one-call
|
||||
non-throwing JSON text workflow for dashboards that need parse, validation,
|
||||
summary, and action-plan output from the readiness artifact.
|
||||
`loadProjectReleaseReadinessArtifactUrlWorkflow()` provides the same
|
||||
SDK-side workflow from an artifact URL and caller-provided fetch
|
||||
implementation, including fetch status and next-command output.
|
||||
`createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel()` turns
|
||||
that SDK URL workflow into stable dashboard rows and status text.
|
||||
`createProjectReleaseReadinessArtifactUrlWorkflowActionPlan()` turns blocked
|
||||
URL workflows into ordered input, fetch, and command actions for CI tooling.
|
||||
`createProjectBatchAcceptanceCapabilityMatrix()` classifies per-batch evidence
|
||||
into accepted API/workflow/gate/browser capabilities and rejected entries.
|
||||
`createProjectBatchAcceptanceWorkflow()` records the per-batch acceptance rule
|
||||
that each batch must ship at least one callable API, verifiable workflow,
|
||||
executable gate, or browser capability. Its summary view-model and action-plan
|
||||
helpers provide dashboard rows and the next batch-acceptance gate command.
|
||||
`createProjectBatchAcceptanceChecklist()` combines those results into
|
||||
pass/blocked checklist rows for external release dashboards.
|
||||
`createProjectBatchAcceptanceReport()` packages the matrix, workflow,
|
||||
summary, action plan, and checklist into one stable CI/dashboard object.
|
||||
`createProjectBatchAcceptanceReportValidation()` and
|
||||
`createProjectBatchAcceptanceReportJsonWorkflow()` validate that object from
|
||||
parsed data or JSON text without throwing.
|
||||
`createProjectBatchAcceptanceReportValidationSummaryViewModel()` and
|
||||
`createProjectBatchAcceptanceReportValidationActionPlan()` expose dashboard
|
||||
rows and the next artifact gate command for blocked report validation.
|
||||
`loadProjectBatchAcceptanceReportUrlWorkflow()` fetches hosted batch
|
||||
acceptance report JSON through a caller-provided `fetch` implementation, then
|
||||
runs the same validation workflow without throwing.
|
||||
`createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()` and
|
||||
`createProjectBatchAcceptanceReportUrlWorkflowActionPlan()` expose dashboard
|
||||
rows and ordered input/fetch/gate actions for that URL workflow.
|
||||
The executable batch acceptance gate writes
|
||||
`build/project-batch-acceptance.json` with
|
||||
`write_project_batch_acceptance_artifact.mjs` and validates it through
|
||||
`verify_project_batch_acceptance_artifact.sh`, ending with
|
||||
`project_batch_acceptance_artifact_node_smoke=ok`.
|
||||
Workflow overview shells can use
|
||||
`getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()`
|
||||
and
|
||||
`getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel()`
|
||||
to display the embedded release gate execution summary without parsing
|
||||
artifact internals. They can render or mount those per-gate rows through
|
||||
`getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()`,
|
||||
`getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()`,
|
||||
`getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()`,
|
||||
`renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`, and
|
||||
`mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`, and
|
||||
`getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()` to show
|
||||
the URL workflow next action or command without executing gates. Direct JSON
|
||||
validation callers can use
|
||||
`getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()` for the
|
||||
artifact-validation next-command plan. They can render or mount that action
|
||||
plan through
|
||||
`getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()`,
|
||||
`getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()`,
|
||||
`getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()`,
|
||||
`renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`, and
|
||||
`mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`.
|
||||
Browser shells can also pass the JSON to
|
||||
`linuxCncIniPanelWorkflowOverviewApi.validateWorkflowOverviewReleaseReadinessArtifactJson()`
|
||||
through the workflow overview iframe and receive stable validation rows. They
|
||||
can call `runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()` when they
|
||||
need parse, validation, summary, action-plan, and gate execution summary
|
||||
output in one workflow result, and
|
||||
`getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()`
|
||||
when they need stable dashboard rows for that JSON workflow. The JSON
|
||||
workflow action plan is available through
|
||||
`getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()`. They
|
||||
can render or mount the JSON workflow state into caller-owned DOM through
|
||||
`getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()`,
|
||||
`getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()`,
|
||||
`renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`, and
|
||||
`mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`. They
|
||||
can also call
|
||||
`linuxCncIniPanelWorkflowOverviewApi.loadWorkflowOverviewReleaseReadinessArtifactUrl()`
|
||||
to fetch the artifact URL, validate it, and optionally mount the resulting
|
||||
read-only render state. That URL workflow includes a validation summary
|
||||
view-model and can be summarized with
|
||||
`getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()`.
|
||||
The same workflow can expose the ordered next-action plan through
|
||||
`getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()`. Shells
|
||||
can render or mount the URL workflow state through
|
||||
`getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`,
|
||||
`getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`,
|
||||
`getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`,
|
||||
`renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`, and
|
||||
`mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`.
|
||||
Direct artifact JSON validation can expose its own next-command plan through
|
||||
`getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()`.
|
||||
When the outer shell needs DOM output, the workflow
|
||||
overview iframe also exposes
|
||||
display, render-state, DOM readiness, renderer, and mount helpers for the
|
||||
same release readiness artifact validation.
|
||||
- OPFS/session callers can use `createMachineSessionPersistenceSummary()` or
|
||||
`linuxCncIniPanelApi.getMachineSessionPersistenceSummary()` to inspect
|
||||
machine files, session snapshot, session readiness, session load, readonly
|
||||
status, and shell handoff readiness as stable rows. Shell handoff workflow
|
||||
summaries can include that report as the `persistence-summary` row beside the
|
||||
API surface inventory row, and launch/workflow-overview APIs expose the same
|
||||
helper for external shells. The summary also has display and render-state
|
||||
helpers for caller-owned DOM.
|
||||
- OPFS/session persistence also has a dedicated browser workflow gate,
|
||||
`verify_opfs_session_workflow_browser.sh`, covering save machine files, save
|
||||
G-code, save session snapshot, session readiness, load into WASM, and
|
||||
persistence summary output.
|
||||
- OPFS/session persistence is host-side storage glue. Parameter and tool-table
|
||||
behavior still comes from the LinuxCNC-backed interpreter SDK calls.
|
||||
- Sim-config inventory promotion remains evidence-driven and must keep
|
||||
`unexpected_fail=0`.
|
||||
|
||||
## Required release gates
|
||||
|
||||
Run the single project release gate before claiming the current scope is
|
||||
release-ready:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/host/verify_project_release_gate.sh
|
||||
```
|
||||
|
||||
It executes the same minimum release validation commands:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh
|
||||
wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh
|
||||
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
|
||||
wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
The host aggregate gate includes SDK, OPFS, docs, UI Node, WASM Node, and
|
||||
browser smoke coverage. The project release gate then writes and verifies a
|
||||
machine-readable release readiness artifact:
|
||||
|
||||
```text
|
||||
project_release_readiness_artifact=...
|
||||
project_release_readiness_artifact_node_smoke=ok
|
||||
```
|
||||
|
||||
A passing aggregate ends with:
|
||||
|
||||
```text
|
||||
host_wasm_opfs_browser_smokes=ok
|
||||
project_release_gate=ok
|
||||
```
|
||||
|
||||
## Expected baseline outputs
|
||||
|
||||
The current sim-config inventory release baseline is:
|
||||
|
||||
```text
|
||||
sim_configs_wasm_node_inventory_executed=28
|
||||
sim_configs_wasm_node_inventory_passed=28
|
||||
sim_configs_wasm_node_inventory_skipped=131
|
||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||
```
|
||||
|
||||
The expected docs and SDK smoke outputs include:
|
||||
|
||||
```text
|
||||
project_release_handoff_docs_node_smoke=ok
|
||||
opfs_session_docs_node_smoke=ok
|
||||
sim_configs_coverage_docs_node_smoke=ok
|
||||
host_runtime_boundary_docs_node_smoke=ok
|
||||
sdk_surface_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
```
|
||||
|
||||
The expected browser INI panel smoke outputs include:
|
||||
|
||||
```text
|
||||
browser_ini_opfs_smoke=ok
|
||||
browser_ini_control_page_smoke=ok
|
||||
browser_ini_launch_smoke=ok
|
||||
browser_ini_workflow_overview_smoke=ok
|
||||
browser_ini_shell_integration_workflow_smoke=ok
|
||||
browser_opfs_session_workflow_smoke=ok
|
||||
browser_release_artifact_url_workflow_smoke=ok
|
||||
```
|
||||
|
||||
## Blocked runtime families
|
||||
|
||||
These runtime families remain blocked:
|
||||
|
||||
- `L4-USER-M-PROCESS`
|
||||
- `L4-TOOL-DB`
|
||||
- `L4-PYTHON-REMAP`
|
||||
|
||||
Do not promote them from skipped/blocked state without LinuxCNC-owned native
|
||||
runtime proof followed by Node/WASM and browser/host validation. The opt-in
|
||||
runtime probes are documented in `docs/host-runtime-boundary-handoff.md`; they
|
||||
must not be run on hosts that do not provide the required LinuxCNC runtime.
|
||||
|
||||
## Acceptance checklist
|
||||
|
||||
- `git diff --check` passes.
|
||||
- Vendor sync and standalone CNC semantic guards pass.
|
||||
- Interpreter WASM Node smoke passes.
|
||||
- Sim-config inventory has `unexpected_fail=0`.
|
||||
- UI Node smoke passes.
|
||||
- Browser INI panel smoke passes.
|
||||
- Release artifact URL browser workflow smoke passes.
|
||||
- Host aggregate smoke passes.
|
||||
- `verify_project_release_gate.sh` passes.
|
||||
- SDK exports match `runtime/sdk/README.md`.
|
||||
- `docs/panel-entry.md` documents the external shell workflow surface.
|
||||
- OPFS/session, sim-config coverage, host/runtime boundary, source reuse, and
|
||||
drift docs all point to the current validation gates.
|
||||
- Blocked runtime families remain documented and are not falsely promoted.
|
||||
|
||||
## Next maintainer rule
|
||||
|
||||
Add new CNC behavior only by reusing vendored LinuxCNC source or a documented
|
||||
runtime-edge adapter. JavaScript may stage files, mount UI state, manage OPFS,
|
||||
load WASM modules, and call C ABI functions; it must not implement G-code,
|
||||
tool, parameter, planner, kinematics, remap, or canonical motion semantics.
|
||||
82
wasm-port/docs/real-browser-simulation-priority.md
Normal file
82
wasm-port/docs/real-browser-simulation-priority.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Real Browser Simulation Priority
|
||||
|
||||
This document is a standing project constraint for all work after 2026-06-16.
|
||||
|
||||
## Primary Objective
|
||||
|
||||
The first priority is to deliver a real browser CNC simulation page, not more
|
||||
project metadata or dashboard-only helpers. Future batches should prefer
|
||||
visible, interactive browser capability that moves the product toward an
|
||||
operator-usable CNC simulation surface.
|
||||
|
||||
The target page must become a real CNC simulation page with:
|
||||
|
||||
- a machine/session loader backed by the existing OPFS/session workflow;
|
||||
- a G-code program view backed by persisted or staged program text;
|
||||
- LinuxCNC-backed interpreter execution through the existing WASM SDK;
|
||||
- machine state panels for run status, active program, machine files, and
|
||||
missing readiness;
|
||||
- a toolpath/preview area driven by LinuxCNC-produced interpreter/canonical
|
||||
output or existing validated runtime events, not JavaScript-owned CNC
|
||||
semantics;
|
||||
- browser smoke coverage that proves the page loads, renders nonempty runtime
|
||||
state, and can run a representative LinuxCNC-backed program flow.
|
||||
|
||||
The first implementation entry point is:
|
||||
|
||||
```text
|
||||
runtime/ui/simulation/index.html
|
||||
```
|
||||
|
||||
Its browser gate is:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
```
|
||||
|
||||
## Batch Priority
|
||||
|
||||
Unless a user explicitly changes priority, choose work in this order:
|
||||
|
||||
1. Real UI/browser simulation page capability.
|
||||
2. Browser workflow smoke for that simulation capability.
|
||||
3. UI APIs needed by the simulation page.
|
||||
4. OPFS/session integration needed by the simulation page.
|
||||
5. SDK/API helpers only when they directly unblock the simulation page.
|
||||
6. Documentation or release metadata only when it creates an executable gate or
|
||||
protects the simulation-page direction.
|
||||
|
||||
Every implementation batch still must produce at least one callable API,
|
||||
verifiable workflow, executable gate, or real UI/browser capability.
|
||||
|
||||
## Boundary Rules
|
||||
|
||||
The browser simulation page must not implement CNC semantics in JavaScript.
|
||||
G-code interpretation, canonical motion behavior, tool semantics, parameter
|
||||
semantics, kinematics, remap behavior, and planner behavior must remain owned by
|
||||
vendored LinuxCNC source and existing LinuxCNC-backed WASM C ABI boundaries.
|
||||
|
||||
JavaScript/browser code may own:
|
||||
|
||||
- layout and interaction state;
|
||||
- OPFS/session persistence;
|
||||
- file staging into the Emscripten filesystem;
|
||||
- calls into the SDK/WASM boundary;
|
||||
- rendering LinuxCNC-produced output, canonical events, and validated runtime
|
||||
summaries;
|
||||
- non-CNC UI state such as selected file, active tab, viewport mode, and
|
||||
validation messages.
|
||||
|
||||
## Required Evidence
|
||||
|
||||
The priority is enforced by:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
wasm-port/tests/host/verify_project_release_gate.sh
|
||||
```
|
||||
|
||||
The docs gate must verify that this priority document, the release handoff, the
|
||||
README, and `PROJECT_COMPLETION_TRACKER.md` all point future work toward a real
|
||||
browser CNC simulation page while preserving the LinuxCNC semantic boundary.
|
||||
122
wasm-port/docs/sim-configs-coverage-handoff.md
Normal file
122
wasm-port/docs/sim-configs-coverage-handoff.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Sim-config coverage release handoff
|
||||
|
||||
This document is the short release-gate handoff for
|
||||
`linuxcnc/configs/sim` coverage in the LinuxCNC WASM/browser port. It points to
|
||||
the detailed matrix, fixes the current machine-readable inventory baseline, and
|
||||
records which skipped rows remain blocked instead of promoted.
|
||||
|
||||
Detailed references:
|
||||
|
||||
- `docs/sim-configs-coverage-matrix.md`
|
||||
- `docs/sim-configs-completion-plan.md`
|
||||
- `docs/full-process-boundary-design.md`
|
||||
- `tests/wasm/node/verify_sim_configs_inventory_wasm.mjs`
|
||||
|
||||
## Current inventory baseline
|
||||
|
||||
The current Node/WASM inventory gate is:
|
||||
|
||||
```bash
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
Required output:
|
||||
|
||||
```text
|
||||
sim_configs_wasm_node_inventory_executed=28
|
||||
sim_configs_wasm_node_inventory_passed=28
|
||||
sim_configs_wasm_node_inventory_skipped=131
|
||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||
sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65
|
||||
sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53
|
||||
sim_configs_wasm_node_inventory_skip_L4_TOOL_DB=1
|
||||
sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1
|
||||
sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10
|
||||
sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1
|
||||
sim_configs_wasm_node_inventory=ok
|
||||
```
|
||||
|
||||
The generated artifact baseline is:
|
||||
|
||||
- `build/wasm/sim-configs-inventory/summary.tsv`
|
||||
- `build/wasm/sim-configs-inventory/skip-summary.tsv`
|
||||
- `build/wasm/sim-configs-inventory/boundary-summary.tsv`
|
||||
- `build/wasm/sim-configs-inventory/blocked-dependency-summary.tsv`
|
||||
- `build/wasm/sim-configs-inventory/full-process-boundary-summary.tsv`
|
||||
- `build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-promotion-blockers.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv`
|
||||
- `build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv`
|
||||
|
||||
## Skip reason map
|
||||
|
||||
The TSV artifacts use hyphenated reason names. The shell output uses
|
||||
underscore-safe metric names. Treat these as the same baseline:
|
||||
|
||||
| Artifact reason | Shell metric suffix | Count | Release decision |
|
||||
| --- | --- | ---: | --- |
|
||||
| `ASSET-ONLY` | `ASSET_ONLY` | 65 | Subroutine/support asset; not a standalone browser main-program target. |
|
||||
| `L4-PYTHON-REMAP` | `L4_PYTHON_REMAP` | 53 | Keep inventory-only until a LinuxCNC-owned Python-remap runtime proof exists. |
|
||||
| `L4-TOOL-DB` | `L4_TOOL_DB` | 1 | Keep blocked until a LinuxCNC-owned tool database process/protocol proof exists. |
|
||||
| `L4-USER-M-PROCESS` | `L4_USER_M_PROCESS` | 1 | Keep blocked until a LinuxCNC-owned external user-M process state proof exists. |
|
||||
| `NON_MAIN_CLASS` | `NON_MAIN_CLASS` | 10 | Covered by class representative or macro/load policy, not direct standalone promotion. |
|
||||
| `UPSTREAM-DEMO` | `UPSTREAM_DEMO` | 1 | Preserved upstream demo edge; do not force pass. |
|
||||
|
||||
## Promotion policy
|
||||
|
||||
Rows may only move from skip/inventory to promoted coverage when all of these
|
||||
are true:
|
||||
|
||||
1. The row has a LinuxCNC-owned runtime proof for the behavior being promoted.
|
||||
2. Native proof passes first.
|
||||
3. Node/WASM proof passes after native proof.
|
||||
4. Browser/host proof passes after Node/WASM proof.
|
||||
5. `verify_no_standalone_cnc_semantics.sh` still passes.
|
||||
6. Generated inventory artifacts and docs agree on the new status.
|
||||
|
||||
Do not promote these runtime families from JavaScript glue alone:
|
||||
|
||||
- `L4-PYTHON-REMAP`
|
||||
- `L4-TOOL-DB`
|
||||
- `L4-USER-M-PROCESS`
|
||||
|
||||
## Release gate
|
||||
|
||||
Use this gate when changing sim-config coverage, promotion state, or inventory
|
||||
documentation:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/docs/node/verify_sim_configs_coverage_docs.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
The host aggregate must continue to include:
|
||||
|
||||
```text
|
||||
sim_configs_wasm_node_inventory_executed=28
|
||||
sim_configs_wasm_node_inventory_passed=28
|
||||
sim_configs_wasm_node_inventory_skipped=131
|
||||
sim_configs_wasm_node_inventory_unexpected_fail=0
|
||||
opfs_session_docs_node_smoke=ok
|
||||
sim_configs_coverage_docs_node_smoke=ok
|
||||
host_wasm_opfs_browser_smokes=ok
|
||||
```
|
||||
|
||||
## Blocked runtime probes
|
||||
|
||||
The opt-in runtime probes remain disabled on this host unless the required
|
||||
LinuxCNC runtime processes are available:
|
||||
|
||||
```bash
|
||||
ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_millturn_user_m_runtime.sh
|
||||
ENABLE_TOOL_DB_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_tool_db_runtime.sh
|
||||
ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1 bash wasm-port/tests/native/probe_python_remap_runtime.sh
|
||||
```
|
||||
|
||||
These commands are proof gates for future work, not part of the current
|
||||
release gate. The current inventory must continue to record the corresponding
|
||||
families as blocked.
|
||||
@@ -29,6 +29,11 @@ Current validation is intentionally mechanical:
|
||||
documented Python/remap runtime-edge stubs in
|
||||
`runtime/core/linuxcnc_wrap/linuxcnc_interp_edge_stubs.cpp`.
|
||||
|
||||
The project-level release handoff in `docs/project-release-handoff.md` links
|
||||
this source reuse map to the README, panel entry documentation, SDK surface,
|
||||
OPFS/session release gate, sim-config coverage gate, host/runtime blocked
|
||||
families, and drift report.
|
||||
|
||||
## Reuse Matrix
|
||||
|
||||
| Capability | LinuxCNC source files | Port classification | Standalone boundary | Current validation |
|
||||
|
||||
103
wasm-port/runtime/opfs/machine-session-readiness.js
Normal file
103
wasm-port/runtime/opfs/machine-session-readiness.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import { loadTextFile } from "./file-service.js";
|
||||
import {
|
||||
gcodeProgramPath,
|
||||
machineIniPath,
|
||||
normalizeOpfsPath,
|
||||
parameterFilePath,
|
||||
sessionSnapshotPath,
|
||||
toolTablePath,
|
||||
} from "./path-model.js";
|
||||
import { loadMachineSessionSnapshot } from "./snapshot-store.js";
|
||||
|
||||
function checkResult(name, path, ok, error = null) {
|
||||
return {
|
||||
name,
|
||||
path,
|
||||
ok,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
async function checkTextFile(name, path, storage) {
|
||||
try {
|
||||
const text = await loadTextFile(path, storage);
|
||||
return {
|
||||
...checkResult(name, path, true),
|
||||
bytes: text.length,
|
||||
};
|
||||
} catch (error) {
|
||||
return checkResult(name, path, false, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveReadinessPaths(machineId, options = {}) {
|
||||
const paths = {
|
||||
ini: options.iniOpfsPath ?? machineIniPath(machineId, options.iniFilename),
|
||||
parameters:
|
||||
options.parameterOpfsPath ?? parameterFilePath(machineId, options.parameterFilename),
|
||||
toolTable:
|
||||
options.toolTableOpfsPath ?? toolTablePath(machineId, options.toolTableFilename),
|
||||
};
|
||||
|
||||
if (options.gcodeOpfsPath !== undefined) {
|
||||
paths.gcode = normalizeOpfsPath(options.gcodeOpfsPath);
|
||||
} else if (options.gcodeFilename !== undefined) {
|
||||
paths.gcode = gcodeProgramPath(options.gcodeFilename);
|
||||
}
|
||||
|
||||
if (options.sessionId !== undefined) {
|
||||
paths.snapshot = sessionSnapshotPath(options.sessionId, options.snapshotFilename);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
function summarizeReadiness(checks, snapshotResult) {
|
||||
const failed = checks.filter((check) => !check.ok);
|
||||
const snapshotFailed = snapshotResult && !snapshotResult.ok ? [snapshotResult] : [];
|
||||
return [...failed, ...snapshotFailed];
|
||||
}
|
||||
|
||||
export async function readMachineSessionReadiness(machineId, options = {}) {
|
||||
const paths = resolveReadinessPaths(machineId, options);
|
||||
const checks = [
|
||||
await checkTextFile("ini", paths.ini, options.storage),
|
||||
await checkTextFile("parameters", paths.parameters, options.storage),
|
||||
await checkTextFile("toolTable", paths.toolTable, options.storage),
|
||||
];
|
||||
|
||||
if (options.gcodeRequired === true || paths.gcode !== undefined) {
|
||||
checks.push(await checkTextFile("gcode", paths.gcode, options.storage));
|
||||
}
|
||||
|
||||
let snapshot = null;
|
||||
let snapshotCheck = null;
|
||||
if (options.sessionId !== undefined) {
|
||||
try {
|
||||
snapshot = await loadMachineSessionSnapshot(options.sessionId, machineId, {
|
||||
storage: options.storage,
|
||||
filename: options.snapshotFilename,
|
||||
});
|
||||
snapshotCheck = checkResult("snapshot", paths.snapshot, true);
|
||||
} catch (error) {
|
||||
snapshotCheck = checkResult("snapshot", paths.snapshot, false, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
const failures = summarizeReadiness(checks, snapshotCheck);
|
||||
return {
|
||||
machineId,
|
||||
ready: failures.length === 0,
|
||||
phase: failures.length === 0 ? "ready" : "blocked",
|
||||
paths,
|
||||
checks,
|
||||
snapshotCheck,
|
||||
snapshot,
|
||||
missing: failures.map((failure) => failure.name),
|
||||
errors: failures.map((failure) => ({
|
||||
name: failure.name,
|
||||
path: failure.path,
|
||||
error: failure.error,
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -23,11 +23,128 @@ Use `src/index.js` for stable imports:
|
||||
|
||||
```js
|
||||
import {
|
||||
INI_PANEL_ENTRIES,
|
||||
analyzeIniRuntimeBoundaries,
|
||||
createControlPageBrowserApiManifest,
|
||||
createControlPageBrowserApiMethods,
|
||||
createControlPageBrowserApiSchema,
|
||||
createIniPanelLaunchApiManifest,
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createIniPanelShellApiSurfaceInventory,
|
||||
createIniPanelShellControlPageContract,
|
||||
createIniPanelShellIntegrationManifest,
|
||||
createIniPanelShellIntegrationReadiness,
|
||||
createIniPanelShellIntegrationSchema,
|
||||
createIniPanelShellIntegrationWorkflowPlan,
|
||||
createIniPanelShellSessionReadinessWorkflowReport,
|
||||
createIniPanelShellViewModel,
|
||||
createIniPanelShellWorkflowOverviewContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState,
|
||||
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
createLinuxCncIniSdk,
|
||||
createLinuxCncInterpSdk,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createMachineSessionSnapshotPayload,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseGateResultMatrix,
|
||||
createProjectReleaseReadinessArtifactJsonWorkflow,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
createProjectReleaseReadinessArtifactValidationActionPlan,
|
||||
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
createProjectReleaseReadinessReport,
|
||||
createProjectReleaseReadinessSummaryViewModel,
|
||||
createSessionSnapshot,
|
||||
defaultMachinePaths,
|
||||
gcodeFilenameFromProgramPath,
|
||||
gcodeProgramPath,
|
||||
getIniPanelEntryByHref,
|
||||
getIniPanelEntryManifest,
|
||||
getOpfsRoot,
|
||||
getVisibleIniPanelEntries,
|
||||
isSupportedIniPanelControlPageApiManifest,
|
||||
isSupportedIniPanelLaunchApiManifest,
|
||||
isSupportedIniPanelShellIntegrationManifest,
|
||||
loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl,
|
||||
loadProjectReleaseReadinessArtifactUrlWorkflow,
|
||||
loadMachineSessionFromOpfs,
|
||||
loadMachineSessionSnapshot,
|
||||
loadMachineTextFiles,
|
||||
loadMachineToolTableFromOpfs,
|
||||
loadTextFile,
|
||||
machineFilePaths,
|
||||
machineIniPath,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
normalizeOpfsPath,
|
||||
parameterFilePath,
|
||||
parseProjectReleaseReadinessArtifactJson,
|
||||
planIniFileContextStaging,
|
||||
planSimConfigStaging,
|
||||
createProjectBatchAcceptanceActionPlan,
|
||||
createProjectBatchAcceptanceCapabilityMatrix,
|
||||
createProjectBatchAcceptanceChecklist,
|
||||
createProjectBatchAcceptanceReport,
|
||||
createProjectBatchAcceptanceReportJsonWorkflow,
|
||||
createProjectBatchAcceptanceReportUrlWorkflowActionPlan,
|
||||
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel,
|
||||
createProjectBatchAcceptanceReportValidationActionPlan,
|
||||
createProjectBatchAcceptanceReportValidationSummaryViewModel,
|
||||
createProjectBatchAcceptanceReportValidation,
|
||||
createProjectBatchAcceptanceSummaryViewModel,
|
||||
createProjectBatchAcceptanceWorkflow,
|
||||
loadProjectBatchAcceptanceReportUrlWorkflow,
|
||||
readMachineSessionReadiness,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
restoreMachineParametersFromOpfs,
|
||||
saveMachineSessionSnapshot,
|
||||
saveMachineTextFiles,
|
||||
saveSessionSnapshot,
|
||||
saveTextFile,
|
||||
sessionSnapshotPath,
|
||||
toolTablePath,
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson,
|
||||
validateSessionSnapshot,
|
||||
} from "./src/index.js";
|
||||
```
|
||||
|
||||
@@ -123,3 +240,258 @@ change interpreter semantics.
|
||||
path but keeps the runner loop going after LinuxCNC reports an error, matching
|
||||
upstream `rs274 -n 0` regression tests such as `tests/interp/oword-unwind`.
|
||||
It does not implement or reinterpret LinuxCNC error semantics.
|
||||
|
||||
## Project release readiness export
|
||||
|
||||
`createProjectReleaseGateManifest()` returns the required release gate IDs,
|
||||
commands, expected smoke outputs, and gate count for CI dashboards and external
|
||||
SDK callers. `createProjectReleaseGateResultMatrix()` maps observed gate output
|
||||
and explicit gate results onto that manifest as stable `passed`/`unknown` rows.
|
||||
`createProjectReleaseGateActionPlan()` turns that matrix into pending gate
|
||||
commands, the next gate command, stable display rows, and a shell script that
|
||||
external CI can execute or show without duplicating the manifest order.
|
||||
`createProjectReleaseGateExecutionManifest()` packages the same gate manifest
|
||||
with caller-provided explicit results and observed output evidence. Each row
|
||||
includes the command, expected output, matched observed output when present,
|
||||
evidence kind, and `passed`/`unknown` status so CI or browser dashboards can
|
||||
render release-gate execution state without scraping shell scripts.
|
||||
`createProjectReleaseGateExecutionSummaryViewModel()` turns that execution
|
||||
manifest into stable dashboard status text, evidence counts, next missing gate,
|
||||
summary rows, and normalized per-gate rows.
|
||||
`createProjectReleaseReadinessReport()` returns a machine-readable release
|
||||
readiness report that embeds the same manifest, result matrix, action plan,
|
||||
gate execution manifest, gate execution summary view-model, sim-config
|
||||
inventory baseline, and blocked runtime families. It is evidence driven: the
|
||||
default report is `ready: false` until the caller supplies observed gate output
|
||||
such as `project_release_gate=ok` or explicit gate results.
|
||||
`createProjectReleaseReadinessSummaryViewModel()` turns the report into stable
|
||||
status text, a status line, and display rows for dashboards without executing
|
||||
any gate.
|
||||
`createProjectBatchAcceptanceCapabilityMatrix()` normalizes per-batch evidence
|
||||
and classifies accepted `api`, `workflow`, `gate`, and `browser` capabilities
|
||||
separately from rejected docs-only or unknown entries.
|
||||
`createProjectBatchAcceptanceWorkflow()` packages that capability matrix with
|
||||
release-gate evidence for the project rule that each batch must produce at
|
||||
least one callable API, verifiable workflow, executable gate, or browser
|
||||
capability. The companion
|
||||
`createProjectBatchAcceptanceSummaryViewModel()` and
|
||||
`createProjectBatchAcceptanceActionPlan()` helpers make that evidence
|
||||
dashboard-friendly and provide the next gate command when the batch acceptance
|
||||
smoke has not been observed. `createProjectBatchAcceptanceChecklist()` combines
|
||||
the capability matrix, gate evidence, and action plan into pass/blocked rows
|
||||
for release dashboards. `createProjectBatchAcceptanceReport()` packages the
|
||||
matrix, workflow, summary, action plan, and checklist into one stable object
|
||||
for external CI or release dashboards.
|
||||
`createProjectBatchAcceptanceReportValidation()` validates that report shape
|
||||
without copying test assertions.
|
||||
`createProjectBatchAcceptanceReportValidationSummaryViewModel()` and
|
||||
`createProjectBatchAcceptanceReportValidationActionPlan()` expose dashboard rows
|
||||
and the next artifact gate command for blocked report validation.
|
||||
`createProjectBatchAcceptanceReportJsonWorkflow()` is the non-throwing JSON
|
||||
workflow for external CI systems that exchange batch acceptance reports as
|
||||
strings.
|
||||
`loadProjectBatchAcceptanceReportUrlWorkflow()` is the fetch-driven URL
|
||||
workflow for hosted batch acceptance reports. It returns fetch status, the JSON
|
||||
workflow, report validation, and missing evidence without throwing.
|
||||
`createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()` and
|
||||
`createProjectBatchAcceptanceReportUrlWorkflowActionPlan()` turn the same URL
|
||||
workflow into dashboard rows and ordered input/fetch/gate actions.
|
||||
|
||||
This helper does not execute shell commands and does not inspect or implement
|
||||
CNC behavior. It only packages project-level release evidence and blocked
|
||||
runtime policy that are already documented in `docs/project-release-handoff.md`
|
||||
and `../PROJECT_COMPLETION_TRACKER.md`.
|
||||
|
||||
The executable release gate uses the same helper to write
|
||||
`build/project-release-readiness.json` after all project checks pass, then
|
||||
validates that artifact with
|
||||
`tests/host/verify_project_release_readiness_artifact.sh`. External tools can
|
||||
load that JSON with `parseProjectReleaseReadinessArtifactJson()` and validate
|
||||
it with `createProjectReleaseReadinessArtifactValidation()` without copying the
|
||||
host test assertions. The validation result includes `expectedGateCount` and
|
||||
`expectedGateIds` so external callers can compare an artifact against the
|
||||
current manifest, plus `gateManifestReady`, `gateExecutionManifestReady`,
|
||||
`gateExecutionSummaryReady`, `gateResultMatrixReady`, and
|
||||
`gateActionPlanReady` so stale artifacts that omit the embedded gate evidence
|
||||
chain fail the same executable gate.
|
||||
`createProjectReleaseReadinessArtifactValidationSummaryViewModel()` turns that
|
||||
validation result into stable status text and display rows for dashboards or
|
||||
browser shells without reinterpreting the artifact fields.
|
||||
`createProjectReleaseReadinessArtifactValidationActionPlan()` turns a blocked
|
||||
artifact validation result into the exact project release gate and artifact
|
||||
validation commands to run next, plus the missing evidence list and shell
|
||||
script. Ready validations return no commands.
|
||||
`createProjectReleaseReadinessArtifactJsonWorkflow()` is the non-throwing
|
||||
one-call wrapper for dashboards that receive artifact JSON text. It parses,
|
||||
validates, builds the validation summary, and builds the action plan. Invalid
|
||||
JSON returns a blocked workflow with `parsed: false` and the same next-command
|
||||
action plan instead of throwing.
|
||||
`loadProjectReleaseReadinessArtifactUrlWorkflow()` is the SDK-side URL workflow
|
||||
for callers that supply a `fetch` implementation. It fetches the artifact URL,
|
||||
runs the JSON workflow, and returns fetch status, validation, summary, and
|
||||
action-plan output without executing release gates.
|
||||
`createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel()` turns the
|
||||
URL workflow result into stable dashboard status text and rows for ready,
|
||||
missing-input, fetch-failure, and artifact-validation-blocked states.
|
||||
`createProjectReleaseReadinessArtifactUrlWorkflowActionPlan()` turns the same
|
||||
URL workflow into ordered workflow-level actions, distinguishing caller input,
|
||||
fetch checks, and executable gate commands while preserving a shell script for
|
||||
command actions.
|
||||
`tests/sdk/node/verify_project_release_artifact_url_workflow.sh` is the
|
||||
dedicated executable SDK gate for that URL workflow. It verifies caller-provided
|
||||
fetch handling, ready summaries, action plans, missing input, HTTP failure,
|
||||
thrown fetch errors, and ends with
|
||||
`project_release_artifact_url_workflow_node_smoke=ok`.
|
||||
`tests/sdk/node/verify_project_batch_acceptance_workflow.sh` is the executable
|
||||
SDK gate for the per-batch acceptance workflow. It runs the workflow smoke,
|
||||
writes `build/project-batch-acceptance.json` through
|
||||
`write_project_batch_acceptance_artifact.mjs`, validates that artifact through
|
||||
`verify_project_batch_acceptance_artifact.sh`, and emits
|
||||
`project_batch_acceptance_workflow_node_smoke=ok` before ending with
|
||||
`project_batch_acceptance_artifact_node_smoke=ok`.
|
||||
|
||||
## OPFS/session persistence exports
|
||||
|
||||
The SDK entrypoint re-exports the OPFS/session persistence helpers used by the
|
||||
browser panel so external callers can stage the same files without importing
|
||||
private module paths:
|
||||
|
||||
- `getOpfsRoot()`, `saveTextFile()`, and `loadTextFile()` for browser OPFS text
|
||||
persistence.
|
||||
- `machineIniPath()`, `toolTablePath()`, `parameterFilePath()`,
|
||||
`gcodeProgramPath()`, `sessionSnapshotPath()`, `defaultMachinePaths()`, and
|
||||
`normalizeOpfsPath()` for stable OPFS path construction.
|
||||
- `createMachineSessionSnapshotPayload()`, `createSessionSnapshot()`,
|
||||
`saveSessionSnapshot()`, `saveMachineSessionSnapshot()`,
|
||||
`loadMachineSessionSnapshot()`, and `validateSessionSnapshot()` for session
|
||||
snapshot persistence.
|
||||
- `machineFilePaths()`, `saveMachineTextFiles()`, `loadMachineTextFiles()`, and
|
||||
`gcodeFilenameFromProgramPath()` for machine-file and G-code file helpers.
|
||||
- `readMachineSessionReadiness()` for checking persisted INI, parameter,
|
||||
tool-table, optional G-code, and optional session snapshot files before
|
||||
loading a session.
|
||||
- `restoreMachineParametersFromOpfs()`, `loadMachineToolTableFromOpfs()`, and
|
||||
`loadMachineSessionFromOpfs()` for loading OPFS machine state into the
|
||||
LinuxCNC-backed interpreter SDK.
|
||||
|
||||
These helpers only move and validate host files. Parameter and tool-table
|
||||
effects still come from the LinuxCNC-backed interpreter SDK methods they call.
|
||||
|
||||
## INI panel shell handoff exports
|
||||
|
||||
The SDK entrypoint also re-exports the stable INI panel shell handoff surface
|
||||
for external browser shells:
|
||||
|
||||
- `INI_PANEL_ENTRIES`, `getIniPanelEntryManifest()`,
|
||||
`getIniPanelEntryByHref()`, `getVisibleIniPanelEntries()`, and
|
||||
`createIniPanelLauncherLinkViewModel()` for discovering launch pages and
|
||||
rendering shell-owned launcher links from the stable entry manifest.
|
||||
- `createIniPanelLaunchApiManifest()` and
|
||||
`isSupportedIniPanelLaunchApiManifest()` for launch API negotiation.
|
||||
- `createControlPageBrowserApiSchema()`,
|
||||
`createControlPageBrowserApiManifest()`,
|
||||
`createControlPageBrowserApiMethods()`,
|
||||
`createIniPanelShellControlPageContract()`, and
|
||||
`isSupportedIniPanelControlPageApiManifest()` for read-only control-page API
|
||||
negotiation.
|
||||
- `createIniPanelShellIntegrationSchema()`,
|
||||
`createIniPanelShellIntegrationManifest()`,
|
||||
`createIniPanelShellIntegrationReadiness()`,
|
||||
`createIniPanelShellIntegrationWorkflowPlan()`, and
|
||||
`isSupportedIniPanelShellIntegrationManifest()` for checking launch and
|
||||
control-page compatibility before embedding.
|
||||
- `createIniPanelShellViewModel()` for a complete read-only shell integration
|
||||
bundle.
|
||||
- `createMachineSessionPersistenceSummary()` for a machine-readable OPFS/session
|
||||
persistence summary covering machine files, session snapshot, session
|
||||
readiness, session load, readonly status, and shell handoff readiness rows.
|
||||
`createMachineSessionPersistenceDisplayViewModel()` and
|
||||
`createMachineSessionPersistenceRenderState()` turn the summary into stable
|
||||
display and render data.
|
||||
`createIniPanelShellSessionHandoffWorkflowSummary()` can include this as the
|
||||
read-only `persistence-summary` row.
|
||||
- `createIniPanelShellWorkflowOverviewContract()` for discovering the workflow
|
||||
overview iframe API contract.
|
||||
- `validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson()` for the
|
||||
read-only workflow overview artifact validation wrapper exposed by
|
||||
`workflow-overview.html`. Use
|
||||
`runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()` when
|
||||
a shell already has artifact JSON and needs parse, validation, summary,
|
||||
action-plan, and gate execution summary output in one non-throwing workflow.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()`
|
||||
turns that JSON workflow into stable dashboard status text and rows.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()`
|
||||
exposes the JSON workflow next-command plan without requiring callers to read
|
||||
internal workflow fields.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`,
|
||||
and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()`
|
||||
let a shell render or mount that JSON workflow render-state into caller-owned
|
||||
DOM without executing release gate commands.
|
||||
Use
|
||||
`loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()` when a
|
||||
browser or shell should fetch the artifact JSON URL, validate it, and
|
||||
optionally mount the resulting render-state into caller-owned DOM. The URL
|
||||
workflow result includes `validationSummaryViewModel` and
|
||||
`gateExecutionSummaryViewModel` for dashboards.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()`
|
||||
turns the full URL workflow result into stable status text and rows.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()`
|
||||
exposes the same URL workflow next-action/next-command plan through the
|
||||
browser workflow overview surface.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`,
|
||||
and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()`
|
||||
let callers render the URL workflow status into their own DOM.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()`
|
||||
exposes the direct artifact-validation next-command plan when callers already
|
||||
have parsed or validated artifact JSON.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`,
|
||||
and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()`
|
||||
let callers render that validation action plan into caller-owned DOM.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()`
|
||||
and
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel()`
|
||||
expose the embedded release gate execution summary for external shells without
|
||||
requiring callers to parse artifact internals.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`,
|
||||
and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()`
|
||||
let callers render the per-gate execution summary rows into caller-owned DOM.
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract()`,
|
||||
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness()`,
|
||||
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState()`, and
|
||||
`mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState()` when an
|
||||
outer shell needs to render that validation into caller-owned DOM.
|
||||
- `createIniPanelShellApiSurfaceInventory()` for a machine-readable inventory
|
||||
of launch, control-page, workflow overview, persistence, and session
|
||||
readiness report API counts, including persistence summary row counts.
|
||||
`createIniPanelShellViewModel()` also includes
|
||||
this inventory as a read-only workflow summary row so browser overview pages
|
||||
and external shells can display API coverage without private imports.
|
||||
- `createIniPanelShellSessionReadinessWorkflowReport()` for a machine-readable
|
||||
session readiness workflow report with phase, missing reasons, status line,
|
||||
and stable rows.
|
||||
- `createIniPanelShellWorkflowOverviewEmbeddingMountDomContract()` and
|
||||
`createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness()` for
|
||||
workflow overview embedding mount DOM checks.
|
||||
- `renderIniPanelShellWorkflowOverviewEmbeddingMountState()` for rendering a
|
||||
mount render-state into caller-owned DOM.
|
||||
- `mountIniPanelShellWorkflowOverviewEmbeddingMountState()` for a non-throwing
|
||||
workflow overview embedding mount wrapper that returns structured blocked
|
||||
results when DOM nodes are missing.
|
||||
|
||||
The workflow overview embedding helpers are UI/runtime boundary helpers only.
|
||||
They render status and rows already produced by the shell view-model; they do
|
||||
not parse G-code, execute action plans, or implement CNC semantics.
|
||||
|
||||
@@ -1,7 +1,178 @@
|
||||
export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
|
||||
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
|
||||
export {
|
||||
createProjectBatchAcceptanceActionPlan,
|
||||
createProjectBatchAcceptanceCapabilityMatrix,
|
||||
createProjectBatchAcceptanceChecklist,
|
||||
createProjectBatchAcceptanceReport,
|
||||
createProjectBatchAcceptanceReportJsonWorkflow,
|
||||
createProjectBatchAcceptanceReportUrlWorkflowActionPlan,
|
||||
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel,
|
||||
createProjectBatchAcceptanceReportValidationActionPlan,
|
||||
createProjectBatchAcceptanceReportValidationSummaryViewModel,
|
||||
createProjectBatchAcceptanceReportValidation,
|
||||
createProjectBatchAcceptanceSummaryViewModel,
|
||||
createProjectBatchAcceptanceWorkflow,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseGateResultMatrix,
|
||||
createProjectReleaseReadinessArtifactJsonWorkflow,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
createProjectReleaseReadinessArtifactValidationActionPlan,
|
||||
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
createProjectReleaseReadinessReport,
|
||||
createProjectReleaseReadinessSummaryViewModel,
|
||||
loadProjectBatchAcceptanceReportUrlWorkflow,
|
||||
loadProjectReleaseReadinessArtifactUrlWorkflow,
|
||||
parseProjectReleaseReadinessArtifactJson,
|
||||
} from "./project-release-readiness.js";
|
||||
export {
|
||||
analyzeIniRuntimeBoundaries,
|
||||
planIniFileContextStaging,
|
||||
planSimConfigStaging,
|
||||
} from "./sim-config-staging.js";
|
||||
|
||||
export {
|
||||
defaultMachinePaths,
|
||||
gcodeProgramPath,
|
||||
machineIniPath,
|
||||
machineRoot,
|
||||
normalizeOpfsPath,
|
||||
opfsPath,
|
||||
parameterFilePath,
|
||||
previewCachePath,
|
||||
sessionSnapshotPath,
|
||||
splitOpfsPath,
|
||||
toolTablePath,
|
||||
} from "../../opfs/path-model.js";
|
||||
export {
|
||||
ensureParentDir,
|
||||
getOpfsRoot,
|
||||
loadTextFile,
|
||||
saveTextFile,
|
||||
} from "../../opfs/file-service.js";
|
||||
export {
|
||||
createMachineSessionSnapshotPayload,
|
||||
createSessionSnapshot,
|
||||
loadMachineSessionSnapshot,
|
||||
loadSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
saveSessionSnapshot,
|
||||
validateSessionSnapshot,
|
||||
} from "../../opfs/snapshot-store.js";
|
||||
export {
|
||||
gcodeFilenameFromProgramPath,
|
||||
loadGcodeProgram,
|
||||
loadMachineTextFiles,
|
||||
machineFilePaths,
|
||||
saveGcodeProgram,
|
||||
saveMachineTextFiles,
|
||||
} from "../../opfs/machine-file-store.js";
|
||||
export {
|
||||
restoreMachineParametersFromOpfs,
|
||||
saveMachineParametersToOpfs,
|
||||
} from "../../opfs/linuxcnc-parameter-bridge.js";
|
||||
export {
|
||||
loadMachineToolTableFromOpfs,
|
||||
saveMachineToolTableToOpfs,
|
||||
} from "../../opfs/linuxcnc-tool-table-bridge.js";
|
||||
export { loadMachineSessionFromOpfs } from "../../opfs/linuxcnc-machine-session-bridge.js";
|
||||
export { readMachineSessionReadiness } from "../../opfs/machine-session-readiness.js";
|
||||
|
||||
export {
|
||||
INI_PANEL_ENTRIES,
|
||||
createControlPageBrowserApiManifest,
|
||||
createControlPageBrowserApiMethods,
|
||||
createControlPageBrowserApiSchema,
|
||||
createIniPanelLaunchApiManifest,
|
||||
createIniPanelLaunchApiSchema,
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createIniPanelShellIntegrationManifest,
|
||||
createIniPanelShellIntegrationReadiness,
|
||||
createIniPanelShellIntegrationSchema,
|
||||
createIniPanelShellIntegrationWorkflowPlan,
|
||||
createIniPanelShellControlPageContract,
|
||||
createIniPanelShellSessionHandoffCompatibilityReport,
|
||||
createIniPanelShellSessionHandoffPackage,
|
||||
createIniPanelShellSessionHandoffPackageMountDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackageMountRenderState,
|
||||
createIniPanelShellSessionHandoffPackageMountState,
|
||||
createIniPanelShellSessionHandoffPackageReport,
|
||||
createIniPanelShellSessionHandoffPackageRenderState,
|
||||
createIniPanelShellSessionHandoffPackageSchema,
|
||||
createIniPanelShellSessionReadinessWorkflowReport,
|
||||
createIniPanelShellApiSurfaceInventory,
|
||||
createIniPanelShellSessionHandoffSummary,
|
||||
createIniPanelShellSessionHandoffSummarySchema,
|
||||
createIniPanelShellViewModel,
|
||||
createIniPanelShellWorkflowOverviewContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountRenderState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingRenderState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingReport,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState,
|
||||
createIniPanelShellWorkflowOverviewReadiness,
|
||||
getIniPanelEntryByHref,
|
||||
getIniPanelEntryManifest,
|
||||
getVisibleIniPanelEntries,
|
||||
isSupportedIniPanelControlPageApiManifest,
|
||||
isSupportedIniPanelLaunchApiManifest,
|
||||
isSupportedIniPanelShellIntegrationManifest,
|
||||
isSupportedIniPanelShellSessionHandoffPackage,
|
||||
isSupportedIniPanelShellSessionHandoffSummary,
|
||||
loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl,
|
||||
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
|
||||
mountIniPanelShellSessionHandoff,
|
||||
mountIniPanelShellWorkflowOverviewEmbedding,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingState,
|
||||
renderIniPanelShellSessionHandoffMountState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson,
|
||||
} from "../../ui/ini-panel/ui-shell.js";
|
||||
|
||||
1971
wasm-port/runtime/sdk/src/project-release-readiness.js
Normal file
1971
wasm-port/runtime/sdk/src/project-release-readiness.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,9 @@ import {
|
||||
import {
|
||||
loadMachineSessionFromOpfs,
|
||||
} from "../../opfs/linuxcnc-machine-session-bridge.js";
|
||||
import {
|
||||
readMachineSessionReadiness,
|
||||
} from "../../opfs/machine-session-readiness.js";
|
||||
import {
|
||||
loadMachineSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
@@ -37,11 +40,15 @@ import {
|
||||
} from "./run-workflow.js";
|
||||
import {
|
||||
loadMachineSessionIntoWasmWorkflow,
|
||||
readMachineSessionReadinessWorkflow,
|
||||
restoreMachineSessionSnapshotWorkflow,
|
||||
saveMachineSessionSnapshotWorkflow,
|
||||
} from "./session-workflow.js";
|
||||
import {
|
||||
createMachineSessionControlView,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createMachineSessionStateSnapshot,
|
||||
createMachineSessionStateBundle,
|
||||
createMachineSessionStateReport,
|
||||
@@ -167,6 +174,25 @@ function getMachineSessionWorkflowStatus() {
|
||||
return createMachineSessionWorkflowStatus(window.linuxCncIniPanelMachineSessionState);
|
||||
}
|
||||
|
||||
function getMachineSessionPersistenceSummary(options = {}) {
|
||||
return createMachineSessionPersistenceSummary({
|
||||
state: window.linuxCncIniPanelMachineSessionState,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
function getMachineSessionPersistenceDisplayViewModel(options = {}) {
|
||||
return createMachineSessionPersistenceDisplayViewModel(
|
||||
getMachineSessionPersistenceSummary(options),
|
||||
);
|
||||
}
|
||||
|
||||
function getMachineSessionPersistenceRenderState(options = {}) {
|
||||
return createMachineSessionPersistenceRenderState(
|
||||
getMachineSessionPersistenceDisplayViewModel(options),
|
||||
);
|
||||
}
|
||||
|
||||
function getMachineSessionStateBundle() {
|
||||
return createMachineSessionStateBundle(window.linuxCncIniPanelMachineSessionState);
|
||||
}
|
||||
@@ -199,6 +225,9 @@ window.linuxCncIniPanelApi = {
|
||||
getMachineSessionStateReport,
|
||||
getMachineSessionStateReportExport,
|
||||
getMachineSessionWorkflowStatus,
|
||||
getMachineSessionPersistenceSummary,
|
||||
getMachineSessionPersistenceDisplayViewModel,
|
||||
getMachineSessionPersistenceRenderState,
|
||||
renderMachineSessionControlView: renderCurrentMachineSessionControlView,
|
||||
};
|
||||
window.getMachineSessionControlView = getMachineSessionControlView;
|
||||
@@ -206,6 +235,9 @@ window.getMachineSessionStateBundle = getMachineSessionStateBundle;
|
||||
window.getMachineSessionStateReport = getMachineSessionStateReport;
|
||||
window.getMachineSessionStateReportExport = getMachineSessionStateReportExport;
|
||||
window.getMachineSessionWorkflowStatus = getMachineSessionWorkflowStatus;
|
||||
window.getMachineSessionPersistenceSummary = getMachineSessionPersistenceSummary;
|
||||
window.getMachineSessionPersistenceDisplayViewModel = getMachineSessionPersistenceDisplayViewModel;
|
||||
window.getMachineSessionPersistenceRenderState = getMachineSessionPersistenceRenderState;
|
||||
window.renderMachineSessionControlView = renderCurrentMachineSessionControlView;
|
||||
window.linuxCncIniPanelMachineSessionState = panelMachineSessionState;
|
||||
renderCurrentMachineSessionControlView();
|
||||
@@ -486,6 +518,14 @@ async function loadMachineSessionIntoWasm() {
|
||||
return loadedSession;
|
||||
}
|
||||
|
||||
function readCurrentMachineSessionReadiness() {
|
||||
return readMachineSessionReadiness(MACHINE_ID, {
|
||||
sessionId: UI_SESSION.snapshotId,
|
||||
snapshotFilename: UI_SESSION.snapshotFilename,
|
||||
gcodeFilename: UI_SESSION.defaultGcodeFilename,
|
||||
});
|
||||
}
|
||||
|
||||
async function loadSample() {
|
||||
setLog(`Fetching ${SAMPLE_PATH}`);
|
||||
const response = await fetch(SAMPLE_PATH);
|
||||
@@ -674,6 +714,7 @@ document.getElementById("restore-session-snapshot").addEventListener("click", as
|
||||
snapshotLabel: sessionSnapshotLabel(UI_SESSION),
|
||||
loadMachineSessionSnapshot,
|
||||
loadMachineTextFiles,
|
||||
readReadiness: readCurrentMachineSessionReadiness,
|
||||
});
|
||||
editor.value = workflow.editorIniText;
|
||||
setField("sessionIni", workflow.fields.sessionIni);
|
||||
@@ -684,6 +725,7 @@ document.getElementById("restore-session-snapshot").addEventListener("click", as
|
||||
restoredSessionSnapshot = workflow.snapshot;
|
||||
updatePanelMachineSessionState({
|
||||
sessionSnapshot: workflow.summary,
|
||||
sessionReadiness: workflow.readiness,
|
||||
sessionLoad: null,
|
||||
selectedProgram: null,
|
||||
fields: workflow.fields,
|
||||
@@ -707,17 +749,27 @@ document.getElementById("restore-session-snapshot").addEventListener("click", as
|
||||
|
||||
document.getElementById("load-session").addEventListener("click", async () => {
|
||||
try {
|
||||
const readinessWorkflow = await readMachineSessionReadinessWorkflow({
|
||||
readReadiness: readCurrentMachineSessionReadiness,
|
||||
});
|
||||
const workflow = await loadMachineSessionIntoWasmWorkflow({
|
||||
loadSession: () => loadMachineSessionIntoWasm(),
|
||||
});
|
||||
loadedSession = workflow.loadedSession;
|
||||
updatePanelMachineSessionState({
|
||||
sessionReadiness: readinessWorkflow.readiness,
|
||||
sessionLoad: workflow.loadSummary,
|
||||
fields: workflow.fields,
|
||||
fields: {
|
||||
...readinessWorkflow.fields,
|
||||
...workflow.fields,
|
||||
},
|
||||
lastAction: "load-session",
|
||||
error: null,
|
||||
});
|
||||
setLog(workflow.logLines.join("\n"));
|
||||
setLog([
|
||||
...readinessWorkflow.logLines,
|
||||
...workflow.logLines,
|
||||
].join("\n"));
|
||||
} catch (error) {
|
||||
updatePanelMachineSessionState({
|
||||
lastAction: "load-session",
|
||||
@@ -736,6 +788,7 @@ document.getElementById("restore-load-session").addEventListener("click", async
|
||||
snapshotLabel: sessionSnapshotLabel(UI_SESSION),
|
||||
loadMachineSessionSnapshot,
|
||||
loadMachineTextFiles,
|
||||
readReadiness: readCurrentMachineSessionReadiness,
|
||||
loadSession: () => loadMachineSessionIntoWasm(),
|
||||
});
|
||||
editor.value = workflow.editorIniText;
|
||||
@@ -750,6 +803,7 @@ document.getElementById("restore-load-session").addEventListener("click", async
|
||||
}
|
||||
updatePanelMachineSessionState({
|
||||
sessionSnapshot: workflow.summary,
|
||||
sessionReadiness: workflow.readiness,
|
||||
sessionLoad: workflow.loadSummary,
|
||||
selectedProgram: null,
|
||||
fields: workflow.fields,
|
||||
|
||||
@@ -5,6 +5,7 @@ export function createControlPageSessionLoadState({
|
||||
controlViewReady = false,
|
||||
stateBundle = null,
|
||||
workflowStatus = null,
|
||||
sessionReadiness = null,
|
||||
controlView = null,
|
||||
error = null,
|
||||
} = {}) {
|
||||
@@ -15,11 +16,19 @@ export function createControlPageSessionLoadState({
|
||||
controlViewReady,
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
sessionReadiness,
|
||||
controlView,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
function getSessionReadiness(stateBundle, workflowStatus) {
|
||||
return workflowStatus?.sessionReadiness
|
||||
?? stateBundle?.workflowStatus?.sessionReadiness
|
||||
?? stateBundle?.report?.sessionReadiness
|
||||
?? null;
|
||||
}
|
||||
|
||||
export function loadMachineSessionForControlPageWorkflow({
|
||||
getPanelWindow,
|
||||
getPanelApi,
|
||||
@@ -63,22 +72,28 @@ export function loadMachineSessionForControlPageWorkflow({
|
||||
}
|
||||
|
||||
if (!controlView) {
|
||||
const stateBundle = panelApi.getMachineSessionStateBundle?.() ?? null;
|
||||
const workflowStatus = panelApi.getMachineSessionWorkflowStatus?.() ?? null;
|
||||
return createControlPageSessionLoadState({
|
||||
phase: "waiting-session",
|
||||
panelReady: true,
|
||||
apiReady: true,
|
||||
stateBundle: panelApi.getMachineSessionStateBundle?.() ?? null,
|
||||
workflowStatus: panelApi.getMachineSessionWorkflowStatus?.() ?? null,
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
sessionReadiness: getSessionReadiness(stateBundle, workflowStatus),
|
||||
});
|
||||
}
|
||||
|
||||
const stateBundle = panelApi.getMachineSessionStateBundle?.() ?? null;
|
||||
const workflowStatus = panelApi.getMachineSessionWorkflowStatus?.() ?? null;
|
||||
return createControlPageSessionLoadState({
|
||||
phase: "ready",
|
||||
panelReady: true,
|
||||
apiReady: true,
|
||||
controlViewReady: true,
|
||||
stateBundle: panelApi.getMachineSessionStateBundle?.() ?? null,
|
||||
workflowStatus: panelApi.getMachineSessionWorkflowStatus?.() ?? null,
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
sessionReadiness: getSessionReadiness(stateBundle, workflowStatus),
|
||||
controlView,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ import {
|
||||
createMachineSessionControlPageController,
|
||||
} from "./control-page-controller.js";
|
||||
import {
|
||||
createControlPageBrowserApiManifest,
|
||||
createControlPageBrowserApiMethods,
|
||||
createControlPageBrowserApiSchema,
|
||||
createIniPanelShellControlPageContract,
|
||||
isSupportedIniPanelControlPageApiManifest,
|
||||
} from "./ui-shell.js";
|
||||
|
||||
const panelFrameNode = document.getElementById("panel-frame");
|
||||
@@ -23,6 +26,9 @@ const controller = createMachineSessionControlPageController({
|
||||
controller.mount();
|
||||
|
||||
window.linuxCncIniPanelControlPageApi = {
|
||||
isSupportedControlPageApiManifest: isSupportedIniPanelControlPageApiManifest,
|
||||
getControlPageApiSchema: createControlPageBrowserApiSchema,
|
||||
getControlPageApiManifest: createControlPageBrowserApiManifest,
|
||||
getControlPageApiMethods: createControlPageBrowserApiMethods,
|
||||
getControlView: controller.getControlView,
|
||||
getControlPageContract: createIniPanelShellControlPageContract,
|
||||
|
||||
@@ -9,6 +9,11 @@ export const INI_PANEL_ENTRIES = [
|
||||
title: "Open read-only control view",
|
||||
href: "./control-page.html",
|
||||
},
|
||||
{
|
||||
id: "workflow-overview",
|
||||
title: "Open shell handoff overview",
|
||||
href: "./workflow-overview.html",
|
||||
},
|
||||
];
|
||||
|
||||
export function getIniPanelEntryManifest() {
|
||||
|
||||
@@ -46,6 +46,27 @@
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.handoff-status {
|
||||
border-top: 1px solid var(--border);
|
||||
padding-top: 12px;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.handoff-status dl {
|
||||
margin: 0;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(120px, 180px) 1fr;
|
||||
gap: 6px 12px;
|
||||
font: 13px/1.4 "Segoe UI", "Noto Sans", sans-serif;
|
||||
}
|
||||
.handoff-status dt {
|
||||
color: var(--muted);
|
||||
}
|
||||
.handoff-status dd {
|
||||
margin: 0;
|
||||
color: var(--text);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
@@ -65,11 +86,25 @@
|
||||
<main>
|
||||
<section class="panel">
|
||||
<h1>LinuxCNC INI Panel</h1>
|
||||
<p>Choose the editable panel or the read-only control view.</p>
|
||||
<p>Choose the editable panel, the read-only control view, or the shell handoff overview.</p>
|
||||
<div class="links">
|
||||
<a data-entry-id="edit-run" href="./index.html">Open edit and run panel</a>
|
||||
<a data-entry-id="control-view" href="./control-page.html">Open read-only control view</a>
|
||||
<a data-entry-id="workflow-overview" href="./workflow-overview.html">Open shell handoff overview</a>
|
||||
<a data-entry-id="real-simulation" href="../simulation/index.html">Open real browser simulation</a>
|
||||
</div>
|
||||
<section class="handoff-status" aria-label="Shell handoff compatibility" data-handoff-shell>
|
||||
<p data-handoff-status>Checking shell handoff compatibility.</p>
|
||||
<dl data-handoff-rows></dl>
|
||||
</section>
|
||||
<section class="handoff-status" aria-label="Shell handoff mount readiness" data-handoff-mount>
|
||||
<p data-handoff-mount-status>Checking shell handoff mount readiness.</p>
|
||||
<dl data-handoff-mount-rows></dl>
|
||||
</section>
|
||||
<section class="handoff-status" aria-label="Shell handoff workflow summary" data-handoff-workflow>
|
||||
<p data-handoff-workflow-status>Checking shell handoff workflow summary.</p>
|
||||
<dl data-handoff-workflow-rows></dl>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
<script type="module">
|
||||
@@ -77,6 +112,21 @@
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createIniPanelShellViewModel,
|
||||
isSupportedIniPanelLaunchApiManifest,
|
||||
createIniPanelShellSessionHandoffMountDomContract,
|
||||
createIniPanelShellSessionHandoffMountDomReadiness,
|
||||
mountIniPanelShellSessionHandoff,
|
||||
mountIniPanelShellSessionHandoffFromControlPage,
|
||||
readIniPanelShellSessionHandoffStatusFromControlPage,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createIniPanelShellSessionHandoffWorkflowSummary,
|
||||
createIniPanelShellSessionReadinessWorkflowReport,
|
||||
createIniPanelShellSessionHandoffWorkflowDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffWorkflowRenderState,
|
||||
createIniPanelShellSessionHandoffWorkflowDomContract,
|
||||
createIniPanelShellSessionHandoffWorkflowDomReadiness,
|
||||
renderIniPanelShellSessionHandoffWorkflowState,
|
||||
mountIniPanelShellSessionHandoffWorkflow,
|
||||
renderIniPanelShellSessionHandoffMountState,
|
||||
} from "./ui-shell.js";
|
||||
|
||||
const shellViewModel = createIniPanelShellViewModel();
|
||||
@@ -89,11 +139,327 @@
|
||||
link.textContent = linkModel.label;
|
||||
link.href = linkModel.href;
|
||||
}
|
||||
const handoffRenderState = shellViewModel.shellSessionHandoffPackageRenderState;
|
||||
const handoffShell = document.querySelector("[data-handoff-shell]");
|
||||
handoffShell.dataset.handoffPhase = handoffRenderState.dataset.handoffPhase;
|
||||
handoffShell.dataset.handoffReady = handoffRenderState.dataset.handoffReady;
|
||||
handoffShell.dataset.handoffScope = handoffRenderState.dataset.handoffScope;
|
||||
document.querySelector("[data-handoff-status]").textContent = handoffRenderState.statusLine;
|
||||
const handoffRows = document.querySelector("[data-handoff-rows]");
|
||||
for (const row of handoffRenderState.rows) {
|
||||
const term = document.createElement("dt");
|
||||
term.dataset.handoffRow = row.id;
|
||||
term.textContent = row.label;
|
||||
const detail = document.createElement("dd");
|
||||
detail.dataset.handoffValue = row.id;
|
||||
detail.textContent = row.value;
|
||||
handoffRows.append(term, detail);
|
||||
}
|
||||
const handoffMountRenderState = shellViewModel.shellSessionHandoffPackageMountRenderState;
|
||||
renderIniPanelShellSessionHandoffMountState(handoffMountRenderState, { documentRef: document });
|
||||
const handoffWorkflowRenderState = shellViewModel.shellSessionHandoffWorkflowRenderState;
|
||||
renderIniPanelShellSessionHandoffWorkflowState(handoffWorkflowRenderState, { documentRef: document });
|
||||
|
||||
window.linuxCncIniPanelLaunchApi = {
|
||||
isSupportedLaunchApiManifest: isSupportedIniPanelLaunchApiManifest,
|
||||
getLaunchApiSchema: () => shellViewModel.launchApiSchema,
|
||||
getLaunchApiManifest: () => shellViewModel.launchApiManifest,
|
||||
getShellIntegrationSchema: () => shellViewModel.shellIntegrationSchema,
|
||||
getShellIntegrationManifest: () => shellViewModel.shellIntegrationManifest,
|
||||
getShellIntegrationReadiness: () => shellViewModel.shellIntegrationReadiness,
|
||||
getShellSessionHandoffSummarySchema: () => shellViewModel.shellSessionHandoffSummarySchema,
|
||||
getShellSessionHandoffSummary: () => shellViewModel.shellSessionHandoffSummary,
|
||||
isSupportedShellSessionHandoffSummary: shellViewModel.isSupportedShellSessionHandoffSummary,
|
||||
getShellSessionHandoffCompatibilityReport: () => shellViewModel.shellSessionHandoffCompatibilityReport,
|
||||
getShellSessionHandoffDisplayViewModel: () => shellViewModel.shellSessionHandoffDisplayViewModel,
|
||||
getShellSessionHandoffSnapshot: () => shellViewModel.shellSessionHandoffSnapshot,
|
||||
getShellSessionHandoffRenderState: () => shellViewModel.shellSessionHandoffRenderState,
|
||||
getShellSessionHandoffActionPlan: () => shellViewModel.shellSessionHandoffActionPlan,
|
||||
getShellSessionHandoffChecklist: () => shellViewModel.shellSessionHandoffChecklist,
|
||||
getShellSessionHandoffPackageSchema: () => shellViewModel.shellSessionHandoffPackageSchema,
|
||||
getShellSessionHandoffPackage: () => shellViewModel.shellSessionHandoffPackage,
|
||||
getShellSessionHandoffPackageReport: () => shellViewModel.shellSessionHandoffPackageReport,
|
||||
getShellSessionHandoffPackageDisplayViewModel: () => shellViewModel.shellSessionHandoffPackageDisplayViewModel,
|
||||
getShellSessionHandoffPackageRenderState: () => shellViewModel.shellSessionHandoffPackageRenderState,
|
||||
getShellSessionHandoffPackageMountState: (sessionLoadState) => (
|
||||
sessionLoadState
|
||||
? shellViewModel.createShellSessionHandoffPackageMountState({
|
||||
handoffPackage: shellViewModel.shellSessionHandoffPackage,
|
||||
packageRenderState: shellViewModel.shellSessionHandoffPackageRenderState,
|
||||
sessionLoadState,
|
||||
})
|
||||
: shellViewModel.shellSessionHandoffPackageMountState
|
||||
),
|
||||
getShellSessionHandoffPackageMountDisplayViewModel: (mountState) => (
|
||||
shellViewModel.createShellSessionHandoffPackageMountDisplayViewModel(
|
||||
mountState ?? shellViewModel.shellSessionHandoffPackageMountState,
|
||||
)
|
||||
),
|
||||
getShellSessionHandoffPackageMountRenderState: (mountState) => (
|
||||
shellViewModel.createShellSessionHandoffPackageMountRenderState(
|
||||
shellViewModel.createShellSessionHandoffPackageMountDisplayViewModel(
|
||||
mountState ?? shellViewModel.shellSessionHandoffPackageMountState,
|
||||
),
|
||||
)
|
||||
),
|
||||
getShellSessionHandoffMountDomContract: (options) => (
|
||||
createIniPanelShellSessionHandoffMountDomContract(options)
|
||||
),
|
||||
getShellSessionHandoffMountDomReadiness: (options) => (
|
||||
createIniPanelShellSessionHandoffMountDomReadiness(options ?? { documentRef: document })
|
||||
),
|
||||
renderShellSessionHandoffMountState: (renderState, options) => (
|
||||
renderIniPanelShellSessionHandoffMountState(renderState, options ?? { documentRef: document })
|
||||
),
|
||||
mountShellSessionHandoff: (options) => (
|
||||
mountIniPanelShellSessionHandoff(options ?? {
|
||||
renderState: shellViewModel.shellSessionHandoffPackageMountRenderState,
|
||||
documentRef: document,
|
||||
})
|
||||
),
|
||||
mountShellSessionHandoffFromControlPage: (options) => (
|
||||
mountIniPanelShellSessionHandoffFromControlPage({
|
||||
handoffPackage: shellViewModel.shellSessionHandoffPackage,
|
||||
packageRenderState: shellViewModel.shellSessionHandoffPackageRenderState,
|
||||
documentRef: document,
|
||||
...(options ?? {}),
|
||||
})
|
||||
),
|
||||
readShellSessionHandoffStatusFromControlPage: (options) => (
|
||||
readIniPanelShellSessionHandoffStatusFromControlPage({
|
||||
handoffSummary: shellViewModel.shellSessionHandoffSummary,
|
||||
...(options ?? {}),
|
||||
})
|
||||
),
|
||||
getMachineSessionPersistenceSummary: (options) => (
|
||||
createMachineSessionPersistenceSummary(options)
|
||||
),
|
||||
getShellSessionHandoffWorkflowSummary: (options) => (
|
||||
createIniPanelShellSessionHandoffWorkflowSummary(options ?? {
|
||||
mountWorkflow: {
|
||||
phase: shellViewModel.shellSessionHandoffPackageMountRenderState.phase,
|
||||
ready: shellViewModel.shellSessionHandoffPackageMountRenderState.ready,
|
||||
statusLine: shellViewModel.shellSessionHandoffPackageMountRenderState.statusLine,
|
||||
},
|
||||
controlPageMountWorkflow: {
|
||||
phase: shellViewModel.shellSessionHandoffPackageMountState.phase,
|
||||
ready: shellViewModel.shellSessionHandoffPackageMountState.ready,
|
||||
statusLine: shellViewModel.shellSessionHandoffPackageMountState.statusLine,
|
||||
},
|
||||
readonlyStatusResult: shellViewModel.readShellSessionHandoffStatusFromControlPage({
|
||||
handoffSummary: shellViewModel.shellSessionHandoffSummary,
|
||||
}),
|
||||
})
|
||||
),
|
||||
getShellSessionReadinessWorkflowReport: (options) => (
|
||||
createIniPanelShellSessionReadinessWorkflowReport(options ?? {
|
||||
workflowSummary: shellViewModel.shellSessionHandoffWorkflowSummary,
|
||||
})
|
||||
),
|
||||
getShellApiSurfaceInventory: (options) => (
|
||||
shellViewModel.createShellApiSurfaceInventory(options ?? {
|
||||
launchApiManifest: shellViewModel.launchApiManifest,
|
||||
controlPageApiManifest: shellViewModel.controlPage.browserApiManifest,
|
||||
workflowOverviewContract: shellViewModel.shellWorkflowOverviewContract,
|
||||
sessionReadinessWorkflowReport: shellViewModel.shellSessionReadinessWorkflowReport,
|
||||
})
|
||||
),
|
||||
getShellSessionHandoffWorkflowDisplayViewModel: (options) => (
|
||||
createIniPanelShellSessionHandoffWorkflowDisplayViewModel(
|
||||
options ?? shellViewModel.shellSessionHandoffWorkflowSummary,
|
||||
)
|
||||
),
|
||||
getShellSessionHandoffWorkflowRenderState: (options) => (
|
||||
createIniPanelShellSessionHandoffWorkflowRenderState(
|
||||
options ?? shellViewModel.shellSessionHandoffWorkflowDisplayViewModel,
|
||||
)
|
||||
),
|
||||
getShellSessionHandoffWorkflowDomContract: (options) => (
|
||||
createIniPanelShellSessionHandoffWorkflowDomContract(options)
|
||||
),
|
||||
getShellSessionHandoffWorkflowDomReadiness: (options) => (
|
||||
createIniPanelShellSessionHandoffWorkflowDomReadiness(options ?? { documentRef: document })
|
||||
),
|
||||
renderShellSessionHandoffWorkflowState: (renderState, options) => (
|
||||
renderIniPanelShellSessionHandoffWorkflowState(renderState, options ?? { documentRef: document })
|
||||
),
|
||||
mountShellSessionHandoffWorkflow: (options) => (
|
||||
mountIniPanelShellSessionHandoffWorkflow(options ?? {
|
||||
renderState: shellViewModel.shellSessionHandoffWorkflowRenderState,
|
||||
documentRef: document,
|
||||
})
|
||||
),
|
||||
getWorkflowOverviewContract: (options) => (
|
||||
shellViewModel.createWorkflowOverviewContract(options)
|
||||
),
|
||||
getWorkflowOverviewReadiness: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReadiness(options)
|
||||
),
|
||||
mountWorkflowOverviewEmbedding: (options) => (
|
||||
shellViewModel.mountWorkflowOverviewEmbedding(options)
|
||||
),
|
||||
runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow: (options) => (
|
||||
shellViewModel.runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel: (workflow) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan: (workflow) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState: (summaryViewModel) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState(summaryViewModel)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: (renderState, options) => (
|
||||
shellViewModel.renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: (options) => (
|
||||
shellViewModel.mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
loadWorkflowOverviewReleaseReadinessArtifactUrl: (options) => (
|
||||
shellViewModel.loadWorkflowOverviewReleaseReadinessArtifactUrl(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel: (workflow) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan: (workflow) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState: (summaryViewModel) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState(summaryViewModel)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: (renderState, options) => (
|
||||
shellViewModel.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: (options) => (
|
||||
shellViewModel.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: (validation) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(validation)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan: (validation) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationActionPlan(validation)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState: (actionPlan) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState(actionPlan)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: (renderState, options) => (
|
||||
shellViewModel.renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: (options) => (
|
||||
shellViewModel.mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel: (validation) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel(validation)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel: (workflow) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState: (summaryViewModel) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(summaryViewModel)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness: (options) => (
|
||||
shellViewModel.createWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: (renderState, options) => (
|
||||
shellViewModel.renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: (options) => (
|
||||
shellViewModel.mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingReport: (embeddingResult, contract) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingReport(embeddingResult, contract)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingDisplayViewModel: (report) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingDisplayViewModel(report)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingRenderState: (displayViewModel) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingRenderState(displayViewModel)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingDomContract: (options) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingDomReadiness: (options) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingDomReadiness(options)
|
||||
),
|
||||
renderWorkflowOverviewEmbeddingState: (renderState, options) => (
|
||||
shellViewModel.renderWorkflowOverviewEmbeddingState(renderState, options ?? { documentRef: document })
|
||||
),
|
||||
mountWorkflowOverviewEmbeddingState: (options) => (
|
||||
shellViewModel.mountWorkflowOverviewEmbeddingState(options ?? {
|
||||
documentRef: document,
|
||||
})
|
||||
),
|
||||
getWorkflowOverviewEmbeddingMountDisplayViewModel: (mountResult) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingMountDisplayViewModel(mountResult)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingMountRenderState: (displayViewModel) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingMountRenderState(displayViewModel)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingMountDomContract: (options) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingMountDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingMountDomReadiness: (options) => (
|
||||
shellViewModel.createWorkflowOverviewEmbeddingMountDomReadiness(options)
|
||||
),
|
||||
renderWorkflowOverviewEmbeddingMountState: (renderState, options) => (
|
||||
shellViewModel.renderWorkflowOverviewEmbeddingMountState(renderState, options ?? { documentRef: document })
|
||||
),
|
||||
mountWorkflowOverviewEmbeddingMountState: (options) => (
|
||||
shellViewModel.mountWorkflowOverviewEmbeddingMountState(options ?? {
|
||||
documentRef: document,
|
||||
})
|
||||
),
|
||||
isSupportedShellSessionHandoffPackage: shellViewModel.isSupportedShellSessionHandoffPackage,
|
||||
getLauncherLinks: () => shellViewModel.launcherLinks,
|
||||
getControlPageApiMethods: () => shellViewModel.controlPage.browserApiMethods,
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ export function createIniPanelStateSummary({
|
||||
machine = null,
|
||||
machineFiles = null,
|
||||
sessionSnapshot = null,
|
||||
sessionReadiness = null,
|
||||
sessionLoad = null,
|
||||
runReadiness = null,
|
||||
run = null,
|
||||
@@ -17,6 +18,7 @@ export function createIniPanelStateSummary({
|
||||
machine,
|
||||
machineFiles,
|
||||
sessionSnapshot,
|
||||
sessionReadiness,
|
||||
sessionLoad,
|
||||
runReadiness,
|
||||
run,
|
||||
@@ -29,6 +31,7 @@ export function createMachineSessionStateSnapshot({
|
||||
machine = null,
|
||||
machineFiles = null,
|
||||
sessionSnapshot = null,
|
||||
sessionReadiness = null,
|
||||
sessionLoad = null,
|
||||
runReadiness = null,
|
||||
selectedProgram = null,
|
||||
@@ -44,6 +47,7 @@ export function createMachineSessionStateSnapshot({
|
||||
machine,
|
||||
machineFiles,
|
||||
sessionSnapshot,
|
||||
sessionReadiness,
|
||||
sessionLoad,
|
||||
runReadiness,
|
||||
run,
|
||||
@@ -72,6 +76,7 @@ export function createMachineSessionStateReport(state = {}) {
|
||||
const badges = state.badges ?? {};
|
||||
const fields = state.fields ?? {};
|
||||
const sessionSnapshot = state.sessionSnapshot ?? {};
|
||||
const sessionReadiness = state.sessionReadiness ?? null;
|
||||
const sessionLoad = state.sessionLoad ?? {};
|
||||
const runReadiness = state.runReadiness ?? {};
|
||||
const selectedProgram = state.selectedProgram ?? {};
|
||||
@@ -90,6 +95,7 @@ export function createMachineSessionStateReport(state = {}) {
|
||||
toolTable: fields.sessionToolTable ?? sessionSnapshot.toolTableOpfsPath ?? sessionLoad.toolTableWasmPath ?? null,
|
||||
gcode: fields.sessionGcode ?? sessionSnapshot.gcodeOpfsPath ?? null,
|
||||
},
|
||||
sessionReadiness,
|
||||
loaded: {
|
||||
iniWasmPath: sessionLoad.iniWasmPath ?? null,
|
||||
parameterWasmPath: sessionLoad.parameterWasmPath ?? null,
|
||||
@@ -141,6 +147,7 @@ export function createMachineSessionWorkflowStatus(state = {}) {
|
||||
return {
|
||||
readiness: report.readiness,
|
||||
overview,
|
||||
sessionReadiness: report.sessionReadiness ?? null,
|
||||
runReadiness: report.runReadiness,
|
||||
lastAction: report.status.lastAction,
|
||||
error: report.status.error,
|
||||
@@ -149,6 +156,156 @@ export function createMachineSessionWorkflowStatus(state = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
export function createMachineSessionPersistenceSummary({
|
||||
state = {},
|
||||
report = createMachineSessionStateReport(state),
|
||||
workflowStatus = createMachineSessionWorkflowStatus(state),
|
||||
readonlyStatus = null,
|
||||
handoffSummary = null,
|
||||
handoffWorkflowSummary = null,
|
||||
} = {}) {
|
||||
const readiness = report.readiness ?? {};
|
||||
const machineFiles = state.machineFiles ?? {};
|
||||
const session = report.session ?? {};
|
||||
const loaded = report.loaded ?? {};
|
||||
const sessionReadiness = report.sessionReadiness ?? workflowStatus.sessionReadiness ?? null;
|
||||
const opfsReady = String(readiness.opfs ?? "").toLowerCase().includes("ready");
|
||||
const machineFileLabels = [
|
||||
machineFiles.iniOpfsPath || session.ini ? "ini" : null,
|
||||
machineFiles.parameterOpfsPath || session.parameters ? "parameters" : null,
|
||||
machineFiles.toolTableOpfsPath || session.toolTable ? "tool-table" : null,
|
||||
machineFiles.gcodeOpfsPath || session.gcode ? "g-code" : null,
|
||||
].filter(Boolean);
|
||||
const machineFilesReady = machineFileLabels.length > 0;
|
||||
const snapshotReady = Boolean(session.snapshotLabel);
|
||||
const sessionReadinessReady = sessionReadiness?.ready === true;
|
||||
const sessionLoadReady = Boolean(loaded.iniWasmPath);
|
||||
const readonlyStatusValue = readonlyStatus
|
||||
? (readonlyStatus.ready === true ? "ready" : readonlyStatus.phase ?? "available")
|
||||
: "not-provided";
|
||||
const handoffValue = handoffWorkflowSummary
|
||||
? (handoffWorkflowSummary.ready === true ? "ready" : handoffWorkflowSummary.phase ?? "blocked")
|
||||
: (
|
||||
handoffSummary
|
||||
? (handoffSummary.ready === true ? "ready" : handoffSummary.phase ?? "blocked")
|
||||
: "not-provided"
|
||||
);
|
||||
const checks = {
|
||||
opfs: opfsReady,
|
||||
machineFiles: machineFilesReady,
|
||||
sessionSnapshot: snapshotReady,
|
||||
sessionReadiness: sessionReadinessReady,
|
||||
sessionLoad: sessionLoadReady,
|
||||
};
|
||||
const missing = Object.entries(checks)
|
||||
.filter(([, passed]) => !passed)
|
||||
.map(([name]) => name);
|
||||
const ready = missing.length === 0;
|
||||
|
||||
return {
|
||||
apiName: "machine-session-persistence-summary",
|
||||
summaryVersion: 1,
|
||||
phase: ready ? "ready" : "waiting",
|
||||
ready,
|
||||
checks,
|
||||
missing,
|
||||
counts: {
|
||||
machineFiles: machineFileLabels.length,
|
||||
missing: missing.length,
|
||||
},
|
||||
rows: [
|
||||
{
|
||||
id: "opfs",
|
||||
label: "OPFS",
|
||||
value: readiness.opfs ?? "missing",
|
||||
},
|
||||
{
|
||||
id: "machine-files",
|
||||
label: "Machine files",
|
||||
value: machineFileLabels.length > 0 ? machineFileLabels.join(", ") : "missing",
|
||||
},
|
||||
{
|
||||
id: "session-snapshot",
|
||||
label: "Session snapshot",
|
||||
value: session.snapshotLabel ?? "missing",
|
||||
},
|
||||
{
|
||||
id: "session-readiness",
|
||||
label: "Session readiness",
|
||||
value: sessionReadiness
|
||||
? (sessionReadiness.ready ? "ready" : `blocked: ${sessionReadiness.missing?.join(", ") || "unknown"}`)
|
||||
: "missing",
|
||||
},
|
||||
{
|
||||
id: "session-load",
|
||||
label: "Session load",
|
||||
value: loaded.iniWasmPath ? `loaded: ${loaded.iniWasmPath}` : "missing",
|
||||
},
|
||||
{
|
||||
id: "readonly-status",
|
||||
label: "Readonly status",
|
||||
value: readonlyStatusValue,
|
||||
},
|
||||
{
|
||||
id: "handoff-readiness",
|
||||
label: "Handoff readiness",
|
||||
value: handoffValue,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function createMachineSessionPersistenceDisplayViewModel(
|
||||
summary = createMachineSessionPersistenceSummary(),
|
||||
) {
|
||||
const missing = Array.isArray(summary?.missing) ? summary.missing : [];
|
||||
const rows = Array.isArray(summary?.rows)
|
||||
? summary.rows.map(({ id, label, value }) => ({
|
||||
id,
|
||||
label,
|
||||
value: value == null ? "" : String(value),
|
||||
}))
|
||||
: [];
|
||||
return {
|
||||
phase: summary?.phase === "ready" ? "ready" : "waiting",
|
||||
title: "OPFS/session persistence",
|
||||
statusText: summary?.ready ? "Ready" : "Waiting",
|
||||
detailText: missing.length > 0 ? missing.join(", ") : "No missing persistence requirements",
|
||||
rows,
|
||||
};
|
||||
}
|
||||
|
||||
export function createMachineSessionPersistenceRenderState(
|
||||
displayViewModel = createMachineSessionPersistenceDisplayViewModel(),
|
||||
) {
|
||||
const rows = Array.isArray(displayViewModel?.rows)
|
||||
? displayViewModel.rows.map(({ id, label, value }) => ({
|
||||
id,
|
||||
label,
|
||||
value: value == null ? "" : String(value),
|
||||
}))
|
||||
: [];
|
||||
const ready = displayViewModel?.phase === "ready" && displayViewModel?.statusText === "Ready";
|
||||
const statusText = displayViewModel?.statusText ?? (ready ? "Ready" : "Waiting");
|
||||
const detailText = displayViewModel?.detailText ?? "No missing persistence requirements";
|
||||
return {
|
||||
apiName: "machine-session-persistence-render-state",
|
||||
renderStateVersion: 1,
|
||||
phase: ready ? "ready" : "waiting",
|
||||
ready,
|
||||
title: displayViewModel?.title ?? "OPFS/session persistence",
|
||||
statusText,
|
||||
detailText,
|
||||
statusLine: `${statusText}: ${detailText}`,
|
||||
rows,
|
||||
dataset: {
|
||||
persistencePhase: ready ? "ready" : "waiting",
|
||||
persistenceReady: ready ? "true" : "false",
|
||||
persistenceScope: "opfs-session",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createMachineSessionWorkflowOverview(report = {}) {
|
||||
const readiness = report.readiness ?? {};
|
||||
const loaded = report.loaded ?? {};
|
||||
|
||||
@@ -4,12 +4,17 @@ export function createMachineSessionReadonlyStatus({
|
||||
controlView = null,
|
||||
} = {}) {
|
||||
const overview = stateBundle?.overview ?? workflowStatus?.overview ?? null;
|
||||
const sessionReadiness = workflowStatus?.sessionReadiness
|
||||
?? stateBundle?.workflowStatus?.sessionReadiness
|
||||
?? stateBundle?.report?.sessionReadiness
|
||||
?? null;
|
||||
return {
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
overview,
|
||||
controlView,
|
||||
report: stateBundle?.report ?? null,
|
||||
sessionReadiness,
|
||||
runReadiness: workflowStatus?.runReadiness ?? stateBundle?.report?.runReadiness ?? null,
|
||||
run: workflowStatus?.run ?? stateBundle?.report?.run ?? null,
|
||||
session: workflowStatus?.session ?? stateBundle?.report?.session ?? null,
|
||||
@@ -42,6 +47,7 @@ export function createMachineSessionReadonlyStatusApiManifest() {
|
||||
"overview",
|
||||
"controlView",
|
||||
"report",
|
||||
"sessionReadiness",
|
||||
"runReadiness",
|
||||
"run",
|
||||
"session",
|
||||
|
||||
@@ -52,6 +52,47 @@ export function machineSessionLoadWorkflowLogLines(summary) {
|
||||
];
|
||||
}
|
||||
|
||||
export function createMachineSessionReadinessFieldState(readiness) {
|
||||
return {
|
||||
sessionReadinessPhase: readiness.phase,
|
||||
sessionReadinessReady: readiness.ready ? "ready" : "blocked",
|
||||
sessionReadinessMissing: readiness.missing.length === 0 ? "none" : readiness.missing.join(", "),
|
||||
};
|
||||
}
|
||||
|
||||
export function machineSessionReadinessLogLines(readiness) {
|
||||
const checks = Array.isArray(readiness.checks) ? readiness.checks : [];
|
||||
return [
|
||||
`Session readiness: ${readiness.ready ? "ready" : "blocked"}`,
|
||||
...checks.map((check) => {
|
||||
const status = check.ok ? "ok" : `blocked (${check.error})`;
|
||||
return `${check.name}: ${check.path} -> ${status}`;
|
||||
}),
|
||||
...(readiness.snapshotCheck
|
||||
? [
|
||||
`snapshot: ${readiness.snapshotCheck.path} -> ${
|
||||
readiness.snapshotCheck.ok ? "ok" : `blocked (${readiness.snapshotCheck.error})`
|
||||
}`,
|
||||
]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
export async function readMachineSessionReadinessWorkflow({
|
||||
readiness,
|
||||
readReadiness,
|
||||
} = {}) {
|
||||
const sessionReadiness = readiness ?? await readReadiness?.();
|
||||
if (!sessionReadiness) {
|
||||
throw new Error("A machine session readiness reader is required.");
|
||||
}
|
||||
return {
|
||||
readiness: sessionReadiness,
|
||||
fields: createMachineSessionReadinessFieldState(sessionReadiness),
|
||||
logLines: machineSessionReadinessLogLines(sessionReadiness),
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadMachineSessionIntoWasmWorkflow({ loadSession }) {
|
||||
if (typeof loadSession !== "function") {
|
||||
throw new Error("A machine session loader is required.");
|
||||
@@ -114,6 +155,7 @@ export async function restoreMachineSessionSnapshotWorkflow({
|
||||
snapshotLabel,
|
||||
loadMachineSessionSnapshot,
|
||||
loadMachineTextFiles,
|
||||
readReadiness = null,
|
||||
loadSession = null,
|
||||
}) {
|
||||
const snapshot = await loadMachineSessionSnapshot(
|
||||
@@ -129,21 +171,36 @@ export async function restoreMachineSessionSnapshotWorkflow({
|
||||
editorIniText: files.ini,
|
||||
summary,
|
||||
fields,
|
||||
readiness: null,
|
||||
loadSummary: null,
|
||||
loadedSession: null,
|
||||
logLines: sessionSnapshotRestoreLogLines(summary),
|
||||
};
|
||||
if (readReadiness) {
|
||||
const readinessWorkflow = await readMachineSessionReadinessWorkflow({ readReadiness });
|
||||
result.readiness = readinessWorkflow.readiness;
|
||||
result.fields = {
|
||||
...result.fields,
|
||||
...readinessWorkflow.fields,
|
||||
};
|
||||
result.logLines = [
|
||||
...result.logLines,
|
||||
...readinessWorkflow.logLines,
|
||||
];
|
||||
}
|
||||
if (loadSession) {
|
||||
const loadWorkflow = await loadMachineSessionIntoWasmWorkflow({ loadSession });
|
||||
result.loadedSession = loadWorkflow.loadedSession;
|
||||
result.loadSummary = loadWorkflow.loadSummary;
|
||||
result.fields = {
|
||||
...fields,
|
||||
...(result.readiness ? createMachineSessionReadinessFieldState(result.readiness) : {}),
|
||||
...loadWorkflow.fields,
|
||||
sessionGcode: summary.gcodeOpfsPath,
|
||||
};
|
||||
result.logLines = [
|
||||
...sessionSnapshotSummaryLogLines(summary),
|
||||
...(result.readiness ? machineSessionReadinessLogLines(result.readiness) : []),
|
||||
...loadWorkflow.logLines,
|
||||
`G-code: ${summary.gcodeOpfsPath ?? "-"}`,
|
||||
];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
387
wasm-port/runtime/ui/ini-panel/workflow-overview.html
Normal file
387
wasm-port/runtime/ui/ini-panel/workflow-overview.html
Normal file
@@ -0,0 +1,387 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>LinuxCNC INI Workflow Overview</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--bg: #f2f5f8;
|
||||
--panel: #ffffff;
|
||||
--border: #cfd8e1;
|
||||
--text: #17212b;
|
||||
--muted: #5a6875;
|
||||
--accent: #1366a6;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Segoe UI", "Noto Sans", sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
}
|
||||
main {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 24px;
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
.panel {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 30px rgba(17, 34, 51, 0.06);
|
||||
padding: 16px;
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font: 13px/1.5 "Segoe UI", "Noto Sans", sans-serif;
|
||||
}
|
||||
.overview-status {
|
||||
border-top: 1px solid var(--border);
|
||||
padding-top: 12px;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.overview-status dl {
|
||||
margin: 0;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(120px, 180px) 1fr;
|
||||
gap: 6px 12px;
|
||||
font: 13px/1.4 "Segoe UI", "Noto Sans", sans-serif;
|
||||
}
|
||||
.overview-status dt {
|
||||
color: var(--muted);
|
||||
}
|
||||
.overview-status dd {
|
||||
margin: 0;
|
||||
color: var(--text);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<section class="panel">
|
||||
<h1>LinuxCNC INI Workflow Overview</h1>
|
||||
<p>Read-only shell handoff overview for mount, readonly status, and overall workflow readiness.</p>
|
||||
<section class="overview-status" aria-label="Shell handoff workflow overview" data-handoff-workflow>
|
||||
<p data-handoff-workflow-status>Checking shell handoff workflow summary.</p>
|
||||
<dl data-handoff-workflow-rows></dl>
|
||||
</section>
|
||||
</section>
|
||||
</main>
|
||||
<script type="module">
|
||||
import {
|
||||
createIniPanelShellViewModel,
|
||||
createIniPanelShellSessionHandoffWorkflowDomContract,
|
||||
createIniPanelShellSessionHandoffWorkflowDomReadiness,
|
||||
createIniPanelShellSessionHandoffWorkflowRenderState,
|
||||
createIniPanelShellSessionReadinessWorkflowReport,
|
||||
createIniPanelShellWorkflowOverviewContract,
|
||||
createIniPanelShellWorkflowOverviewReadiness,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingReport,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingRenderState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomReadiness,
|
||||
mountIniPanelShellWorkflowOverviewEmbedding,
|
||||
createMachineSessionPersistenceSummary,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingState,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountRenderState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson,
|
||||
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
|
||||
loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
renderIniPanelShellSessionHandoffWorkflowState,
|
||||
mountIniPanelShellSessionHandoffWorkflow,
|
||||
} from "./ui-shell.js";
|
||||
|
||||
const shellViewModel = createIniPanelShellViewModel();
|
||||
const workflowRenderState = shellViewModel.shellSessionHandoffWorkflowRenderState;
|
||||
renderIniPanelShellSessionHandoffWorkflowState(workflowRenderState, { documentRef: document });
|
||||
|
||||
window.linuxCncIniPanelWorkflowOverviewApi = {
|
||||
getWorkflowOverviewRenderState: () => shellViewModel.shellSessionHandoffWorkflowRenderState,
|
||||
getWorkflowOverviewDisplayViewModel: () => shellViewModel.shellSessionHandoffWorkflowDisplayViewModel,
|
||||
getWorkflowOverviewSummary: () => shellViewModel.shellSessionHandoffWorkflowSummary,
|
||||
getWorkflowOverviewSessionReadinessReport: (options) => (
|
||||
createIniPanelShellSessionReadinessWorkflowReport(options ?? {
|
||||
workflowSummary: shellViewModel.shellSessionHandoffWorkflowSummary,
|
||||
})
|
||||
),
|
||||
getWorkflowOverviewApiSurfaceInventory: (options) => (
|
||||
shellViewModel.createShellApiSurfaceInventory(options ?? {
|
||||
launchApiManifest: shellViewModel.launchApiManifest,
|
||||
controlPageApiManifest: shellViewModel.controlPage.browserApiManifest,
|
||||
workflowOverviewContract: shellViewModel.shellWorkflowOverviewContract,
|
||||
sessionReadinessWorkflowReport: shellViewModel.shellSessionReadinessWorkflowReport,
|
||||
})
|
||||
),
|
||||
getWorkflowOverviewPersistenceSummary: (options) => (
|
||||
createMachineSessionPersistenceSummary(options)
|
||||
),
|
||||
validateWorkflowOverviewReleaseReadinessArtifactJson: (artifactJson) => (
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(artifactJson)
|
||||
),
|
||||
runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow: (options) => (
|
||||
runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel: (workflow) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan: (workflow) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState: (summaryViewModel) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState(summaryViewModel)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: (renderState, options) => (
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
loadWorkflowOverviewReleaseReadinessArtifactUrl: (options) => (
|
||||
loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel: (workflow) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan: (workflow) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState: (summaryViewModel) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState(summaryViewModel)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: (renderState, options) => (
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: (validation) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(validation)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan: (validation) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan(validation)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState: (actionPlan) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState(actionPlan)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: (renderState, options) => (
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel: (validation) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel(validation)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel: (workflow) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel(workflow)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState: (summaryViewModel) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(summaryViewModel)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: (renderState, options) => (
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState(
|
||||
renderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState(
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel: (validation) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel(validation)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactRenderState: (displayViewModel) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState(displayViewModel)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewReleaseReadinessArtifactDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness(options ?? { documentRef: document })
|
||||
),
|
||||
renderWorkflowOverviewReleaseReadinessArtifactState: (renderState, options) => (
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState(renderState, options ?? { documentRef: document })
|
||||
),
|
||||
mountWorkflowOverviewReleaseReadinessArtifactState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState(options ?? { documentRef: document })
|
||||
),
|
||||
getWorkflowOverviewDomContract: (options) => (
|
||||
createIniPanelShellSessionHandoffWorkflowDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewDomReadiness: (options) => (
|
||||
createIniPanelShellSessionHandoffWorkflowDomReadiness(options ?? { documentRef: document })
|
||||
),
|
||||
renderWorkflowOverview: (renderState, options) => (
|
||||
renderIniPanelShellSessionHandoffWorkflowState(
|
||||
renderState ?? shellViewModel.shellSessionHandoffWorkflowRenderState,
|
||||
options ?? { documentRef: document },
|
||||
)
|
||||
),
|
||||
mountWorkflowOverview: (options) => (
|
||||
mountIniPanelShellSessionHandoffWorkflow(options ?? {
|
||||
renderState: shellViewModel.shellSessionHandoffWorkflowRenderState,
|
||||
documentRef: document,
|
||||
})
|
||||
),
|
||||
createWorkflowOverviewRenderState: createIniPanelShellSessionHandoffWorkflowRenderState,
|
||||
getWorkflowOverviewContract: () => createIniPanelShellWorkflowOverviewContract(),
|
||||
getWorkflowOverviewReadinessFromApi: () => createIniPanelShellWorkflowOverviewReadiness({
|
||||
overviewApi: window.linuxCncIniPanelWorkflowOverviewApi,
|
||||
}),
|
||||
mountWorkflowOverviewEmbedding: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewEmbedding({
|
||||
overviewApi: window.linuxCncIniPanelWorkflowOverviewApi,
|
||||
...(options ?? {}),
|
||||
})
|
||||
),
|
||||
getWorkflowOverviewEmbeddingReport: (embeddingResult, contract) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingReport(embeddingResult, contract)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingDisplayViewModel: (report) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel(report)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingRenderState: (displayViewModel) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingRenderState(displayViewModel)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomReadiness(options ?? { documentRef: document })
|
||||
),
|
||||
renderWorkflowOverviewEmbeddingState: (renderState, options) => (
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingState(renderState, options ?? { documentRef: document })
|
||||
),
|
||||
mountWorkflowOverviewEmbeddingState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingState(options ?? {
|
||||
documentRef: document,
|
||||
})
|
||||
),
|
||||
getWorkflowOverviewEmbeddingMountDisplayViewModel: (mountResult) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel(mountResult)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingMountRenderState: (displayViewModel) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountRenderState(displayViewModel)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingMountDomContract: (options) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract(options)
|
||||
),
|
||||
getWorkflowOverviewEmbeddingMountDomReadiness: (options) => (
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness(options ?? { documentRef: document })
|
||||
),
|
||||
renderWorkflowOverviewEmbeddingMountState: (renderState, options) => (
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState(renderState, options ?? { documentRef: document })
|
||||
),
|
||||
mountWorkflowOverviewEmbeddingMountState: (options) => (
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState(options ?? {
|
||||
documentRef: document,
|
||||
})
|
||||
),
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
338
wasm-port/runtime/ui/simulation/index.html
Normal file
338
wasm-port/runtime/ui/simulation/index.html
Normal file
@@ -0,0 +1,338 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>LinuxCNC Browser Simulation</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--page: #eef2f5;
|
||||
--surface: #ffffff;
|
||||
--line: #c8d3dc;
|
||||
--text: #1a252f;
|
||||
--muted: #596875;
|
||||
--blue: #1263a3;
|
||||
--green: #188160;
|
||||
--steel: #6f7f8d;
|
||||
--warn: #a9561b;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
font-family: "Segoe UI", "Noto Sans", sans-serif;
|
||||
color: var(--text);
|
||||
background: var(--page);
|
||||
}
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
min-height: 100vh;
|
||||
}
|
||||
header {
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: var(--surface);
|
||||
padding: 14px 20px;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 650;
|
||||
}
|
||||
.status-strip {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: end;
|
||||
}
|
||||
.badge {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
padding: 5px 8px;
|
||||
background: #f8fafb;
|
||||
color: var(--muted);
|
||||
font: 12px/1.2 "Segoe UI", sans-serif;
|
||||
}
|
||||
.badge strong {
|
||||
color: var(--green);
|
||||
font-weight: 700;
|
||||
}
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 0.75fr) minmax(420px, 1.35fr) minmax(300px, 0.9fr);
|
||||
min-height: 0;
|
||||
}
|
||||
section {
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
border-right: 1px solid var(--line);
|
||||
background: var(--surface);
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
}
|
||||
section:last-child { border-right: 0; }
|
||||
.section-head {
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding: 12px 14px;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 650;
|
||||
}
|
||||
.hint {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.program-lines {
|
||||
overflow: auto;
|
||||
padding: 10px 0;
|
||||
background: #fbfcfd;
|
||||
font: 13px/1.45 "SFMono-Regular", "Consolas", monospace;
|
||||
}
|
||||
.program-lines div {
|
||||
display: grid;
|
||||
grid-template-columns: 48px minmax(0, 1fr);
|
||||
gap: 10px;
|
||||
padding: 4px 12px;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
.program-lines div[data-active="true"] {
|
||||
background: #e9f4ef;
|
||||
border-left-color: var(--green);
|
||||
}
|
||||
.program-lines span {
|
||||
color: var(--steel);
|
||||
text-align: right;
|
||||
user-select: none;
|
||||
}
|
||||
.program-lines code {
|
||||
color: var(--text);
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.preview {
|
||||
background: #f8fafb;
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
}
|
||||
.toolpath-wrap {
|
||||
min-height: 0;
|
||||
padding: 18px;
|
||||
display: grid;
|
||||
}
|
||||
svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 320px;
|
||||
border: 1px solid var(--line);
|
||||
background: #ffffff;
|
||||
}
|
||||
.grid-line {
|
||||
stroke: #edf1f4;
|
||||
stroke-width: 0.01;
|
||||
}
|
||||
.toolpath {
|
||||
fill: none;
|
||||
stroke: var(--blue);
|
||||
stroke-width: 0.035;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.toolhead {
|
||||
fill: var(--warn);
|
||||
stroke: #ffffff;
|
||||
stroke-width: 0.025;
|
||||
}
|
||||
.readout {
|
||||
border-top: 1px solid var(--line);
|
||||
padding: 12px 14px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
background: var(--surface);
|
||||
}
|
||||
.axis-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
.axis-grid div {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
background: #fbfcfd;
|
||||
}
|
||||
.axis-grid span:first-child {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.axis-grid span:last-child {
|
||||
font: 14px/1.2 "SFMono-Regular", "Consolas", monospace;
|
||||
}
|
||||
dl {
|
||||
margin: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 128px minmax(0, 1fr);
|
||||
gap: 7px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
dt { color: var(--muted); }
|
||||
dd {
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
font-family: "SFMono-Regular", "Consolas", monospace;
|
||||
}
|
||||
.motion-table-wrap {
|
||||
overflow: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font: 12px/1.4 "Segoe UI", sans-serif;
|
||||
}
|
||||
th, td {
|
||||
border-bottom: 1px solid #edf1f4;
|
||||
padding: 7px 8px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: #f8fafb;
|
||||
z-index: 1;
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
td:nth-child(3), td:nth-child(4) {
|
||||
font-family: "SFMono-Regular", "Consolas", monospace;
|
||||
}
|
||||
pre {
|
||||
margin: 0;
|
||||
overflow: auto;
|
||||
padding: 12px;
|
||||
background: #111820;
|
||||
color: #dbe7ef;
|
||||
font: 11px/1.45 "SFMono-Regular", "Consolas", monospace;
|
||||
max-height: 170px;
|
||||
}
|
||||
@media (max-width: 1040px) {
|
||||
.workspace { grid-template-columns: 1fr; }
|
||||
section { border-right: 0; border-bottom: 1px solid var(--line); }
|
||||
svg { min-height: 280px; }
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
header { grid-template-columns: 1fr; }
|
||||
.status-strip { justify-content: start; }
|
||||
.axis-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
dl { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-simulation-ready="false">
|
||||
<main>
|
||||
<header>
|
||||
<h1>LinuxCNC Browser Simulation</h1>
|
||||
<div class="status-strip">
|
||||
<span class="badge">Simulation <strong data-simulation-status>loading</strong></span>
|
||||
<span class="badge" data-runtime-status>LinuxCNC interpreter WASM</span>
|
||||
</div>
|
||||
</header>
|
||||
<div class="workspace">
|
||||
<section aria-label="G-code program">
|
||||
<div class="section-head">
|
||||
<h2>Program</h2>
|
||||
<p class="hint">Executed through the LinuxCNC-backed WASM interpreter.</p>
|
||||
</div>
|
||||
<div class="program-lines" data-program-lines></div>
|
||||
</section>
|
||||
<section aria-label="Toolpath preview">
|
||||
<div class="section-head">
|
||||
<h2>Toolpath Preview</h2>
|
||||
<p class="hint">Rendered from LinuxCNC canonical motion events.</p>
|
||||
</div>
|
||||
<div class="preview">
|
||||
<div class="toolpath-wrap">
|
||||
<svg data-toolpath-svg role="img" aria-label="LinuxCNC canonical toolpath preview" viewBox="-1 -1 2 2">
|
||||
<line class="grid-line" x1="-10" y1="0" x2="10" y2="0"></line>
|
||||
<line class="grid-line" x1="0" y1="-10" x2="0" y2="10"></line>
|
||||
<polyline class="toolpath" data-toolpath-polyline points=""></polyline>
|
||||
<circle class="toolhead" data-toolpath-head cx="0" cy="0" r="0.055"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="readout">
|
||||
<div class="axis-grid">
|
||||
<div><span>X</span><span data-axis="x">0.000</span></div>
|
||||
<div><span>Y</span><span data-axis="y">0.000</span></div>
|
||||
<div><span>Z</span><span data-axis="z">0.000</span></div>
|
||||
<div><span>A</span><span data-axis="a">0.000</span></div>
|
||||
<div><span>B</span><span data-axis="b">0.000</span></div>
|
||||
<div><span>C</span><span data-axis="c">0.000</span></div>
|
||||
</div>
|
||||
<dl data-simulation-summary-rows></dl>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section aria-label="Machine state">
|
||||
<div class="section-head">
|
||||
<h2>Machine State</h2>
|
||||
<p class="hint">Active line, statement, motion rows, and canonical output.</p>
|
||||
</div>
|
||||
<div class="motion-table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Motion</th>
|
||||
<th>Line</th>
|
||||
<th>Position</th>
|
||||
<th>Statement</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-motion-rows></tbody>
|
||||
</table>
|
||||
<div class="readout">
|
||||
<dl>
|
||||
<dt>Active line</dt>
|
||||
<dd data-active-line>-</dd>
|
||||
<dt>Statement</dt>
|
||||
<dd data-active-statement>-</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<pre data-canonical-output></pre>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<script type="module">
|
||||
import { runRealBrowserSimulation } from "./simulation-app.js";
|
||||
|
||||
try {
|
||||
const state = await runRealBrowserSimulation({ documentRef: document });
|
||||
window.linuxCncRealSimulationState = state;
|
||||
window.linuxCncRealSimulationApi = {
|
||||
getState: () => window.linuxCncRealSimulationState,
|
||||
};
|
||||
} catch (error) {
|
||||
document.body.dataset.simulationReady = "false";
|
||||
document.querySelector("[data-simulation-status]").textContent = "failed";
|
||||
document.querySelector("[data-canonical-output]").textContent = error?.stack ?? error?.message ?? String(error);
|
||||
throw error;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
239
wasm-port/runtime/ui/simulation/simulation-app.js
Normal file
239
wasm-port/runtime/ui/simulation/simulation-app.js
Normal file
@@ -0,0 +1,239 @@
|
||||
import { createLinuxCncInterpSdk } from "../../sdk/src/index.js";
|
||||
|
||||
export const DEFAULT_SIMULATION_PROGRAM = [
|
||||
"G0 X0 Y0 Z0",
|
||||
"G1 X1 Y0 Z0 F100",
|
||||
"G1 X1 Y1 Z0",
|
||||
"G1 X0 Y1 Z0",
|
||||
"G1 X0 Y0 Z0",
|
||||
"M2",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
const AXES = ["x", "y", "z", "a", "b", "c", "u", "v", "w"];
|
||||
|
||||
function readCanonicalNumber(line, name) {
|
||||
const match = line.match(new RegExp(`(?:^| )${name}=([-+0-9.eE]+)`));
|
||||
return match ? Number(match[1]) : null;
|
||||
}
|
||||
|
||||
function programLineMap(programText) {
|
||||
const lines = new Map();
|
||||
programText.split(/\r?\n/).forEach((line, index) => {
|
||||
lines.set(index + 1, line.trim() || "(blank)");
|
||||
});
|
||||
return lines;
|
||||
}
|
||||
|
||||
export function parseLinuxCncCanonicalMotion(resultText, programText = "") {
|
||||
const axes = Object.fromEntries(AXES.map((axis) => [axis, 0]));
|
||||
const sourceLines = programLineMap(programText);
|
||||
const motion = [];
|
||||
|
||||
for (const line of String(resultText).split("\n")) {
|
||||
const event = line.match(/^canon_event=(STRAIGHT_TRAVERSE|STRAIGHT_FEED|ARC_FEED)\b/);
|
||||
if (!event) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const axis of AXES) {
|
||||
const value = readCanonicalNumber(line, axis);
|
||||
if (value !== null && Number.isFinite(value)) {
|
||||
axes[axis] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const sourceLine = readCanonicalNumber(line, "line");
|
||||
motion.push({
|
||||
type: event[1],
|
||||
line: Number.isFinite(sourceLine) ? sourceLine : null,
|
||||
statement: Number.isFinite(sourceLine) ? (sourceLines.get(sourceLine) ?? "-") : "-",
|
||||
axes: { ...axes },
|
||||
raw: line,
|
||||
});
|
||||
}
|
||||
|
||||
return motion;
|
||||
}
|
||||
|
||||
export function createSimulationSummary({ programText, resultText, motion }) {
|
||||
const canonicalLines = String(resultText).split("\n").filter((line) => line.startsWith("canon_event="));
|
||||
const finalAxes = motion.at(-1)?.axes ?? Object.fromEntries(AXES.map((axis) => [axis, 0]));
|
||||
return {
|
||||
apiName: "real-browser-simulation-summary",
|
||||
summaryVersion: 1,
|
||||
ready: motion.length > 0,
|
||||
phase: motion.length > 0 ? "ready" : "blocked",
|
||||
programLineCount: programText.split(/\r?\n/).filter(Boolean).length,
|
||||
canonicalEventCount: canonicalLines.length,
|
||||
motionEventCount: motion.length,
|
||||
finalAxes,
|
||||
rows: [
|
||||
{ id: "runtime", label: "Runtime", value: "LinuxCNC interpreter WASM" },
|
||||
{ id: "program-lines", label: "Program lines", value: `${programText.split(/\r?\n/).filter(Boolean).length}` },
|
||||
{ id: "canonical-events", label: "Canonical events", value: `${canonicalLines.length}` },
|
||||
{ id: "motion-events", label: "Motion events", value: `${motion.length}` },
|
||||
{ id: "final-position", label: "Final XYZ", value: formatPosition(finalAxes) },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function createToolpathViewBox(motion, padding = 0.25) {
|
||||
const points = motion.map(({ axes }) => ({ x: axes.x ?? 0, y: axes.y ?? 0 }));
|
||||
if (points.length === 0) {
|
||||
return { minX: -1, minY: -1, width: 2, height: 2 };
|
||||
}
|
||||
const xs = points.map(({ x }) => x);
|
||||
const ys = points.map(({ y }) => y);
|
||||
const minX = Math.min(...xs) - padding;
|
||||
const maxX = Math.max(...xs) + padding;
|
||||
const minY = Math.min(...ys) - padding;
|
||||
const maxY = Math.max(...ys) + padding;
|
||||
return {
|
||||
minX,
|
||||
minY,
|
||||
width: Math.max(0.1, maxX - minX),
|
||||
height: Math.max(0.1, maxY - minY),
|
||||
};
|
||||
}
|
||||
|
||||
export function createToolpathPolylinePoints(motion) {
|
||||
return motion.map(({ axes }) => `${axes.x ?? 0},${-(axes.y ?? 0)}`).join(" ");
|
||||
}
|
||||
|
||||
export function formatPosition(axes = {}) {
|
||||
return `X ${formatAxis(axes.x)} Y ${formatAxis(axes.y)} Z ${formatAxis(axes.z)}`;
|
||||
}
|
||||
|
||||
function formatAxis(value) {
|
||||
return Number.isFinite(value) ? Number(value).toFixed(3) : "0.000";
|
||||
}
|
||||
|
||||
function setText(documentRef, selector, value) {
|
||||
const node = documentRef.querySelector(selector);
|
||||
if (node) {
|
||||
node.textContent = value;
|
||||
}
|
||||
}
|
||||
|
||||
function renderRows(documentRef, rows) {
|
||||
const container = documentRef.querySelector("[data-simulation-summary-rows]");
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
container.textContent = "";
|
||||
for (const row of rows) {
|
||||
const term = documentRef.createElement("dt");
|
||||
term.textContent = row.label;
|
||||
term.dataset.simulationRow = row.id;
|
||||
const detail = documentRef.createElement("dd");
|
||||
detail.textContent = row.value;
|
||||
detail.dataset.simulationValue = row.id;
|
||||
container.append(term, detail);
|
||||
}
|
||||
}
|
||||
|
||||
function renderProgramLines(documentRef, programText, activeLine) {
|
||||
const container = documentRef.querySelector("[data-program-lines]");
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
container.textContent = "";
|
||||
programText.split(/\r?\n/).forEach((line, index) => {
|
||||
if (!line && index === programText.split(/\r?\n/).length - 1) {
|
||||
return;
|
||||
}
|
||||
const row = documentRef.createElement("div");
|
||||
row.dataset.programLine = `${index + 1}`;
|
||||
row.dataset.active = activeLine === index + 1 ? "true" : "false";
|
||||
const number = documentRef.createElement("span");
|
||||
number.textContent = `${index + 1}`;
|
||||
const text = documentRef.createElement("code");
|
||||
text.textContent = line || " ";
|
||||
row.append(number, text);
|
||||
container.append(row);
|
||||
});
|
||||
}
|
||||
|
||||
function renderAxisReadout(documentRef, axes) {
|
||||
for (const axis of ["x", "y", "z", "a", "b", "c"]) {
|
||||
setText(documentRef, `[data-axis="${axis}"]`, formatAxis(axes?.[axis]));
|
||||
}
|
||||
}
|
||||
|
||||
function renderToolpath(documentRef, motion) {
|
||||
const svg = documentRef.querySelector("[data-toolpath-svg]");
|
||||
const polyline = documentRef.querySelector("[data-toolpath-polyline]");
|
||||
const head = documentRef.querySelector("[data-toolpath-head]");
|
||||
if (!svg || !polyline || !head) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewBox = createToolpathViewBox(motion);
|
||||
svg.setAttribute(
|
||||
"viewBox",
|
||||
`${viewBox.minX} ${-(viewBox.minY + viewBox.height)} ${viewBox.width} ${viewBox.height}`,
|
||||
);
|
||||
polyline.setAttribute("points", createToolpathPolylinePoints(motion));
|
||||
const last = motion.at(-1)?.axes ?? { x: 0, y: 0 };
|
||||
head.setAttribute("cx", `${last.x ?? 0}`);
|
||||
head.setAttribute("cy", `${-(last.y ?? 0)}`);
|
||||
}
|
||||
|
||||
function renderMotionTable(documentRef, motion) {
|
||||
const body = documentRef.querySelector("[data-motion-rows]");
|
||||
if (!body) {
|
||||
return;
|
||||
}
|
||||
body.textContent = "";
|
||||
for (const [index, item] of motion.entries()) {
|
||||
const row = documentRef.createElement("tr");
|
||||
row.dataset.motionRow = `${index}`;
|
||||
for (const value of [
|
||||
item.type,
|
||||
item.line ?? "-",
|
||||
formatPosition(item.axes),
|
||||
item.statement,
|
||||
]) {
|
||||
const cell = documentRef.createElement("td");
|
||||
cell.textContent = `${value}`;
|
||||
row.append(cell);
|
||||
}
|
||||
body.append(row);
|
||||
}
|
||||
}
|
||||
|
||||
export function renderSimulationState(documentRef, state) {
|
||||
documentRef.body.dataset.simulationReady = state.summary.ready ? "true" : "false";
|
||||
setText(documentRef, "[data-simulation-status]", state.summary.ready ? "ready" : "blocked");
|
||||
setText(documentRef, "[data-runtime-status]", state.summary.rows[0].value);
|
||||
setText(documentRef, "[data-active-line]", `${state.motion.at(-1)?.line ?? "-"}`);
|
||||
setText(documentRef, "[data-active-statement]", state.motion.at(-1)?.statement ?? "-");
|
||||
setText(documentRef, "[data-canonical-output]", state.resultText);
|
||||
renderRows(documentRef, state.summary.rows);
|
||||
renderProgramLines(documentRef, state.programText, state.motion.at(-1)?.line ?? null);
|
||||
renderAxisReadout(documentRef, state.summary.finalAxes);
|
||||
renderToolpath(documentRef, state.motion);
|
||||
renderMotionTable(documentRef, state.motion);
|
||||
}
|
||||
|
||||
export async function runRealBrowserSimulation({
|
||||
documentRef = document,
|
||||
programText = DEFAULT_SIMULATION_PROGRAM,
|
||||
interpFactory = createLinuxCncInterpSdk,
|
||||
} = {}) {
|
||||
const interp = await interpFactory();
|
||||
const resultText = interp.runProgram(programText);
|
||||
const motion = parseLinuxCncCanonicalMotion(resultText, programText);
|
||||
const summary = createSimulationSummary({ programText, resultText, motion });
|
||||
const state = {
|
||||
apiName: "real-browser-simulation-state",
|
||||
stateVersion: 1,
|
||||
programText,
|
||||
resultText,
|
||||
motion,
|
||||
summary,
|
||||
};
|
||||
renderSimulationState(documentRef, state);
|
||||
return state;
|
||||
}
|
||||
@@ -62,6 +62,9 @@ TOOL_TABLE = browser-tool.tbl
|
||||
const controlDoc = await loadControlPageFrame();
|
||||
const controlPageApi = controlDoc.defaultView.linuxCncIniPanelControlPageApi;
|
||||
const expectedControlPageApiMethods = [
|
||||
"isSupportedControlPageApiManifest",
|
||||
"getControlPageApiSchema",
|
||||
"getControlPageApiManifest",
|
||||
"getControlPageApiMethods",
|
||||
"getControlPageContract",
|
||||
"getControlView",
|
||||
@@ -72,6 +75,9 @@ TOOL_TABLE = browser-tool.tbl
|
||||
"renderControlPage",
|
||||
];
|
||||
if (
|
||||
!controlPageApi?.isSupportedControlPageApiManifest ||
|
||||
!controlPageApi?.getControlPageApiSchema ||
|
||||
!controlPageApi?.getControlPageApiManifest ||
|
||||
!controlPageApi?.getControlPageApiMethods ||
|
||||
!controlPageApi?.getControlView ||
|
||||
!controlPageApi?.getControlPageContract ||
|
||||
@@ -94,6 +100,39 @@ TOOL_TABLE = browser-tool.tbl
|
||||
"control page browser API namespace methods",
|
||||
);
|
||||
|
||||
const controlPageApiSchema = controlPageApi.getControlPageApiSchema();
|
||||
assertEqual(
|
||||
controlPageApiSchema.manifestVersion,
|
||||
1,
|
||||
"control page API schema manifest version",
|
||||
);
|
||||
assertEqual(
|
||||
controlPageApiSchema.fields.includes("readonlyStatusApiManifest"),
|
||||
true,
|
||||
"control page API schema readonly status field",
|
||||
);
|
||||
const controlPageApiManifest = controlPageApi.getControlPageApiManifest();
|
||||
assertEqual(
|
||||
controlPageApi.isSupportedControlPageApiManifest(controlPageApiManifest),
|
||||
true,
|
||||
"control page API supports manifest",
|
||||
);
|
||||
assertEqual(
|
||||
controlPageApi.isSupportedControlPageApiManifest({ ...controlPageApiManifest, apiName: "other" }),
|
||||
false,
|
||||
"control page API rejects unsupported manifest name",
|
||||
);
|
||||
assertEqual(
|
||||
controlPageApiManifest.methods.join(","),
|
||||
expectedControlPageApiMethods.join(","),
|
||||
"control page API manifest methods",
|
||||
);
|
||||
assertEqual(
|
||||
controlPageApiManifest.readonlyStatusApiManifest.apiName,
|
||||
"machine-session-readonly-status",
|
||||
"control page API manifest readonly status API name",
|
||||
);
|
||||
|
||||
const controlPageContract = controlPageApi.getControlPageContract();
|
||||
assertEqual(
|
||||
controlPageContract.methodNames.join(","),
|
||||
@@ -179,6 +218,11 @@ TOOL_TABLE = browser-tool.tbl
|
||||
"load-session",
|
||||
"control page readonly status workflow last action after load",
|
||||
);
|
||||
assertEqual(
|
||||
loadedStatus.sessionReadiness.ready,
|
||||
true,
|
||||
"control page readonly status session readiness",
|
||||
);
|
||||
assertEqual(
|
||||
loadedStatus.controlView.status.lastAction,
|
||||
"load-session",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
createControlPageBrowserApiMethods,
|
||||
createIniPanelLaunchApiManifest,
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createIniPanelShellIntegrationManifest,
|
||||
createIniPanelShellViewModel,
|
||||
getIniPanelEntryByHref,
|
||||
getVisibleIniPanelEntries,
|
||||
@@ -22,9 +23,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadLaunchFrame() {
|
||||
async function loadFrame(src) {
|
||||
const frame = document.createElement("iframe");
|
||||
frame.src = "../../runtime/ui/ini-panel/launch.html";
|
||||
frame.src = src;
|
||||
document.body.appendChild(frame);
|
||||
await new Promise((resolve, reject) => {
|
||||
frame.addEventListener("load", resolve, { once: true });
|
||||
@@ -33,11 +34,15 @@
|
||||
return frame.contentDocument;
|
||||
}
|
||||
|
||||
async function loadLaunchFrame() {
|
||||
return loadFrame("../../runtime/ui/ini-panel/launch.html");
|
||||
}
|
||||
|
||||
try {
|
||||
const visibleEntries = getVisibleIniPanelEntries();
|
||||
assertEqual(visibleEntries.length, 2, "launch visible entry length");
|
||||
assertEqual(visibleEntries.length, 3, "launch visible entry length");
|
||||
const launcherLinks = createIniPanelLauncherLinkViewModel();
|
||||
assertEqual(launcherLinks.length, 2, "launcher link model length");
|
||||
assertEqual(launcherLinks.length, 3, "launcher link model length");
|
||||
assertEqual(launcherLinks[0].entryId, "edit-run", "launcher link entry id");
|
||||
assertEqual(launcherLinks[0].label, "Open edit and run panel", "launcher link label");
|
||||
assertEqual(launcherLinks[0].href, "./index.html", "launcher link href");
|
||||
@@ -47,6 +52,11 @@
|
||||
"Open read-only control view",
|
||||
"launch manifest control entry",
|
||||
);
|
||||
assertEqual(
|
||||
getIniPanelEntryByHref("./workflow-overview.html").title,
|
||||
"Open shell handoff overview",
|
||||
"launch manifest workflow entry",
|
||||
);
|
||||
assertEqual(
|
||||
createIniPanelShellViewModel().controlPage.browserApiMethods.join(","),
|
||||
createControlPageBrowserApiMethods().join(","),
|
||||
@@ -69,6 +79,47 @@
|
||||
!launchApi?.isSupportedLaunchApiManifest ||
|
||||
!launchApi?.getLaunchApiSchema ||
|
||||
!launchApi?.getLaunchApiManifest ||
|
||||
!launchApi?.getShellIntegrationSchema ||
|
||||
!launchApi?.getShellIntegrationManifest ||
|
||||
!launchApi?.getShellIntegrationReadiness ||
|
||||
!launchApi?.getShellSessionHandoffSummarySchema ||
|
||||
!launchApi?.getShellSessionHandoffSummary ||
|
||||
!launchApi?.isSupportedShellSessionHandoffSummary ||
|
||||
!launchApi?.getShellSessionHandoffCompatibilityReport ||
|
||||
!launchApi?.getShellSessionHandoffDisplayViewModel ||
|
||||
!launchApi?.getShellSessionHandoffSnapshot ||
|
||||
!launchApi?.getShellSessionHandoffRenderState ||
|
||||
!launchApi?.getShellSessionHandoffActionPlan ||
|
||||
!launchApi?.getShellSessionHandoffChecklist ||
|
||||
!launchApi?.getShellSessionHandoffPackageSchema ||
|
||||
!launchApi?.getShellSessionHandoffPackage ||
|
||||
!launchApi?.getShellSessionHandoffPackageReport ||
|
||||
!launchApi?.getShellSessionHandoffPackageDisplayViewModel ||
|
||||
!launchApi?.getShellSessionHandoffPackageRenderState ||
|
||||
!launchApi?.getShellSessionHandoffPackageMountState ||
|
||||
!launchApi?.getShellSessionHandoffPackageMountDisplayViewModel ||
|
||||
!launchApi?.getShellSessionHandoffPackageMountRenderState ||
|
||||
!launchApi?.getShellSessionReadinessWorkflowReport ||
|
||||
!launchApi?.getShellApiSurfaceInventory ||
|
||||
!launchApi?.getMachineSessionPersistenceSummary ||
|
||||
!launchApi?.renderShellSessionHandoffMountState ||
|
||||
!launchApi?.getWorkflowOverviewContract ||
|
||||
!launchApi?.getWorkflowOverviewReadiness ||
|
||||
!launchApi?.mountWorkflowOverviewEmbedding ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingReport ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingDisplayViewModel ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingRenderState ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingDomContract ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingDomReadiness ||
|
||||
!launchApi?.renderWorkflowOverviewEmbeddingState ||
|
||||
!launchApi?.mountWorkflowOverviewEmbeddingState ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingMountDisplayViewModel ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingMountRenderState ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingMountDomContract ||
|
||||
!launchApi?.getWorkflowOverviewEmbeddingMountDomReadiness ||
|
||||
!launchApi?.renderWorkflowOverviewEmbeddingMountState ||
|
||||
!launchApi?.mountWorkflowOverviewEmbeddingMountState ||
|
||||
!launchApi?.isSupportedShellSessionHandoffPackage ||
|
||||
!launchApi?.getLauncherLinks ||
|
||||
!launchApi?.getControlPageApiMethods
|
||||
) {
|
||||
@@ -108,12 +159,470 @@
|
||||
);
|
||||
assertEqual(
|
||||
launchApiManifest.methods.join(","),
|
||||
"isSupportedLaunchApiManifest,getLaunchApiSchema,getLaunchApiManifest,getLauncherLinks,getControlPageApiMethods",
|
||||
createIniPanelLaunchApiManifest().methods.join(","),
|
||||
"launch API manifest methods",
|
||||
);
|
||||
const shellIntegrationSchema = launchApi.getShellIntegrationSchema();
|
||||
const shellIntegrationManifest = launchApi.getShellIntegrationManifest();
|
||||
const shellIntegrationReadiness = launchApi.getShellIntegrationReadiness();
|
||||
const shellSessionHandoffSummarySchema = launchApi.getShellSessionHandoffSummarySchema();
|
||||
const shellSessionHandoffSummary = launchApi.getShellSessionHandoffSummary();
|
||||
const shellSessionHandoffCompatibilityReport = launchApi.getShellSessionHandoffCompatibilityReport();
|
||||
const shellSessionHandoffDisplayViewModel = launchApi.getShellSessionHandoffDisplayViewModel();
|
||||
const shellSessionHandoffSnapshot = launchApi.getShellSessionHandoffSnapshot();
|
||||
const shellSessionHandoffRenderState = launchApi.getShellSessionHandoffRenderState();
|
||||
const shellSessionHandoffActionPlan = launchApi.getShellSessionHandoffActionPlan();
|
||||
const shellSessionHandoffChecklist = launchApi.getShellSessionHandoffChecklist();
|
||||
const shellSessionHandoffPackageSchema = launchApi.getShellSessionHandoffPackageSchema();
|
||||
const shellSessionHandoffPackage = launchApi.getShellSessionHandoffPackage();
|
||||
const shellSessionHandoffPackageReport = launchApi.getShellSessionHandoffPackageReport();
|
||||
const shellSessionHandoffPackageDisplayViewModel = launchApi.getShellSessionHandoffPackageDisplayViewModel();
|
||||
const shellSessionHandoffPackageRenderState = launchApi.getShellSessionHandoffPackageRenderState();
|
||||
const shellSessionHandoffPackageMountState = launchApi.getShellSessionHandoffPackageMountState();
|
||||
const shellSessionHandoffPackageMountDisplayViewModel = launchApi.getShellSessionHandoffPackageMountDisplayViewModel();
|
||||
const shellSessionHandoffPackageMountRenderState = launchApi.getShellSessionHandoffPackageMountRenderState();
|
||||
const shellSessionReadinessWorkflowReport = launchApi.getShellSessionReadinessWorkflowReport();
|
||||
const shellApiSurfaceInventory = launchApi.getShellApiSurfaceInventory();
|
||||
const launchPersistenceSummary = launchApi.getMachineSessionPersistenceSummary();
|
||||
const workflowOverviewContract = launchApi.getWorkflowOverviewContract();
|
||||
assertEqual(
|
||||
shellIntegrationSchema.manifestVersion,
|
||||
1,
|
||||
"shell integration schema manifest version",
|
||||
);
|
||||
assertEqual(
|
||||
shellIntegrationManifest.apiName,
|
||||
"ini-panel-shell-integration",
|
||||
"shell integration manifest API name",
|
||||
);
|
||||
assertEqual(
|
||||
shellIntegrationManifest.compatibility.launchApiManifest,
|
||||
true,
|
||||
"shell integration launch compatibility",
|
||||
);
|
||||
assertEqual(
|
||||
shellIntegrationManifest.compatibility.controlPageApiManifest,
|
||||
true,
|
||||
"shell integration control-page compatibility",
|
||||
);
|
||||
assertEqual(
|
||||
shellIntegrationManifest.controlPageApiManifest.apiName,
|
||||
"ini-panel-control-page",
|
||||
"shell integration control-page manifest name",
|
||||
);
|
||||
assertEqual(
|
||||
shellIntegrationReadiness.ready,
|
||||
true,
|
||||
"shell integration readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellIntegrationReadiness.counts.persistenceCapabilities,
|
||||
3,
|
||||
"shell integration readiness capability count",
|
||||
);
|
||||
assertEqual(
|
||||
shellIntegrationReadiness.missing.join(","),
|
||||
"",
|
||||
"shell integration readiness missing checks",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffSummary.ready,
|
||||
true,
|
||||
"shell session handoff summary readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffSummarySchema.summaryVersion,
|
||||
1,
|
||||
"shell session handoff summary schema version",
|
||||
);
|
||||
assertEqual(
|
||||
launchApi.isSupportedShellSessionHandoffSummary(shellSessionHandoffSummary),
|
||||
true,
|
||||
"shell session handoff summary supported",
|
||||
);
|
||||
assertEqual(
|
||||
launchApi.isSupportedShellSessionHandoffSummary({ ...shellSessionHandoffSummary, summaryVersion: 2 }),
|
||||
false,
|
||||
"shell session handoff summary rejects version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffCompatibilityReport.compatible,
|
||||
true,
|
||||
"shell session handoff compatibility report",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffCompatibilityReport.summarySupported,
|
||||
true,
|
||||
"shell session handoff summary support report",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffCompatibilityReport.missingFields.join(","),
|
||||
"",
|
||||
"shell session handoff compatibility missing fields",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffDisplayViewModel.statusText,
|
||||
"Ready",
|
||||
"shell session handoff display status",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffDisplayViewModel.rows.length,
|
||||
5,
|
||||
"shell session handoff display rows",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffSnapshot.snapshotVersion,
|
||||
1,
|
||||
"shell session handoff snapshot version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffSnapshot.ready,
|
||||
true,
|
||||
"shell session handoff snapshot readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffRenderState.renderStateVersion,
|
||||
1,
|
||||
"shell session handoff render-state version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffRenderState.ready,
|
||||
true,
|
||||
"shell session handoff render-state readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffRenderState.statusLine,
|
||||
"Ready: No blocking reasons",
|
||||
"shell session handoff render-state status",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffActionPlan.planVersion,
|
||||
1,
|
||||
"shell session handoff action-plan version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffActionPlan.ready,
|
||||
true,
|
||||
"shell session handoff action-plan readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffActionPlan.nextSteps.map(({ id }) => id).join(","),
|
||||
"open-control-page,read-readonly-status",
|
||||
"shell session handoff action-plan next steps",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffActionPlan.requiredMethods.readStatus,
|
||||
"readControlPageStatus",
|
||||
"shell session handoff action-plan read method",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffChecklist.checklistVersion,
|
||||
1,
|
||||
"shell session handoff checklist version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffChecklist.ready,
|
||||
true,
|
||||
"shell session handoff checklist readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffChecklist.checks.map(({ status }) => status).join(","),
|
||||
"pass,pass,pass,pass,pass",
|
||||
"shell session handoff checklist checks",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffChecklist.nextStepIds.join(","),
|
||||
"open-control-page,read-readonly-status",
|
||||
"shell session handoff checklist next step ids",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackage.packageVersion,
|
||||
1,
|
||||
"shell session handoff package version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageSchema.packageVersion,
|
||||
1,
|
||||
"shell session handoff package schema version",
|
||||
);
|
||||
assertEqual(
|
||||
launchApi.isSupportedShellSessionHandoffPackage(shellSessionHandoffPackage),
|
||||
true,
|
||||
"shell session handoff package supported",
|
||||
);
|
||||
assertEqual(
|
||||
launchApi.isSupportedShellSessionHandoffPackage({ ...shellSessionHandoffPackage, packageVersion: 2 }),
|
||||
false,
|
||||
"shell session handoff package rejects version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageReport.ready,
|
||||
true,
|
||||
"shell session handoff package report readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageReport.rows.length,
|
||||
6,
|
||||
"shell session handoff package report rows",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageReport.missingPackageFields.join(","),
|
||||
"",
|
||||
"shell session handoff package report missing fields",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageDisplayViewModel.statusText,
|
||||
"Ready",
|
||||
"shell session handoff package display status",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageDisplayViewModel.rows.length,
|
||||
shellSessionHandoffPackageReport.rows.length,
|
||||
"shell session handoff package display rows",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageRenderState.renderStateVersion,
|
||||
1,
|
||||
"shell session handoff package render-state version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageRenderState.ready,
|
||||
true,
|
||||
"shell session handoff package render-state readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageRenderState.rows.length,
|
||||
shellSessionHandoffPackageDisplayViewModel.rows.length,
|
||||
"shell session handoff package render-state rows",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageMountState.mountStateVersion,
|
||||
1,
|
||||
"shell session handoff package mount-state version",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageMountState.packageReady,
|
||||
true,
|
||||
"shell session handoff package mount-state package readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageMountState.targetPage.href,
|
||||
"./control-page.html",
|
||||
"shell session handoff package mount-state target",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageMountState.requiredMethods.readStatus,
|
||||
"readControlPageStatus",
|
||||
"shell session handoff package mount-state read method",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageMountState.sessionLoadState.phase,
|
||||
"waiting-panel",
|
||||
"shell session handoff package mount-state default session",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageMountDisplayViewModel.detailText,
|
||||
"session-load-state",
|
||||
"shell session handoff package mount display detail",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackageMountRenderState.statusLine,
|
||||
"Blocked: session-load-state",
|
||||
"shell session handoff package mount render-state status",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionReadinessWorkflowReport.apiName,
|
||||
"ini-panel-shell-session-readiness-workflow-report",
|
||||
"shell session readiness workflow report API",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionReadinessWorkflowReport.rows.length,
|
||||
3,
|
||||
"shell session readiness workflow report rows",
|
||||
);
|
||||
assertEqual(
|
||||
shellApiSurfaceInventory.apiName,
|
||||
"ini-panel-shell-api-surface-inventory",
|
||||
"shell API surface inventory API",
|
||||
);
|
||||
assertEqual(
|
||||
shellApiSurfaceInventory.counts.sessionReadinessRows,
|
||||
3,
|
||||
"shell API surface inventory session readiness rows",
|
||||
);
|
||||
assertEqual(
|
||||
shellApiSurfaceInventory.counts.persistenceSummaryRows,
|
||||
7,
|
||||
"shell API surface inventory persistence summary rows",
|
||||
);
|
||||
assertEqual(
|
||||
launchPersistenceSummary.apiName,
|
||||
"machine-session-persistence-summary",
|
||||
"launch API persistence summary API",
|
||||
);
|
||||
assertEqual(
|
||||
launchPersistenceSummary.rows.length,
|
||||
7,
|
||||
"launch API persistence summary rows",
|
||||
);
|
||||
assertEqual(
|
||||
workflowOverviewContract.pageHref,
|
||||
"./workflow-overview.html",
|
||||
"workflow overview contract page",
|
||||
);
|
||||
assertEqual(
|
||||
workflowOverviewContract.apiName,
|
||||
"linuxCncIniPanelWorkflowOverviewApi",
|
||||
"workflow overview contract API name",
|
||||
);
|
||||
assertEqual(
|
||||
workflowOverviewContract.methodNames.includes("mountWorkflowOverview"),
|
||||
true,
|
||||
"workflow overview contract mount method",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackage.ready,
|
||||
true,
|
||||
"shell session handoff package readiness",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackage.manifests.launchMethods.includes("getShellSessionHandoffPackage"),
|
||||
true,
|
||||
"shell session handoff package launch methods",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffPackage.manifests.controlPageMethods.includes("readControlPageStatus"),
|
||||
true,
|
||||
"shell session handoff package control methods",
|
||||
);
|
||||
assertEqual(
|
||||
JSON.stringify(shellSessionHandoffSnapshot.readiness),
|
||||
JSON.stringify(shellIntegrationReadiness),
|
||||
"shell session handoff snapshot readiness object",
|
||||
);
|
||||
assertEqual(
|
||||
JSON.stringify(shellSessionHandoffSnapshot.summary),
|
||||
JSON.stringify(shellSessionHandoffSummary),
|
||||
"shell session handoff snapshot summary object",
|
||||
);
|
||||
assertEqual(
|
||||
JSON.stringify(shellSessionHandoffSnapshot.displayViewModel),
|
||||
JSON.stringify(shellSessionHandoffDisplayViewModel),
|
||||
"shell session handoff snapshot display object",
|
||||
);
|
||||
assertEqual(
|
||||
JSON.stringify(shellSessionHandoffRenderState.rows),
|
||||
JSON.stringify(shellSessionHandoffDisplayViewModel.rows),
|
||||
"shell session handoff render-state rows",
|
||||
);
|
||||
assertEqual(
|
||||
JSON.stringify(shellSessionHandoffActionPlan.displayRows),
|
||||
JSON.stringify(shellSessionHandoffRenderState.rows),
|
||||
"shell session handoff action-plan rows",
|
||||
);
|
||||
assertEqual(
|
||||
JSON.stringify(shellSessionHandoffChecklist.displayRows),
|
||||
JSON.stringify(shellSessionHandoffRenderState.rows),
|
||||
"shell session handoff checklist rows",
|
||||
);
|
||||
assertEqual(
|
||||
JSON.stringify(shellSessionHandoffPackage.checklist),
|
||||
JSON.stringify(shellSessionHandoffChecklist),
|
||||
"shell session handoff package checklist",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector("[data-handoff-status]").textContent,
|
||||
"Ready: No blocking reasons",
|
||||
"shell session handoff DOM status",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector("[data-handoff-shell]").dataset.handoffPhase,
|
||||
"ready",
|
||||
"shell session handoff DOM phase",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector("[data-handoff-shell]").dataset.handoffReady,
|
||||
"true",
|
||||
"shell session handoff DOM ready",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector("[data-handoff-shell]").dataset.handoffScope,
|
||||
"package",
|
||||
"shell session handoff DOM scope",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector('[data-handoff-value="package-supported"]').textContent,
|
||||
"supported",
|
||||
"shell session handoff DOM package support",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector('[data-handoff-value="package-ready"]').textContent,
|
||||
"ready",
|
||||
"shell session handoff DOM package readiness",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector('[data-handoff-value="missing-package-fields"]').textContent,
|
||||
"none",
|
||||
"shell session handoff DOM missing package fields",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector('[data-handoff-value="blocking-reasons"]').textContent,
|
||||
"none",
|
||||
"shell session handoff DOM blocking reasons",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector("[data-handoff-mount-status]").textContent,
|
||||
"Blocked: session-load-state",
|
||||
"shell session handoff mount DOM status",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector("[data-handoff-mount]").dataset.handoffPhase,
|
||||
"blocked",
|
||||
"shell session handoff mount DOM phase",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector("[data-handoff-mount]").dataset.handoffReady,
|
||||
"false",
|
||||
"shell session handoff mount DOM ready",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector("[data-handoff-mount]").dataset.handoffScope,
|
||||
"mount",
|
||||
"shell session handoff mount DOM scope",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector('[data-handoff-mount-value="session-load-state"]').textContent,
|
||||
"waiting-panel",
|
||||
"shell session handoff mount DOM session state",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector('[data-handoff-mount-value="session-load-state"]').dataset.handoffMountStatus,
|
||||
"blocked",
|
||||
"shell session handoff mount DOM session status",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffSummary.targetPage.href,
|
||||
"./control-page.html",
|
||||
"shell session handoff summary target",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffSummary.readonlyStatusFields.includes("session"),
|
||||
true,
|
||||
"shell session handoff summary readonly fields",
|
||||
);
|
||||
assertEqual(
|
||||
shellSessionHandoffSummary.persistenceCapabilities.length,
|
||||
3,
|
||||
"shell session handoff summary persistence capabilities",
|
||||
);
|
||||
assertEqual(
|
||||
JSON.stringify(shellIntegrationManifest),
|
||||
JSON.stringify(createIniPanelShellIntegrationManifest()),
|
||||
"shell integration manifest",
|
||||
);
|
||||
assertEqual(
|
||||
launchApiManifest.visibleEntries.length,
|
||||
2,
|
||||
3,
|
||||
"launch API manifest visible entry count",
|
||||
);
|
||||
assertEqual(
|
||||
@@ -121,6 +630,11 @@
|
||||
"./control-page.html",
|
||||
"launch API manifest control target",
|
||||
);
|
||||
assertEqual(
|
||||
launchApiManifest.targetPages[2].href,
|
||||
"./workflow-overview.html",
|
||||
"launch API manifest workflow target",
|
||||
);
|
||||
assertEqual(
|
||||
launchApiManifest.persistenceCapabilities.length,
|
||||
3,
|
||||
@@ -146,9 +660,14 @@
|
||||
"Open read-only control view",
|
||||
"launch panel control link",
|
||||
);
|
||||
assertEqual(
|
||||
launchDoc.querySelector('a[href="./workflow-overview.html"]').textContent,
|
||||
"Open shell handoff overview",
|
||||
"launch panel workflow link",
|
||||
);
|
||||
assertEqual(
|
||||
launchApi.getLauncherLinks().length,
|
||||
2,
|
||||
3,
|
||||
"launch API launcher link count",
|
||||
);
|
||||
assertEqual(
|
||||
@@ -156,6 +675,78 @@
|
||||
createControlPageBrowserApiMethods().join(","),
|
||||
"launch API control-page browser methods",
|
||||
);
|
||||
const workflowOverviewDoc = await loadFrame("../../runtime/ui/ini-panel/workflow-overview.html");
|
||||
const workflowOverviewApi = workflowOverviewDoc.defaultView.linuxCncIniPanelWorkflowOverviewApi;
|
||||
if (
|
||||
!workflowOverviewApi?.getWorkflowOverviewRenderState ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewContract ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewReadinessFromApi ||
|
||||
!workflowOverviewApi?.mountWorkflowOverviewEmbedding ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingReport ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingDisplayViewModel ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingRenderState ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingDomContract ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingDomReadiness ||
|
||||
!workflowOverviewApi?.renderWorkflowOverviewEmbeddingState ||
|
||||
!workflowOverviewApi?.mountWorkflowOverviewEmbeddingState ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingMountDisplayViewModel ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingMountRenderState ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingMountDomContract ||
|
||||
!workflowOverviewApi?.getWorkflowOverviewEmbeddingMountDomReadiness ||
|
||||
!workflowOverviewApi?.renderWorkflowOverviewEmbeddingMountState ||
|
||||
!workflowOverviewApi?.mountWorkflowOverviewEmbeddingMountState
|
||||
) {
|
||||
throw new Error("workflow overview API namespace did not expose shell helpers");
|
||||
}
|
||||
const workflowOverviewReadiness = launchApi.getWorkflowOverviewReadiness({
|
||||
overviewApi: workflowOverviewApi,
|
||||
});
|
||||
const workflowOverviewEmbedding = launchApi.mountWorkflowOverviewEmbedding({
|
||||
overviewApi: workflowOverviewApi,
|
||||
});
|
||||
const workflowOverviewEmbeddingReport = launchApi.getWorkflowOverviewEmbeddingReport(
|
||||
workflowOverviewEmbedding,
|
||||
);
|
||||
const workflowOverviewEmbeddingDisplay = launchApi.getWorkflowOverviewEmbeddingDisplayViewModel(
|
||||
workflowOverviewEmbeddingReport,
|
||||
);
|
||||
const workflowOverviewEmbeddingRenderState = launchApi.getWorkflowOverviewEmbeddingRenderState(
|
||||
workflowOverviewEmbeddingDisplay,
|
||||
);
|
||||
const workflowOverviewEmbeddingDomContract = launchApi.getWorkflowOverviewEmbeddingDomContract();
|
||||
const workflowOverviewEmbeddingDomReadiness = launchApi.getWorkflowOverviewEmbeddingDomReadiness({
|
||||
documentRef: workflowOverviewDoc,
|
||||
contract: workflowOverviewEmbeddingDomContract,
|
||||
});
|
||||
assertEqual(workflowOverviewApi.getWorkflowOverviewContract().pageHref, "./workflow-overview.html", "workflow overview page contract");
|
||||
assertEqual(workflowOverviewReadiness.ready, true, "workflow overview readiness");
|
||||
assertEqual(workflowOverviewReadiness.missing.join(","), "", "workflow overview readiness missing");
|
||||
assertEqual(workflowOverviewApi.getWorkflowOverviewReadinessFromApi().ready, true, "workflow overview self readiness");
|
||||
assertEqual(workflowOverviewEmbedding.apiName, "ini-panel-shell-workflow-overview-embedding", "workflow overview embedding API name");
|
||||
assertEqual(workflowOverviewEmbedding.pageHref, "./workflow-overview.html", "workflow overview embedding page");
|
||||
assertEqual(workflowOverviewEmbedding.ready, false, "workflow overview embedding default readiness");
|
||||
assertEqual(workflowOverviewEmbedding.renderState.dataset.handoffScope, "workflow", "workflow overview embedding scope");
|
||||
assertEqual(workflowOverviewEmbedding.mountResult.renderResult.rowCount, 8, "workflow overview embedding rows");
|
||||
assertEqual(workflowOverviewEmbedding.error, null, "workflow overview embedding error");
|
||||
assertEqual(workflowOverviewEmbeddingReport.apiName, "ini-panel-shell-workflow-overview-embedding-report", "workflow overview embedding report API name");
|
||||
assertEqual(workflowOverviewEmbeddingReport.ready, false, "workflow overview embedding report readiness");
|
||||
assertEqual(workflowOverviewEmbeddingReport.missingMethods.join(","), "", "workflow overview embedding report missing methods");
|
||||
assertEqual(workflowOverviewEmbeddingReport.renderRowCount, 8, "workflow overview embedding report row count");
|
||||
assertEqual(workflowOverviewEmbeddingReport.rows.length, 6, "workflow overview embedding report rows");
|
||||
assertEqual(workflowOverviewEmbeddingDisplay.title, "Workflow overview embedding", "workflow overview embedding display title");
|
||||
assertEqual(workflowOverviewEmbeddingDisplay.statusText, "Blocked", "workflow overview embedding display status");
|
||||
assertEqual(workflowOverviewEmbeddingDisplay.rows.length, 6, "workflow overview embedding display rows");
|
||||
assertEqual(workflowOverviewEmbeddingRenderState.statusLine, "Blocked: embedding:blocked, mount:blocked", "workflow overview embedding render-state status");
|
||||
assertEqual(workflowOverviewEmbeddingRenderState.dataset.handoffScope, "workflow-overview-embedding", "workflow overview embedding render-state scope");
|
||||
assertEqual(workflowOverviewEmbeddingDomContract.selectors.mount, "[data-workflow-overview-embedding]", "workflow overview embedding DOM contract mount");
|
||||
assertEqual(workflowOverviewEmbeddingDomContract.rowDatasetKeys.detail[0], "workflowOverviewEmbeddingValue", "workflow overview embedding DOM contract row key");
|
||||
assertEqual(workflowOverviewEmbeddingDomReadiness.ready, false, "workflow overview embedding DOM readiness");
|
||||
assertEqual(workflowOverviewEmbeddingDomReadiness.missing.join(","), "mount,status,rows", "workflow overview embedding DOM readiness missing");
|
||||
assertEqual(
|
||||
workflowOverviewApi.getWorkflowOverviewEmbeddingDomReadiness().ready,
|
||||
false,
|
||||
"workflow overview self embedding DOM readiness",
|
||||
);
|
||||
status.textContent = "browser_ini_launch_smoke=ok";
|
||||
console.log("browser_ini_launch_smoke=ok");
|
||||
} catch (error) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -376,6 +376,9 @@ TOOL_TABLE = browser-tool.tbl
|
||||
!uiApi?.getMachineSessionStateReport ||
|
||||
!uiApi?.getMachineSessionStateReportExport ||
|
||||
!uiApi?.getMachineSessionWorkflowStatus ||
|
||||
!uiApi?.getMachineSessionPersistenceSummary ||
|
||||
!uiApi?.getMachineSessionPersistenceDisplayViewModel ||
|
||||
!uiApi?.getMachineSessionPersistenceRenderState ||
|
||||
!uiApi?.renderMachineSessionControlView
|
||||
) {
|
||||
throw new Error("INI panel API namespace did not expose state getters");
|
||||
@@ -813,6 +816,44 @@ TOOL_TABLE = browser-tool.tbl
|
||||
2,
|
||||
"UI machine session workflow status canonical event count",
|
||||
);
|
||||
const machineSessionPersistenceSummary = uiApi.getMachineSessionPersistenceSummary();
|
||||
assertEqual(
|
||||
machineSessionPersistenceSummary.apiName,
|
||||
"machine-session-persistence-summary",
|
||||
"UI machine session persistence summary API name",
|
||||
);
|
||||
assertEqual(
|
||||
machineSessionPersistenceSummary.ready,
|
||||
true,
|
||||
"UI machine session persistence summary readiness",
|
||||
);
|
||||
assertEqual(
|
||||
machineSessionPersistenceSummary.rows.find((row) => row.id === "machine-files")?.value,
|
||||
"ini, parameters, tool-table, g-code",
|
||||
"UI machine session persistence summary machine files",
|
||||
);
|
||||
assertEqual(
|
||||
machineSessionPersistenceSummary.rows.find((row) => row.id === "session-load")?.value,
|
||||
"loaded: /work/session-machine.ini",
|
||||
"UI machine session persistence summary session load",
|
||||
);
|
||||
const machineSessionPersistenceDisplay = uiApi.getMachineSessionPersistenceDisplayViewModel();
|
||||
assertEqual(
|
||||
machineSessionPersistenceDisplay.statusText,
|
||||
"Ready",
|
||||
"UI machine session persistence display status",
|
||||
);
|
||||
const machineSessionPersistenceRenderState = uiApi.getMachineSessionPersistenceRenderState();
|
||||
assertEqual(
|
||||
machineSessionPersistenceRenderState.statusLine,
|
||||
"Ready: No missing persistence requirements",
|
||||
"UI machine session persistence render-state status",
|
||||
);
|
||||
assertEqual(
|
||||
machineSessionPersistenceRenderState.dataset.persistenceScope,
|
||||
"opfs-session",
|
||||
"UI machine session persistence render-state scope",
|
||||
);
|
||||
const machineSessionStateBundle = uiApi.getMachineSessionStateBundle();
|
||||
assertEqual(
|
||||
machineSessionStateBundle.report.status.lastAction,
|
||||
|
||||
621
wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html
Normal file
621
wasm-port/tests/browser/ini_panel_workflow_overview_smoke.html
Normal file
@@ -0,0 +1,621 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>LinuxCNC INI Workflow Overview Smoke</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="status">running</pre>
|
||||
<script type="module">
|
||||
function assertEqual(actual, expected, label) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(`${label}: expected ${expected}, got ${actual}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadWorkflowOverviewFrame() {
|
||||
const frame = document.createElement("iframe");
|
||||
frame.src = "../../runtime/ui/ini-panel/workflow-overview.html";
|
||||
document.body.appendChild(frame);
|
||||
await new Promise((resolve, reject) => {
|
||||
frame.addEventListener("load", resolve, { once: true });
|
||||
frame.addEventListener("error", reject, { once: true });
|
||||
});
|
||||
return frame.contentDocument;
|
||||
}
|
||||
|
||||
try {
|
||||
const workflowDoc = await loadWorkflowOverviewFrame();
|
||||
const workflowApi = workflowDoc.defaultView.linuxCncIniPanelWorkflowOverviewApi;
|
||||
if (
|
||||
!workflowApi?.getWorkflowOverviewRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewDisplayViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewSummary ||
|
||||
!workflowApi?.getWorkflowOverviewSessionReadinessReport ||
|
||||
!workflowApi?.getWorkflowOverviewApiSurfaceInventory ||
|
||||
!workflowApi?.getWorkflowOverviewPersistenceSummary ||
|
||||
!workflowApi?.validateWorkflowOverviewReleaseReadinessArtifactJson ||
|
||||
!workflowApi?.runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState ||
|
||||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState ||
|
||||
!workflowApi?.loadWorkflowOverviewReleaseReadinessArtifactUrl ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState ||
|
||||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState ||
|
||||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState ||
|
||||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverviewReleaseReadinessArtifactState ||
|
||||
!workflowApi?.mountWorkflowOverviewReleaseReadinessArtifactState ||
|
||||
!workflowApi?.getWorkflowOverviewDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverview ||
|
||||
!workflowApi?.mountWorkflowOverview ||
|
||||
!workflowApi?.createWorkflowOverviewRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingReport ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingDisplayViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverviewEmbeddingState ||
|
||||
!workflowApi?.mountWorkflowOverviewEmbeddingState ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingMountDisplayViewModel ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingMountRenderState ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingMountDomContract ||
|
||||
!workflowApi?.getWorkflowOverviewEmbeddingMountDomReadiness ||
|
||||
!workflowApi?.renderWorkflowOverviewEmbeddingMountState ||
|
||||
!workflowApi?.mountWorkflowOverviewEmbeddingMountState
|
||||
) {
|
||||
throw new Error("workflow overview API namespace did not expose overview helpers");
|
||||
}
|
||||
|
||||
const renderState = workflowApi.getWorkflowOverviewRenderState();
|
||||
const displayViewModel = workflowApi.getWorkflowOverviewDisplayViewModel();
|
||||
const summary = workflowApi.getWorkflowOverviewSummary();
|
||||
const sessionReadinessReport = workflowApi.getWorkflowOverviewSessionReadinessReport();
|
||||
const apiSurfaceInventory = workflowApi.getWorkflowOverviewApiSurfaceInventory();
|
||||
const persistenceSummary = workflowApi.getWorkflowOverviewPersistenceSummary();
|
||||
const releaseArtifactJson = await fetch("../../tests/fixtures/project-release-readiness-ready.json")
|
||||
.then((response) => response.text());
|
||||
const releaseArtifactValidation = workflowApi.validateWorkflowOverviewReleaseReadinessArtifactJson(
|
||||
releaseArtifactJson,
|
||||
);
|
||||
const releaseArtifactJsonWorkflow = workflowApi.runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow({
|
||||
artifactJson: releaseArtifactJson,
|
||||
});
|
||||
const releaseArtifactJsonWorkflowSummary = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel(releaseArtifactJsonWorkflow);
|
||||
const releaseArtifactJsonWorkflowActionPlan = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan(releaseArtifactJsonWorkflow);
|
||||
const releaseArtifactJsonWorkflowRenderState = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState(releaseArtifactJsonWorkflowSummary);
|
||||
const releaseArtifactValidationSummary = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(releaseArtifactValidation);
|
||||
const releaseArtifactValidationActionPlan = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan(releaseArtifactValidation);
|
||||
const releaseArtifactValidationActionPlanRenderState = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState(
|
||||
releaseArtifactValidationActionPlan,
|
||||
);
|
||||
const releaseArtifactDisplay = workflowApi.getWorkflowOverviewReleaseReadinessArtifactDisplayViewModel(
|
||||
releaseArtifactValidation,
|
||||
);
|
||||
const releaseArtifactRenderState = workflowApi.getWorkflowOverviewReleaseReadinessArtifactRenderState(
|
||||
releaseArtifactDisplay,
|
||||
);
|
||||
const releaseArtifactDomContract = workflowApi.getWorkflowOverviewReleaseReadinessArtifactDomContract();
|
||||
const releaseArtifactDomReadinessMissing = workflowApi.getWorkflowOverviewReleaseReadinessArtifactDomReadiness();
|
||||
const releaseArtifactJsonWorkflowDomReadinessMissing = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness();
|
||||
const domContract = workflowApi.getWorkflowOverviewDomContract();
|
||||
const domReadiness = workflowApi.getWorkflowOverviewDomReadiness();
|
||||
const renderResult = workflowApi.renderWorkflowOverview();
|
||||
const mountResult = workflowApi.mountWorkflowOverview();
|
||||
const embeddingReport = workflowApi.getWorkflowOverviewEmbeddingReport({
|
||||
phase: mountResult.phase,
|
||||
ready: mountResult.ready,
|
||||
pageHref: "./workflow-overview.html",
|
||||
readiness: {
|
||||
ready: true,
|
||||
missing: [],
|
||||
},
|
||||
renderState,
|
||||
mountResult,
|
||||
error: null,
|
||||
});
|
||||
const embeddingDisplay = workflowApi.getWorkflowOverviewEmbeddingDisplayViewModel(embeddingReport);
|
||||
const embeddingRenderState = workflowApi.getWorkflowOverviewEmbeddingRenderState(embeddingDisplay);
|
||||
const embeddingDomContract = workflowApi.getWorkflowOverviewEmbeddingDomContract();
|
||||
const embeddingDomReadiness = workflowApi.getWorkflowOverviewEmbeddingDomReadiness();
|
||||
|
||||
assertEqual(summary.summaryVersion, 1, "workflow overview summary version");
|
||||
assertEqual(
|
||||
sessionReadinessReport.apiName,
|
||||
"ini-panel-shell-session-readiness-workflow-report",
|
||||
"workflow overview session readiness report API name",
|
||||
);
|
||||
assertEqual(sessionReadinessReport.rows.length, 3, "workflow overview session readiness report rows");
|
||||
assertEqual(
|
||||
apiSurfaceInventory.apiName,
|
||||
"ini-panel-shell-api-surface-inventory",
|
||||
"workflow overview API surface inventory API name",
|
||||
);
|
||||
assertEqual(apiSurfaceInventory.rows.length, 6, "workflow overview API surface inventory rows");
|
||||
assertEqual(
|
||||
apiSurfaceInventory.counts.persistenceSummaryRows,
|
||||
7,
|
||||
"workflow overview API surface inventory persistence summary rows",
|
||||
);
|
||||
assertEqual(
|
||||
persistenceSummary.apiName,
|
||||
"machine-session-persistence-summary",
|
||||
"workflow overview persistence summary API name",
|
||||
);
|
||||
assertEqual(persistenceSummary.rows.length, 7, "workflow overview persistence summary rows");
|
||||
assertEqual(
|
||||
releaseArtifactValidation.apiName,
|
||||
"ini-panel-shell-workflow-overview-release-readiness-artifact-validation",
|
||||
"workflow overview release artifact validation API name",
|
||||
);
|
||||
assertEqual(releaseArtifactValidation.ready, true, "workflow overview release artifact validation readiness");
|
||||
assertEqual(releaseArtifactValidation.gateCount, 12, "workflow overview release artifact validation gates");
|
||||
assertEqual(
|
||||
releaseArtifactValidationSummary.statusLine,
|
||||
"Ready: Artifact evidence complete",
|
||||
"workflow overview release artifact validation summary",
|
||||
);
|
||||
assertEqual(releaseArtifactDisplay.statusText, "Ready", "workflow overview release artifact display status");
|
||||
assertEqual(releaseArtifactRenderState.statusLine, "Ready: No blocking reasons", "workflow overview release artifact render status");
|
||||
assertEqual(
|
||||
releaseArtifactRenderState.dataset.handoffScope,
|
||||
"workflow-overview-release-readiness-artifact",
|
||||
"workflow overview release artifact render scope",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactDomContract.selectors.mount,
|
||||
"[data-workflow-overview-release-readiness-artifact]",
|
||||
"workflow overview release artifact DOM mount selector",
|
||||
);
|
||||
assertEqual(releaseArtifactDomReadinessMissing.ready, false, "workflow overview release artifact missing DOM readiness");
|
||||
assertEqual(displayViewModel.title, "Shell handoff workflow", "workflow overview display title");
|
||||
assertEqual(renderState.renderStateVersion, 1, "workflow overview render-state version");
|
||||
assertEqual(renderState.dataset.handoffScope, "workflow", "workflow overview render-state scope");
|
||||
assertEqual(domContract.selectors.mount, "[data-handoff-workflow]", "workflow overview DOM contract mount selector");
|
||||
assertEqual(domReadiness.ready, true, "workflow overview DOM readiness");
|
||||
assertEqual(renderResult.rendered, true, "workflow overview render result");
|
||||
assertEqual(renderResult.rowCount, 8, "workflow overview render row count");
|
||||
assertEqual(mountResult.ready, false, "workflow overview mount result readiness");
|
||||
assertEqual(mountResult.renderResult.rendered, true, "workflow overview mount result rendered");
|
||||
assertEqual(embeddingReport.apiName, "ini-panel-shell-workflow-overview-embedding-report", "workflow overview embedding report API name");
|
||||
assertEqual(embeddingReport.ready, false, "workflow overview embedding report readiness");
|
||||
assertEqual(embeddingReport.renderRowCount, 8, "workflow overview embedding report row count");
|
||||
assertEqual(embeddingReport.rows.length, 6, "workflow overview embedding report rows");
|
||||
assertEqual(embeddingDisplay.title, "Workflow overview embedding", "workflow overview embedding display title");
|
||||
assertEqual(embeddingDisplay.statusText, "Blocked", "workflow overview embedding display status");
|
||||
assertEqual(embeddingDisplay.rows.length, 6, "workflow overview embedding display rows");
|
||||
assertEqual(embeddingRenderState.statusLine, "Blocked: embedding:blocked, mount:blocked", "workflow overview embedding render-state status");
|
||||
assertEqual(embeddingRenderState.dataset.handoffScope, "workflow-overview-embedding", "workflow overview embedding render-state scope");
|
||||
assertEqual(embeddingDomContract.selectors.status, "[data-workflow-overview-embedding-status]", "workflow overview embedding DOM contract status selector");
|
||||
assertEqual(embeddingDomContract.renderResultFields.includes("rowIds"), true, "workflow overview embedding DOM contract render fields");
|
||||
assertEqual(embeddingDomReadiness.ready, false, "workflow overview embedding DOM readiness");
|
||||
assertEqual(embeddingDomReadiness.missing.join(","), "mount,status,rows", "workflow overview embedding DOM readiness missing");
|
||||
const embeddingMount = workflowDoc.createElement("section");
|
||||
embeddingMount.dataset.workflowOverviewEmbedding = "";
|
||||
const embeddingStatus = workflowDoc.createElement("p");
|
||||
embeddingStatus.dataset.workflowOverviewEmbeddingStatus = "";
|
||||
const embeddingRows = workflowDoc.createElement("dl");
|
||||
embeddingRows.dataset.workflowOverviewEmbeddingRows = "";
|
||||
embeddingMount.append(embeddingStatus, embeddingRows);
|
||||
workflowDoc.body.append(embeddingMount);
|
||||
const embeddingRenderResult = workflowApi.renderWorkflowOverviewEmbeddingState(embeddingRenderState);
|
||||
const embeddingMountState = workflowApi.mountWorkflowOverviewEmbeddingState({
|
||||
renderState: embeddingRenderState,
|
||||
});
|
||||
const embeddingMountDisplay = workflowApi.getWorkflowOverviewEmbeddingMountDisplayViewModel(
|
||||
embeddingMountState,
|
||||
);
|
||||
const embeddingMountRenderState = workflowApi.getWorkflowOverviewEmbeddingMountRenderState(
|
||||
embeddingMountDisplay,
|
||||
);
|
||||
const embeddingMountDomContract = workflowApi.getWorkflowOverviewEmbeddingMountDomContract();
|
||||
const embeddingMountDomReadinessMissing = workflowApi.getWorkflowOverviewEmbeddingMountDomReadiness();
|
||||
assertEqual(embeddingRenderResult.rendered, true, "workflow overview embedding render result");
|
||||
assertEqual(embeddingRenderResult.rowCount, 6, "workflow overview embedding render row count");
|
||||
assertEqual(embeddingRenderResult.dataset.handoffScope, "workflow-overview-embedding", "workflow overview embedding render scope");
|
||||
assertEqual(embeddingStatus.textContent, embeddingRenderState.statusLine, "workflow overview embedding rendered status");
|
||||
assertEqual(embeddingMountState.apiName, "ini-panel-shell-workflow-overview-embedding-dom-mount", "workflow overview embedding mount API");
|
||||
assertEqual(embeddingMountState.ready, false, "workflow overview embedding mount readiness");
|
||||
assertEqual(embeddingMountState.renderResult.rowCount, 6, "workflow overview embedding mount row count");
|
||||
assertEqual(embeddingMountDisplay.statusText, "Blocked", "workflow overview embedding mount display status");
|
||||
assertEqual(embeddingMountDisplay.rows.length, 7, "workflow overview embedding mount display rows");
|
||||
assertEqual(embeddingMountRenderState.statusLine, "Blocked: mount:blocked", "workflow overview embedding mount render status");
|
||||
assertEqual(embeddingMountRenderState.rows.length, 7, "workflow overview embedding mount render rows");
|
||||
assertEqual(embeddingMountDomContract.selectors.mount, "[data-workflow-overview-embedding-mount]", "workflow overview embedding mount DOM selector");
|
||||
assertEqual(embeddingMountDomContract.rowDatasetKeys.term[0], "workflowOverviewEmbeddingMountRow", "workflow overview embedding mount row key");
|
||||
assertEqual(embeddingMountDomReadinessMissing.ready, false, "workflow overview embedding mount missing DOM readiness");
|
||||
assertEqual(embeddingMountDomReadinessMissing.missing.join(","), "mount,status,rows", "workflow overview embedding mount missing DOM nodes");
|
||||
const embeddingMountStateNode = workflowDoc.createElement("section");
|
||||
embeddingMountStateNode.dataset.workflowOverviewEmbeddingMount = "";
|
||||
const embeddingMountStatus = workflowDoc.createElement("p");
|
||||
embeddingMountStatus.dataset.workflowOverviewEmbeddingMountStatus = "";
|
||||
const embeddingMountRows = workflowDoc.createElement("dl");
|
||||
embeddingMountRows.dataset.workflowOverviewEmbeddingMountRows = "";
|
||||
embeddingMountStateNode.append(embeddingMountStatus, embeddingMountRows);
|
||||
workflowDoc.body.append(embeddingMountStateNode);
|
||||
const embeddingMountDomReadinessReady = workflowApi.getWorkflowOverviewEmbeddingMountDomReadiness();
|
||||
const embeddingMountRenderResult = workflowApi.renderWorkflowOverviewEmbeddingMountState(
|
||||
embeddingMountRenderState,
|
||||
);
|
||||
const embeddingMountDomMount = workflowApi.mountWorkflowOverviewEmbeddingMountState({
|
||||
renderState: embeddingMountRenderState,
|
||||
});
|
||||
assertEqual(embeddingMountDomReadinessReady.ready, true, "workflow overview embedding mount ready DOM readiness");
|
||||
assertEqual(embeddingMountDomReadinessReady.missing.join(","), "", "workflow overview embedding mount ready DOM missing");
|
||||
assertEqual(embeddingMountRenderResult.rendered, true, "workflow overview embedding mount render result");
|
||||
assertEqual(embeddingMountRenderResult.rowCount, 7, "workflow overview embedding mount render row count");
|
||||
assertEqual(embeddingMountRenderResult.dataset.handoffScope, "workflow-overview-embedding-mount", "workflow overview embedding mount render scope");
|
||||
assertEqual(embeddingMountDomMount.apiName, "ini-panel-shell-workflow-overview-embedding-mount-dom-mount", "workflow overview embedding mount DOM mount API");
|
||||
assertEqual(embeddingMountDomMount.ready, false, "workflow overview embedding mount DOM mount readiness");
|
||||
assertEqual(embeddingMountDomMount.renderResult.rowCount, 7, "workflow overview embedding mount DOM mount row count");
|
||||
assertEqual(embeddingMountDomMount.error, null, "workflow overview embedding mount DOM mount error");
|
||||
assertEqual(embeddingMountStatus.textContent, embeddingMountRenderState.statusLine, "workflow overview embedding mount rendered status");
|
||||
const releaseArtifactNode = workflowDoc.createElement("section");
|
||||
releaseArtifactNode.dataset.workflowOverviewReleaseReadinessArtifact = "";
|
||||
const releaseArtifactStatus = workflowDoc.createElement("p");
|
||||
releaseArtifactStatus.dataset.workflowOverviewReleaseReadinessArtifactStatus = "";
|
||||
const releaseArtifactRows = workflowDoc.createElement("dl");
|
||||
releaseArtifactRows.dataset.workflowOverviewReleaseReadinessArtifactRows = "";
|
||||
releaseArtifactNode.append(releaseArtifactStatus, releaseArtifactRows);
|
||||
workflowDoc.body.append(releaseArtifactNode);
|
||||
const releaseArtifactDomReadinessReady = workflowApi.getWorkflowOverviewReleaseReadinessArtifactDomReadiness();
|
||||
const releaseArtifactRenderResult = workflowApi.renderWorkflowOverviewReleaseReadinessArtifactState(
|
||||
releaseArtifactRenderState,
|
||||
);
|
||||
const releaseArtifactMountResult = workflowApi.mountWorkflowOverviewReleaseReadinessArtifactState({
|
||||
renderState: releaseArtifactRenderState,
|
||||
});
|
||||
const releaseArtifactValidationActionPlanNode = workflowDoc.createElement("section");
|
||||
releaseArtifactValidationActionPlanNode.dataset.workflowOverviewReleaseReadinessArtifactValidationActionPlan = "";
|
||||
const releaseArtifactValidationActionPlanStatus = workflowDoc.createElement("p");
|
||||
releaseArtifactValidationActionPlanStatus.dataset.workflowOverviewReleaseReadinessArtifactValidationActionPlanStatus = "";
|
||||
const releaseArtifactValidationActionPlanRows = workflowDoc.createElement("dl");
|
||||
releaseArtifactValidationActionPlanRows.dataset.workflowOverviewReleaseReadinessArtifactValidationActionPlanRows = "";
|
||||
releaseArtifactValidationActionPlanNode.append(
|
||||
releaseArtifactValidationActionPlanStatus,
|
||||
releaseArtifactValidationActionPlanRows,
|
||||
);
|
||||
workflowDoc.body.append(releaseArtifactValidationActionPlanNode);
|
||||
const releaseArtifactValidationActionPlanDomReadinessReady = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness();
|
||||
const releaseArtifactValidationActionPlanRenderResult = workflowApi
|
||||
.renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState(
|
||||
releaseArtifactValidationActionPlanRenderState,
|
||||
);
|
||||
const releaseArtifactValidationActionPlanMountResult = workflowApi
|
||||
.mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState({
|
||||
renderState: releaseArtifactValidationActionPlanRenderState,
|
||||
});
|
||||
const releaseArtifactJsonWorkflowNode = workflowDoc.createElement("section");
|
||||
releaseArtifactJsonWorkflowNode.dataset.workflowOverviewReleaseReadinessArtifactJsonWorkflow = "";
|
||||
const releaseArtifactJsonWorkflowStatus = workflowDoc.createElement("p");
|
||||
releaseArtifactJsonWorkflowStatus.dataset.workflowOverviewReleaseReadinessArtifactJsonWorkflowStatus = "";
|
||||
const releaseArtifactJsonWorkflowRows = workflowDoc.createElement("dl");
|
||||
releaseArtifactJsonWorkflowRows.dataset.workflowOverviewReleaseReadinessArtifactJsonWorkflowRows = "";
|
||||
releaseArtifactJsonWorkflowNode.append(releaseArtifactJsonWorkflowStatus, releaseArtifactJsonWorkflowRows);
|
||||
workflowDoc.body.append(releaseArtifactJsonWorkflowNode);
|
||||
const releaseArtifactJsonWorkflowDomReadinessReady = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness();
|
||||
const releaseArtifactJsonWorkflowRenderResult = workflowApi
|
||||
.renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState(
|
||||
releaseArtifactJsonWorkflowRenderState,
|
||||
);
|
||||
const releaseArtifactJsonWorkflowMountResult = workflowApi
|
||||
.mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState({
|
||||
renderState: releaseArtifactJsonWorkflowRenderState,
|
||||
});
|
||||
const releaseArtifactUrlWorkflow = await workflowApi.loadWorkflowOverviewReleaseReadinessArtifactUrl({
|
||||
artifactUrl: "../../tests/fixtures/project-release-readiness-ready.json",
|
||||
mount: true,
|
||||
});
|
||||
const releaseArtifactUrlWorkflowSummary = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(releaseArtifactUrlWorkflow);
|
||||
const releaseArtifactUrlWorkflowActionPlan = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(releaseArtifactUrlWorkflow);
|
||||
const releaseArtifactUrlWorkflowRenderState = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState(releaseArtifactUrlWorkflowSummary);
|
||||
const releaseArtifactUrlWorkflowNode = workflowDoc.createElement("section");
|
||||
releaseArtifactUrlWorkflowNode.dataset.workflowOverviewReleaseReadinessArtifactUrlWorkflow = "";
|
||||
const releaseArtifactUrlWorkflowStatus = workflowDoc.createElement("p");
|
||||
releaseArtifactUrlWorkflowStatus.dataset.workflowOverviewReleaseReadinessArtifactUrlWorkflowStatus = "";
|
||||
const releaseArtifactUrlWorkflowRows = workflowDoc.createElement("dl");
|
||||
releaseArtifactUrlWorkflowRows.dataset.workflowOverviewReleaseReadinessArtifactUrlWorkflowRows = "";
|
||||
releaseArtifactUrlWorkflowNode.append(releaseArtifactUrlWorkflowStatus, releaseArtifactUrlWorkflowRows);
|
||||
workflowDoc.body.append(releaseArtifactUrlWorkflowNode);
|
||||
const releaseArtifactUrlWorkflowDomReadinessReady = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness();
|
||||
const releaseArtifactUrlWorkflowRenderResult = workflowApi
|
||||
.renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState(
|
||||
releaseArtifactUrlWorkflowRenderState,
|
||||
);
|
||||
const releaseArtifactUrlWorkflowMountResult = workflowApi
|
||||
.mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState({
|
||||
renderState: releaseArtifactUrlWorkflowRenderState,
|
||||
});
|
||||
const releaseArtifactGateExecutionSummary = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel(releaseArtifactValidation);
|
||||
const releaseArtifactGateExecutionSummaryRenderState = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(
|
||||
releaseArtifactGateExecutionSummary,
|
||||
);
|
||||
const releaseArtifactGateExecutionSummaryNode = workflowDoc.createElement("section");
|
||||
releaseArtifactGateExecutionSummaryNode.dataset.workflowOverviewReleaseReadinessArtifactGateExecutionSummary = "";
|
||||
const releaseArtifactGateExecutionSummaryStatus = workflowDoc.createElement("p");
|
||||
releaseArtifactGateExecutionSummaryStatus.dataset.workflowOverviewReleaseReadinessArtifactGateExecutionSummaryStatus = "";
|
||||
const releaseArtifactGateExecutionSummaryRows = workflowDoc.createElement("dl");
|
||||
releaseArtifactGateExecutionSummaryRows.dataset.workflowOverviewReleaseReadinessArtifactGateExecutionSummaryRows = "";
|
||||
releaseArtifactGateExecutionSummaryNode.append(
|
||||
releaseArtifactGateExecutionSummaryStatus,
|
||||
releaseArtifactGateExecutionSummaryRows,
|
||||
);
|
||||
workflowDoc.body.append(releaseArtifactGateExecutionSummaryNode);
|
||||
const releaseArtifactGateExecutionSummaryDomReadinessReady = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness();
|
||||
const releaseArtifactGateExecutionSummaryRenderResult = workflowApi
|
||||
.renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState(
|
||||
releaseArtifactGateExecutionSummaryRenderState,
|
||||
);
|
||||
const releaseArtifactGateExecutionSummaryMountResult = workflowApi
|
||||
.mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState({
|
||||
renderState: releaseArtifactGateExecutionSummaryRenderState,
|
||||
});
|
||||
const releaseArtifactUrlGateExecutionSummary = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel(releaseArtifactUrlWorkflow);
|
||||
assertEqual(releaseArtifactDomReadinessReady.ready, true, "workflow overview release artifact DOM readiness");
|
||||
assertEqual(releaseArtifactRenderResult.rendered, true, "workflow overview release artifact render result");
|
||||
assertEqual(releaseArtifactRenderResult.rowCount, 8, "workflow overview release artifact render row count");
|
||||
assertEqual(
|
||||
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
|
||||
true,
|
||||
"workflow overview release artifact execution manifest row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactRenderResult.rowIds.includes("gate-execution-summary"),
|
||||
true,
|
||||
"workflow overview release artifact execution summary row",
|
||||
);
|
||||
assertEqual(releaseArtifactMountResult.ready, true, "workflow overview release artifact mount readiness");
|
||||
assertEqual(releaseArtifactMountResult.renderResult.rowCount, 8, "workflow overview release artifact mount rows");
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowDomReadinessMissing.missing.join(","),
|
||||
"mount,status,rows",
|
||||
"workflow overview release artifact JSON workflow missing DOM readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowDomReadinessReady.ready,
|
||||
true,
|
||||
"workflow overview release artifact JSON workflow DOM readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowRenderResult.rowCount,
|
||||
5,
|
||||
"workflow overview release artifact JSON workflow render rows",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowRenderResult.rowIds.includes("gate-execution-summary"),
|
||||
true,
|
||||
"workflow overview release artifact JSON workflow gate row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowMountResult.ready,
|
||||
true,
|
||||
"workflow overview release artifact JSON workflow mount readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowStatus.textContent,
|
||||
"Ready: Artifact JSON workflow complete",
|
||||
"workflow overview release artifact JSON workflow rendered status",
|
||||
);
|
||||
assertEqual(releaseArtifactUrlWorkflow.ready, true, "workflow overview release artifact URL workflow readiness");
|
||||
assertEqual(releaseArtifactUrlWorkflow.httpStatus, 200, "workflow overview release artifact URL workflow status");
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflow.validationSummaryViewModel.statusLine,
|
||||
"Ready: Artifact evidence complete",
|
||||
"workflow overview release artifact URL workflow summary",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowSummary.statusLine,
|
||||
"Ready: Artifact URL workflow complete",
|
||||
"workflow overview release artifact URL workflow summary view model",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowRenderState.dataset.handoffScope,
|
||||
"workflow-overview-release-readiness-artifact-url-workflow",
|
||||
"workflow overview release artifact URL workflow render scope",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowDomReadinessReady.ready,
|
||||
true,
|
||||
"workflow overview release artifact URL workflow DOM readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowCount,
|
||||
8,
|
||||
"workflow overview release artifact URL workflow render rows",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowRenderResult.rowIds.includes("mount-result"),
|
||||
true,
|
||||
"workflow overview release artifact URL workflow mount row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowMountResult.ready,
|
||||
true,
|
||||
"workflow overview release artifact URL workflow DOM mount readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowStatus.textContent,
|
||||
"Ready: Artifact URL workflow complete",
|
||||
"workflow overview release artifact URL workflow rendered status",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflow.statusLine,
|
||||
"Ready: Artifact JSON workflow complete",
|
||||
"workflow overview release artifact JSON workflow",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowSummary.statusLine,
|
||||
"Ready: Artifact JSON workflow complete",
|
||||
"workflow overview release artifact JSON workflow summary",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowActionPlan.shellScript,
|
||||
"# Project release readiness artifact validation is already ready.\n",
|
||||
"workflow overview release artifact JSON workflow action plan",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflowRenderState.dataset.handoffScope,
|
||||
"workflow-overview-release-readiness-artifact-json-workflow",
|
||||
"workflow overview release artifact JSON workflow render state scope",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflow.gateExecutionSummaryViewModel.statusLine,
|
||||
"Ready: All release gate evidence present",
|
||||
"workflow overview release artifact JSON workflow gate summary",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactJsonWorkflow.actionPlan.nextCommand,
|
||||
null,
|
||||
"workflow overview release artifact JSON workflow action plan",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlWorkflowActionPlan.shellScript,
|
||||
"# Project release readiness artifact URL workflow is already ready.\n",
|
||||
"workflow overview release artifact URL workflow action plan",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactValidationActionPlan.shellScript,
|
||||
"# Project release readiness artifact validation is already ready.\n",
|
||||
"workflow overview release artifact validation action plan",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactValidationActionPlanRenderState.statusLine,
|
||||
"Ready: Artifact validation action plan complete",
|
||||
"workflow overview release artifact validation action-plan render state",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactValidationActionPlanDomReadinessReady.ready,
|
||||
true,
|
||||
"workflow overview release artifact validation action-plan DOM readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactValidationActionPlanRenderResult.rowIds.includes("next-command"),
|
||||
true,
|
||||
"workflow overview release artifact validation action-plan next-command row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactValidationActionPlanMountResult.ready,
|
||||
true,
|
||||
"workflow overview release artifact validation action-plan mount readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactValidationActionPlanStatus.textContent,
|
||||
"Ready: Artifact validation action plan complete",
|
||||
"workflow overview release artifact validation action-plan rendered status",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactGateExecutionSummary.statusLine,
|
||||
"Ready: All release gate evidence present",
|
||||
"workflow overview release artifact gate execution summary",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactGateExecutionSummaryRenderState.rows.length,
|
||||
17,
|
||||
"workflow overview release artifact gate execution summary render rows",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactGateExecutionSummaryDomReadinessReady.ready,
|
||||
true,
|
||||
"workflow overview release artifact gate execution summary DOM readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactGateExecutionSummaryRenderResult.rowIds.includes("gate-ui-node-smokes"),
|
||||
true,
|
||||
"workflow overview release artifact gate execution summary SDK row",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactGateExecutionSummaryMountResult.ready,
|
||||
true,
|
||||
"workflow overview release artifact gate execution summary mount readiness",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactGateExecutionSummaryStatus.textContent,
|
||||
"Ready: All release gate evidence present",
|
||||
"workflow overview release artifact gate execution summary rendered status",
|
||||
);
|
||||
assertEqual(
|
||||
releaseArtifactUrlGateExecutionSummary.statusLine,
|
||||
"Ready: All release gate evidence present",
|
||||
"workflow overview release artifact URL gate execution summary",
|
||||
);
|
||||
assertEqual(releaseArtifactUrlWorkflow.mountResult.ready, true, "workflow overview release artifact URL workflow mount");
|
||||
assertEqual(releaseArtifactStatus.textContent, releaseArtifactRenderState.statusLine, "workflow overview release artifact rendered status");
|
||||
assertEqual(
|
||||
workflowDoc.querySelector("[data-handoff-workflow]").dataset.handoffScope,
|
||||
"workflow",
|
||||
"workflow overview DOM scope",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector("[data-handoff-workflow-status]").textContent,
|
||||
renderState.statusLine,
|
||||
"workflow overview DOM status line",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector('[data-handoff-workflow-value="blocking-reasons"]').textContent.includes("readonly-status"),
|
||||
true,
|
||||
"workflow overview DOM blocking reasons row",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector('[data-handoff-workflow-value="api-surface-inventory"]').textContent,
|
||||
"ready: 6 rows",
|
||||
"workflow overview DOM API surface inventory row",
|
||||
);
|
||||
assertEqual(
|
||||
workflowDoc.querySelector('[data-handoff-workflow-value="persistence-summary"]').textContent,
|
||||
"waiting: 5 missing",
|
||||
"workflow overview DOM persistence summary row",
|
||||
);
|
||||
|
||||
document.getElementById("status").textContent = "browser_ini_workflow_overview_smoke=ok";
|
||||
console.log("browser_ini_workflow_overview_smoke=ok");
|
||||
} catch (error) {
|
||||
document.getElementById("status").textContent = `browser_ini_workflow_overview_smoke=failed: ${error.message}`;
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
203
wasm-port/tests/browser/opfs_session_workflow_smoke.html
Normal file
203
wasm-port/tests/browser/opfs_session_workflow_smoke.html
Normal file
@@ -0,0 +1,203 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>LinuxCNC OPFS Session Workflow Smoke</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="status">running</pre>
|
||||
<script type="module">
|
||||
import {
|
||||
createLinuxCncIniSdk,
|
||||
createLinuxCncInterpSdk,
|
||||
createMachineSessionPersistenceSummary,
|
||||
gcodeProgramPath,
|
||||
loadGcodeProgram,
|
||||
loadMachineSessionFromOpfs,
|
||||
loadMachineSessionSnapshot,
|
||||
loadMachineTextFiles,
|
||||
machineFilePaths,
|
||||
readMachineSessionReadiness,
|
||||
saveGcodeProgram,
|
||||
saveMachineSessionSnapshot,
|
||||
saveMachineTextFiles,
|
||||
} from "../../runtime/sdk/src/index.js";
|
||||
|
||||
const status = document.getElementById("status");
|
||||
|
||||
function assertEqual(actual, expected, label) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(`${label}: expected ${expected}, got ${actual}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertIncludes(values, expected, label) {
|
||||
if (!values.includes(expected)) {
|
||||
throw new Error(`${label}: expected ${expected} in ${values.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const machineId = "browser-opfs-session-gate";
|
||||
const sessionId = "browser-opfs-session-gate-session";
|
||||
const snapshotFilename = "browser-opfs-session-gate.json";
|
||||
const gcodeFilename = "browser-opfs-session-gate.ngc";
|
||||
const gcodeOpfsPath = gcodeProgramPath(gcodeFilename);
|
||||
const paths = machineFilePaths(machineId);
|
||||
const iniText = `[EMC]
|
||||
MACHINE = browser-opfs-session-gate
|
||||
|
||||
[TRAJ]
|
||||
LINEAR_UNITS = mm
|
||||
COORDINATES = X Y Z
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = trivkins
|
||||
JOINTS = 3
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = linuxcnc.var
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = tool.tbl
|
||||
`;
|
||||
const parameterText = [
|
||||
"5161 0.0",
|
||||
"5162 0.0",
|
||||
"5220 1",
|
||||
"5221 0.0",
|
||||
"5399 0.0",
|
||||
"",
|
||||
].join("\n");
|
||||
const toolTableText = "T1 P1 Z0.0 D0.125 ;browser opfs session gate\n";
|
||||
const gcodeText = "G0 X0 Y0\nG1 X1 Y1 F20\nM2\n";
|
||||
|
||||
const initialReadiness = await readMachineSessionReadiness(machineId, {
|
||||
gcodeFilename,
|
||||
sessionId,
|
||||
snapshotFilename,
|
||||
});
|
||||
assertEqual(initialReadiness.ready, false, "initial readiness");
|
||||
assertIncludes(initialReadiness.missing, "ini", "initial missing INI");
|
||||
assertIncludes(initialReadiness.missing, "parameters", "initial missing parameters");
|
||||
assertIncludes(initialReadiness.missing, "toolTable", "initial missing tool table");
|
||||
assertIncludes(initialReadiness.missing, "gcode", "initial missing G-code");
|
||||
assertIncludes(initialReadiness.missing, "snapshot", "initial missing snapshot");
|
||||
|
||||
await saveMachineTextFiles(machineId, {
|
||||
ini: iniText,
|
||||
parameters: parameterText,
|
||||
toolTable: toolTableText,
|
||||
});
|
||||
await saveGcodeProgram(gcodeFilename, gcodeText);
|
||||
const snapshot = await saveMachineSessionSnapshot(sessionId, machineId, {
|
||||
filename: snapshotFilename,
|
||||
gcodeFilename,
|
||||
createdAt: "2026-06-16T00:00:00.000Z",
|
||||
metadata: {
|
||||
workflow: "opfs-session-workflow-browser-gate",
|
||||
defaultGcodeOpfsPath: gcodeOpfsPath,
|
||||
snapshotLabel: `${sessionId}/${snapshotFilename}`,
|
||||
},
|
||||
});
|
||||
|
||||
const files = await loadMachineTextFiles(machineId);
|
||||
assertEqual(files.ini, iniText, "loaded INI text");
|
||||
assertEqual(files.parameters, parameterText, "loaded parameter text");
|
||||
assertEqual(files.toolTable, toolTableText, "loaded tool table text");
|
||||
assertEqual(await loadGcodeProgram(gcodeFilename), gcodeText, "loaded G-code text");
|
||||
assertEqual(snapshot.payload.files.ini, paths.ini, "snapshot INI path");
|
||||
assertEqual(snapshot.payload.files.parameters, paths.parameters, "snapshot parameter path");
|
||||
assertEqual(snapshot.payload.files.toolTable, paths.toolTable, "snapshot tool table path");
|
||||
assertEqual(snapshot.payload.files.gcode, gcodeOpfsPath, "snapshot G-code path");
|
||||
assertEqual(
|
||||
(await loadMachineSessionSnapshot(sessionId, machineId, { filename: snapshotFilename }))
|
||||
.metadata.workflow,
|
||||
"opfs-session-workflow-browser-gate",
|
||||
"loaded snapshot workflow metadata",
|
||||
);
|
||||
|
||||
const readyReadiness = await readMachineSessionReadiness(machineId, {
|
||||
gcodeFilename,
|
||||
sessionId,
|
||||
snapshotFilename,
|
||||
});
|
||||
assertEqual(readyReadiness.ready, true, "ready readiness");
|
||||
assertEqual(readyReadiness.phase, "ready", "ready phase");
|
||||
assertEqual(readyReadiness.missing.length, 0, "ready missing count");
|
||||
assertEqual(readyReadiness.checks.length, 4, "ready file check count");
|
||||
assertEqual(readyReadiness.snapshotCheck.ok, true, "ready snapshot check");
|
||||
|
||||
const iniSdk = await createLinuxCncIniSdk();
|
||||
const interpSdk = await createLinuxCncInterpSdk();
|
||||
const loadedSession = await loadMachineSessionFromOpfs(interpSdk, machineId, {
|
||||
iniSdk,
|
||||
iniWasmPath: "/work/opfs-session-workflow.ini",
|
||||
parameterWasmPath: "/work/opfs-session-workflow.var",
|
||||
toolTableWasmPath: "/work/opfs-session-workflow.tbl",
|
||||
});
|
||||
assertEqual(loadedSession.machineId, machineId, "loaded session machine");
|
||||
assertEqual(loadedSession.ini.opfsPath, paths.ini, "loaded session INI OPFS path");
|
||||
assertEqual(loadedSession.ini.wasmPath, "/work/opfs-session-workflow.ini", "loaded session INI WASM path");
|
||||
assertEqual(loadedSession.parameters.opfsPath, paths.parameters, "loaded session parameter OPFS path");
|
||||
assertEqual(loadedSession.parameters.wasmPath, "/work/opfs-session-workflow.var", "loaded session parameter WASM path");
|
||||
assertEqual(loadedSession.toolTable.opfsPath, paths.toolTable, "loaded session tool table OPFS path");
|
||||
assertEqual(loadedSession.toolTable.wasmPath, "/work/opfs-session-workflow.tbl", "loaded session tool table WASM path");
|
||||
|
||||
const persistenceSummary = createMachineSessionPersistenceSummary({
|
||||
state: {
|
||||
badges: { opfs: "OPFS: ready" },
|
||||
machineFiles: {
|
||||
iniOpfsPath: paths.ini,
|
||||
parameterOpfsPath: paths.parameters,
|
||||
toolTableOpfsPath: paths.toolTable,
|
||||
gcodeOpfsPath,
|
||||
},
|
||||
sessionSnapshot: {
|
||||
snapshotLabel: `${sessionId}/${snapshotFilename}`,
|
||||
iniOpfsPath: paths.ini,
|
||||
parameterOpfsPath: paths.parameters,
|
||||
toolTableOpfsPath: paths.toolTable,
|
||||
gcodeOpfsPath,
|
||||
workflow: "opfs-session-workflow-browser-gate",
|
||||
},
|
||||
sessionReadiness: readyReadiness,
|
||||
sessionLoad: {
|
||||
iniWasmPath: loadedSession.ini.wasmPath,
|
||||
parameterWasmPath: loadedSession.parameters.wasmPath,
|
||||
toolTableWasmPath: loadedSession.toolTable.wasmPath,
|
||||
},
|
||||
},
|
||||
readonlyStatus: { ready: true },
|
||||
handoffWorkflowSummary: { ready: true },
|
||||
});
|
||||
assertEqual(persistenceSummary.apiName, "machine-session-persistence-summary", "summary API");
|
||||
assertEqual(persistenceSummary.ready, true, "summary ready");
|
||||
assertEqual(persistenceSummary.counts.machineFiles, 4, "summary machine file count");
|
||||
assertEqual(persistenceSummary.missing.length, 0, "summary missing count");
|
||||
assertEqual(
|
||||
persistenceSummary.rows.find(({ id }) => id === "session-readiness")?.value,
|
||||
"ready",
|
||||
"summary readiness row",
|
||||
);
|
||||
assertEqual(
|
||||
persistenceSummary.rows.find(({ id }) => id === "session-load")?.value,
|
||||
"loaded: /work/opfs-session-workflow.ini",
|
||||
"summary load row",
|
||||
);
|
||||
assertEqual(
|
||||
persistenceSummary.rows.find(({ id }) => id === "handoff-readiness")?.value,
|
||||
"ready",
|
||||
"summary handoff row",
|
||||
);
|
||||
|
||||
status.textContent = "browser_opfs_session_workflow_smoke=ok";
|
||||
console.log("browser_opfs_session_workflow_smoke=ok");
|
||||
} catch (error) {
|
||||
status.textContent = `browser_opfs_session_workflow_smoke=failed: ${error.message}`;
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
83
wasm-port/tests/browser/real_simulation_page_smoke.html
Normal file
83
wasm-port/tests/browser/real_simulation_page_smoke.html
Normal file
@@ -0,0 +1,83 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>LinuxCNC Real Simulation Page Smoke</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="status">running</pre>
|
||||
<script type="module">
|
||||
const status = document.getElementById("status");
|
||||
|
||||
async function loadFrame(src) {
|
||||
const frame = document.createElement("iframe");
|
||||
frame.src = src;
|
||||
frame.width = "1280";
|
||||
frame.height = "800";
|
||||
document.body.appendChild(frame);
|
||||
await new Promise((resolve, reject) => {
|
||||
frame.addEventListener("load", resolve, { once: true });
|
||||
frame.addEventListener("error", reject, { once: true });
|
||||
});
|
||||
return frame;
|
||||
}
|
||||
|
||||
async function waitForState(frame) {
|
||||
for (let i = 0; i < 200; i += 1) {
|
||||
const state = frame.contentWindow?.linuxCncRealSimulationState;
|
||||
if (state?.summary?.ready) {
|
||||
return state;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
throw new Error("real simulation page did not expose ready state");
|
||||
}
|
||||
|
||||
try {
|
||||
const frame = await loadFrame("../../runtime/ui/simulation/index.html");
|
||||
const doc = frame.contentDocument;
|
||||
const state = await waitForState(frame);
|
||||
const polyline = doc.querySelector("[data-toolpath-polyline]");
|
||||
const rows = [...doc.querySelectorAll("[data-motion-row]")];
|
||||
const programRows = [...doc.querySelectorAll("[data-program-line]")];
|
||||
const canonicalOutput = doc.querySelector("[data-canonical-output]")?.textContent ?? "";
|
||||
|
||||
if (doc.body.dataset.simulationReady !== "true") {
|
||||
throw new Error("simulation body readiness flag not true");
|
||||
}
|
||||
if (state.apiName !== "real-browser-simulation-state") {
|
||||
throw new Error("simulation state API name drift");
|
||||
}
|
||||
if (state.summary.motionEventCount < 5) {
|
||||
throw new Error("simulation motion event count too low");
|
||||
}
|
||||
if (!canonicalOutput.includes("canon_event=STRAIGHT_FEED line=2 x=1 y=0 z=0")) {
|
||||
throw new Error("simulation canonical output missing LinuxCNC feed event");
|
||||
}
|
||||
if (!polyline?.getAttribute("points")?.includes("1,0")) {
|
||||
throw new Error("simulation toolpath polyline missing expected point");
|
||||
}
|
||||
if (rows.length !== state.motion.length) {
|
||||
throw new Error("simulation motion table row count drift");
|
||||
}
|
||||
if (programRows.length < 6) {
|
||||
throw new Error("simulation program rows missing");
|
||||
}
|
||||
if (doc.querySelector('[data-axis="x"]')?.textContent !== "0.000") {
|
||||
throw new Error("simulation final X axis drift");
|
||||
}
|
||||
if (doc.querySelector('[data-axis="y"]')?.textContent !== "0.000") {
|
||||
throw new Error("simulation final Y axis drift");
|
||||
}
|
||||
if (!doc.querySelector("[data-toolpath-svg]")?.getAttribute("viewBox")) {
|
||||
throw new Error("simulation SVG viewBox missing");
|
||||
}
|
||||
|
||||
status.textContent = "browser_real_simulation_page_smoke=ok";
|
||||
} catch (error) {
|
||||
status.textContent = `browser_real_simulation_page_smoke=fail ${error.stack || error.message}`;
|
||||
throw error;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
101
wasm-port/tests/browser/release_artifact_url_workflow_smoke.html
Normal file
101
wasm-port/tests/browser/release_artifact_url_workflow_smoke.html
Normal file
@@ -0,0 +1,101 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>LinuxCNC Release Artifact URL Workflow Smoke</title>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="status">running</pre>
|
||||
<script type="module">
|
||||
function assertEqual(actual, expected, label) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(`${label}: expected ${expected}, got ${actual}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadWorkflowOverviewFrame() {
|
||||
const frame = document.createElement("iframe");
|
||||
frame.src = "../../runtime/ui/ini-panel/workflow-overview.html";
|
||||
document.body.appendChild(frame);
|
||||
await new Promise((resolve, reject) => {
|
||||
frame.addEventListener("load", resolve, { once: true });
|
||||
frame.addEventListener("error", reject, { once: true });
|
||||
});
|
||||
return frame.contentDocument;
|
||||
}
|
||||
|
||||
try {
|
||||
const workflowDoc = await loadWorkflowOverviewFrame();
|
||||
const workflowApi = workflowDoc.defaultView.linuxCncIniPanelWorkflowOverviewApi;
|
||||
if (
|
||||
!workflowApi?.loadWorkflowOverviewReleaseReadinessArtifactUrl ||
|
||||
!workflowApi?.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel
|
||||
) {
|
||||
throw new Error("workflow overview API did not expose release artifact URL workflow");
|
||||
}
|
||||
|
||||
const missingUrl = await workflowApi.loadWorkflowOverviewReleaseReadinessArtifactUrl();
|
||||
assertEqual(missingUrl.ready, false, "missing URL workflow readiness");
|
||||
assertEqual(missingUrl.statusLine, "Blocked: artifact-url", "missing URL status");
|
||||
|
||||
const missingDom = await workflowApi.loadWorkflowOverviewReleaseReadinessArtifactUrl({
|
||||
artifactUrl: "../../tests/fixtures/project-release-readiness-ready.json",
|
||||
mount: true,
|
||||
});
|
||||
assertEqual(missingDom.ready, false, "missing DOM workflow readiness");
|
||||
assertEqual(missingDom.validation.ready, true, "missing DOM validation readiness");
|
||||
assertEqual(missingDom.missing.join(","), "mount,status,rows", "missing DOM reasons");
|
||||
|
||||
const releaseArtifactNode = workflowDoc.createElement("section");
|
||||
releaseArtifactNode.dataset.workflowOverviewReleaseReadinessArtifact = "";
|
||||
const releaseArtifactStatus = workflowDoc.createElement("p");
|
||||
releaseArtifactStatus.dataset.workflowOverviewReleaseReadinessArtifactStatus = "";
|
||||
const releaseArtifactRows = workflowDoc.createElement("dl");
|
||||
releaseArtifactRows.dataset.workflowOverviewReleaseReadinessArtifactRows = "";
|
||||
releaseArtifactNode.append(releaseArtifactStatus, releaseArtifactRows);
|
||||
workflowDoc.body.append(releaseArtifactNode);
|
||||
|
||||
const readyWorkflow = await workflowApi.loadWorkflowOverviewReleaseReadinessArtifactUrl({
|
||||
artifactUrl: "../../tests/fixtures/project-release-readiness-ready.json",
|
||||
mount: true,
|
||||
});
|
||||
const readyWorkflowSummary = workflowApi
|
||||
.getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(readyWorkflow);
|
||||
assertEqual(
|
||||
readyWorkflow.apiName,
|
||||
"ini-panel-shell-workflow-overview-release-readiness-artifact-url-workflow",
|
||||
"ready workflow API name",
|
||||
);
|
||||
assertEqual(readyWorkflow.ready, true, "ready workflow readiness");
|
||||
assertEqual(readyWorkflow.httpStatus, 200, "ready workflow HTTP status");
|
||||
assertEqual(readyWorkflow.validation.gateCount, 12, "ready workflow gate count");
|
||||
assertEqual(
|
||||
readyWorkflow.validationSummaryViewModel.statusLine,
|
||||
"Ready: Artifact evidence complete",
|
||||
"ready workflow validation summary",
|
||||
);
|
||||
assertEqual(
|
||||
readyWorkflowSummary.statusLine,
|
||||
"Ready: Artifact URL workflow complete",
|
||||
"ready workflow summary view model",
|
||||
);
|
||||
assertEqual(readyWorkflow.renderState.statusLine, "Ready: No blocking reasons", "ready workflow status");
|
||||
assertEqual(readyWorkflow.mountResult.ready, true, "ready workflow mount readiness");
|
||||
assertEqual(readyWorkflow.mountResult.renderResult.rowCount, 6, "ready workflow mount rows");
|
||||
assertEqual(
|
||||
releaseArtifactStatus.textContent,
|
||||
"Ready: No blocking reasons",
|
||||
"ready workflow rendered status",
|
||||
);
|
||||
|
||||
document.getElementById("status").textContent = "browser_release_artifact_url_workflow_smoke=ok";
|
||||
console.log("browser_release_artifact_url_workflow_smoke=ok");
|
||||
} catch (error) {
|
||||
document.getElementById("status").textContent =
|
||||
`browser_release_artifact_url_workflow_smoke=failed: ${error.message}`;
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -63,9 +63,13 @@ PORT="$(cat "$PORT_FILE")"
|
||||
URL="http://127.0.0.1:$PORT/tests/browser/ini_panel_smoke.html"
|
||||
CONTROL_URL="http://127.0.0.1:$PORT/tests/browser/ini_panel_control_page_smoke.html"
|
||||
LAUNCH_URL="http://127.0.0.1:$PORT/tests/browser/ini_panel_launch_smoke.html"
|
||||
WORKFLOW_OVERVIEW_URL="http://127.0.0.1:$PORT/tests/browser/ini_panel_workflow_overview_smoke.html"
|
||||
SHELL_INTEGRATION_URL="http://127.0.0.1:$PORT/tests/browser/ini_panel_shell_integration_workflow_smoke.html"
|
||||
OUT="$TMP_DIR/chromium.out"
|
||||
CONTROL_OUT="$TMP_DIR/chromium-control.out"
|
||||
LAUNCH_OUT="$TMP_DIR/chromium-launch.out"
|
||||
WORKFLOW_OVERVIEW_OUT="$TMP_DIR/chromium-workflow-overview.out"
|
||||
SHELL_INTEGRATION_OUT="$TMP_DIR/chromium-shell-integration.out"
|
||||
|
||||
"$CHROMIUM" \
|
||||
--headless=new \
|
||||
@@ -117,3 +121,37 @@ if ! grep -Fq "browser_ini_launch_smoke=ok" "$LAUNCH_OUT"; then
|
||||
fi
|
||||
|
||||
echo "browser_ini_launch_smoke=ok"
|
||||
|
||||
"$CHROMIUM" \
|
||||
--headless=new \
|
||||
--disable-gpu \
|
||||
--no-sandbox \
|
||||
--user-data-dir="$CHROME_PROFILE" \
|
||||
--virtual-time-budget=10000 \
|
||||
--dump-dom \
|
||||
"$WORKFLOW_OVERVIEW_URL" >"$WORKFLOW_OVERVIEW_OUT" 2>&1
|
||||
|
||||
if ! grep -Fq "browser_ini_workflow_overview_smoke=ok" "$WORKFLOW_OVERVIEW_OUT"; then
|
||||
echo "browser INI workflow overview smoke failed" >&2
|
||||
sed -n '1,220p' "$WORKFLOW_OVERVIEW_OUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "browser_ini_workflow_overview_smoke=ok"
|
||||
|
||||
"$CHROMIUM" \
|
||||
--headless=new \
|
||||
--disable-gpu \
|
||||
--no-sandbox \
|
||||
--user-data-dir="$CHROME_PROFILE" \
|
||||
--virtual-time-budget=10000 \
|
||||
--dump-dom \
|
||||
"$SHELL_INTEGRATION_URL" >"$SHELL_INTEGRATION_OUT" 2>&1
|
||||
|
||||
if ! grep -Fq "browser_ini_shell_integration_workflow_smoke=ok" "$SHELL_INTEGRATION_OUT"; then
|
||||
echo "browser INI shell integration workflow smoke failed" >&2
|
||||
sed -n '1,220p' "$SHELL_INTEGRATION_OUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "browser_ini_shell_integration_workflow_smoke=ok"
|
||||
|
||||
81
wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh
Executable file
81
wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CHROMIUM="${CHROMIUM:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}"
|
||||
|
||||
if [[ -z "$CHROMIUM" ]]; then
|
||||
echo "missing Chromium-compatible browser; set CHROMIUM=/path/to/browser" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${SKIP_INI_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_ini_panel.sh"
|
||||
fi
|
||||
if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_wasm_core.sh"
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
PORT_FILE="$TMP_DIR/port"
|
||||
SERVER_LOG="$TMP_DIR/server.log"
|
||||
CHROME_PROFILE="$TMP_DIR/chrome-profile"
|
||||
mkdir -p "$CHROME_PROFILE"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${SERVER_PID:-}" ]]; then
|
||||
kill "$SERVER_PID" 2>/dev/null || true
|
||||
wait "$SERVER_PID" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
python3 - <<'PY' "$ROOT_DIR" "$PORT_FILE" >"$SERVER_LOG" 2>&1 &
|
||||
import functools
|
||||
import http.server
|
||||
import pathlib
|
||||
import socketserver
|
||||
import sys
|
||||
|
||||
root = pathlib.Path(sys.argv[1])
|
||||
port_file = pathlib.Path(sys.argv[2])
|
||||
|
||||
handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(root))
|
||||
with socketserver.TCPServer(("127.0.0.1", 0), handler) as httpd:
|
||||
port_file.write_text(str(httpd.server_address[1]), encoding="ascii")
|
||||
httpd.serve_forever()
|
||||
PY
|
||||
SERVER_PID=$!
|
||||
|
||||
for _ in $(seq 1 100); do
|
||||
[[ -s "$PORT_FILE" ]] && break
|
||||
sleep 0.05
|
||||
done
|
||||
|
||||
if [[ ! -s "$PORT_FILE" ]]; then
|
||||
echo "OPFS/session workflow browser smoke HTTP server did not start" >&2
|
||||
cat "$SERVER_LOG" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PORT="$(cat "$PORT_FILE")"
|
||||
URL="http://127.0.0.1:$PORT/tests/browser/opfs_session_workflow_smoke.html"
|
||||
OUT="$TMP_DIR/chromium.out"
|
||||
|
||||
"$CHROMIUM" \
|
||||
--headless=new \
|
||||
--disable-gpu \
|
||||
--no-sandbox \
|
||||
--user-data-dir="$CHROME_PROFILE" \
|
||||
--virtual-time-budget=10000 \
|
||||
--dump-dom \
|
||||
"$URL" >"$OUT" 2>&1
|
||||
|
||||
if ! grep -Fq "browser_opfs_session_workflow_smoke=ok" "$OUT"; then
|
||||
echo "OPFS/session workflow browser smoke failed" >&2
|
||||
sed -n '1,220p' "$OUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "browser_opfs_session_workflow_smoke=ok"
|
||||
78
wasm-port/tests/browser/verify_real_simulation_browser.sh
Executable file
78
wasm-port/tests/browser/verify_real_simulation_browser.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CHROMIUM="${CHROMIUM:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}"
|
||||
|
||||
if [[ -z "$CHROMIUM" ]]; then
|
||||
echo "missing Chromium-compatible browser; set CHROMIUM=/path/to/browser" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_wasm_core.sh"
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
PORT_FILE="$TMP_DIR/port"
|
||||
SERVER_LOG="$TMP_DIR/server.log"
|
||||
CHROME_PROFILE="$TMP_DIR/chrome-profile"
|
||||
mkdir -p "$CHROME_PROFILE"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${SERVER_PID:-}" ]]; then
|
||||
kill "$SERVER_PID" 2>/dev/null || true
|
||||
wait "$SERVER_PID" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
python3 - <<'PY' "$ROOT_DIR" "$PORT_FILE" >"$SERVER_LOG" 2>&1 &
|
||||
import functools
|
||||
import http.server
|
||||
import pathlib
|
||||
import socketserver
|
||||
import sys
|
||||
|
||||
root = pathlib.Path(sys.argv[1])
|
||||
port_file = pathlib.Path(sys.argv[2])
|
||||
|
||||
handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(root))
|
||||
with socketserver.TCPServer(("127.0.0.1", 0), handler) as httpd:
|
||||
port_file.write_text(str(httpd.server_address[1]), encoding="ascii")
|
||||
httpd.serve_forever()
|
||||
PY
|
||||
SERVER_PID=$!
|
||||
|
||||
for _ in $(seq 1 100); do
|
||||
[[ -s "$PORT_FILE" ]] && break
|
||||
sleep 0.05
|
||||
done
|
||||
|
||||
if [[ ! -s "$PORT_FILE" ]]; then
|
||||
echo "real simulation browser smoke HTTP server did not start" >&2
|
||||
cat "$SERVER_LOG" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PORT="$(cat "$PORT_FILE")"
|
||||
URL="http://127.0.0.1:$PORT/tests/browser/real_simulation_page_smoke.html"
|
||||
OUT="$TMP_DIR/chromium-real-simulation.out"
|
||||
|
||||
"$CHROMIUM" \
|
||||
--headless=new \
|
||||
--disable-gpu \
|
||||
--no-sandbox \
|
||||
--user-data-dir="$CHROME_PROFILE" \
|
||||
--virtual-time-budget=10000 \
|
||||
--dump-dom \
|
||||
"$URL" >"$OUT" 2>&1
|
||||
|
||||
if ! grep -Fq "browser_real_simulation_page_smoke=ok" "$OUT"; then
|
||||
echo "browser real simulation page smoke failed" >&2
|
||||
sed -n '1,260p' "$OUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "browser_real_simulation_page_smoke=ok"
|
||||
81
wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh
Executable file
81
wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh
Executable file
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CHROMIUM="${CHROMIUM:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}"
|
||||
|
||||
if [[ -z "$CHROMIUM" ]]; then
|
||||
echo "missing Chromium-compatible browser; set CHROMIUM=/path/to/browser" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${SKIP_INI_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_ini_panel.sh"
|
||||
fi
|
||||
if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_wasm_core.sh"
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
PORT_FILE="$TMP_DIR/port"
|
||||
SERVER_LOG="$TMP_DIR/server.log"
|
||||
CHROME_PROFILE="$TMP_DIR/chrome-profile"
|
||||
mkdir -p "$CHROME_PROFILE"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${SERVER_PID:-}" ]]; then
|
||||
kill "$SERVER_PID" 2>/dev/null || true
|
||||
wait "$SERVER_PID" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
python3 - <<'PY' "$ROOT_DIR" "$PORT_FILE" >"$SERVER_LOG" 2>&1 &
|
||||
import functools
|
||||
import http.server
|
||||
import pathlib
|
||||
import socketserver
|
||||
import sys
|
||||
|
||||
root = pathlib.Path(sys.argv[1])
|
||||
port_file = pathlib.Path(sys.argv[2])
|
||||
|
||||
handler = functools.partial(http.server.SimpleHTTPRequestHandler, directory=str(root))
|
||||
with socketserver.TCPServer(("127.0.0.1", 0), handler) as httpd:
|
||||
port_file.write_text(str(httpd.server_address[1]), encoding="ascii")
|
||||
httpd.serve_forever()
|
||||
PY
|
||||
SERVER_PID=$!
|
||||
|
||||
for _ in $(seq 1 100); do
|
||||
[[ -s "$PORT_FILE" ]] && break
|
||||
sleep 0.05
|
||||
done
|
||||
|
||||
if [[ ! -s "$PORT_FILE" ]]; then
|
||||
echo "release artifact URL workflow browser smoke HTTP server did not start" >&2
|
||||
cat "$SERVER_LOG" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PORT="$(cat "$PORT_FILE")"
|
||||
URL="http://127.0.0.1:$PORT/tests/browser/release_artifact_url_workflow_smoke.html"
|
||||
OUT="$TMP_DIR/chromium.out"
|
||||
|
||||
"$CHROMIUM" \
|
||||
--headless=new \
|
||||
--disable-gpu \
|
||||
--no-sandbox \
|
||||
--user-data-dir="$CHROME_PROFILE" \
|
||||
--virtual-time-budget=10000 \
|
||||
--dump-dom \
|
||||
"$URL" >"$OUT" 2>&1
|
||||
|
||||
if ! grep -Fq "browser_release_artifact_url_workflow_smoke=ok" "$OUT"; then
|
||||
echo "release artifact URL workflow browser smoke failed" >&2
|
||||
sed -n '1,220p' "$OUT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "browser_release_artifact_url_workflow_smoke=ok"
|
||||
100
wasm-port/tests/docs/node/verify_host_runtime_boundary_docs.mjs
Normal file
100
wasm-port/tests/docs/node/verify_host_runtime_boundary_docs.mjs
Normal file
@@ -0,0 +1,100 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
|
||||
function literalRegExp(text) {
|
||||
return new RegExp(text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
||||
}
|
||||
|
||||
function parseTsv(text) {
|
||||
const [headerLine, ...lines] = text.trim().split(/\r?\n/);
|
||||
const headers = headerLine.split("\t");
|
||||
return lines.map((line) => {
|
||||
const cells = line.split("\t");
|
||||
return Object.fromEntries(headers.map((header, index) => [header, cells[index] ?? ""]));
|
||||
});
|
||||
}
|
||||
|
||||
const docText = readFileSync(resolve(root, "docs/host-runtime-boundary-handoff.md"), "utf8");
|
||||
const fullProcessText = readFileSync(resolve(root, "docs/full-process-boundary-design.md"), "utf8");
|
||||
const simHandoffText = readFileSync(resolve(root, "docs/sim-configs-coverage-handoff.md"), "utf8");
|
||||
const inventoryTestText = readFileSync(
|
||||
resolve(root, "tests/wasm/node/verify_sim_configs_inventory_wasm.mjs"),
|
||||
"utf8",
|
||||
);
|
||||
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
|
||||
const readmeText = readFileSync(resolve(root, "README.md"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
|
||||
for (const phrase of [
|
||||
"Host/runtime boundary release handoff",
|
||||
"runtime-boundary-host-readiness-rollup.tsv",
|
||||
"runtime-boundary-promotion-blockers.tsv",
|
||||
"runtime-boundary-opt-in-probe-dispatch-rollup.tsv",
|
||||
"runtime-boundary-native-evidence-acceptance-gate.tsv",
|
||||
"blocked-runtime-promotion-lock.tsv",
|
||||
"host_readiness_status=host_blocked_for_all_opt_in_native_probes",
|
||||
"missing_host_requirements=halcmd,halrun,linuxcnc,milltask",
|
||||
"dispatch_status=dispatch_blocked_for_all_opt_in_native_probes",
|
||||
"current_probe_status=skipped_missing_host_runtime",
|
||||
"native_evidence_status=pending_until_native_pass",
|
||||
"evidence_acceptance_allowed=0",
|
||||
"ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1",
|
||||
"ENABLE_TOOL_DB_RUNTIME_PROBE=1",
|
||||
"ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1",
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
"L4-PYTHON-REMAP",
|
||||
"host_runtime_boundary_docs_node_smoke=ok",
|
||||
]) {
|
||||
assert.match(docText, literalRegExp(phrase));
|
||||
}
|
||||
|
||||
for (const phrase of [
|
||||
"runtime-boundary-host-readiness-rollup.tsv",
|
||||
"runtime-boundary-opt-in-probe-dispatch-rollup.tsv",
|
||||
"runtime-boundary-native-evidence-acceptance-gate.tsv",
|
||||
]) {
|
||||
assert.match(fullProcessText, literalRegExp(phrase));
|
||||
assert.match(inventoryTestText, literalRegExp(phrase));
|
||||
assert.match(docText, literalRegExp(phrase));
|
||||
}
|
||||
|
||||
assert.match(simHandoffText, /L4-PYTHON-REMAP/);
|
||||
assert.match(hostSmokeText, /verify_host_runtime_boundary_docs\.sh/);
|
||||
assert.match(readmeText, /docs\/host-runtime-boundary-handoff\.md/);
|
||||
assert.match(trackerText, /Host\/runtime boundary/);
|
||||
|
||||
const hostRollupPath = resolve(
|
||||
root,
|
||||
"build/wasm/sim-configs-inventory/runtime-boundary-host-readiness-rollup.tsv",
|
||||
);
|
||||
if (existsSync(hostRollupPath)) {
|
||||
const [row] = parseTsv(readFileSync(hostRollupPath, "utf8"));
|
||||
assert.equal(row.host_readiness_status, "host_blocked_for_all_opt_in_native_probes");
|
||||
assert.equal(row.family_count, "3");
|
||||
assert.equal(row.host_ready_family_count, "0");
|
||||
assert.equal(row.host_blocked_family_count, "3");
|
||||
assert.equal(row.missing_host_requirements, "halcmd,halrun,linuxcnc,milltask");
|
||||
assert.equal(row.execution_enabled, "0");
|
||||
assert.equal(row.promotion_allowed, "0");
|
||||
}
|
||||
|
||||
const dispatchRollupPath = resolve(
|
||||
root,
|
||||
"build/wasm/sim-configs-inventory/runtime-boundary-opt-in-probe-dispatch-rollup.tsv",
|
||||
);
|
||||
if (existsSync(dispatchRollupPath)) {
|
||||
const [row] = parseTsv(readFileSync(dispatchRollupPath, "utf8"));
|
||||
assert.equal(row.dispatch_status, "dispatch_blocked_for_all_opt_in_native_probes");
|
||||
assert.equal(row.dispatch_allowed_count, "0");
|
||||
assert.equal(row.dispatch_blocked_count, "3");
|
||||
assert.equal(row.execution_enabled, "0");
|
||||
assert.equal(row.promotion_allowed, "0");
|
||||
}
|
||||
|
||||
console.log("host_runtime_boundary_docs_node_smoke=ok");
|
||||
6
wasm-port/tests/docs/node/verify_host_runtime_boundary_docs.sh
Executable file
6
wasm-port/tests/docs/node/verify_host_runtime_boundary_docs.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
node "$ROOT_DIR/tests/docs/node/verify_host_runtime_boundary_docs.mjs"
|
||||
46
wasm-port/tests/docs/node/verify_opfs_session_docs.mjs
Normal file
46
wasm-port/tests/docs/node/verify_opfs_session_docs.mjs
Normal file
@@ -0,0 +1,46 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
|
||||
const docText = readFileSync(resolve(root, "docs/opfs-session-persistence.md"), "utf8");
|
||||
const readmeText = readFileSync(resolve(root, "README.md"), "utf8");
|
||||
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
|
||||
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
|
||||
const browserSmokeText = readFileSync(resolve(root, "tests/browser/verify_ini_panel_browser.sh"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
|
||||
for (const phrase of [
|
||||
"OPFS/session persistence and release gate",
|
||||
"runtime/sdk/src/index.js",
|
||||
"createMachineSessionSnapshotPayload",
|
||||
"readMachineSessionReadiness",
|
||||
"loadMachineSessionFromOpfs",
|
||||
"restoreMachineParametersFromOpfs",
|
||||
"loadMachineToolTableFromOpfs",
|
||||
"browser_ini_opfs_smoke=ok",
|
||||
"browser_opfs_session_workflow_smoke=ok",
|
||||
"verify_opfs_session_workflow_browser.sh",
|
||||
"sdk_surface_node_smoke=ok",
|
||||
"host_wasm_opfs_browser_smokes=ok",
|
||||
"verify_no_standalone_cnc_semantics.sh",
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
"L4-PYTHON-REMAP",
|
||||
]) {
|
||||
assert.match(docText, new RegExp(phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
||||
}
|
||||
|
||||
assert.match(readmeText, /docs\/opfs-session-persistence\.md/);
|
||||
assert.match(sdkReadmeText, /OPFS\/session persistence exports/);
|
||||
assert.match(sdkReadmeText, /readMachineSessionReadiness/);
|
||||
assert.match(hostSmokeText, /verify_sdk_surface\.sh/);
|
||||
assert.match(hostSmokeText, /verify_file_service\.sh/);
|
||||
assert.match(hostSmokeText, /verify_opfs_session_workflow_browser\.sh/);
|
||||
assert.match(browserSmokeText, /browser_ini_opfs_smoke=ok/);
|
||||
assert.match(trackerText, /OPFS\/session persistence/);
|
||||
|
||||
console.log("opfs_session_docs_node_smoke=ok");
|
||||
6
wasm-port/tests/docs/node/verify_opfs_session_docs.sh
Executable file
6
wasm-port/tests/docs/node/verify_opfs_session_docs.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
node "$ROOT_DIR/tests/docs/node/verify_opfs_session_docs.mjs"
|
||||
@@ -0,0 +1,262 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
|
||||
function literalRegExp(text) {
|
||||
return new RegExp(text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
||||
}
|
||||
|
||||
const handoffText = readFileSync(resolve(root, "docs/project-release-handoff.md"), "utf8");
|
||||
const readmeText = readFileSync(resolve(root, "README.md"), "utf8");
|
||||
const panelEntryText = readFileSync(resolve(root, "docs/panel-entry.md"), "utf8");
|
||||
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
|
||||
const realBrowserSimulationPriorityText = readFileSync(
|
||||
resolve(root, "docs/real-browser-simulation-priority.md"),
|
||||
"utf8",
|
||||
);
|
||||
const sourceReuseText = readFileSync(resolve(root, "docs/source-reuse-map.md"), "utf8");
|
||||
const driftText = readFileSync(resolve(root, "docs/drift-report.md"), "utf8");
|
||||
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
|
||||
const projectReleaseGateText = readFileSync(resolve(root, "tests/host/verify_project_release_gate.sh"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
|
||||
for (const phrase of [
|
||||
"Project release handoff",
|
||||
"docs/panel-entry.md",
|
||||
"runtime/sdk/README.md",
|
||||
"docs/opfs-session-persistence.md",
|
||||
"docs/real-browser-simulation-priority.md",
|
||||
"docs/sim-configs-coverage-handoff.md",
|
||||
"docs/host-runtime-boundary-handoff.md",
|
||||
"docs/source-reuse-map.md",
|
||||
"docs/drift-report.md",
|
||||
"PROJECT_COMPLETION_TRACKER.md",
|
||||
"runtime/sdk/src/index.js",
|
||||
"real browser CNC simulation page",
|
||||
"runtime/ui/simulation/index.html",
|
||||
"verify_real_simulation_browser.sh",
|
||||
"LinuxCNC-backed interpreter execution",
|
||||
"toolpath/preview area",
|
||||
"createProjectReleaseGateManifest()",
|
||||
"createProjectReleaseGateResultMatrix()",
|
||||
"createProjectReleaseGateActionPlan()",
|
||||
"createProjectReleaseGateExecutionManifest()",
|
||||
"createProjectReleaseGateExecutionSummaryViewModel()",
|
||||
"createProjectReleaseReadinessReport()",
|
||||
"createProjectReleaseReadinessSummaryViewModel()",
|
||||
"parseProjectReleaseReadinessArtifactJson()",
|
||||
"createProjectReleaseReadinessArtifactJsonWorkflow()",
|
||||
"createProjectReleaseReadinessArtifactValidation()",
|
||||
"createProjectReleaseReadinessArtifactValidationActionPlan()",
|
||||
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()",
|
||||
"loadProjectReleaseReadinessArtifactUrlWorkflow()",
|
||||
"createProjectReleaseReadinessArtifactUrlWorkflowActionPlan()",
|
||||
"createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel()",
|
||||
"createProjectBatchAcceptanceCapabilityMatrix()",
|
||||
"createProjectBatchAcceptanceWorkflow()",
|
||||
"createProjectBatchAcceptanceChecklist()",
|
||||
"createProjectBatchAcceptanceReport()",
|
||||
"createProjectBatchAcceptanceReportValidation()",
|
||||
"createProjectBatchAcceptanceReportValidationSummaryViewModel()",
|
||||
"createProjectBatchAcceptanceReportValidationActionPlan()",
|
||||
"createProjectBatchAcceptanceReportJsonWorkflow()",
|
||||
"loadProjectBatchAcceptanceReportUrlWorkflow()",
|
||||
"createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()",
|
||||
"createProjectBatchAcceptanceReportUrlWorkflowActionPlan()",
|
||||
"write_project_batch_acceptance_artifact.mjs",
|
||||
"verify_project_batch_acceptance_artifact.sh",
|
||||
"embedded gate manifest, execution manifest, result matrix, and",
|
||||
"validateWorkflowOverviewReleaseReadinessArtifactJson()",
|
||||
"runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()",
|
||||
"mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()",
|
||||
"loadWorkflowOverviewReleaseReadinessArtifactUrl()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()",
|
||||
"mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()",
|
||||
"mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()",
|
||||
"mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel()",
|
||||
"render-state",
|
||||
"DOM readiness",
|
||||
"createIniPanelShellApiSurfaceInventory()",
|
||||
"createIniPanelShellSessionReadinessWorkflowReport()",
|
||||
"workflow overview embedding mount DOM",
|
||||
"verify_no_standalone_cnc_semantics.sh",
|
||||
"verify_host_smokes.sh",
|
||||
"verify_project_release_gate.sh",
|
||||
"build/project-release-readiness.json",
|
||||
"verify_project_release_readiness_artifact.sh",
|
||||
"verify_project_release_artifact_url_workflow.sh",
|
||||
"verify_project_batch_acceptance_workflow.sh",
|
||||
"verify_real_browser_simulation_priority_docs.sh",
|
||||
"verify_opfs_session_workflow_browser.sh",
|
||||
"host_wasm_opfs_browser_smokes=ok",
|
||||
"project_release_readiness_artifact_node_smoke=ok",
|
||||
"project_release_artifact_url_workflow_node_smoke=ok",
|
||||
"project_batch_acceptance_workflow_node_smoke=ok",
|
||||
"project_batch_acceptance_artifact_node_smoke=ok",
|
||||
"real_browser_simulation_priority_docs_node_smoke=ok",
|
||||
"project_release_gate=ok",
|
||||
"sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"browser_ini_shell_integration_workflow_smoke=ok",
|
||||
"browser_opfs_session_workflow_smoke=ok",
|
||||
"browser_release_artifact_url_workflow_smoke=ok",
|
||||
"browser_real_simulation_page_smoke=ok",
|
||||
"project_release_handoff_docs_node_smoke=ok",
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
"L4-PYTHON-REMAP",
|
||||
]) {
|
||||
assert.match(handoffText, literalRegExp(phrase));
|
||||
}
|
||||
|
||||
for (const phrase of [
|
||||
"docs/project-release-handoff.md",
|
||||
"docs/real-browser-simulation-priority.md",
|
||||
"project_release_handoff_docs_node_smoke=ok",
|
||||
"real_browser_simulation_priority_docs_node_smoke=ok",
|
||||
]) {
|
||||
assert.match(readmeText, literalRegExp(phrase));
|
||||
assert.match(hostSmokeText, literalRegExp("verify_project_release_handoff_docs.sh"));
|
||||
}
|
||||
|
||||
for (const phrase of [
|
||||
"git diff --check",
|
||||
"verify_vendor_sync.sh",
|
||||
"verify_no_standalone_cnc_semantics.sh",
|
||||
"verify_interp_wasm.sh",
|
||||
"verify_sim_configs_inventory_wasm.sh",
|
||||
"verify_ini_panel_browser.sh",
|
||||
"verify_opfs_session_workflow_browser.sh",
|
||||
"verify_release_artifact_url_workflow_browser.sh",
|
||||
"verify_project_batch_acceptance_workflow.sh",
|
||||
"verify_real_browser_simulation_priority_docs.sh",
|
||||
"verify_ui_node_smokes.sh",
|
||||
"verify_host_smokes.sh",
|
||||
"write_project_release_readiness_artifact.mjs",
|
||||
"verify_project_release_readiness_artifact.sh",
|
||||
"verify_project_release_artifact_url_workflow.sh",
|
||||
"verify_project_batch_acceptance_workflow.sh",
|
||||
"verify_real_browser_simulation_priority_docs.sh",
|
||||
"project_release_gate=ok",
|
||||
]) {
|
||||
assert.match(projectReleaseGateText, literalRegExp(phrase));
|
||||
}
|
||||
|
||||
for (const phrase of [
|
||||
"docs/project-release-handoff.md",
|
||||
"runtime/sdk/src/index.js",
|
||||
"workflow overview embedding",
|
||||
]) {
|
||||
assert.match(panelEntryText, literalRegExp(phrase));
|
||||
}
|
||||
|
||||
for (const phrase of [
|
||||
"OPFS/session persistence exports",
|
||||
"Project release readiness export",
|
||||
"createProjectReleaseGateManifest()",
|
||||
"createProjectReleaseGateResultMatrix()",
|
||||
"createProjectReleaseGateActionPlan()",
|
||||
"createProjectReleaseGateExecutionSummaryViewModel()",
|
||||
"createProjectReleaseReadinessReport()",
|
||||
"createProjectReleaseReadinessSummaryViewModel()",
|
||||
"parseProjectReleaseReadinessArtifactJson()",
|
||||
"createProjectReleaseReadinessArtifactJsonWorkflow()",
|
||||
"createProjectReleaseReadinessArtifactValidation()",
|
||||
"createProjectReleaseReadinessArtifactValidationActionPlan()",
|
||||
"createProjectReleaseReadinessArtifactValidationSummaryViewModel()",
|
||||
"gateManifestReady",
|
||||
"gateExecutionManifestReady",
|
||||
"gateExecutionSummaryReady",
|
||||
"gateResultMatrixReady",
|
||||
"gateActionPlanReady",
|
||||
"loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl()",
|
||||
"loadProjectReleaseReadinessArtifactUrlWorkflow()",
|
||||
"createProjectReleaseReadinessArtifactUrlWorkflowActionPlan()",
|
||||
"createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel()",
|
||||
"createProjectBatchAcceptanceCapabilityMatrix()",
|
||||
"createProjectBatchAcceptanceWorkflow()",
|
||||
"createProjectBatchAcceptanceChecklist()",
|
||||
"createProjectBatchAcceptanceReport()",
|
||||
"createProjectBatchAcceptanceReportValidation()",
|
||||
"createProjectBatchAcceptanceReportValidationSummaryViewModel()",
|
||||
"createProjectBatchAcceptanceReportValidationActionPlan()",
|
||||
"createProjectBatchAcceptanceReportJsonWorkflow()",
|
||||
"loadProjectBatchAcceptanceReportUrlWorkflow()",
|
||||
"createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()",
|
||||
"createProjectBatchAcceptanceReportUrlWorkflowActionPlan()",
|
||||
"createProjectBatchAcceptanceSummaryViewModel()",
|
||||
"createProjectBatchAcceptanceActionPlan()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness()",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness()",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState()",
|
||||
"runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract()",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness()",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState()",
|
||||
"verify_project_release_artifact_url_workflow.sh",
|
||||
"verify_project_batch_acceptance_workflow.sh",
|
||||
"verify_project_batch_acceptance_artifact.sh",
|
||||
"project_release_artifact_url_workflow_node_smoke=ok",
|
||||
"project_batch_acceptance_workflow_node_smoke=ok",
|
||||
"project_batch_acceptance_artifact_node_smoke=ok",
|
||||
"build/project-release-readiness.json",
|
||||
"INI panel shell handoff exports",
|
||||
"createIniPanelShellApiSurfaceInventory()",
|
||||
"createIniPanelShellSessionReadinessWorkflowReport()",
|
||||
]) {
|
||||
assert.match(sdkReadmeText, literalRegExp(phrase));
|
||||
}
|
||||
|
||||
assert.match(sourceReuseText, literalRegExp("docs/project-release-handoff.md"));
|
||||
assert.match(driftText, literalRegExp("docs/project-release-handoff.md"));
|
||||
assert.match(realBrowserSimulationPriorityText, literalRegExp("first priority is to deliver a real browser CNC simulation page"));
|
||||
assert.match(realBrowserSimulationPriorityText, literalRegExp("must not implement CNC semantics in JavaScript"));
|
||||
assert.match(trackerText, /Documentation\/release gate/);
|
||||
assert.match(trackerText, /Real browser simulation priority/);
|
||||
assert.match(trackerText, /Project release gate wrapper/);
|
||||
assert.match(trackerText, /Final Acceptance Checklist/);
|
||||
|
||||
console.log("project_release_handoff_docs_node_smoke=ok");
|
||||
6
wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh
Executable file
6
wasm-port/tests/docs/node/verify_project_release_handoff_docs.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
node "$ROOT_DIR/tests/docs/node/verify_project_release_handoff_docs.mjs"
|
||||
@@ -0,0 +1,53 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
|
||||
function literalRegExp(text) {
|
||||
return new RegExp(text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
||||
}
|
||||
|
||||
const priorityText = readFileSync(resolve(root, "docs/real-browser-simulation-priority.md"), "utf8");
|
||||
const handoffText = readFileSync(resolve(root, "docs/project-release-handoff.md"), "utf8");
|
||||
const readmeText = readFileSync(resolve(root, "README.md"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
|
||||
const releaseGateText = readFileSync(resolve(root, "tests/host/verify_project_release_gate.sh"), "utf8");
|
||||
const simulationHtmlText = readFileSync(resolve(root, "runtime/ui/simulation/index.html"), "utf8");
|
||||
const simulationSmokeText = readFileSync(resolve(root, "tests/browser/verify_real_simulation_browser.sh"), "utf8");
|
||||
|
||||
for (const phrase of [
|
||||
"Real Browser Simulation Priority",
|
||||
"first priority is to deliver a real browser CNC simulation page",
|
||||
"machine/session loader",
|
||||
"G-code program view",
|
||||
"LinuxCNC-backed interpreter execution",
|
||||
"toolpath/preview area",
|
||||
"browser smoke coverage",
|
||||
"must not implement CNC semantics in JavaScript",
|
||||
"vendored LinuxCNC source",
|
||||
"wasm-port/tests/docs/node/verify_real_browser_simulation_priority_docs.sh",
|
||||
"runtime/ui/simulation/index.html",
|
||||
"wasm-port/tests/browser/verify_real_simulation_browser.sh",
|
||||
]) {
|
||||
assert.match(priorityText, literalRegExp(phrase));
|
||||
}
|
||||
|
||||
for (const text of [handoffText, readmeText, trackerText]) {
|
||||
assert.match(text, literalRegExp("docs/real-browser-simulation-priority.md"));
|
||||
assert.match(text, /real browser CNC simulation page|真实 UI\/browser 数控仿真页面/);
|
||||
assert.match(text, /LinuxCNC semantic boundary|LinuxCNC 语义边界|LinuxCNC-backed/);
|
||||
}
|
||||
|
||||
assert.match(hostSmokeText, /verify_real_browser_simulation_priority_docs\.sh/);
|
||||
assert.match(hostSmokeText, /verify_real_simulation_browser\.sh/);
|
||||
assert.match(releaseGateText, /verify_real_simulation_browser\.sh/);
|
||||
assert.match(simulationHtmlText, /LinuxCNC Browser Simulation/);
|
||||
assert.match(simulationHtmlText, /data-toolpath-svg/);
|
||||
assert.match(simulationHtmlText, /data-program-lines/);
|
||||
assert.match(simulationSmokeText, /browser_real_simulation_page_smoke=ok/);
|
||||
|
||||
console.log("real_browser_simulation_priority_docs_node_smoke=ok");
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
node "$ROOT_DIR/tests/docs/node/verify_real_browser_simulation_priority_docs.mjs"
|
||||
@@ -0,0 +1,89 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
|
||||
function literalRegExp(text) {
|
||||
return new RegExp(text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
||||
}
|
||||
|
||||
function parseTsv(text) {
|
||||
const [headerLine, ...lines] = text.trim().split(/\r?\n/);
|
||||
const headers = headerLine.split("\t");
|
||||
return lines.map((line) => {
|
||||
const cells = line.split("\t");
|
||||
return Object.fromEntries(headers.map((header, index) => [header, cells[index] ?? ""]));
|
||||
});
|
||||
}
|
||||
|
||||
const docText = readFileSync(resolve(root, "docs/sim-configs-coverage-handoff.md"), "utf8");
|
||||
const matrixText = readFileSync(resolve(root, "docs/sim-configs-coverage-matrix.md"), "utf8");
|
||||
const completionPlanText = readFileSync(resolve(root, "docs/sim-configs-completion-plan.md"), "utf8");
|
||||
const inventoryTestText = readFileSync(
|
||||
resolve(root, "tests/wasm/node/verify_sim_configs_inventory_wasm.mjs"),
|
||||
"utf8",
|
||||
);
|
||||
const hostSmokeText = readFileSync(resolve(root, "tests/host/verify_host_smokes.sh"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
|
||||
for (const phrase of [
|
||||
"Sim-config coverage release handoff",
|
||||
"sim_configs_wasm_node_inventory_executed=28",
|
||||
"sim_configs_wasm_node_inventory_passed=28",
|
||||
"sim_configs_wasm_node_inventory_skipped=131",
|
||||
"sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"sim_configs_wasm_node_inventory_skip_ASSET_ONLY=65",
|
||||
"sim_configs_wasm_node_inventory_skip_L4_PYTHON_REMAP=53",
|
||||
"sim_configs_wasm_node_inventory_skip_L4_TOOL_DB=1",
|
||||
"sim_configs_wasm_node_inventory_skip_L4_USER_M_PROCESS=1",
|
||||
"sim_configs_wasm_node_inventory_skip_NON_MAIN_CLASS=10",
|
||||
"sim_configs_wasm_node_inventory_skip_UPSTREAM_DEMO=1",
|
||||
"build/wasm/sim-configs-inventory/skip-summary.tsv",
|
||||
"build/wasm/sim-configs-inventory/blocked-dependency-summary.tsv",
|
||||
"verify_no_standalone_cnc_semantics.sh",
|
||||
"verify_sim_configs_coverage_docs.sh",
|
||||
"L4-PYTHON-REMAP",
|
||||
"L4-TOOL-DB",
|
||||
"L4-USER-M-PROCESS",
|
||||
]) {
|
||||
assert.match(docText, literalRegExp(phrase));
|
||||
}
|
||||
|
||||
for (const reason of [
|
||||
"ASSET-ONLY",
|
||||
"L4-PYTHON-REMAP",
|
||||
"L4-TOOL-DB",
|
||||
"L4-USER-M-PROCESS",
|
||||
"NON_MAIN_CLASS",
|
||||
"UPSTREAM-DEMO",
|
||||
]) {
|
||||
assert.match(matrixText, literalRegExp(reason));
|
||||
assert.match(completionPlanText, literalRegExp(reason));
|
||||
assert.match(docText, literalRegExp(reason));
|
||||
}
|
||||
|
||||
assert.match(inventoryTestText, /sim_configs_wasm_node_inventory_skip_/);
|
||||
assert.match(hostSmokeText, /verify_sim_configs_inventory_wasm\.sh/);
|
||||
assert.match(hostSmokeText, /verify_sim_configs_coverage_docs\.sh/);
|
||||
assert.match(trackerText, /Sim config coverage|sim-config coverage/);
|
||||
|
||||
const skipSummaryPath = resolve(root, "build/wasm/sim-configs-inventory/skip-summary.tsv");
|
||||
if (existsSync(skipSummaryPath)) {
|
||||
const rows = parseTsv(readFileSync(skipSummaryPath, "utf8"));
|
||||
assert.deepEqual(
|
||||
Object.fromEntries(rows.map((row) => [row.skip_or_block_reason, Number(row.count)])),
|
||||
{
|
||||
"ASSET-ONLY": 65,
|
||||
"L4-PYTHON-REMAP": 53,
|
||||
"L4-TOOL-DB": 1,
|
||||
"L4-USER-M-PROCESS": 1,
|
||||
NON_MAIN_CLASS: 10,
|
||||
"UPSTREAM-DEMO": 1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
console.log("sim_configs_coverage_docs_node_smoke=ok");
|
||||
6
wasm-port/tests/docs/node/verify_sim_configs_coverage_docs.sh
Executable file
6
wasm-port/tests/docs/node/verify_sim_configs_coverage_docs.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
node "$ROOT_DIR/tests/docs/node/verify_sim_configs_coverage_docs.mjs"
|
||||
873
wasm-port/tests/fixtures/project-release-readiness-ready.json
vendored
Normal file
873
wasm-port/tests/fixtures/project-release-readiness-ready.json
vendored
Normal file
@@ -0,0 +1,873 @@
|
||||
{
|
||||
"apiName": "project-release-readiness-report",
|
||||
"reportVersion": 1,
|
||||
"phase": "ready",
|
||||
"ready": true,
|
||||
"missing": [],
|
||||
"releaseGate": {
|
||||
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
"passed": true,
|
||||
"expectedOutput": "project_release_gate=ok"
|
||||
},
|
||||
"simConfigInventory": {
|
||||
"executed": 28,
|
||||
"passed": 28,
|
||||
"skipped": 131,
|
||||
"unexpectedFail": 0
|
||||
},
|
||||
"blockedRuntimeFamilies": [
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
"L4-PYTHON-REMAP"
|
||||
],
|
||||
"promotedRuntimeFamilies": [],
|
||||
"promotedBlockedFamilies": [],
|
||||
"gateManifest": {
|
||||
"apiName": "project-release-gate-manifest",
|
||||
"manifestVersion": 1,
|
||||
"gateCount": 12,
|
||||
"gateIds": [
|
||||
"diff-check",
|
||||
"vendor-sync",
|
||||
"standalone-cnc-semantics",
|
||||
"interp-wasm",
|
||||
"sim-config-inventory",
|
||||
"ini-panel-browser",
|
||||
"opfs-session-browser",
|
||||
"release-artifact-url-browser",
|
||||
"project-batch-acceptance",
|
||||
"ui-node-smokes",
|
||||
"host-smokes",
|
||||
"project-release-gate"
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"id": "diff-check",
|
||||
"label": "Git diff check",
|
||||
"command": "git diff --check",
|
||||
"expectedOutput": null
|
||||
},
|
||||
{
|
||||
"id": "vendor-sync",
|
||||
"label": "Vendor sync guard",
|
||||
"command": "wasm-port/tools/verify_vendor_sync.sh",
|
||||
"expectedOutput": "vendor sync up to date"
|
||||
},
|
||||
{
|
||||
"id": "standalone-cnc-semantics",
|
||||
"label": "Standalone CNC semantics guard",
|
||||
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
|
||||
"expectedOutput": "standalone CNC semantics guard complete"
|
||||
},
|
||||
{
|
||||
"id": "interp-wasm",
|
||||
"label": "Interpreter WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
|
||||
"expectedOutput": "interp_wasm_node_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "sim-config-inventory",
|
||||
"label": "Sim config inventory WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
|
||||
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0"
|
||||
},
|
||||
{
|
||||
"id": "ini-panel-browser",
|
||||
"label": "INI panel browser smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
|
||||
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "opfs-session-browser",
|
||||
"label": "OPFS session browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
|
||||
"expectedOutput": "browser_opfs_session_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "release-artifact-url-browser",
|
||||
"label": "Release artifact URL browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
|
||||
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "project-batch-acceptance",
|
||||
"label": "Project batch acceptance workflow smoke",
|
||||
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "ui-node-smokes",
|
||||
"label": "UI Node smokes",
|
||||
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
|
||||
"expectedOutput": "ui_node_smokes=ok"
|
||||
},
|
||||
{
|
||||
"id": "host-smokes",
|
||||
"label": "Host aggregate smoke",
|
||||
"command": "wasm-port/tests/host/verify_host_smokes.sh",
|
||||
"expectedOutput": "host_wasm_opfs_browser_smokes=ok"
|
||||
},
|
||||
{
|
||||
"id": "project-release-gate",
|
||||
"label": "Project release gate",
|
||||
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
"expectedOutput": "project_release_gate=ok"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gateExecutionManifest": {
|
||||
"apiName": "project-release-gate-execution-manifest",
|
||||
"manifestVersion": 1,
|
||||
"phase": "ready",
|
||||
"ready": true,
|
||||
"gateCount": 12,
|
||||
"passedCount": 12,
|
||||
"unknownCount": 0,
|
||||
"missingGateIds": [],
|
||||
"manifest": {
|
||||
"apiName": "project-release-gate-manifest",
|
||||
"manifestVersion": 1,
|
||||
"gateCount": 12,
|
||||
"gateIds": [
|
||||
"diff-check",
|
||||
"vendor-sync",
|
||||
"standalone-cnc-semantics",
|
||||
"interp-wasm",
|
||||
"sim-config-inventory",
|
||||
"ini-panel-browser",
|
||||
"opfs-session-browser",
|
||||
"release-artifact-url-browser",
|
||||
"project-batch-acceptance",
|
||||
"ui-node-smokes",
|
||||
"host-smokes",
|
||||
"project-release-gate"
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"id": "diff-check",
|
||||
"label": "Git diff check",
|
||||
"command": "git diff --check",
|
||||
"expectedOutput": null
|
||||
},
|
||||
{
|
||||
"id": "vendor-sync",
|
||||
"label": "Vendor sync guard",
|
||||
"command": "wasm-port/tools/verify_vendor_sync.sh",
|
||||
"expectedOutput": "vendor sync up to date"
|
||||
},
|
||||
{
|
||||
"id": "standalone-cnc-semantics",
|
||||
"label": "Standalone CNC semantics guard",
|
||||
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
|
||||
"expectedOutput": "standalone CNC semantics guard complete"
|
||||
},
|
||||
{
|
||||
"id": "interp-wasm",
|
||||
"label": "Interpreter WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
|
||||
"expectedOutput": "interp_wasm_node_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "sim-config-inventory",
|
||||
"label": "Sim config inventory WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
|
||||
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0"
|
||||
},
|
||||
{
|
||||
"id": "ini-panel-browser",
|
||||
"label": "INI panel browser smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
|
||||
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "opfs-session-browser",
|
||||
"label": "OPFS session browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
|
||||
"expectedOutput": "browser_opfs_session_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "release-artifact-url-browser",
|
||||
"label": "Release artifact URL browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
|
||||
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "project-batch-acceptance",
|
||||
"label": "Project batch acceptance workflow smoke",
|
||||
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "ui-node-smokes",
|
||||
"label": "UI Node smokes",
|
||||
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
|
||||
"expectedOutput": "ui_node_smokes=ok"
|
||||
},
|
||||
{
|
||||
"id": "host-smokes",
|
||||
"label": "Host aggregate smoke",
|
||||
"command": "wasm-port/tests/host/verify_host_smokes.sh",
|
||||
"expectedOutput": "host_wasm_opfs_browser_smokes=ok"
|
||||
},
|
||||
{
|
||||
"id": "project-release-gate",
|
||||
"label": "Project release gate",
|
||||
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
"expectedOutput": "project_release_gate=ok"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": "diff-check",
|
||||
"label": "Git diff check",
|
||||
"command": "git diff --check",
|
||||
"expectedOutput": null,
|
||||
"observedOutput": null,
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "explicit-result"
|
||||
},
|
||||
{
|
||||
"id": "vendor-sync",
|
||||
"label": "Vendor sync guard",
|
||||
"command": "wasm-port/tools/verify_vendor_sync.sh",
|
||||
"expectedOutput": "vendor sync up to date",
|
||||
"observedOutput": "vendor sync up to date",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "standalone-cnc-semantics",
|
||||
"label": "Standalone CNC semantics guard",
|
||||
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
|
||||
"expectedOutput": "standalone CNC semantics guard complete",
|
||||
"observedOutput": "standalone CNC semantics guard complete",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "interp-wasm",
|
||||
"label": "Interpreter WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
|
||||
"expectedOutput": "interp_wasm_node_smoke=ok",
|
||||
"observedOutput": "interp_wasm_node_smoke=ok",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "sim-config-inventory",
|
||||
"label": "Sim config inventory WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
|
||||
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "ini-panel-browser",
|
||||
"label": "INI panel browser smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
|
||||
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
|
||||
"observedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "opfs-session-browser",
|
||||
"label": "OPFS session browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
|
||||
"expectedOutput": "browser_opfs_session_workflow_smoke=ok",
|
||||
"observedOutput": "browser_opfs_session_workflow_smoke=ok",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "release-artifact-url-browser",
|
||||
"label": "Release artifact URL browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
|
||||
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok",
|
||||
"observedOutput": "browser_release_artifact_url_workflow_smoke=ok",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "project-batch-acceptance",
|
||||
"label": "Project batch acceptance workflow smoke",
|
||||
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
|
||||
"observedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "ui-node-smokes",
|
||||
"label": "UI Node smokes",
|
||||
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
|
||||
"expectedOutput": "ui_node_smokes=ok",
|
||||
"observedOutput": "ui_node_smokes=ok",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "host-smokes",
|
||||
"label": "Host aggregate smoke",
|
||||
"command": "wasm-port/tests/host/verify_host_smokes.sh",
|
||||
"expectedOutput": "host_wasm_opfs_browser_smokes=ok",
|
||||
"observedOutput": "host_wasm_opfs_browser_smokes=ok",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
},
|
||||
{
|
||||
"id": "project-release-gate",
|
||||
"label": "Project release gate",
|
||||
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
"expectedOutput": "project_release_gate=ok",
|
||||
"observedOutput": "project_release_gate=ok",
|
||||
"observedOutputCount": 11,
|
||||
"status": "passed",
|
||||
"passed": true,
|
||||
"evidence": "expected-output"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gateExecutionSummaryViewModel": {
|
||||
"apiName": "project-release-gate-execution-summary-view-model",
|
||||
"viewModelVersion": 1,
|
||||
"phase": "ready",
|
||||
"ready": true,
|
||||
"title": "Project release gate execution",
|
||||
"statusText": "Ready",
|
||||
"detailText": "All release gate evidence present",
|
||||
"statusLine": "Ready: All release gate evidence present",
|
||||
"gateCount": 12,
|
||||
"passedCount": 12,
|
||||
"unknownCount": 0,
|
||||
"missingGateIds": [],
|
||||
"nextGateId": null,
|
||||
"evidenceCounts": {
|
||||
"explicit-result": 1,
|
||||
"expected-output": 11
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": "gate-results",
|
||||
"label": "Release gate evidence",
|
||||
"value": "12/12 passed"
|
||||
},
|
||||
{
|
||||
"id": "unknown-gates",
|
||||
"label": "Unknown gates",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"id": "next-gate",
|
||||
"label": "Next gate",
|
||||
"value": "none"
|
||||
},
|
||||
{
|
||||
"id": "expected-output-evidence",
|
||||
"label": "Expected-output evidence",
|
||||
"value": "11"
|
||||
},
|
||||
{
|
||||
"id": "explicit-result-evidence",
|
||||
"label": "Explicit-result evidence",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"id": "missing",
|
||||
"label": "Missing gate evidence",
|
||||
"value": "none"
|
||||
}
|
||||
],
|
||||
"gateRows": [
|
||||
{
|
||||
"id": "diff-check",
|
||||
"label": "Git diff check",
|
||||
"command": "git diff --check",
|
||||
"expectedOutput": null,
|
||||
"observedOutput": null,
|
||||
"evidence": "explicit-result",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "vendor-sync",
|
||||
"label": "Vendor sync guard",
|
||||
"command": "wasm-port/tools/verify_vendor_sync.sh",
|
||||
"expectedOutput": "vendor sync up to date",
|
||||
"observedOutput": "vendor sync up to date",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "standalone-cnc-semantics",
|
||||
"label": "Standalone CNC semantics guard",
|
||||
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
|
||||
"expectedOutput": "standalone CNC semantics guard complete",
|
||||
"observedOutput": "standalone CNC semantics guard complete",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "interp-wasm",
|
||||
"label": "Interpreter WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
|
||||
"expectedOutput": "interp_wasm_node_smoke=ok",
|
||||
"observedOutput": "interp_wasm_node_smoke=ok",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "sim-config-inventory",
|
||||
"label": "Sim config inventory WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
|
||||
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"observedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "ini-panel-browser",
|
||||
"label": "INI panel browser smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
|
||||
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
|
||||
"observedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "opfs-session-browser",
|
||||
"label": "OPFS session browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
|
||||
"expectedOutput": "browser_opfs_session_workflow_smoke=ok",
|
||||
"observedOutput": "browser_opfs_session_workflow_smoke=ok",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "release-artifact-url-browser",
|
||||
"label": "Release artifact URL browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
|
||||
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok",
|
||||
"observedOutput": "browser_release_artifact_url_workflow_smoke=ok",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "project-batch-acceptance",
|
||||
"label": "Project batch acceptance workflow smoke",
|
||||
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
|
||||
"observedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "ui-node-smokes",
|
||||
"label": "UI Node smokes",
|
||||
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
|
||||
"expectedOutput": "ui_node_smokes=ok",
|
||||
"observedOutput": "ui_node_smokes=ok",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "host-smokes",
|
||||
"label": "Host aggregate smoke",
|
||||
"command": "wasm-port/tests/host/verify_host_smokes.sh",
|
||||
"expectedOutput": "host_wasm_opfs_browser_smokes=ok",
|
||||
"observedOutput": "host_wasm_opfs_browser_smokes=ok",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "project-release-gate",
|
||||
"label": "Project release gate",
|
||||
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
"expectedOutput": "project_release_gate=ok",
|
||||
"observedOutput": "project_release_gate=ok",
|
||||
"evidence": "expected-output",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"gateResultMatrix": {
|
||||
"apiName": "project-release-gate-result-matrix",
|
||||
"matrixVersion": 1,
|
||||
"phase": "ready",
|
||||
"ready": true,
|
||||
"gateCount": 12,
|
||||
"passedCount": 12,
|
||||
"unknownCount": 0,
|
||||
"missingGateIds": [],
|
||||
"manifest": {
|
||||
"apiName": "project-release-gate-manifest",
|
||||
"manifestVersion": 1,
|
||||
"gateCount": 12,
|
||||
"gateIds": [
|
||||
"diff-check",
|
||||
"vendor-sync",
|
||||
"standalone-cnc-semantics",
|
||||
"interp-wasm",
|
||||
"sim-config-inventory",
|
||||
"ini-panel-browser",
|
||||
"opfs-session-browser",
|
||||
"release-artifact-url-browser",
|
||||
"project-batch-acceptance",
|
||||
"ui-node-smokes",
|
||||
"host-smokes",
|
||||
"project-release-gate"
|
||||
],
|
||||
"gates": [
|
||||
{
|
||||
"id": "diff-check",
|
||||
"label": "Git diff check",
|
||||
"command": "git diff --check",
|
||||
"expectedOutput": null
|
||||
},
|
||||
{
|
||||
"id": "vendor-sync",
|
||||
"label": "Vendor sync guard",
|
||||
"command": "wasm-port/tools/verify_vendor_sync.sh",
|
||||
"expectedOutput": "vendor sync up to date"
|
||||
},
|
||||
{
|
||||
"id": "standalone-cnc-semantics",
|
||||
"label": "Standalone CNC semantics guard",
|
||||
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
|
||||
"expectedOutput": "standalone CNC semantics guard complete"
|
||||
},
|
||||
{
|
||||
"id": "interp-wasm",
|
||||
"label": "Interpreter WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
|
||||
"expectedOutput": "interp_wasm_node_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "sim-config-inventory",
|
||||
"label": "Sim config inventory WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
|
||||
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0"
|
||||
},
|
||||
{
|
||||
"id": "ini-panel-browser",
|
||||
"label": "INI panel browser smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
|
||||
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "opfs-session-browser",
|
||||
"label": "OPFS session browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
|
||||
"expectedOutput": "browser_opfs_session_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "release-artifact-url-browser",
|
||||
"label": "Release artifact URL browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
|
||||
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "project-batch-acceptance",
|
||||
"label": "Project batch acceptance workflow smoke",
|
||||
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok"
|
||||
},
|
||||
{
|
||||
"id": "ui-node-smokes",
|
||||
"label": "UI Node smokes",
|
||||
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
|
||||
"expectedOutput": "ui_node_smokes=ok"
|
||||
},
|
||||
{
|
||||
"id": "host-smokes",
|
||||
"label": "Host aggregate smoke",
|
||||
"command": "wasm-port/tests/host/verify_host_smokes.sh",
|
||||
"expectedOutput": "host_wasm_opfs_browser_smokes=ok"
|
||||
},
|
||||
{
|
||||
"id": "project-release-gate",
|
||||
"label": "Project release gate",
|
||||
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
"expectedOutput": "project_release_gate=ok"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rows": [
|
||||
{
|
||||
"id": "diff-check",
|
||||
"label": "Git diff check",
|
||||
"command": "git diff --check",
|
||||
"expectedOutput": null,
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "vendor-sync",
|
||||
"label": "Vendor sync guard",
|
||||
"command": "wasm-port/tools/verify_vendor_sync.sh",
|
||||
"expectedOutput": "vendor sync up to date",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "standalone-cnc-semantics",
|
||||
"label": "Standalone CNC semantics guard",
|
||||
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
|
||||
"expectedOutput": "standalone CNC semantics guard complete",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "interp-wasm",
|
||||
"label": "Interpreter WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
|
||||
"expectedOutput": "interp_wasm_node_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "sim-config-inventory",
|
||||
"label": "Sim config inventory WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
|
||||
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "ini-panel-browser",
|
||||
"label": "INI panel browser smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
|
||||
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "opfs-session-browser",
|
||||
"label": "OPFS session browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
|
||||
"expectedOutput": "browser_opfs_session_workflow_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "release-artifact-url-browser",
|
||||
"label": "Release artifact URL browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
|
||||
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "project-batch-acceptance",
|
||||
"label": "Project batch acceptance workflow smoke",
|
||||
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "ui-node-smokes",
|
||||
"label": "UI Node smokes",
|
||||
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
|
||||
"expectedOutput": "ui_node_smokes=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "host-smokes",
|
||||
"label": "Host aggregate smoke",
|
||||
"command": "wasm-port/tests/host/verify_host_smokes.sh",
|
||||
"expectedOutput": "host_wasm_opfs_browser_smokes=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "project-release-gate",
|
||||
"label": "Project release gate",
|
||||
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
"expectedOutput": "project_release_gate=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"gateActionPlan": {
|
||||
"apiName": "project-release-gate-action-plan",
|
||||
"planVersion": 1,
|
||||
"phase": "ready",
|
||||
"ready": true,
|
||||
"gateCount": 12,
|
||||
"completedCount": 12,
|
||||
"pendingCount": 0,
|
||||
"completedGateIds": [
|
||||
"diff-check",
|
||||
"vendor-sync",
|
||||
"standalone-cnc-semantics",
|
||||
"interp-wasm",
|
||||
"sim-config-inventory",
|
||||
"ini-panel-browser",
|
||||
"opfs-session-browser",
|
||||
"release-artifact-url-browser",
|
||||
"project-batch-acceptance",
|
||||
"ui-node-smokes",
|
||||
"host-smokes",
|
||||
"project-release-gate"
|
||||
],
|
||||
"pendingGateIds": [],
|
||||
"nextGateId": null,
|
||||
"nextCommand": null,
|
||||
"commands": [],
|
||||
"shellScript": "# Project release gates are already marked passed.\n",
|
||||
"rows": []
|
||||
},
|
||||
"gates": [
|
||||
{
|
||||
"id": "diff-check",
|
||||
"label": "Git diff check",
|
||||
"command": "git diff --check",
|
||||
"expectedOutput": null,
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "vendor-sync",
|
||||
"label": "Vendor sync guard",
|
||||
"command": "wasm-port/tools/verify_vendor_sync.sh",
|
||||
"expectedOutput": "vendor sync up to date",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "standalone-cnc-semantics",
|
||||
"label": "Standalone CNC semantics guard",
|
||||
"command": "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
|
||||
"expectedOutput": "standalone CNC semantics guard complete",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "interp-wasm",
|
||||
"label": "Interpreter WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
|
||||
"expectedOutput": "interp_wasm_node_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "sim-config-inventory",
|
||||
"label": "Sim config inventory WASM Node smoke",
|
||||
"command": "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
|
||||
"expectedOutput": "sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "ini-panel-browser",
|
||||
"label": "INI panel browser smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
|
||||
"expectedOutput": "browser_ini_shell_integration_workflow_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "opfs-session-browser",
|
||||
"label": "OPFS session browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_opfs_session_workflow_browser.sh",
|
||||
"expectedOutput": "browser_opfs_session_workflow_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "release-artifact-url-browser",
|
||||
"label": "Release artifact URL browser workflow smoke",
|
||||
"command": "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
|
||||
"expectedOutput": "browser_release_artifact_url_workflow_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "project-batch-acceptance",
|
||||
"label": "Project batch acceptance workflow smoke",
|
||||
"command": "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
"expectedOutput": "project_batch_acceptance_workflow_node_smoke=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "ui-node-smokes",
|
||||
"label": "UI Node smokes",
|
||||
"command": "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
|
||||
"expectedOutput": "ui_node_smokes=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "host-smokes",
|
||||
"label": "Host aggregate smoke",
|
||||
"command": "wasm-port/tests/host/verify_host_smokes.sh",
|
||||
"expectedOutput": "host_wasm_opfs_browser_smokes=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
},
|
||||
{
|
||||
"id": "project-release-gate",
|
||||
"label": "Project release gate",
|
||||
"command": "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
"expectedOutput": "project_release_gate=ok",
|
||||
"status": "passed",
|
||||
"passed": true
|
||||
}
|
||||
],
|
||||
"rows": [
|
||||
{
|
||||
"id": "project-release-gate",
|
||||
"label": "Project release gate",
|
||||
"value": "passed"
|
||||
},
|
||||
{
|
||||
"id": "sim-config-inventory",
|
||||
"label": "Sim config inventory",
|
||||
"value": "unexpected_fail=0"
|
||||
},
|
||||
{
|
||||
"id": "blocked-runtime-families",
|
||||
"label": "Blocked runtime families",
|
||||
"value": "L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -13,8 +13,16 @@ SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_nc_files_wasm.sh"
|
||||
SKIP_TP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_tp_wasm.sh"
|
||||
"$ROOT_DIR/tests/opfs/node/verify_file_service.sh"
|
||||
"$ROOT_DIR/tests/sdk/node/verify_sdk_surface.sh"
|
||||
"$ROOT_DIR/tests/docs/node/verify_opfs_session_docs.sh"
|
||||
"$ROOT_DIR/tests/docs/node/verify_sim_configs_coverage_docs.sh"
|
||||
"$ROOT_DIR/tests/docs/node/verify_host_runtime_boundary_docs.sh"
|
||||
"$ROOT_DIR/tests/docs/node/verify_project_release_handoff_docs.sh"
|
||||
"$ROOT_DIR/tests/docs/node/verify_real_browser_simulation_priority_docs.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ui_node_smokes.sh"
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_ini_panel_browser.sh"
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_real_simulation_browser.sh"
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_opfs_session_workflow_browser.sh"
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_interp_browser.sh"
|
||||
|
||||
echo "host_wasm_opfs_browser_smokes=ok"
|
||||
|
||||
27
wasm-port/tests/host/verify_project_release_gate.sh
Executable file
27
wasm-port/tests/host/verify_project_release_gate.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
REPO_ROOT="$(cd "$ROOT_DIR/.." && pwd)"
|
||||
|
||||
cd "$REPO_ROOT"
|
||||
|
||||
git diff --check
|
||||
"$ROOT_DIR/tools/verify_vendor_sync.sh"
|
||||
"$ROOT_DIR/tools/verify_no_standalone_cnc_semantics.sh"
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_interp_wasm.sh"
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.sh"
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_ini_panel_browser.sh"
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_real_simulation_browser.sh"
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_opfs_session_workflow_browser.sh"
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_release_artifact_url_workflow_browser.sh"
|
||||
"$ROOT_DIR/tests/sdk/node/verify_project_batch_acceptance_workflow.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ui_node_smokes.sh"
|
||||
"$ROOT_DIR/tests/sdk/node/verify_project_release_artifact_url_workflow.sh"
|
||||
"$ROOT_DIR/tests/docs/node/verify_real_browser_simulation_priority_docs.sh"
|
||||
"$ROOT_DIR/tests/host/verify_host_smokes.sh"
|
||||
|
||||
node "$ROOT_DIR/tests/host/write_project_release_readiness_artifact.mjs"
|
||||
"$ROOT_DIR/tests/host/verify_project_release_readiness_artifact.sh"
|
||||
|
||||
echo "project_release_gate=ok"
|
||||
@@ -0,0 +1,54 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import {
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
parseProjectReleaseReadinessArtifactJson,
|
||||
} 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-release-readiness.json");
|
||||
const artifact = parseProjectReleaseReadinessArtifactJson(readFileSync(artifactPath, "utf8"));
|
||||
const validation = createProjectReleaseReadinessArtifactValidation(artifact);
|
||||
|
||||
assert.equal(validation.apiName, "project-release-readiness-artifact-validation");
|
||||
assert.equal(validation.ready, true);
|
||||
assert.equal(validation.phase, "ready");
|
||||
assert.deepEqual(validation.missing, []);
|
||||
assert.equal(validation.artifactApiName, "project-release-readiness-report");
|
||||
assert.equal(validation.artifactVersion, 1);
|
||||
assert.equal(validation.gateCount, 12);
|
||||
assert.equal(validation.expectedGateCount, 12);
|
||||
assert.equal(validation.gateManifestReady, true);
|
||||
assert.equal(validation.gateExecutionManifestReady, true);
|
||||
assert.equal(validation.gateExecutionSummaryReady, true);
|
||||
assert.equal(validation.gateResultMatrixReady, true);
|
||||
assert.equal(validation.gateActionPlanReady, true);
|
||||
assert.deepEqual(validation.expectedGateIds, [
|
||||
"diff-check",
|
||||
"vendor-sync",
|
||||
"standalone-cnc-semantics",
|
||||
"interp-wasm",
|
||||
"sim-config-inventory",
|
||||
"ini-panel-browser",
|
||||
"opfs-session-browser",
|
||||
"release-artifact-url-browser",
|
||||
"project-batch-acceptance",
|
||||
"ui-node-smokes",
|
||||
"host-smokes",
|
||||
"project-release-gate",
|
||||
]);
|
||||
assert.equal(artifact.gateManifest.apiName, "project-release-gate-manifest");
|
||||
assert.equal(artifact.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
|
||||
assert.equal(artifact.gateExecutionManifest.passedCount, 12);
|
||||
assert.equal(artifact.gateExecutionSummaryViewModel.apiName, "project-release-gate-execution-summary-view-model");
|
||||
assert.equal(artifact.gateExecutionSummaryViewModel.rows[0].value, "12/12 passed");
|
||||
assert.equal(artifact.gateResultMatrix.apiName, "project-release-gate-result-matrix");
|
||||
assert.equal(artifact.gateActionPlan.apiName, "project-release-gate-action-plan");
|
||||
assert.equal(artifact.gateActionPlan.pendingCount, 0);
|
||||
assert.equal(artifact.gateActionPlan.nextCommand, null);
|
||||
|
||||
console.log("project_release_readiness_artifact_node_smoke=ok");
|
||||
6
wasm-port/tests/host/verify_project_release_readiness_artifact.sh
Executable file
6
wasm-port/tests/host/verify_project_release_readiness_artifact.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
|
||||
node "$ROOT_DIR/tests/host/verify_project_release_readiness_artifact.mjs" "$@"
|
||||
@@ -0,0 +1,47 @@
|
||||
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { createProjectReleaseReadinessReport } 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-release-readiness.json");
|
||||
|
||||
const passedGateResults = {
|
||||
"diff-check": true,
|
||||
"vendor-sync": true,
|
||||
"standalone-cnc-semantics": true,
|
||||
"interp-wasm": true,
|
||||
"sim-config-inventory": true,
|
||||
"ini-panel-browser": true,
|
||||
"opfs-session-browser": true,
|
||||
"release-artifact-url-browser": true,
|
||||
"project-batch-acceptance": true,
|
||||
"ui-node-smokes": true,
|
||||
"host-smokes": true,
|
||||
"project-release-gate": true,
|
||||
};
|
||||
|
||||
const report = createProjectReleaseReadinessReport({
|
||||
gateResults: passedGateResults,
|
||||
observedOutputs: [
|
||||
"vendor sync up to date",
|
||||
"standalone CNC semantics guard complete",
|
||||
"interp_wasm_node_smoke=ok",
|
||||
"sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"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",
|
||||
],
|
||||
});
|
||||
|
||||
mkdirSync(dirname(outputPath), { recursive: true });
|
||||
writeFileSync(`${outputPath}.tmp`, `${JSON.stringify(report, null, 2)}\n`);
|
||||
renameSync(`${outputPath}.tmp`, outputPath);
|
||||
|
||||
console.log(`project_release_readiness_artifact=${outputPath}`);
|
||||
@@ -43,6 +43,9 @@ import {
|
||||
import {
|
||||
loadMachineSessionFromOpfs,
|
||||
} from "../../../runtime/opfs/linuxcnc-machine-session-bridge.js";
|
||||
import {
|
||||
readMachineSessionReadiness,
|
||||
} from "../../../runtime/opfs/machine-session-readiness.js";
|
||||
|
||||
class MockFileHandle {
|
||||
constructor(name) {
|
||||
@@ -420,6 +423,54 @@ assertGcodeProgramStoragePath(
|
||||
);
|
||||
assert.equal(await loadGcodeProgram("custom-fixture.ngc", { storage }), "G1 X1 F10\nM2\n");
|
||||
|
||||
const readyMachineSession = await readMachineSessionReadiness("xyzab-tdr", {
|
||||
storage,
|
||||
sessionId: "machine-session-1",
|
||||
snapshotFilename: "machine-session-snapshot.json",
|
||||
gcodeFilename: "fixture.ngc",
|
||||
});
|
||||
assert.equal(readyMachineSession.ready, true);
|
||||
assert.equal(readyMachineSession.phase, "ready");
|
||||
assert.deepEqual(readyMachineSession.missing, []);
|
||||
assert.deepEqual(readyMachineSession.errors, []);
|
||||
assert.deepEqual(
|
||||
readyMachineSession.checks.map((check) => [check.name, check.ok, check.path]),
|
||||
[
|
||||
["ini", true, "linuxcnc/machines/xyzab-tdr/machine.ini"],
|
||||
["parameters", true, "linuxcnc/machines/xyzab-tdr/linuxcnc.var"],
|
||||
["toolTable", true, "linuxcnc/machines/xyzab-tdr/tool.tbl"],
|
||||
["gcode", true, "linuxcnc/gcode/fixture.ngc"],
|
||||
],
|
||||
);
|
||||
assert.deepEqual(readyMachineSession.snapshotCheck, {
|
||||
name: "snapshot",
|
||||
path: "linuxcnc/sessions/machine-session-1/machine-session-snapshot.json",
|
||||
ok: true,
|
||||
error: null,
|
||||
});
|
||||
assert.equal(readyMachineSession.snapshot.sessionId, "machine-session-1");
|
||||
|
||||
const missingMachineSession = await readMachineSessionReadiness("missing-machine", {
|
||||
storage,
|
||||
sessionId: "missing-session",
|
||||
gcodeRequired: true,
|
||||
gcodeFilename: "missing.ngc",
|
||||
});
|
||||
assert.equal(missingMachineSession.ready, false);
|
||||
assert.equal(missingMachineSession.phase, "blocked");
|
||||
assert.deepEqual(missingMachineSession.missing, [
|
||||
"ini",
|
||||
"parameters",
|
||||
"toolTable",
|
||||
"gcode",
|
||||
"snapshot",
|
||||
]);
|
||||
assert.deepEqual(
|
||||
missingMachineSession.errors.map((error) => error.name),
|
||||
["ini", "parameters", "toolTable", "gcode", "snapshot"],
|
||||
);
|
||||
assert.match(missingMachineSession.errors[0].error, /missing directory|missing file/);
|
||||
|
||||
const bridgeFiles = new Map();
|
||||
const bridgeInterp = {
|
||||
writeTextFile(path, text) {
|
||||
@@ -897,6 +948,13 @@ await assert.rejects(
|
||||
() => loadGcodeProgram("../escape.ngc", { storage }),
|
||||
/Invalid G-code filename/,
|
||||
);
|
||||
await assert.rejects(
|
||||
() => readMachineSessionReadiness("xyzab-tdr", {
|
||||
storage,
|
||||
gcodeOpfsPath: "../escape.ngc",
|
||||
}),
|
||||
/Invalid OPFS path/,
|
||||
);
|
||||
await assert.rejects(
|
||||
() => saveSessionSnapshot("session-1", {}, {
|
||||
storage,
|
||||
|
||||
@@ -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");
|
||||
6
wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh
Executable file
6
wasm-port/tests/sdk/node/verify_project_batch_acceptance_artifact.sh
Executable 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" "$@"
|
||||
@@ -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");
|
||||
9
wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh
Executable file
9
wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh
Executable 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"
|
||||
@@ -0,0 +1,157 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createProjectReleaseReadinessReport,
|
||||
loadProjectReleaseReadinessArtifactUrlWorkflow,
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const manifest = createProjectReleaseGateManifest();
|
||||
const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
}));
|
||||
|
||||
let fetchedUrl = null;
|
||||
const readyWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
fetchRef: async (url) => {
|
||||
fetchedUrl = url;
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => readyArtifactJson,
|
||||
};
|
||||
},
|
||||
});
|
||||
const readySummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(readyWorkflow);
|
||||
const readyActionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(readyWorkflow);
|
||||
|
||||
assert.equal(fetchedUrl, "/project-release-readiness.json");
|
||||
assert.equal(readyWorkflow.apiName, "project-release-readiness-artifact-url-workflow");
|
||||
assert.equal(readyWorkflow.workflowVersion, 1);
|
||||
assert.equal(readyWorkflow.ready, true);
|
||||
assert.equal(readyWorkflow.fetched, true);
|
||||
assert.equal(readyWorkflow.httpStatus, 200);
|
||||
assert.equal(readyWorkflow.fetchError, null);
|
||||
assert.equal(readyWorkflow.jsonWorkflow.ready, true);
|
||||
assert.equal(readyWorkflow.validation.ready, true);
|
||||
assert.equal(readyWorkflow.actionPlan.ready, true);
|
||||
assert.equal(readySummary.apiName, "project-release-readiness-artifact-url-workflow-summary-view-model");
|
||||
assert.equal(readySummary.statusLine, "Ready: Artifact URL workflow complete");
|
||||
assert.deepEqual(
|
||||
readySummary.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["artifact-url", "/project-release-readiness.json"],
|
||||
["fetch", "ready (200)"],
|
||||
["json-workflow", "ready"],
|
||||
["artifact-validation", "ready"],
|
||||
["next-command", "none"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
assert.deepEqual(readyActionPlan, {
|
||||
apiName: "project-release-readiness-artifact-url-workflow-action-plan",
|
||||
planVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
missing: [],
|
||||
actionCount: 0,
|
||||
commandCount: 0,
|
||||
nextActionId: null,
|
||||
nextCommand: null,
|
||||
actions: [],
|
||||
commands: [],
|
||||
shellScript: "# Project release readiness artifact URL workflow is already ready.\n",
|
||||
rows: [],
|
||||
});
|
||||
|
||||
const missingInputWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "",
|
||||
fetchRef: null,
|
||||
});
|
||||
const missingInputSummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
missingInputWorkflow,
|
||||
);
|
||||
const missingInputActionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(
|
||||
missingInputWorkflow,
|
||||
);
|
||||
assert.equal(missingInputWorkflow.ready, false);
|
||||
assert.equal(missingInputWorkflow.fetched, false);
|
||||
assert.deepEqual(missingInputWorkflow.missing.slice(0, 2), ["artifact-url", "fetch"]);
|
||||
assert.match(missingInputSummary.statusLine, /^Blocked: artifact-url, fetch/);
|
||||
assert.equal(missingInputSummary.rows.find(({ id }) => id === "artifact-url")?.value, "missing");
|
||||
assert.equal(missingInputActionPlan.phase, "blocked");
|
||||
assert.equal(missingInputActionPlan.actionCount, 4);
|
||||
assert.equal(missingInputActionPlan.commandCount, 2);
|
||||
assert.equal(missingInputActionPlan.nextActionId, "provide-artifact-url");
|
||||
assert.equal(
|
||||
missingInputActionPlan.nextCommand,
|
||||
"wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
);
|
||||
assert.deepEqual(
|
||||
missingInputActionPlan.actions.slice(0, 2).map(({ id, kind, command }) => [id, kind, command]),
|
||||
[
|
||||
["provide-artifact-url", "input", null],
|
||||
["provide-fetch", "input", null],
|
||||
],
|
||||
);
|
||||
assert.match(missingInputActionPlan.shellScript, /^#!\/usr\/bin\/env bash\nset -euo pipefail\n/);
|
||||
|
||||
const httpFailureWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/missing.json",
|
||||
fetchRef: async () => ({
|
||||
ok: false,
|
||||
status: 404,
|
||||
text: async () => "",
|
||||
}),
|
||||
});
|
||||
const httpFailureSummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
httpFailureWorkflow,
|
||||
);
|
||||
const httpFailureActionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(
|
||||
httpFailureWorkflow,
|
||||
);
|
||||
assert.equal(httpFailureWorkflow.ready, false);
|
||||
assert.equal(httpFailureWorkflow.fetched, false);
|
||||
assert.equal(httpFailureWorkflow.httpStatus, 404);
|
||||
assert.match(httpFailureWorkflow.fetchError, /404/);
|
||||
assert.equal(httpFailureWorkflow.missing[0], "artifact-fetch");
|
||||
assert.equal(
|
||||
httpFailureWorkflow.actionPlan.nextCommand,
|
||||
"wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
);
|
||||
assert.match(httpFailureSummary.statusLine, /^Blocked: artifact-fetch/);
|
||||
assert.equal(httpFailureSummary.rows.find(({ id }) => id === "fetch")?.value, "blocked");
|
||||
assert.equal(httpFailureActionPlan.phase, "blocked");
|
||||
assert.equal(httpFailureActionPlan.actionCount, 3);
|
||||
assert.equal(httpFailureActionPlan.commandCount, 2);
|
||||
assert.equal(httpFailureActionPlan.nextActionId, "verify-artifact-url-fetch");
|
||||
assert.equal(
|
||||
httpFailureActionPlan.rows[0].value,
|
||||
"fetch",
|
||||
);
|
||||
|
||||
const thrownFetchWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/throws.json",
|
||||
fetchRef: async () => {
|
||||
throw new Error("network unavailable");
|
||||
},
|
||||
});
|
||||
const thrownFetchSummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
thrownFetchWorkflow,
|
||||
);
|
||||
const thrownFetchActionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(
|
||||
thrownFetchWorkflow,
|
||||
);
|
||||
assert.equal(thrownFetchWorkflow.ready, false);
|
||||
assert.equal(thrownFetchWorkflow.fetched, false);
|
||||
assert.equal(thrownFetchWorkflow.httpStatus, null);
|
||||
assert.equal(thrownFetchWorkflow.fetchError, "network unavailable");
|
||||
assert.equal(thrownFetchWorkflow.missing[0], "artifact-fetch");
|
||||
assert.match(thrownFetchSummary.statusLine, /^Blocked: artifact-fetch/);
|
||||
assert.equal(thrownFetchActionPlan.nextActionId, "verify-artifact-url-fetch");
|
||||
assert.equal(thrownFetchActionPlan.commands.length, 2);
|
||||
|
||||
console.log("project_release_artifact_url_workflow_node_smoke=ok");
|
||||
6
wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.sh
Executable file
6
wasm-port/tests/sdk/node/verify_project_release_artifact_url_workflow.sh
Executable 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_release_artifact_url_workflow.mjs"
|
||||
@@ -0,0 +1,501 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseGateResultMatrix,
|
||||
createProjectReleaseReadinessArtifactJsonWorkflow,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
createProjectReleaseReadinessArtifactValidationActionPlan,
|
||||
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
createProjectReleaseReadinessReport,
|
||||
createProjectReleaseReadinessSummaryViewModel,
|
||||
loadProjectReleaseReadinessArtifactUrlWorkflow,
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const manifest = createProjectReleaseGateManifest();
|
||||
|
||||
assert.equal(manifest.apiName, "project-release-gate-manifest");
|
||||
assert.equal(manifest.manifestVersion, 1);
|
||||
assert.equal(manifest.gateCount, 12);
|
||||
assert.deepEqual(manifest.gateIds, [
|
||||
"diff-check",
|
||||
"vendor-sync",
|
||||
"standalone-cnc-semantics",
|
||||
"interp-wasm",
|
||||
"sim-config-inventory",
|
||||
"ini-panel-browser",
|
||||
"opfs-session-browser",
|
||||
"release-artifact-url-browser",
|
||||
"project-batch-acceptance",
|
||||
"ui-node-smokes",
|
||||
"host-smokes",
|
||||
"project-release-gate",
|
||||
]);
|
||||
assert.deepEqual(
|
||||
manifest.gates.map(({ expectedOutput }) => expectedOutput),
|
||||
[
|
||||
null,
|
||||
"vendor sync up to date",
|
||||
"standalone CNC semantics guard complete",
|
||||
"interp_wasm_node_smoke=ok",
|
||||
"sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
"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",
|
||||
],
|
||||
);
|
||||
assert.equal(
|
||||
manifest.gates.find(({ id }) => id === "release-artifact-url-browser")?.command,
|
||||
"SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_release_artifact_url_workflow_browser.sh",
|
||||
);
|
||||
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: {
|
||||
"diff-check": true,
|
||||
},
|
||||
observedOutputs: [
|
||||
"vendor sync up to date",
|
||||
"browser_release_artifact_url_workflow_smoke=ok",
|
||||
],
|
||||
});
|
||||
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, 12);
|
||||
assert.equal(partialMatrix.passedCount, 3);
|
||||
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",
|
||||
]);
|
||||
assert.deepEqual(
|
||||
partialMatrix.rows.filter(({ passed }) => passed).map(({ id }) => id),
|
||||
[
|
||||
"diff-check",
|
||||
"vendor-sync",
|
||||
"release-artifact-url-browser",
|
||||
],
|
||||
);
|
||||
|
||||
const partialExecutionManifest = createProjectReleaseGateExecutionManifest({
|
||||
gateResults: {
|
||||
"diff-check": true,
|
||||
},
|
||||
observedOutputs: [
|
||||
"vendor sync up to date",
|
||||
"browser_release_artifact_url_workflow_smoke=ok",
|
||||
],
|
||||
});
|
||||
assert.equal(partialExecutionManifest.apiName, "project-release-gate-execution-manifest");
|
||||
assert.equal(partialExecutionManifest.manifestVersion, 1);
|
||||
assert.equal(partialExecutionManifest.phase, "waiting");
|
||||
assert.equal(partialExecutionManifest.ready, false);
|
||||
assert.equal(partialExecutionManifest.gateCount, 12);
|
||||
assert.equal(partialExecutionManifest.passedCount, 3);
|
||||
assert.equal(partialExecutionManifest.unknownCount, 9);
|
||||
assert.deepEqual(partialExecutionManifest.missingGateIds, partialMatrix.missingGateIds);
|
||||
assert.deepEqual(
|
||||
partialExecutionManifest.rows.filter(({ passed }) => passed).map(({ id, evidence }) => [id, evidence]),
|
||||
[
|
||||
["diff-check", "explicit-result"],
|
||||
["vendor-sync", "expected-output"],
|
||||
["release-artifact-url-browser", "expected-output"],
|
||||
],
|
||||
);
|
||||
assert.equal(
|
||||
partialExecutionManifest.rows.find(({ id }) => id === "vendor-sync")?.observedOutput,
|
||||
"vendor sync up to date",
|
||||
);
|
||||
assert.equal(
|
||||
partialExecutionManifest.rows.find(({ id }) => id === "standalone-cnc-semantics")?.observedOutput,
|
||||
null,
|
||||
);
|
||||
const partialExecutionSummary = createProjectReleaseGateExecutionSummaryViewModel(partialExecutionManifest);
|
||||
assert.equal(partialExecutionSummary.apiName, "project-release-gate-execution-summary-view-model");
|
||||
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, 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, 9);
|
||||
assert.equal(partialExecutionSummary.nextGateId, "standalone-cnc-semantics");
|
||||
assert.deepEqual(partialExecutionSummary.evidenceCounts, {
|
||||
"explicit-result": 1,
|
||||
"expected-output": 2,
|
||||
missing: 9,
|
||||
});
|
||||
assert.deepEqual(
|
||||
partialExecutionSummary.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["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, project-batch-acceptance, ui-node-smokes, host-smokes, project-release-gate"],
|
||||
],
|
||||
);
|
||||
assert.equal(partialExecutionSummary.gateRows.length, manifest.gateCount);
|
||||
assert.equal(
|
||||
partialExecutionSummary.gateRows.find(({ id }) => id === "vendor-sync")?.evidence,
|
||||
"expected-output",
|
||||
);
|
||||
|
||||
const partialActionPlan = createProjectReleaseGateActionPlan({ gateResultMatrix: partialMatrix });
|
||||
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, 12);
|
||||
assert.equal(partialActionPlan.completedCount, 3);
|
||||
assert.equal(partialActionPlan.pendingCount, 9);
|
||||
assert.deepEqual(partialActionPlan.completedGateIds, [
|
||||
"diff-check",
|
||||
"vendor-sync",
|
||||
"release-artifact-url-browser",
|
||||
]);
|
||||
assert.deepEqual(partialActionPlan.pendingGateIds, [
|
||||
"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(partialActionPlan.nextGateId, "standalone-cnc-semantics");
|
||||
assert.equal(partialActionPlan.nextCommand, "wasm-port/tools/verify_no_standalone_cnc_semantics.sh");
|
||||
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");
|
||||
assert.match(partialActionPlan.shellScript, /^#!\/usr\/bin\/env bash\nset -euo pipefail\n/);
|
||||
assert.match(partialActionPlan.shellScript, /wasm-port\/tests\/host\/verify_project_release_gate\.sh/);
|
||||
|
||||
const report = createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
});
|
||||
assert.deepEqual(report.gateManifest, manifest);
|
||||
assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
|
||||
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, "12/12 passed");
|
||||
assert.equal(report.gateExecutionSummaryViewModel.nextGateId, null);
|
||||
assert.equal(report.gateResultMatrix.ready, true);
|
||||
assert.equal(report.gateResultMatrix.passedCount, manifest.gateCount);
|
||||
assert.equal(report.gateActionPlan.ready, true);
|
||||
assert.equal(report.gateActionPlan.pendingCount, 0);
|
||||
assert.equal(report.gateActionPlan.nextCommand, null);
|
||||
assert.equal(report.gateActionPlan.shellScript, "# Project release gates are already marked passed.\n");
|
||||
assert.deepEqual(report.gates.map(({ id }) => id), manifest.gateIds);
|
||||
assert.equal(report.gates.length, manifest.gateCount);
|
||||
assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
||||
apiName: "project-release-readiness-summary-view-model",
|
||||
viewModelVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
title: "Project release readiness",
|
||||
statusText: "Ready",
|
||||
detailText: "No blocking reasons",
|
||||
statusLine: "Ready: No blocking reasons",
|
||||
rows: [
|
||||
{
|
||||
id: "gate-results",
|
||||
label: "Release gates",
|
||||
value: "12/12 passed",
|
||||
},
|
||||
{
|
||||
id: "unknown-gates",
|
||||
label: "Unknown gates",
|
||||
value: "0",
|
||||
},
|
||||
{
|
||||
id: "sim-config-inventory",
|
||||
label: "Sim config inventory",
|
||||
value: "unexpected_fail=0",
|
||||
},
|
||||
{
|
||||
id: "blocked-runtime-families",
|
||||
label: "Blocked runtime families",
|
||||
value: "L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP",
|
||||
},
|
||||
{
|
||||
id: "missing",
|
||||
label: "Missing readiness",
|
||||
value: "none",
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
|
||||
"Waiting: project-release-gate",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
||||
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
||||
"promoted: L4-TOOL-DB",
|
||||
);
|
||||
|
||||
const artifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
||||
createProjectReleaseReadinessArtifactValidation(report),
|
||||
);
|
||||
const artifactValidationActionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(
|
||||
createProjectReleaseReadinessArtifactValidation(report),
|
||||
);
|
||||
const artifactJsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({
|
||||
artifactJson: JSON.stringify(report),
|
||||
});
|
||||
const artifactUrlWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
fetchRef: async () => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(report),
|
||||
}),
|
||||
});
|
||||
const artifactUrlWorkflowSummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
artifactUrlWorkflow,
|
||||
);
|
||||
assert.equal(
|
||||
artifactValidationSummary.apiName,
|
||||
"project-release-readiness-artifact-validation-summary-view-model",
|
||||
);
|
||||
assert.equal(artifactValidationSummary.phase, "ready");
|
||||
assert.equal(artifactValidationSummary.statusLine, "Ready: Artifact evidence complete");
|
||||
assert.deepEqual(
|
||||
artifactValidationSummary.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["artifact", "project-release-readiness-report"],
|
||||
["gate-count", "12/12"],
|
||||
["gate-manifest", "ready"],
|
||||
["gate-execution-manifest", "ready"],
|
||||
["gate-execution-summary", "ready"],
|
||||
["gate-result-matrix", "ready"],
|
||||
["gate-action-plan", "ready"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
assert.deepEqual(artifactValidationActionPlan, {
|
||||
apiName: "project-release-readiness-artifact-validation-action-plan",
|
||||
planVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
missing: [],
|
||||
missingCount: 0,
|
||||
commandCount: 0,
|
||||
nextActionId: null,
|
||||
nextCommand: null,
|
||||
commands: [],
|
||||
shellScript: "# Project release readiness artifact validation is already ready.\n",
|
||||
rows: [
|
||||
{
|
||||
id: "artifact-validation",
|
||||
label: "Artifact validation",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "missing",
|
||||
label: "Missing artifact evidence",
|
||||
value: "none",
|
||||
},
|
||||
{
|
||||
id: "next-command",
|
||||
label: "Next command",
|
||||
value: "none",
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.equal(artifactJsonWorkflow.apiName, "project-release-readiness-artifact-json-workflow");
|
||||
assert.equal(artifactJsonWorkflow.workflowVersion, 1);
|
||||
assert.equal(artifactJsonWorkflow.phase, "ready");
|
||||
assert.equal(artifactJsonWorkflow.ready, true);
|
||||
assert.equal(artifactJsonWorkflow.parsed, true);
|
||||
assert.equal(artifactJsonWorkflow.parseError, null);
|
||||
assert.equal(artifactJsonWorkflow.artifactApiName, "project-release-readiness-report");
|
||||
assert.equal(artifactJsonWorkflow.validation.ready, true);
|
||||
assert.equal(artifactJsonWorkflow.summaryViewModel.statusLine, "Ready: Artifact evidence complete");
|
||||
assert.equal(artifactJsonWorkflow.actionPlan.commandCount, 0);
|
||||
assert.deepEqual(
|
||||
artifactJsonWorkflow.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["parse", "ready"],
|
||||
["validation", "ready"],
|
||||
["next-command", "none"],
|
||||
],
|
||||
);
|
||||
assert.equal(artifactUrlWorkflow.apiName, "project-release-readiness-artifact-url-workflow");
|
||||
assert.equal(artifactUrlWorkflow.workflowVersion, 1);
|
||||
assert.equal(artifactUrlWorkflow.phase, "ready");
|
||||
assert.equal(artifactUrlWorkflow.ready, true);
|
||||
assert.equal(artifactUrlWorkflow.artifactUrl, "/project-release-readiness.json");
|
||||
assert.equal(artifactUrlWorkflow.fetched, true);
|
||||
assert.equal(artifactUrlWorkflow.httpStatus, 200);
|
||||
assert.equal(artifactUrlWorkflow.fetchError, null);
|
||||
assert.equal(artifactUrlWorkflow.jsonWorkflow.ready, true);
|
||||
assert.equal(artifactUrlWorkflow.validation.ready, true);
|
||||
assert.equal(artifactUrlWorkflow.summaryViewModel.statusLine, "Ready: Artifact evidence complete");
|
||||
assert.equal(artifactUrlWorkflow.actionPlan.commandCount, 0);
|
||||
assert.deepEqual(
|
||||
artifactUrlWorkflow.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["fetch", "ready"],
|
||||
["json-workflow", "ready"],
|
||||
["next-command", "none"],
|
||||
],
|
||||
);
|
||||
assert.equal(
|
||||
artifactUrlWorkflowSummary.apiName,
|
||||
"project-release-readiness-artifact-url-workflow-summary-view-model",
|
||||
);
|
||||
assert.equal(artifactUrlWorkflowSummary.viewModelVersion, 1);
|
||||
assert.equal(artifactUrlWorkflowSummary.phase, "ready");
|
||||
assert.equal(artifactUrlWorkflowSummary.statusLine, "Ready: Artifact URL workflow complete");
|
||||
assert.deepEqual(
|
||||
artifactUrlWorkflowSummary.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["artifact-url", "/project-release-readiness.json"],
|
||||
["fetch", "ready (200)"],
|
||||
["json-workflow", "ready"],
|
||||
["artifact-validation", "ready"],
|
||||
["next-command", "none"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
const staleArtifactValidationSummary = createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
||||
createProjectReleaseReadinessArtifactValidation({}),
|
||||
);
|
||||
const staleArtifactValidation = createProjectReleaseReadinessArtifactValidation({});
|
||||
const staleArtifactValidationActionPlan = createProjectReleaseReadinessArtifactValidationActionPlan(staleArtifactValidation);
|
||||
const staleArtifactJsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({
|
||||
artifactJson: "{}",
|
||||
});
|
||||
const invalidArtifactJsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({
|
||||
artifactJson: "{not-json",
|
||||
});
|
||||
const missingArtifactUrlWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "",
|
||||
fetchRef: null,
|
||||
});
|
||||
const failedArtifactUrlWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/missing.json",
|
||||
fetchRef: async () => ({
|
||||
ok: false,
|
||||
status: 404,
|
||||
text: async () => "",
|
||||
}),
|
||||
});
|
||||
const missingArtifactUrlWorkflowSummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
missingArtifactUrlWorkflow,
|
||||
);
|
||||
const failedArtifactUrlWorkflowSummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
failedArtifactUrlWorkflow,
|
||||
);
|
||||
assert.equal(staleArtifactValidationSummary.phase, "blocked");
|
||||
assert.match(staleArtifactValidationSummary.statusLine, /^Blocked: apiName, reportVersion/);
|
||||
assert.equal(staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-action-plan")?.value, "missing");
|
||||
assert.equal(
|
||||
staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-execution-manifest")?.value,
|
||||
"missing",
|
||||
);
|
||||
assert.equal(
|
||||
staleArtifactValidationSummary.rows.find(({ id }) => id === "gate-execution-summary")?.value,
|
||||
"missing",
|
||||
);
|
||||
assert.equal(staleArtifactValidationActionPlan.phase, "blocked");
|
||||
assert.equal(staleArtifactValidationActionPlan.ready, false);
|
||||
assert.equal(staleArtifactValidationActionPlan.missingCount, staleArtifactValidation.missing.length);
|
||||
assert.equal(staleArtifactValidationActionPlan.commandCount, 2);
|
||||
assert.equal(staleArtifactValidationActionPlan.nextActionId, "project-release-gate");
|
||||
assert.equal(
|
||||
staleArtifactValidationActionPlan.nextCommand,
|
||||
"wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
);
|
||||
assert.equal(
|
||||
staleArtifactValidationActionPlan.commands[1].command,
|
||||
"wasm-port/tests/host/verify_project_release_readiness_artifact.sh build/project-release-readiness.json",
|
||||
);
|
||||
assert.match(staleArtifactValidationActionPlan.shellScript, /^#!\/usr\/bin\/env bash\nset -euo pipefail\n/);
|
||||
assert.match(staleArtifactValidationActionPlan.shellScript, /verify_project_release_readiness_artifact\.sh/);
|
||||
assert.equal(
|
||||
staleArtifactValidationActionPlan.rows.find(({ id }) => id === "missing")?.value,
|
||||
staleArtifactValidation.missing.join(", "),
|
||||
);
|
||||
assert.equal(staleArtifactJsonWorkflow.parsed, true);
|
||||
assert.equal(staleArtifactJsonWorkflow.ready, false);
|
||||
assert.equal(staleArtifactJsonWorkflow.phase, "blocked");
|
||||
assert.equal(staleArtifactJsonWorkflow.actionPlan.commandCount, 2);
|
||||
assert.deepEqual(staleArtifactJsonWorkflow.missing, staleArtifactValidation.missing);
|
||||
assert.equal(invalidArtifactJsonWorkflow.parsed, false);
|
||||
assert.equal(invalidArtifactJsonWorkflow.ready, false);
|
||||
assert.equal(invalidArtifactJsonWorkflow.phase, "blocked");
|
||||
assert.match(invalidArtifactJsonWorkflow.parseError, /JSON/);
|
||||
assert.equal(invalidArtifactJsonWorkflow.missing[0], "artifact-json");
|
||||
assert.equal(
|
||||
invalidArtifactJsonWorkflow.actionPlan.nextCommand,
|
||||
"wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
);
|
||||
assert.deepEqual(
|
||||
invalidArtifactJsonWorkflow.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["parse", "blocked"],
|
||||
["validation", "blocked"],
|
||||
["next-command", "wasm-port/tests/host/verify_project_release_gate.sh"],
|
||||
],
|
||||
);
|
||||
assert.equal(missingArtifactUrlWorkflow.ready, false);
|
||||
assert.equal(missingArtifactUrlWorkflow.fetched, false);
|
||||
assert.deepEqual(missingArtifactUrlWorkflow.missing.slice(0, 2), ["artifact-url", "fetch"]);
|
||||
assert.equal(failedArtifactUrlWorkflow.ready, false);
|
||||
assert.equal(failedArtifactUrlWorkflow.fetched, false);
|
||||
assert.equal(failedArtifactUrlWorkflow.httpStatus, 404);
|
||||
assert.match(failedArtifactUrlWorkflow.fetchError, /404/);
|
||||
assert.equal(failedArtifactUrlWorkflow.missing[0], "artifact-fetch");
|
||||
assert.equal(
|
||||
failedArtifactUrlWorkflow.actionPlan.nextCommand,
|
||||
"wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
);
|
||||
assert.equal(missingArtifactUrlWorkflowSummary.phase, "blocked");
|
||||
assert.match(missingArtifactUrlWorkflowSummary.statusLine, /^Blocked: artifact-url, fetch/);
|
||||
assert.equal(
|
||||
missingArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "artifact-url")?.value,
|
||||
"missing",
|
||||
);
|
||||
assert.equal(failedArtifactUrlWorkflowSummary.phase, "blocked");
|
||||
assert.match(failedArtifactUrlWorkflowSummary.statusLine, /^Blocked: artifact-fetch/);
|
||||
assert.equal(
|
||||
failedArtifactUrlWorkflowSummary.rows.find(({ id }) => id === "fetch")?.value,
|
||||
"blocked",
|
||||
);
|
||||
|
||||
console.log("project_release_gate_manifest_node_smoke=ok");
|
||||
6
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
Executable file
6
wasm-port/tests/sdk/node/verify_project_release_gate_manifest.sh
Executable 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_release_gate_manifest.mjs"
|
||||
926
wasm-port/tests/sdk/node/verify_sdk_surface.mjs
Normal file
926
wasm-port/tests/sdk/node/verify_sdk_surface.mjs
Normal file
@@ -0,0 +1,926 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import {
|
||||
INI_PANEL_ENTRIES,
|
||||
analyzeIniRuntimeBoundaries,
|
||||
createControlPageBrowserApiManifest,
|
||||
createControlPageBrowserApiMethods,
|
||||
createControlPageBrowserApiSchema,
|
||||
createIniPanelLaunchApiManifest,
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createIniPanelShellApiSurfaceInventory,
|
||||
createIniPanelShellControlPageContract,
|
||||
createIniPanelShellIntegrationManifest,
|
||||
createIniPanelShellIntegrationReadiness,
|
||||
createIniPanelShellIntegrationSchema,
|
||||
createIniPanelShellIntegrationWorkflowPlan,
|
||||
createIniPanelShellSessionReadinessWorkflowReport,
|
||||
createIniPanelShellViewModel,
|
||||
createIniPanelShellWorkflowOverviewContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
createLinuxCncIniSdk,
|
||||
createLinuxCncInterpSdk,
|
||||
createMachineSessionSnapshotPayload,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createProjectBatchAcceptanceActionPlan,
|
||||
createProjectBatchAcceptanceCapabilityMatrix,
|
||||
createProjectBatchAcceptanceChecklist,
|
||||
createProjectBatchAcceptanceReport,
|
||||
createProjectBatchAcceptanceReportJsonWorkflow,
|
||||
createProjectBatchAcceptanceReportUrlWorkflowActionPlan,
|
||||
createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel,
|
||||
createProjectBatchAcceptanceReportValidationActionPlan,
|
||||
createProjectBatchAcceptanceReportValidationSummaryViewModel,
|
||||
createProjectBatchAcceptanceReportValidation,
|
||||
createProjectBatchAcceptanceSummaryViewModel,
|
||||
createProjectBatchAcceptanceWorkflow,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseGateResultMatrix,
|
||||
createProjectReleaseReadinessArtifactJsonWorkflow,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
createProjectReleaseReadinessArtifactValidationActionPlan,
|
||||
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
createProjectReleaseReadinessReport,
|
||||
createProjectReleaseReadinessSummaryViewModel,
|
||||
parseProjectReleaseReadinessArtifactJson,
|
||||
createSessionSnapshot,
|
||||
defaultMachinePaths,
|
||||
gcodeFilenameFromProgramPath,
|
||||
gcodeProgramPath,
|
||||
getIniPanelEntryByHref,
|
||||
getIniPanelEntryManifest,
|
||||
getOpfsRoot,
|
||||
getVisibleIniPanelEntries,
|
||||
isSupportedIniPanelControlPageApiManifest,
|
||||
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,
|
||||
loadMachineTextFiles,
|
||||
loadMachineToolTableFromOpfs,
|
||||
loadTextFile,
|
||||
machineFilePaths,
|
||||
machineIniPath,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
normalizeOpfsPath,
|
||||
parameterFilePath,
|
||||
planIniFileContextStaging,
|
||||
planSimConfigStaging,
|
||||
readMachineSessionReadiness,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
restoreMachineParametersFromOpfs,
|
||||
saveMachineSessionSnapshot,
|
||||
saveMachineTextFiles,
|
||||
saveSessionSnapshot,
|
||||
saveTextFile,
|
||||
sessionSnapshotPath,
|
||||
toolTablePath,
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson,
|
||||
validateSessionSnapshot,
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
const sdkIndexText = readFileSync(resolve(root, "runtime/sdk/src/index.js"), "utf8");
|
||||
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
|
||||
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],
|
||||
["createProjectReleaseGateManifest", createProjectReleaseGateManifest],
|
||||
["createProjectReleaseGateResultMatrix", createProjectReleaseGateResultMatrix],
|
||||
["createProjectReleaseReadinessArtifactJsonWorkflow", createProjectReleaseReadinessArtifactJsonWorkflow],
|
||||
[
|
||||
"createProjectReleaseReadinessArtifactUrlWorkflowActionPlan",
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
],
|
||||
[
|
||||
"createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
],
|
||||
["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation],
|
||||
[
|
||||
"createProjectReleaseReadinessArtifactValidationActionPlan",
|
||||
createProjectReleaseReadinessArtifactValidationActionPlan,
|
||||
],
|
||||
[
|
||||
"createProjectReleaseReadinessArtifactValidationSummaryViewModel",
|
||||
createProjectReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
],
|
||||
["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport],
|
||||
["createProjectReleaseReadinessSummaryViewModel", createProjectReleaseReadinessSummaryViewModel],
|
||||
["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson],
|
||||
[
|
||||
"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],
|
||||
["analyzeIniRuntimeBoundaries", analyzeIniRuntimeBoundaries],
|
||||
["getIniPanelEntryManifest", getIniPanelEntryManifest],
|
||||
["getIniPanelEntryByHref", getIniPanelEntryByHref],
|
||||
["getVisibleIniPanelEntries", getVisibleIniPanelEntries],
|
||||
["createIniPanelLauncherLinkViewModel", createIniPanelLauncherLinkViewModel],
|
||||
["createMachineSessionPersistenceDisplayViewModel", createMachineSessionPersistenceDisplayViewModel],
|
||||
["createMachineSessionPersistenceRenderState", createMachineSessionPersistenceRenderState],
|
||||
["createMachineSessionPersistenceSummary", createMachineSessionPersistenceSummary],
|
||||
["getOpfsRoot", getOpfsRoot],
|
||||
["saveTextFile", saveTextFile],
|
||||
["loadTextFile", loadTextFile],
|
||||
["machineIniPath", machineIniPath],
|
||||
["toolTablePath", toolTablePath],
|
||||
["parameterFilePath", parameterFilePath],
|
||||
["gcodeProgramPath", gcodeProgramPath],
|
||||
["sessionSnapshotPath", sessionSnapshotPath],
|
||||
["defaultMachinePaths", defaultMachinePaths],
|
||||
["normalizeOpfsPath", normalizeOpfsPath],
|
||||
["createMachineSessionSnapshotPayload", createMachineSessionSnapshotPayload],
|
||||
["createSessionSnapshot", createSessionSnapshot],
|
||||
["saveSessionSnapshot", saveSessionSnapshot],
|
||||
["saveMachineSessionSnapshot", saveMachineSessionSnapshot],
|
||||
["loadMachineSessionSnapshot", loadMachineSessionSnapshot],
|
||||
["validateSessionSnapshot", validateSessionSnapshot],
|
||||
["machineFilePaths", machineFilePaths],
|
||||
["saveMachineTextFiles", saveMachineTextFiles],
|
||||
["loadMachineTextFiles", loadMachineTextFiles],
|
||||
["gcodeFilenameFromProgramPath", gcodeFilenameFromProgramPath],
|
||||
["restoreMachineParametersFromOpfs", restoreMachineParametersFromOpfs],
|
||||
["loadMachineToolTableFromOpfs", loadMachineToolTableFromOpfs],
|
||||
["loadMachineSessionFromOpfs", loadMachineSessionFromOpfs],
|
||||
["readMachineSessionReadiness", readMachineSessionReadiness],
|
||||
["createControlPageBrowserApiSchema", createControlPageBrowserApiSchema],
|
||||
["createControlPageBrowserApiManifest", createControlPageBrowserApiManifest],
|
||||
["createControlPageBrowserApiMethods", createControlPageBrowserApiMethods],
|
||||
["createIniPanelShellControlPageContract", createIniPanelShellControlPageContract],
|
||||
["isSupportedIniPanelControlPageApiManifest", isSupportedIniPanelControlPageApiManifest],
|
||||
["createIniPanelLaunchApiManifest", createIniPanelLaunchApiManifest],
|
||||
["isSupportedIniPanelLaunchApiManifest", isSupportedIniPanelLaunchApiManifest],
|
||||
["createIniPanelShellIntegrationSchema", createIniPanelShellIntegrationSchema],
|
||||
["createIniPanelShellIntegrationManifest", createIniPanelShellIntegrationManifest],
|
||||
["createIniPanelShellIntegrationReadiness", createIniPanelShellIntegrationReadiness],
|
||||
["createIniPanelShellIntegrationWorkflowPlan", createIniPanelShellIntegrationWorkflowPlan],
|
||||
["isSupportedIniPanelShellIntegrationManifest", isSupportedIniPanelShellIntegrationManifest],
|
||||
["createIniPanelShellViewModel", createIniPanelShellViewModel],
|
||||
["createIniPanelShellApiSurfaceInventory", createIniPanelShellApiSurfaceInventory],
|
||||
["createIniPanelShellSessionReadinessWorkflowReport", createIniPanelShellSessionReadinessWorkflowReport],
|
||||
["createIniPanelShellWorkflowOverviewContract", createIniPanelShellWorkflowOverviewContract],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingMountDomContract",
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness",
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,
|
||||
],
|
||||
[
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness",
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness,
|
||||
],
|
||||
[
|
||||
"renderIniPanelShellWorkflowOverviewEmbeddingMountState",
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
],
|
||||
[
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState",
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
],
|
||||
[
|
||||
"mountIniPanelShellWorkflowOverviewEmbeddingMountState",
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
],
|
||||
[
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState",
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
],
|
||||
];
|
||||
|
||||
for (const [name, value] of requiredExports) {
|
||||
assert.equal(typeof value, "function", `${name} SDK export drift`);
|
||||
assert.match(sdkIndexText, new RegExp(`\\b${name}\\b`));
|
||||
assert.match(sdkReadmeText, new RegExp(`\\b${name}\\b`));
|
||||
}
|
||||
|
||||
assert.equal(machineIniPath("sdk-mill"), "linuxcnc/machines/sdk-mill/machine.ini");
|
||||
assert.deepEqual(defaultMachinePaths("sdk-mill"), machineFilePaths("sdk-mill"));
|
||||
assert.equal(gcodeProgramPath("demo.ngc"), "linuxcnc/gcode/demo.ngc");
|
||||
assert.equal(sessionSnapshotPath("sdk-session"), "linuxcnc/sessions/sdk-session/snapshot.json");
|
||||
assert.equal(normalizeOpfsPath("linuxcnc/machines/sdk-mill/tool.tbl"), "linuxcnc/machines/sdk-mill/tool.tbl");
|
||||
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"]);
|
||||
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
|
||||
assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []);
|
||||
assert.equal(releaseReadinessWaiting.gateActionPlan.apiName, "project-release-gate-action-plan");
|
||||
assert.equal(releaseReadinessWaiting.gateActionPlan.pendingCount, createProjectReleaseGateManifest().gateCount);
|
||||
assert.equal(releaseReadinessWaiting.gateActionPlan.nextGateId, "diff-check");
|
||||
assert.equal(
|
||||
releaseReadinessWaiting.rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
||||
"L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP",
|
||||
);
|
||||
const releaseReadinessReady = createProjectReleaseReadinessReport({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
});
|
||||
assert.equal(releaseReadinessReady.ready, true);
|
||||
assert.equal(releaseReadinessReady.releaseGate.passed, true);
|
||||
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
|
||||
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount);
|
||||
assert.equal(createProjectReleaseGateActionPlan({
|
||||
gateResultMatrix: releaseReadinessReady.gateResultMatrix,
|
||||
}).ready, false);
|
||||
assert.equal(createProjectReleaseGateActionPlan({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
}).shellScript, "# Project release gates are already marked passed.\n");
|
||||
assert.equal(createProjectReleaseReadinessSummaryViewModel(releaseReadinessReady).statusLine, "Ready: No blocking reasons");
|
||||
assert.equal(
|
||||
createProjectReleaseGateResultMatrix({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
}).rows.find(({ id }) => id === "project-release-gate")?.passed,
|
||||
true,
|
||||
);
|
||||
const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
})));
|
||||
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
|
||||
const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact);
|
||||
assert.deepEqual(releaseReadinessArtifactValidation, {
|
||||
apiName: "project-release-readiness-artifact-validation",
|
||||
validationVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
missing: [],
|
||||
artifactApiName: "project-release-readiness-report",
|
||||
artifactVersion: 1,
|
||||
gateCount: createProjectReleaseGateManifest().gateCount,
|
||||
expectedGateCount: createProjectReleaseGateManifest().gateCount,
|
||||
expectedGateIds: createProjectReleaseGateManifest().gateIds,
|
||||
gateManifestReady: true,
|
||||
gateExecutionManifestReady: true,
|
||||
gateExecutionSummaryReady: true,
|
||||
gateResultMatrixReady: true,
|
||||
gateActionPlanReady: true,
|
||||
blockedRuntimeFamilies: [
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
"L4-PYTHON-REMAP",
|
||||
],
|
||||
rows: [
|
||||
{
|
||||
id: "artifact",
|
||||
label: "Readiness artifact",
|
||||
value: "project-release-readiness-report",
|
||||
},
|
||||
{
|
||||
id: "project-release-gate",
|
||||
label: "Project release gate",
|
||||
value: "passed",
|
||||
},
|
||||
{
|
||||
id: "gate-manifest",
|
||||
label: "Gate manifest",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "gate-execution-manifest",
|
||||
label: "Gate execution manifest",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "gate-execution-summary",
|
||||
label: "Gate execution summary",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "gate-result-matrix",
|
||||
label: "Gate result matrix",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "gate-action-plan",
|
||||
label: "Gate action plan",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "validation",
|
||||
label: "Artifact validation",
|
||||
value: "ready",
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
||||
releaseReadinessArtifactValidation,
|
||||
).statusLine,
|
||||
"Ready: Artifact evidence complete",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactValidationActionPlan(releaseReadinessArtifactValidation).shellScript,
|
||||
"# Project release readiness artifact validation is already ready.\n",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactValidationActionPlan(createProjectReleaseReadinessArtifactValidation({}))
|
||||
.nextCommand,
|
||||
"wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactJsonWorkflow({
|
||||
artifactJson: JSON.stringify(releaseReadinessArtifact),
|
||||
}).summaryViewModel.statusLine,
|
||||
"Ready: Artifact evidence complete",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson: "not-json" }).parsed,
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
(await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
fetchRef: async () => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(releaseReadinessArtifact),
|
||||
}),
|
||||
})).summaryViewModel.statusLine,
|
||||
"Ready: Artifact evidence complete",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
fetchRef: async () => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(releaseReadinessArtifact),
|
||||
}),
|
||||
}),
|
||||
).statusLine,
|
||||
"Ready: Artifact URL workflow complete",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(
|
||||
await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
fetchRef: async () => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(releaseReadinessArtifact),
|
||||
}),
|
||||
}),
|
||||
).shellScript,
|
||||
"# Project release readiness artifact URL workflow is already ready.\n",
|
||||
);
|
||||
assert.equal(
|
||||
(await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/missing.json",
|
||||
fetchRef: async () => ({ ok: false, status: 404, text: async () => "" }),
|
||||
})).missing[0],
|
||||
"artifact-fetch",
|
||||
);
|
||||
assert.deepEqual(
|
||||
createProjectReleaseReadinessArtifactValidation({}).missing,
|
||||
[
|
||||
"apiName",
|
||||
"reportVersion",
|
||||
"phase",
|
||||
"ready",
|
||||
"missing",
|
||||
"releaseGate.command",
|
||||
"releaseGate.passed",
|
||||
"releaseGate.expectedOutput",
|
||||
"simConfigInventory.executed",
|
||||
"simConfigInventory.passed",
|
||||
"simConfigInventory.skipped",
|
||||
"simConfigInventory.unexpectedFail",
|
||||
"blockedRuntimeFamilies",
|
||||
"gateManifest",
|
||||
"gateExecutionManifest",
|
||||
"gateExecutionSummaryViewModel",
|
||||
"gateResultMatrix",
|
||||
"gateActionPlan",
|
||||
"gates.length",
|
||||
"gates.passed",
|
||||
"rows.project-release-gate",
|
||||
],
|
||||
);
|
||||
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 () => ({
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(releaseReadinessArtifact),
|
||||
}),
|
||||
});
|
||||
assert.equal(releaseReadinessArtifactUrlWorkflow.ready, true);
|
||||
assert.equal(
|
||||
releaseReadinessArtifactUrlWorkflow.validationSummaryViewModel.statusLine,
|
||||
"Ready: Artifact evidence complete",
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
releaseReadinessArtifactUrlWorkflow,
|
||||
).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(
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(JSON.stringify(releaseReadinessArtifact)),
|
||||
),
|
||||
).dataset.handoffScope,
|
||||
"workflow-overview-release-readiness-artifact",
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract().selectors.rows,
|
||||
"[data-workflow-overview-release-readiness-artifact-rows]",
|
||||
);
|
||||
assert.equal(createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness().ready, false);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessReport({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
||||
}).missing.includes("blocked-runtime-families"),
|
||||
true,
|
||||
);
|
||||
|
||||
assert.deepEqual(getIniPanelEntryManifest(), INI_PANEL_ENTRIES);
|
||||
assert.deepEqual(getVisibleIniPanelEntries(), INI_PANEL_ENTRIES);
|
||||
assert.deepEqual(getIniPanelEntryByHref("./control-page.html"), {
|
||||
id: "control-view",
|
||||
title: "Open read-only control view",
|
||||
href: "./control-page.html",
|
||||
});
|
||||
assert.deepEqual(createIniPanelLauncherLinkViewModel(), [
|
||||
{
|
||||
entryId: "edit-run",
|
||||
href: "./index.html",
|
||||
label: "Open edit and run panel",
|
||||
},
|
||||
{
|
||||
entryId: "control-view",
|
||||
href: "./control-page.html",
|
||||
label: "Open read-only control view",
|
||||
},
|
||||
{
|
||||
entryId: "workflow-overview",
|
||||
href: "./workflow-overview.html",
|
||||
label: "Open shell handoff overview",
|
||||
},
|
||||
]);
|
||||
assert.equal(
|
||||
createMachineSessionPersistenceSummary({
|
||||
state: {
|
||||
badges: { opfs: "OPFS: ready" },
|
||||
fields: { sessionSnapshot: "sdk-session/snapshot.json", sessionIni: "/work/session-machine.ini" },
|
||||
sessionLoad: { iniWasmPath: "/work/session-machine.ini" },
|
||||
sessionReadiness: { phase: "ready", ready: true, missing: [] },
|
||||
},
|
||||
}).rows.find(({ id }) => id === "session-load")?.value,
|
||||
"loaded: /work/session-machine.ini",
|
||||
);
|
||||
assert.equal(
|
||||
createMachineSessionPersistenceRenderState(
|
||||
createMachineSessionPersistenceDisplayViewModel(createMachineSessionPersistenceSummary()),
|
||||
).apiName,
|
||||
"machine-session-persistence-render-state",
|
||||
);
|
||||
|
||||
const controlPageApiSchema = createControlPageBrowserApiSchema();
|
||||
const controlPageApiManifest = createControlPageBrowserApiManifest();
|
||||
assert.equal(controlPageApiSchema.apiName, "ini-panel-control-page");
|
||||
assert.equal(isSupportedIniPanelControlPageApiManifest(controlPageApiManifest), true);
|
||||
assert.deepEqual(controlPageApiManifest.methods, createControlPageBrowserApiMethods());
|
||||
assert.equal(
|
||||
createIniPanelShellControlPageContract().readonlyStatusApiManifest.apiName,
|
||||
"machine-session-readonly-status",
|
||||
);
|
||||
|
||||
const launchManifest = createIniPanelLaunchApiManifest();
|
||||
assert.equal(isSupportedIniPanelLaunchApiManifest(launchManifest), true);
|
||||
assert.equal(
|
||||
launchManifest.methods.includes("mountWorkflowOverviewEmbeddingMountState"),
|
||||
true,
|
||||
);
|
||||
assert.deepEqual(
|
||||
launchManifest.visibleEntries,
|
||||
getVisibleIniPanelEntries().map(({ id, href, title }) => ({ id, href, title })),
|
||||
);
|
||||
|
||||
const shellIntegrationManifest = createIniPanelShellIntegrationManifest();
|
||||
assert.equal(createIniPanelShellIntegrationSchema().apiName, "ini-panel-shell-integration");
|
||||
assert.equal(isSupportedIniPanelShellIntegrationManifest(shellIntegrationManifest), true);
|
||||
assert.equal(createIniPanelShellIntegrationReadiness(shellIntegrationManifest).ready, true);
|
||||
assert.equal(createIniPanelShellIntegrationWorkflowPlan({
|
||||
manifest: shellIntegrationManifest,
|
||||
}).ready, true);
|
||||
assert.equal(
|
||||
createIniPanelShellWorkflowOverviewContract().methodNames.includes("getWorkflowOverviewApiSurfaceInventory"),
|
||||
true,
|
||||
);
|
||||
|
||||
const shellViewModel = createIniPanelShellViewModel();
|
||||
assert.equal(
|
||||
shellViewModel.launchApiManifest.methods.includes("mountWorkflowOverviewEmbeddingMountState"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
shellViewModel.launchApiManifest.methods.includes("getShellSessionReadinessWorkflowReport"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellSessionReadinessWorkflowReport().apiName,
|
||||
"ini-panel-shell-session-readiness-workflow-report",
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellApiSurfaceInventory().apiName,
|
||||
"ini-panel-shell-api-surface-inventory",
|
||||
);
|
||||
assert.equal(shellViewModel.shellApiSurfaceInventory.ready, true);
|
||||
assert.equal(
|
||||
shellViewModel.shellSessionHandoffWorkflowSummary.rows.find(
|
||||
({ id }) => id === "persistence-summary",
|
||||
)?.value,
|
||||
"waiting: 5 missing",
|
||||
);
|
||||
assert.equal(
|
||||
shellViewModel.shellSessionHandoffWorkflowSummary.rows.find(
|
||||
({ id }) => id === "api-surface-inventory",
|
||||
)?.value,
|
||||
"ready: 6 rows",
|
||||
);
|
||||
assert.equal(
|
||||
shellViewModel.mountWorkflowOverviewEmbeddingMountState,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
);
|
||||
|
||||
const blockedMount = mountIniPanelShellWorkflowOverviewEmbeddingMountState({ documentRef: null });
|
||||
assert.equal(blockedMount.apiName, "ini-panel-shell-workflow-overview-embedding-mount-dom-mount");
|
||||
assert.equal(blockedMount.ready, false);
|
||||
assert.deepEqual(blockedMount.domReadiness.missing, ["document", "mount", "status", "rows"]);
|
||||
|
||||
assert.match(sdkReadmeText, /OPFS\/session persistence/);
|
||||
assert.match(sdkReadmeText, /INI panel shell handoff/);
|
||||
assert.match(sdkReadmeText, /workflow overview embedding/);
|
||||
assert.match(trackerText, /SDK\/API surface/);
|
||||
|
||||
console.log("sdk_surface_node_smoke=ok");
|
||||
9
wasm-port/tests/sdk/node/verify_sdk_surface.sh
Executable file
9
wasm-port/tests/sdk/node/verify_sdk_surface.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
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"
|
||||
@@ -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}`);
|
||||
@@ -93,6 +93,7 @@ assert.deepEqual(controller.readStatus(), {
|
||||
overview: null,
|
||||
controlView: expectedView,
|
||||
report: expectedStateBundle.report,
|
||||
sessionReadiness: null,
|
||||
runReadiness: null,
|
||||
run: null,
|
||||
session: null,
|
||||
@@ -109,6 +110,7 @@ assert.deepEqual(controller.loadSessionState(), {
|
||||
controlViewReady: true,
|
||||
stateBundle: expectedStateBundle,
|
||||
workflowStatus: expectedWorkflowStatus,
|
||||
sessionReadiness: null,
|
||||
controlView: expectedView,
|
||||
error: null,
|
||||
});
|
||||
|
||||
@@ -21,6 +21,11 @@ const workflowStatus = {
|
||||
overview: {
|
||||
phase: "ready",
|
||||
},
|
||||
sessionReadiness: {
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
missing: [],
|
||||
},
|
||||
lastAction: "load-session",
|
||||
error: null,
|
||||
};
|
||||
@@ -40,6 +45,7 @@ assert.deepEqual(createControlPageSessionLoadState({ phase: "waiting-panel" }),
|
||||
controlViewReady: false,
|
||||
stateBundle: null,
|
||||
workflowStatus: null,
|
||||
sessionReadiness: null,
|
||||
controlView: null,
|
||||
error: null,
|
||||
});
|
||||
@@ -81,6 +87,7 @@ assert.deepEqual(
|
||||
apiReady: true,
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
sessionReadiness: workflowStatus.sessionReadiness,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -101,6 +108,7 @@ assert.deepEqual(
|
||||
controlViewReady: true,
|
||||
stateBundle,
|
||||
workflowStatus,
|
||||
sessionReadiness: workflowStatus.sessionReadiness,
|
||||
controlView,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -6,20 +6,86 @@ import { dirname, resolve } from "node:path";
|
||||
import {
|
||||
INI_PANEL_ENTRIES,
|
||||
controlPageStatusText,
|
||||
createControlPageBrowserApiManifest,
|
||||
createControlPageSessionLoadState,
|
||||
createControlPageBrowserApiMethods,
|
||||
createControlPageBrowserApiSchema,
|
||||
createIniPanelLaunchApiManifest,
|
||||
createIniPanelLaunchApiSchema,
|
||||
createIniPanelLauncherLinkViewModel,
|
||||
createIniPanelShellControlPageContract,
|
||||
createIniPanelShellIntegrationManifest,
|
||||
createIniPanelShellIntegrationReadiness,
|
||||
createIniPanelShellIntegrationSchema,
|
||||
createIniPanelShellIntegrationWorkflowPlan,
|
||||
createIniPanelShellSessionHandoffActionPlan,
|
||||
createIniPanelShellSessionHandoffChecklist,
|
||||
createIniPanelShellSessionHandoffCompatibilityReport,
|
||||
createIniPanelShellSessionHandoffDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackage,
|
||||
createIniPanelShellSessionHandoffPackageDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackageMountDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackageMountRenderState,
|
||||
createIniPanelShellSessionHandoffPackageMountState,
|
||||
createIniPanelShellSessionHandoffMountDomContract,
|
||||
createIniPanelShellSessionHandoffMountDomReadiness,
|
||||
mountIniPanelShellSessionHandoff,
|
||||
mountIniPanelShellSessionHandoffFromControlPage,
|
||||
readIniPanelShellSessionHandoffStatusFromControlPage,
|
||||
createIniPanelShellSessionHandoffWorkflowSummary,
|
||||
createIniPanelShellSessionHandoffWorkflowDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffWorkflowRenderState,
|
||||
createIniPanelShellSessionHandoffWorkflowDomContract,
|
||||
createIniPanelShellSessionHandoffWorkflowDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewContract,
|
||||
createIniPanelShellWorkflowOverviewReadiness,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingReport,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingRenderState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingDomReadiness,
|
||||
mountIniPanelShellWorkflowOverviewEmbedding,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingState,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountRenderState,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomContract,
|
||||
createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson,
|
||||
loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
renderIniPanelShellSessionHandoffWorkflowState,
|
||||
mountIniPanelShellSessionHandoffWorkflow,
|
||||
renderIniPanelShellSessionHandoffMountState,
|
||||
createIniPanelShellSessionHandoffPackageReport,
|
||||
createIniPanelShellSessionHandoffPackageRenderState,
|
||||
createIniPanelShellSessionHandoffPackageSchema,
|
||||
createIniPanelShellSessionHandoffRenderState,
|
||||
createIniPanelShellSessionHandoffSnapshot,
|
||||
createIniPanelShellSessionHandoffSummary,
|
||||
createIniPanelShellSessionHandoffSummarySchema,
|
||||
createIniPanelShellViewModel,
|
||||
createMachineSessionReadonlyStatus,
|
||||
createMachineSessionReadonlyStatusApiManifest,
|
||||
createMachineSessionReadinessFieldState,
|
||||
machineSessionReadinessLogLines,
|
||||
readMachineSessionReadinessWorkflow,
|
||||
loadMachineSessionForControlPageWorkflow,
|
||||
readMachineSessionReadonlyStatus,
|
||||
refreshMachineSessionControlPage,
|
||||
getVisibleIniPanelEntries,
|
||||
isSupportedIniPanelControlPageApiManifest,
|
||||
isSupportedIniPanelLaunchApiManifest,
|
||||
isSupportedIniPanelShellIntegrationManifest,
|
||||
isSupportedIniPanelShellSessionHandoffPackage,
|
||||
isSupportedIniPanelShellSessionHandoffSummary,
|
||||
} from "../../../runtime/ui/ini-panel/ui-shell.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
@@ -28,6 +94,11 @@ const docText = readFileSync(resolve(root, "docs/panel-entry.md"), "utf8");
|
||||
const shellText = readFileSync(resolve(root, "runtime/ui/ini-panel/ui-shell.js"), "utf8");
|
||||
const controlPageText = readFileSync(resolve(root, "runtime/ui/ini-panel/control-page.js"), "utf8");
|
||||
const launchText = readFileSync(resolve(root, "runtime/ui/ini-panel/launch.html"), "utf8");
|
||||
const browserIniPanelText = readFileSync(resolve(root, "tests/browser/verify_ini_panel_browser.sh"), "utf8");
|
||||
const shellIntegrationWorkflowSmokeText = readFileSync(
|
||||
resolve(root, "tests/browser/ini_panel_shell_integration_workflow_smoke.html"),
|
||||
"utf8",
|
||||
);
|
||||
const refreshWorkflowText = readFileSync(
|
||||
resolve(root, "runtime/ui/ini-panel/control-page-refresh-workflow.js"),
|
||||
"utf8",
|
||||
@@ -36,23 +107,123 @@ const sessionWorkflowText = readFileSync(
|
||||
resolve(root, "runtime/ui/ini-panel/control-page-session-workflow.js"),
|
||||
"utf8",
|
||||
);
|
||||
const iniPanelSessionWorkflowText = readFileSync(
|
||||
resolve(root, "runtime/ui/ini-panel/session-workflow.js"),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const requiredShellExports = [
|
||||
"INI_PANEL_ENTRIES",
|
||||
"controlPageStatusText",
|
||||
"createControlPageBrowserApiManifest",
|
||||
"createControlPageBrowserApiMethods",
|
||||
"createControlPageBrowserApiSchema",
|
||||
"createControlPageSessionLoadState",
|
||||
"createIniPanelLaunchApiManifest",
|
||||
"createIniPanelLaunchApiSchema",
|
||||
"createIniPanelLauncherLinkViewModel",
|
||||
"createIniPanelShellControlPageContract",
|
||||
"createIniPanelShellIntegrationManifest",
|
||||
"createIniPanelShellIntegrationReadiness",
|
||||
"createIniPanelShellIntegrationSchema",
|
||||
"createIniPanelShellIntegrationWorkflowPlan",
|
||||
"createIniPanelShellSessionHandoffActionPlan",
|
||||
"createIniPanelShellSessionHandoffChecklist",
|
||||
"createIniPanelShellSessionHandoffCompatibilityReport",
|
||||
"createIniPanelShellSessionHandoffDisplayViewModel",
|
||||
"createIniPanelShellSessionHandoffPackage",
|
||||
"createIniPanelShellSessionHandoffPackageDisplayViewModel",
|
||||
"createIniPanelShellSessionHandoffPackageMountDisplayViewModel",
|
||||
"createIniPanelShellSessionHandoffPackageMountRenderState",
|
||||
"createIniPanelShellSessionHandoffPackageMountState",
|
||||
"createIniPanelShellSessionHandoffMountDomContract",
|
||||
"createIniPanelShellSessionHandoffMountDomReadiness",
|
||||
"mountIniPanelShellSessionHandoff",
|
||||
"mountIniPanelShellSessionHandoffFromControlPage",
|
||||
"readIniPanelShellSessionHandoffStatusFromControlPage",
|
||||
"createIniPanelShellSessionHandoffWorkflowSummary",
|
||||
"createIniPanelShellSessionHandoffWorkflowDisplayViewModel",
|
||||
"createIniPanelShellSessionHandoffWorkflowRenderState",
|
||||
"createIniPanelShellSessionHandoffWorkflowDomContract",
|
||||
"createIniPanelShellSessionHandoffWorkflowDomReadiness",
|
||||
"createIniPanelShellWorkflowOverviewContract",
|
||||
"createIniPanelShellWorkflowOverviewReadiness",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingReport",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingRenderState",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingDomContract",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingDomReadiness",
|
||||
"mountIniPanelShellWorkflowOverviewEmbedding",
|
||||
"renderIniPanelShellWorkflowOverviewEmbeddingState",
|
||||
"mountIniPanelShellWorkflowOverviewEmbeddingState",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingMountRenderState",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingMountDomContract",
|
||||
"createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness",
|
||||
"renderIniPanelShellWorkflowOverviewEmbeddingMountState",
|
||||
"mountIniPanelShellWorkflowOverviewEmbeddingMountState",
|
||||
"validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson",
|
||||
"runIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflow",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState",
|
||||
"loadIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrl",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDisplayViewModel",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactRenderState",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomContract",
|
||||
"createIniPanelShellWorkflowOverviewReleaseReadinessArtifactDomReadiness",
|
||||
"renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState",
|
||||
"mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactState",
|
||||
"renderIniPanelShellSessionHandoffWorkflowState",
|
||||
"mountIniPanelShellSessionHandoffWorkflow",
|
||||
"renderIniPanelShellSessionHandoffMountState",
|
||||
"createIniPanelShellSessionHandoffPackageReport",
|
||||
"createIniPanelShellSessionHandoffPackageRenderState",
|
||||
"createIniPanelShellSessionHandoffPackageSchema",
|
||||
"createIniPanelShellSessionHandoffRenderState",
|
||||
"createIniPanelShellSessionHandoffSnapshot",
|
||||
"createIniPanelShellSessionHandoffSummary",
|
||||
"createIniPanelShellSessionHandoffSummarySchema",
|
||||
"createIniPanelShellViewModel",
|
||||
"getIniPanelEntryByHref",
|
||||
"getVisibleIniPanelEntries",
|
||||
"createMachineSessionPersistenceDisplayViewModel",
|
||||
"createMachineSessionPersistenceRenderState",
|
||||
"createMachineSessionPersistenceSummary",
|
||||
"isSupportedIniPanelControlPageApiManifest",
|
||||
"isSupportedIniPanelLaunchApiManifest",
|
||||
"isSupportedIniPanelShellIntegrationManifest",
|
||||
"isSupportedIniPanelShellSessionHandoffPackage",
|
||||
"isSupportedIniPanelShellSessionHandoffSummary",
|
||||
"createMachineSessionControlPageController",
|
||||
"createMachineSessionReadonlyStatus",
|
||||
"createMachineSessionReadonlyStatusApiManifest",
|
||||
"createMachineSessionReadinessFieldState",
|
||||
"machineSessionReadinessLogLines",
|
||||
"readMachineSessionReadinessWorkflow",
|
||||
"loadMachineSessionForControlPageWorkflow",
|
||||
"readMachineSessionReadonlyStatus",
|
||||
"refreshMachineSessionControlPage",
|
||||
@@ -81,10 +252,20 @@ for (const exportName of [
|
||||
assert.match(docText, new RegExp(`\\b${exportName}\\b`));
|
||||
}
|
||||
|
||||
for (const exportName of [
|
||||
"createMachineSessionReadinessFieldState",
|
||||
"machineSessionReadinessLogLines",
|
||||
"readMachineSessionReadinessWorkflow",
|
||||
]) {
|
||||
assert.match(iniPanelSessionWorkflowText, new RegExp(`\\bexport (?:async )?function ${exportName}\\b`));
|
||||
assert.match(docText, new RegExp(`\\b${exportName}\\b`));
|
||||
}
|
||||
|
||||
const requiredPages = [
|
||||
["Launch", "runtime/ui/ini-panel/launch.html"],
|
||||
["Edit and run", "runtime/ui/ini-panel/index.html"],
|
||||
["Read-only control view", "runtime/ui/ini-panel/control-page.html"],
|
||||
["Workflow overview", "runtime/ui/ini-panel/workflow-overview.html"],
|
||||
];
|
||||
|
||||
for (const [label, path] of requiredPages) {
|
||||
@@ -102,10 +283,183 @@ assert.deepEqual(
|
||||
);
|
||||
assert.deepEqual(createIniPanelShellViewModel().pages, getVisibleIniPanelEntries());
|
||||
assert.deepEqual(createIniPanelShellViewModel().launcherLinks, createIniPanelLauncherLinkViewModel());
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellIntegrationSchema,
|
||||
createIniPanelShellIntegrationSchema(),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellIntegrationManifest,
|
||||
createIniPanelShellIntegrationManifest(),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellIntegrationReadiness,
|
||||
createIniPanelShellIntegrationReadiness(createIniPanelShellViewModel().shellIntegrationManifest),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellIntegrationWorkflowPlan,
|
||||
createIniPanelShellIntegrationWorkflowPlan({
|
||||
manifest: createIniPanelShellViewModel().shellIntegrationManifest,
|
||||
readiness: createIniPanelShellViewModel().shellIntegrationReadiness,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffSummary,
|
||||
createIniPanelShellSessionHandoffSummary({
|
||||
manifest: createIniPanelShellViewModel().shellIntegrationManifest,
|
||||
readiness: createIniPanelShellViewModel().shellIntegrationReadiness,
|
||||
workflowPlan: createIniPanelShellViewModel().shellIntegrationWorkflowPlan,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffSummarySchema,
|
||||
createIniPanelShellSessionHandoffSummarySchema(),
|
||||
);
|
||||
assert.equal(isSupportedIniPanelShellSessionHandoffSummary(createIniPanelShellViewModel().shellSessionHandoffSummary), true);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffCompatibilityReport,
|
||||
createIniPanelShellSessionHandoffCompatibilityReport(createIniPanelShellViewModel().shellSessionHandoffSummary),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffDisplayViewModel(createIniPanelShellViewModel().shellSessionHandoffCompatibilityReport),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffSnapshot,
|
||||
createIniPanelShellSessionHandoffSnapshot({
|
||||
readiness: createIniPanelShellViewModel().shellIntegrationReadiness,
|
||||
summary: createIniPanelShellViewModel().shellSessionHandoffSummary,
|
||||
compatibilityReport: createIniPanelShellViewModel().shellSessionHandoffCompatibilityReport,
|
||||
displayViewModel: createIniPanelShellViewModel().shellSessionHandoffDisplayViewModel,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffRenderState,
|
||||
createIniPanelShellSessionHandoffRenderState(createIniPanelShellViewModel().shellSessionHandoffSnapshot),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffActionPlan,
|
||||
createIniPanelShellSessionHandoffActionPlan({
|
||||
snapshot: createIniPanelShellViewModel().shellSessionHandoffSnapshot,
|
||||
renderState: createIniPanelShellViewModel().shellSessionHandoffRenderState,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffChecklist,
|
||||
createIniPanelShellSessionHandoffChecklist({
|
||||
actionPlan: createIniPanelShellViewModel().shellSessionHandoffActionPlan,
|
||||
renderState: createIniPanelShellViewModel().shellSessionHandoffRenderState,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackage,
|
||||
createIniPanelShellSessionHandoffPackage({
|
||||
shellIntegrationManifest: createIniPanelShellViewModel().shellIntegrationManifest,
|
||||
launchApiManifest: createIniPanelShellViewModel().shellIntegrationManifest.launchApiManifest,
|
||||
controlPageApiManifest: createIniPanelShellViewModel().shellIntegrationManifest.controlPageApiManifest,
|
||||
snapshot: createIniPanelShellViewModel().shellSessionHandoffSnapshot,
|
||||
renderState: createIniPanelShellViewModel().shellSessionHandoffRenderState,
|
||||
actionPlan: createIniPanelShellViewModel().shellSessionHandoffActionPlan,
|
||||
checklist: createIniPanelShellViewModel().shellSessionHandoffChecklist,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageSchema,
|
||||
createIniPanelShellSessionHandoffPackageSchema(),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageReport,
|
||||
createIniPanelShellSessionHandoffPackageReport(createIniPanelShellViewModel().shellSessionHandoffPackage),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackageDisplayViewModel(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageReport,
|
||||
),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageRenderState,
|
||||
createIniPanelShellSessionHandoffPackageRenderState(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageDisplayViewModel,
|
||||
),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageMountState,
|
||||
createIniPanelShellSessionHandoffPackageMountState({
|
||||
handoffPackage: createIniPanelShellViewModel().shellSessionHandoffPackage,
|
||||
packageRenderState: createIniPanelShellViewModel().shellSessionHandoffPackageRenderState,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageMountDisplayViewModel,
|
||||
createIniPanelShellSessionHandoffPackageMountDisplayViewModel(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageMountState,
|
||||
),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageMountRenderState,
|
||||
createIniPanelShellSessionHandoffPackageMountRenderState(
|
||||
createIniPanelShellViewModel().shellSessionHandoffPackageMountDisplayViewModel,
|
||||
),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellSessionHandoffMountDomContract,
|
||||
createIniPanelShellSessionHandoffMountDomContract(),
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellSessionHandoffMountDomReadiness({ documentRef: null }).ready,
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
mountIniPanelShellSessionHandoff({ documentRef: null }).phase,
|
||||
"blocked",
|
||||
);
|
||||
assert.equal(
|
||||
mountIniPanelShellSessionHandoffFromControlPage({ documentRef: null }).mountWorkflow.phase,
|
||||
"blocked",
|
||||
);
|
||||
assert.equal(
|
||||
readIniPanelShellSessionHandoffStatusFromControlPage().phase,
|
||||
"blocked",
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellSessionHandoffWorkflowSummary().phase,
|
||||
"blocked",
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellSessionHandoffWorkflowDisplayViewModel().phase,
|
||||
"blocked",
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellSessionHandoffWorkflowRenderState().phase,
|
||||
"blocked",
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellSessionHandoffWorkflowDomContract().selectors.mount,
|
||||
"[data-handoff-workflow]",
|
||||
);
|
||||
assert.equal(
|
||||
createIniPanelShellSessionHandoffWorkflowDomReadiness().phase,
|
||||
"blocked",
|
||||
);
|
||||
assert.equal(
|
||||
mountIniPanelShellSessionHandoffWorkflow().phase,
|
||||
"blocked",
|
||||
);
|
||||
assert.equal(isSupportedIniPanelShellSessionHandoffPackage(createIniPanelShellViewModel().shellSessionHandoffPackage), true);
|
||||
assert.equal(isSupportedIniPanelShellIntegrationManifest(createIniPanelShellViewModel().shellIntegrationManifest), true);
|
||||
assert.deepEqual(createIniPanelShellViewModel().launchApiSchema, createIniPanelLaunchApiSchema());
|
||||
assert.deepEqual(createIniPanelShellViewModel().launchApiManifest, createIniPanelLaunchApiManifest());
|
||||
assert.equal(isSupportedIniPanelLaunchApiManifest(createIniPanelShellViewModel().launchApiManifest), true);
|
||||
assert.equal(createIniPanelShellViewModel().launchApiManifest.manifestVersion, createIniPanelLaunchApiSchema().manifestVersion);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().controlPage.browserApiSchema,
|
||||
createControlPageBrowserApiSchema(),
|
||||
);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().controlPage.browserApiManifest,
|
||||
createControlPageBrowserApiManifest(),
|
||||
);
|
||||
assert.equal(isSupportedIniPanelControlPageApiManifest(createIniPanelShellViewModel().controlPage.browserApiManifest), true);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().controlPage.contract,
|
||||
createIniPanelShellControlPageContract(),
|
||||
@@ -122,6 +476,7 @@ assert.deepEqual(createControlPageSessionLoadState({ phase: "waiting-panel" }),
|
||||
controlViewReady: false,
|
||||
stateBundle: null,
|
||||
workflowStatus: null,
|
||||
sessionReadiness: null,
|
||||
controlView: null,
|
||||
error: null,
|
||||
});
|
||||
@@ -138,28 +493,216 @@ assert.equal(createIniPanelShellViewModel().controlPage.readStatus, readMachineS
|
||||
assert.equal(createIniPanelShellViewModel().controlPage.refresh, refreshMachineSessionControlPage);
|
||||
assert.equal(typeof createIniPanelShellViewModel().controlPage.renderView, "function");
|
||||
assert.match(shellText, /\bexport function createControlPageBrowserApiMethods\b/);
|
||||
assert.match(shellText, /\bexport function createControlPageBrowserApiManifest\b/);
|
||||
assert.match(shellText, /\bexport function createControlPageBrowserApiSchema\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelLaunchApiSchema\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelLaunchApiManifest\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellIntegrationSchema\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellIntegrationManifest\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellIntegrationReadiness\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellIntegrationWorkflowPlan\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffActionPlan\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffChecklist\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffCompatibilityReport\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffDisplayViewModel\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffPackage\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffPackageReport\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffPackageSchema\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffRenderState\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffSnapshot\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffSummary\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellSessionHandoffSummarySchema\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingReport\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingRenderState\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingDomContract\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingDomReadiness\b/);
|
||||
assert.match(shellText, /\bexport function renderIniPanelShellWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(shellText, /\bexport function mountIniPanelShellWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingMountRenderState\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingMountDomContract\b/);
|
||||
assert.match(shellText, /\bexport function createIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness\b/);
|
||||
assert.match(shellText, /\bexport function renderIniPanelShellWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(shellText, /\bexport function mountIniPanelShellWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(shellText, /\bexport function isSupportedIniPanelControlPageApiManifest\b/);
|
||||
assert.match(shellText, /\bexport function isSupportedIniPanelLaunchApiManifest\b/);
|
||||
assert.match(shellText, /\bexport function isSupportedIniPanelShellIntegrationManifest\b/);
|
||||
assert.match(shellText, /\bexport function isSupportedIniPanelShellSessionHandoffPackage\b/);
|
||||
assert.match(shellText, /\bexport function isSupportedIniPanelShellSessionHandoffSummary\b/);
|
||||
assert.match(controlPageText, /\bisSupportedControlPageApiManifest\b/);
|
||||
assert.match(controlPageText, /\bgetControlPageApiSchema\b/);
|
||||
assert.match(controlPageText, /\bgetControlPageApiManifest\b/);
|
||||
assert.match(controlPageText, /\bgetControlPageApiMethods\b/);
|
||||
assert.match(launchText, /\blinuxCncIniPanelLaunchApi\b/);
|
||||
assert.match(launchText, /\bisSupportedLaunchApiManifest\b/);
|
||||
assert.match(launchText, /\bgetLaunchApiSchema\b/);
|
||||
assert.match(launchText, /\bgetLaunchApiManifest\b/);
|
||||
assert.match(launchText, /\bgetShellIntegrationSchema\b/);
|
||||
assert.match(launchText, /\bgetShellIntegrationManifest\b/);
|
||||
assert.match(launchText, /\bgetShellIntegrationReadiness\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffSummarySchema\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffSummary\b/);
|
||||
assert.match(launchText, /\bisSupportedShellSessionHandoffSummary\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffCompatibilityReport\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffSnapshot\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffRenderState\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffActionPlan\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffChecklist\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageSchema\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackage\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageReport\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingReport\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingRenderState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingDomContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingMountDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingMountRenderState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingMountDomContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingMountDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(launchText, /\bisSupportedShellSessionHandoffPackage\b/);
|
||||
assert.match(launchText, /\bdata-handoff-shell\b/);
|
||||
assert.match(launchText, /\bdata-handoff-status\b/);
|
||||
assert.match(launchText, /\bdata-handoff-rows\b/);
|
||||
assert.match(launchText, /\bgetControlPageApiMethods\b/);
|
||||
assert.match(controlPageText, /\bgetControlPageContract\b/);
|
||||
assert.match(controlPageText, /\breadControlPageStatus\b/);
|
||||
assert.match(controlPageText, /\bcreateIniPanelShellControlPageContract\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelControlPageApi\.getControlPageApiMethods\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelControlPageApi\.getControlPageApiSchema\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelControlPageApi\.getControlPageApiManifest\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelControlPageApi\.isSupportedControlPageApiManifest\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelControlPageApi\.getControlPageContract\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelControlPageApi\.readControlPageStatus\b/);
|
||||
assert.match(docText, /\bcreateControlPageBrowserApiManifest\b/);
|
||||
assert.match(docText, /\bcreateControlPageBrowserApiSchema\b/);
|
||||
assert.match(docText, /\bcreateIniPanelLaunchApiManifest\b/);
|
||||
assert.match(docText, /\bcreateIniPanelLaunchApiSchema\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellIntegrationManifest\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellIntegrationReadiness\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellIntegrationSchema\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellIntegrationWorkflowPlan\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffActionPlan\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffChecklist\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffCompatibilityReport\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffDisplayViewModel\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffPackage\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffPackageReport\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffPackageSchema\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffRenderState\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffSnapshot\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffSummary\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellSessionHandoffSummarySchema\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingReport\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingDisplayViewModel\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingRenderState\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingDomContract\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingDomReadiness\b/);
|
||||
assert.match(docText, /\brenderIniPanelShellWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(docText, /\bmountIniPanelShellWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingMountDisplayViewModel\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingMountRenderState\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingMountDomContract\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellWorkflowOverviewEmbeddingMountDomReadiness\b/);
|
||||
assert.match(docText, /\brenderIniPanelShellWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(docText, /\bmountIniPanelShellWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(docText, /\bisSupportedIniPanelControlPageApiManifest\b/);
|
||||
assert.match(docText, /\bisSupportedIniPanelLaunchApiManifest\b/);
|
||||
assert.match(docText, /\bisSupportedIniPanelShellIntegrationManifest\b/);
|
||||
assert.match(docText, /\bisSupportedIniPanelShellSessionHandoffPackage\b/);
|
||||
assert.match(docText, /\bisSupportedIniPanelShellSessionHandoffSummary\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.isSupportedLaunchApiManifest\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getLaunchApiSchema\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getLaunchApiManifest\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationSchema\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationManifest\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellIntegrationReadiness\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffSummarySchema\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffSummary\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.isSupportedShellSessionHandoffSummary\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffCompatibilityReport\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffDisplayViewModel\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffSnapshot\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffRenderState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffActionPlan\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffChecklist\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffPackageSchema\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffPackage\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffPackageReport\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingReport\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingDisplayViewModel\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingRenderState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingDomContract\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingDomReadiness\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.renderWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.mountWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingMountDisplayViewModel\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingMountRenderState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingMountDomContract\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getWorkflowOverviewEmbeddingMountDomReadiness\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.renderWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.mountWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.isSupportedShellSessionHandoffPackage\b/);
|
||||
assert.match(docText, /\bdata-handoff-shell\b/);
|
||||
assert.match(docText, /\bdata-handoff-status\b/);
|
||||
assert.match(docText, /\bdata-handoff-rows\b/);
|
||||
assert.match(docText, /\bdata-external-shell-mount\b/);
|
||||
assert.match(docText, /\brenderIniPanelShellSessionHandoffMountState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.renderShellSessionHandoffMountState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffMountDomContract\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffMountDomReadiness\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.mountShellSessionHandoff\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.mountShellSessionHandoffFromControlPage\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.readShellSessionHandoffStatusFromControlPage\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffWorkflowSummary\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffWorkflowDisplayViewModel\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffWorkflowRenderState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffWorkflowDomContract\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getShellSessionHandoffWorkflowDomReadiness\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.renderShellSessionHandoffWorkflowState\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.mountShellSessionHandoffWorkflow\b/);
|
||||
assert.match(docText, /\blinuxCncIniPanelLaunchApi\.getControlPageApiMethods\b/);
|
||||
assert.match(docText, /\bini_panel_shell_integration_workflow_smoke\.html\b/);
|
||||
assert.match(browserIniPanelText, /\bini_panel_shell_integration_workflow_smoke\.html\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellIntegrationReadiness\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffSummary\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bisSupportedShellSessionHandoffSummary\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffCompatibilityReport\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffDisplayViewModel\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffSnapshot\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffRenderState\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffActionPlan\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffChecklist\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffPackageSchema\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffPackage\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffPackageReport\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffPackageMountRenderState\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffMountDomContract\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffMountDomReadiness\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bmountShellSessionHandoff\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bmountShellSessionHandoffFromControlPage\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\breadShellSessionHandoffStatusFromControlPage\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffWorkflowSummary\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffWorkflowDisplayViewModel\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffWorkflowRenderState\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffWorkflowDomContract\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetShellSessionHandoffWorkflowDomReadiness\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\brenderShellSessionHandoffWorkflowState\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bmountShellSessionHandoffWorkflow\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bgetWorkflowOverviewEmbeddingReport\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\brenderShellSessionHandoffMountState\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bdata-external-shell-mount\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bdata-external-mount-value\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bisSupportedShellSessionHandoffPackage\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bhandoffSummary\.handoffMethods\.readStatus\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\breadControlPageStatus\b/);
|
||||
assert.match(shellIntegrationWorkflowSmokeText, /\bbrowser_ini_shell_integration_workflow_smoke=ok\b/);
|
||||
assert.match(docText, /\bOPFS\/session persistence capabilities\b/);
|
||||
assert.match(docText, /\bmachine files\b/);
|
||||
assert.match(docText, /\bsession snapshots\b/);
|
||||
|
||||
@@ -17,6 +17,11 @@ assert.deepEqual(INI_PANEL_ENTRIES, [
|
||||
title: "Open read-only control view",
|
||||
href: "./control-page.html",
|
||||
},
|
||||
{
|
||||
id: "workflow-overview",
|
||||
title: "Open shell handoff overview",
|
||||
href: "./workflow-overview.html",
|
||||
},
|
||||
]);
|
||||
|
||||
const manifest = getIniPanelEntryManifest();
|
||||
@@ -26,6 +31,7 @@ assert.notStrictEqual(manifest[0], INI_PANEL_ENTRIES[0]);
|
||||
|
||||
assert.deepEqual(getIniPanelEntryByHref("./index.html"), INI_PANEL_ENTRIES[0]);
|
||||
assert.deepEqual(getIniPanelEntryByHref("./control-page.html"), INI_PANEL_ENTRIES[1]);
|
||||
assert.deepEqual(getIniPanelEntryByHref("./workflow-overview.html"), INI_PANEL_ENTRIES[2]);
|
||||
assert.equal(getIniPanelEntryByHref("./missing.html"), null);
|
||||
|
||||
manifest[0].title = "changed";
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
createControlPageBrowserApiMethods,
|
||||
createIniPanelLaunchApiManifest,
|
||||
createIniPanelLaunchApiSchema,
|
||||
createIniPanelShellIntegrationManifest,
|
||||
createIniPanelShellViewModel,
|
||||
getVisibleIniPanelEntries,
|
||||
isSupportedIniPanelLaunchApiManifest,
|
||||
@@ -42,6 +43,89 @@ assert.deepEqual(manifest, {
|
||||
"isSupportedLaunchApiManifest",
|
||||
"getLaunchApiSchema",
|
||||
"getLaunchApiManifest",
|
||||
"getShellIntegrationSchema",
|
||||
"getShellIntegrationManifest",
|
||||
"getShellIntegrationReadiness",
|
||||
"getShellSessionHandoffSummarySchema",
|
||||
"getShellSessionHandoffSummary",
|
||||
"isSupportedShellSessionHandoffSummary",
|
||||
"getShellSessionHandoffCompatibilityReport",
|
||||
"getShellSessionHandoffDisplayViewModel",
|
||||
"getShellSessionHandoffSnapshot",
|
||||
"getShellSessionHandoffRenderState",
|
||||
"getShellSessionHandoffActionPlan",
|
||||
"getShellSessionHandoffChecklist",
|
||||
"getShellSessionHandoffPackageSchema",
|
||||
"getShellSessionHandoffPackage",
|
||||
"getShellSessionHandoffPackageReport",
|
||||
"getShellSessionHandoffPackageDisplayViewModel",
|
||||
"getShellSessionHandoffPackageRenderState",
|
||||
"getShellSessionHandoffPackageMountState",
|
||||
"getShellSessionHandoffPackageMountDisplayViewModel",
|
||||
"getShellSessionHandoffPackageMountRenderState",
|
||||
"getShellSessionHandoffMountDomContract",
|
||||
"getShellSessionHandoffMountDomReadiness",
|
||||
"renderShellSessionHandoffMountState",
|
||||
"mountShellSessionHandoff",
|
||||
"mountShellSessionHandoffFromControlPage",
|
||||
"readShellSessionHandoffStatusFromControlPage",
|
||||
"getMachineSessionPersistenceSummary",
|
||||
"getShellSessionHandoffWorkflowSummary",
|
||||
"getShellSessionReadinessWorkflowReport",
|
||||
"getShellApiSurfaceInventory",
|
||||
"getShellSessionHandoffWorkflowDisplayViewModel",
|
||||
"getShellSessionHandoffWorkflowRenderState",
|
||||
"getShellSessionHandoffWorkflowDomContract",
|
||||
"getShellSessionHandoffWorkflowDomReadiness",
|
||||
"renderShellSessionHandoffWorkflowState",
|
||||
"mountShellSessionHandoffWorkflow",
|
||||
"getWorkflowOverviewContract",
|
||||
"getWorkflowOverviewReadiness",
|
||||
"mountWorkflowOverviewEmbedding",
|
||||
"runWorkflowOverviewReleaseReadinessArtifactJsonWorkflow",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
|
||||
"mountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState",
|
||||
"loadWorkflowOverviewReleaseReadinessArtifactUrl",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
|
||||
"mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationActionPlan",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState",
|
||||
"mountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract",
|
||||
"getWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness",
|
||||
"renderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState",
|
||||
"mountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState",
|
||||
"getWorkflowOverviewEmbeddingReport",
|
||||
"getWorkflowOverviewEmbeddingDisplayViewModel",
|
||||
"getWorkflowOverviewEmbeddingRenderState",
|
||||
"getWorkflowOverviewEmbeddingDomContract",
|
||||
"getWorkflowOverviewEmbeddingDomReadiness",
|
||||
"renderWorkflowOverviewEmbeddingState",
|
||||
"mountWorkflowOverviewEmbeddingState",
|
||||
"getWorkflowOverviewEmbeddingMountDisplayViewModel",
|
||||
"getWorkflowOverviewEmbeddingMountRenderState",
|
||||
"getWorkflowOverviewEmbeddingMountDomContract",
|
||||
"getWorkflowOverviewEmbeddingMountDomReadiness",
|
||||
"renderWorkflowOverviewEmbeddingMountState",
|
||||
"mountWorkflowOverviewEmbeddingMountState",
|
||||
"isSupportedShellSessionHandoffPackage",
|
||||
"getLauncherLinks",
|
||||
"getControlPageApiMethods",
|
||||
],
|
||||
@@ -56,6 +140,11 @@ assert.deepEqual(manifest, {
|
||||
href: "./control-page.html",
|
||||
title: "Open read-only control view",
|
||||
},
|
||||
{
|
||||
id: "workflow-overview",
|
||||
href: "./workflow-overview.html",
|
||||
title: "Open shell handoff overview",
|
||||
},
|
||||
],
|
||||
targetPages: [
|
||||
{
|
||||
@@ -66,6 +155,10 @@ assert.deepEqual(manifest, {
|
||||
entryId: "control-view",
|
||||
href: "./control-page.html",
|
||||
},
|
||||
{
|
||||
entryId: "workflow-overview",
|
||||
href: "./workflow-overview.html",
|
||||
},
|
||||
],
|
||||
controlPageApiMethods: createControlPageBrowserApiMethods(),
|
||||
persistenceCapabilities: [
|
||||
@@ -89,6 +182,10 @@ assert.deepEqual(manifest, {
|
||||
|
||||
assert.deepEqual(createIniPanelShellViewModel().launchApiManifest, manifest);
|
||||
assert.deepEqual(createIniPanelShellViewModel().launchApiSchema, schema);
|
||||
assert.deepEqual(
|
||||
createIniPanelShellViewModel().shellIntegrationManifest,
|
||||
createIniPanelShellIntegrationManifest(),
|
||||
);
|
||||
assert.equal(createIniPanelShellViewModel().launchApiManifest.manifestVersion, schema.manifestVersion);
|
||||
assert.equal(isSupportedIniPanelLaunchApiManifest(manifest), true);
|
||||
assert.equal(isSupportedIniPanelLaunchApiManifest({ ...manifest, manifestVersion: 2 }), false);
|
||||
@@ -135,6 +232,95 @@ assert.match(shellText, /\bexport function isSupportedIniPanelLaunchApiManifest\
|
||||
assert.match(launchText, /\bisSupportedLaunchApiManifest\b/);
|
||||
assert.match(launchText, /\bgetLaunchApiSchema\b/);
|
||||
assert.match(launchText, /\bgetLaunchApiManifest\b/);
|
||||
assert.match(launchText, /\bgetShellIntegrationSchema\b/);
|
||||
assert.match(launchText, /\bgetShellIntegrationManifest\b/);
|
||||
assert.match(launchText, /\bgetShellIntegrationReadiness\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffSummarySchema\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffSummary\b/);
|
||||
assert.match(launchText, /\bisSupportedShellSessionHandoffSummary\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffCompatibilityReport\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffSnapshot\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffRenderState\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffActionPlan\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffChecklist\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageSchema\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackage\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageReport\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageRenderState\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageMountState\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageMountDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffPackageMountRenderState\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffMountDomContract\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffMountDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderShellSessionHandoffMountState\b/);
|
||||
assert.match(launchText, /\bmountShellSessionHandoff\b/);
|
||||
assert.match(launchText, /\bmountShellSessionHandoffFromControlPage\b/);
|
||||
assert.match(launchText, /\breadShellSessionHandoffStatusFromControlPage\b/);
|
||||
assert.match(launchText, /\bgetMachineSessionPersistenceSummary\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffWorkflowSummary\b/);
|
||||
assert.match(launchText, /\bgetShellSessionReadinessWorkflowReport\b/);
|
||||
assert.match(launchText, /\bgetShellApiSurfaceInventory\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffWorkflowDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffWorkflowRenderState\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffWorkflowDomContract\b/);
|
||||
assert.match(launchText, /\bgetShellSessionHandoffWorkflowDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderShellSessionHandoffWorkflowState\b/);
|
||||
assert.match(launchText, /\bmountShellSessionHandoffWorkflow\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReadiness\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewEmbedding\b/);
|
||||
assert.match(launchText, /\brunWorkflowOverviewReleaseReadinessArtifactJsonWorkflow\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowSummaryViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowActionPlan\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowRenderState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactJsonWorkflowDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState\b/);
|
||||
assert.match(launchText, /\bloadWorkflowOverviewReleaseReadinessArtifactUrl\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationActionPlan\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewReleaseReadinessArtifactValidationActionPlanState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingReport\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingRenderState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingDomContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewEmbeddingState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingMountDisplayViewModel\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingMountRenderState\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingMountDomContract\b/);
|
||||
assert.match(launchText, /\bgetWorkflowOverviewEmbeddingMountDomReadiness\b/);
|
||||
assert.match(launchText, /\brenderWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(launchText, /\bmountWorkflowOverviewEmbeddingMountState\b/);
|
||||
assert.match(launchText, /\bisSupportedShellSessionHandoffPackage\b/);
|
||||
assert.match(launchText, /\bdata-handoff-workflow\b/);
|
||||
assert.match(launchText, /\bdata-handoff-workflow-status\b/);
|
||||
assert.match(launchText, /\bdata-handoff-workflow-rows\b/);
|
||||
assert.match(launchText, /\bdata-handoff-mount\b/);
|
||||
assert.match(launchText, /\bdata-handoff-mount-status\b/);
|
||||
assert.match(launchText, /\bdata-handoff-mount-rows\b/);
|
||||
assert.match(launchText, /\blinuxCncIniPanelLaunchApi\b/);
|
||||
|
||||
console.log("ini_panel_launch_api_manifest_node_smoke=ok");
|
||||
|
||||
@@ -30,6 +30,11 @@ const stateBundle = {
|
||||
};
|
||||
const workflowStatus = {
|
||||
overview: stateBundle.overview,
|
||||
sessionReadiness: {
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
missing: [],
|
||||
},
|
||||
runReadiness: stateBundle.report.runReadiness,
|
||||
session: stateBundle.report.session,
|
||||
run: stateBundle.report.run,
|
||||
@@ -56,6 +61,7 @@ assert.deepEqual(
|
||||
overview: stateBundle.overview,
|
||||
controlView,
|
||||
report: stateBundle.report,
|
||||
sessionReadiness: workflowStatus.sessionReadiness,
|
||||
runReadiness: stateBundle.report.runReadiness,
|
||||
run: stateBundle.report.run,
|
||||
session: stateBundle.report.session,
|
||||
@@ -73,6 +79,7 @@ assert.deepEqual(createMachineSessionReadonlyStatus(), {
|
||||
overview: null,
|
||||
controlView: null,
|
||||
report: null,
|
||||
sessionReadiness: null,
|
||||
runReadiness: null,
|
||||
run: null,
|
||||
session: null,
|
||||
@@ -103,6 +110,7 @@ assert.deepEqual(createMachineSessionReadonlyStatusApiManifest(), {
|
||||
"overview",
|
||||
"controlView",
|
||||
"report",
|
||||
"sessionReadiness",
|
||||
"runReadiness",
|
||||
"run",
|
||||
"session",
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createMachineSessionReadinessFieldState,
|
||||
createMachineSessionLoadFieldState,
|
||||
loadMachineSessionIntoWasmWorkflow,
|
||||
machineSessionLoadWorkflowLogLines,
|
||||
machineSessionReadinessLogLines,
|
||||
readMachineSessionReadinessWorkflow,
|
||||
restoreMachineSessionSnapshotWorkflow,
|
||||
saveMachineSessionSnapshotWorkflow,
|
||||
} from "../../../runtime/ui/ini-panel/session-workflow.js";
|
||||
@@ -63,6 +66,101 @@ await assert.rejects(
|
||||
/machine session loader/,
|
||||
);
|
||||
|
||||
const readySessionReadiness = {
|
||||
ready: true,
|
||||
phase: "ready",
|
||||
missing: [],
|
||||
checks: [
|
||||
{
|
||||
name: "ini",
|
||||
path: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||
ok: true,
|
||||
error: null,
|
||||
},
|
||||
{
|
||||
name: "parameters",
|
||||
path: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||
ok: true,
|
||||
error: null,
|
||||
},
|
||||
{
|
||||
name: "toolTable",
|
||||
path: "linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||
ok: true,
|
||||
error: null,
|
||||
},
|
||||
],
|
||||
snapshotCheck: {
|
||||
name: "snapshot",
|
||||
path: "linuxcnc/sessions/ui-machine-session/ui-machine-session.json",
|
||||
ok: true,
|
||||
error: null,
|
||||
},
|
||||
};
|
||||
|
||||
const readinessWorkflowCalls = [];
|
||||
const readinessWorkflow = await readMachineSessionReadinessWorkflow({
|
||||
readReadiness: async () => {
|
||||
readinessWorkflowCalls.push(["readReadiness"]);
|
||||
return readySessionReadiness;
|
||||
},
|
||||
});
|
||||
assert.deepEqual(readinessWorkflowCalls, [["readReadiness"]]);
|
||||
assert.equal(readinessWorkflow.readiness, readySessionReadiness);
|
||||
assert.deepEqual(createMachineSessionReadinessFieldState(readySessionReadiness), {
|
||||
sessionReadinessPhase: "ready",
|
||||
sessionReadinessReady: "ready",
|
||||
sessionReadinessMissing: "none",
|
||||
});
|
||||
assert.deepEqual(readinessWorkflow.fields, {
|
||||
sessionReadinessPhase: "ready",
|
||||
sessionReadinessReady: "ready",
|
||||
sessionReadinessMissing: "none",
|
||||
});
|
||||
assert.deepEqual(machineSessionReadinessLogLines(readySessionReadiness), [
|
||||
"Session readiness: ready",
|
||||
"ini: linuxcnc/machines/xyzab-tdr/machine.ini -> ok",
|
||||
"parameters: linuxcnc/machines/xyzab-tdr/linuxcnc.var -> ok",
|
||||
"toolTable: linuxcnc/machines/xyzab-tdr/tool.tbl -> ok",
|
||||
"snapshot: linuxcnc/sessions/ui-machine-session/ui-machine-session.json -> ok",
|
||||
]);
|
||||
assert.deepEqual(readinessWorkflow.logLines, machineSessionReadinessLogLines(readySessionReadiness));
|
||||
await assert.rejects(
|
||||
() => readMachineSessionReadinessWorkflow({}),
|
||||
/machine session readiness reader/,
|
||||
);
|
||||
|
||||
const blockedSessionReadiness = {
|
||||
ready: false,
|
||||
phase: "blocked",
|
||||
missing: ["parameters", "toolTable"],
|
||||
checks: [
|
||||
{
|
||||
name: "parameters",
|
||||
path: "linuxcnc/machines/missing/linuxcnc.var",
|
||||
ok: false,
|
||||
error: "missing file",
|
||||
},
|
||||
{
|
||||
name: "toolTable",
|
||||
path: "linuxcnc/machines/missing/tool.tbl",
|
||||
ok: false,
|
||||
error: "missing file",
|
||||
},
|
||||
],
|
||||
snapshotCheck: null,
|
||||
};
|
||||
assert.deepEqual(createMachineSessionReadinessFieldState(blockedSessionReadiness), {
|
||||
sessionReadinessPhase: "blocked",
|
||||
sessionReadinessReady: "blocked",
|
||||
sessionReadinessMissing: "parameters, toolTable",
|
||||
});
|
||||
assert.deepEqual(machineSessionReadinessLogLines(blockedSessionReadiness), [
|
||||
"Session readiness: blocked",
|
||||
"parameters: linuxcnc/machines/missing/linuxcnc.var -> blocked (missing file)",
|
||||
"toolTable: linuxcnc/machines/missing/tool.tbl -> blocked (missing file)",
|
||||
]);
|
||||
|
||||
const calls = [];
|
||||
const saveWorkflow = await saveMachineSessionSnapshotWorkflow({
|
||||
machineId: "xyzab-tdr",
|
||||
@@ -224,6 +322,75 @@ assert.deepEqual(restoreWorkflow.logLines, [
|
||||
"G-code: linuxcnc/gcode/ui-session.ngc",
|
||||
]);
|
||||
|
||||
const restoreReadinessCalls = [];
|
||||
const restoreReadinessWorkflow = await restoreMachineSessionSnapshotWorkflow({
|
||||
machineId: "xyzab-tdr",
|
||||
snapshotId: "ui-machine-session",
|
||||
snapshotFilename: "ui-machine-session.json",
|
||||
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||
loadMachineSessionSnapshot: async (snapshotId, machineId, options) => {
|
||||
restoreReadinessCalls.push(["loadMachineSessionSnapshot", snapshotId, machineId, options]);
|
||||
return {
|
||||
sessionId: snapshotId,
|
||||
metadata: {
|
||||
workflow: "save-session-snapshot",
|
||||
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||
defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||
},
|
||||
payload: {
|
||||
files: {
|
||||
ini: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||
parameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||
toolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||
gcode: "linuxcnc/gcode/ui-session.ngc",
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
loadMachineTextFiles: async (machineId) => {
|
||||
restoreReadinessCalls.push(["loadMachineTextFiles", machineId]);
|
||||
return {
|
||||
ini: "[EMC]\nMACHINE = xyzab-tdr\n",
|
||||
};
|
||||
},
|
||||
readReadiness: async () => {
|
||||
restoreReadinessCalls.push(["readReadiness"]);
|
||||
return readySessionReadiness;
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(restoreReadinessCalls, [
|
||||
[
|
||||
"loadMachineSessionSnapshot",
|
||||
"ui-machine-session",
|
||||
"xyzab-tdr",
|
||||
{ filename: "ui-machine-session.json" },
|
||||
],
|
||||
["loadMachineTextFiles", "xyzab-tdr"],
|
||||
["readReadiness"],
|
||||
]);
|
||||
assert.equal(restoreReadinessWorkflow.readiness, readySessionReadiness);
|
||||
assert.deepEqual(restoreReadinessWorkflow.fields, {
|
||||
sessionIni: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||
sessionParameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||
sessionToolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||
sessionGcode: "linuxcnc/gcode/ui-session.ngc",
|
||||
sessionSnapshot: "ui-machine-session/ui-machine-session.json",
|
||||
sessionReadinessPhase: "ready",
|
||||
sessionReadinessReady: "ready",
|
||||
sessionReadinessMissing: "none",
|
||||
});
|
||||
assert.deepEqual(restoreReadinessWorkflow.logLines, [
|
||||
"Snapshot: ui-machine-session/ui-machine-session.json",
|
||||
"Workflow: save-session-snapshot",
|
||||
"Default G-code: linuxcnc/gcode/ui-session.ngc",
|
||||
"INI: linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||
"Parameter file: linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||
"Tool table file: linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||
"G-code: linuxcnc/gcode/ui-session.ngc",
|
||||
...machineSessionReadinessLogLines(readySessionReadiness),
|
||||
]);
|
||||
|
||||
const restoreLoadCalls = [];
|
||||
const restoreLoadWorkflow = await restoreMachineSessionSnapshotWorkflow({
|
||||
machineId: "xyzab-tdr",
|
||||
|
||||
@@ -4,7 +4,9 @@ import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import {
|
||||
createControlPageBrowserApiManifest,
|
||||
createControlPageBrowserApiMethods,
|
||||
createControlPageBrowserApiSchema,
|
||||
createIniPanelLaunchApiManifest,
|
||||
createIniPanelShellControlPageContract,
|
||||
createIniPanelShellViewModel,
|
||||
@@ -14,6 +16,7 @@ import {
|
||||
readMachineSessionReadonlyStatus,
|
||||
refreshMachineSessionControlPage,
|
||||
renderMachineSessionControlView,
|
||||
isSupportedIniPanelControlPageApiManifest,
|
||||
} from "../../../runtime/ui/ini-panel/ui-shell.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
@@ -31,6 +34,20 @@ const expectedMethodNames = [
|
||||
|
||||
const contract = createIniPanelShellControlPageContract();
|
||||
const manifest = createMachineSessionReadonlyStatusApiManifest();
|
||||
const browserApiSchema = createControlPageBrowserApiSchema();
|
||||
const browserApiManifest = createControlPageBrowserApiManifest();
|
||||
|
||||
assert.deepEqual(browserApiSchema, {
|
||||
apiName: "ini-panel-control-page",
|
||||
manifestVersion: 1,
|
||||
fields: [
|
||||
"apiName",
|
||||
"manifestVersion",
|
||||
"methods",
|
||||
"contract",
|
||||
"readonlyStatusApiManifest",
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(contract, {
|
||||
methodNames: expectedMethodNames,
|
||||
@@ -42,6 +59,7 @@ assert.deepEqual(contract, {
|
||||
"overview",
|
||||
"controlView",
|
||||
"report",
|
||||
"sessionReadiness",
|
||||
"runReadiness",
|
||||
"run",
|
||||
"session",
|
||||
@@ -55,22 +73,43 @@ assert.deepEqual(contract, {
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(browserApiManifest, {
|
||||
apiName: "ini-panel-control-page",
|
||||
manifestVersion: 1,
|
||||
methods: createControlPageBrowserApiMethods(),
|
||||
contract,
|
||||
readonlyStatusApiManifest: manifest,
|
||||
});
|
||||
assert.equal(isSupportedIniPanelControlPageApiManifest(browserApiManifest), true);
|
||||
assert.equal(isSupportedIniPanelControlPageApiManifest({ ...browserApiManifest, manifestVersion: 2 }), false);
|
||||
assert.equal(isSupportedIniPanelControlPageApiManifest({ ...browserApiManifest, apiName: "other" }), false);
|
||||
const missingReadonlyManifest = { ...browserApiManifest };
|
||||
delete missingReadonlyManifest.readonlyStatusApiManifest;
|
||||
assert.equal(isSupportedIniPanelControlPageApiManifest(missingReadonlyManifest), false);
|
||||
assert.equal(isSupportedIniPanelControlPageApiManifest(null), false);
|
||||
|
||||
const shellViewModel = createIniPanelShellViewModel();
|
||||
assert.deepEqual(shellViewModel.launchApiManifest, createIniPanelLaunchApiManifest());
|
||||
assert.deepEqual(Object.keys(shellViewModel.controlPage), [
|
||||
"browserApiSchema",
|
||||
"browserApiManifest",
|
||||
"browserApiMethods",
|
||||
"contract",
|
||||
"createController",
|
||||
"isSupportedBrowserApiManifest",
|
||||
"loadSessionState",
|
||||
"readonlyStatusApiManifest",
|
||||
"readStatus",
|
||||
"refresh",
|
||||
"renderView",
|
||||
]);
|
||||
assert.deepEqual(shellViewModel.controlPage.browserApiSchema, browserApiSchema);
|
||||
assert.deepEqual(shellViewModel.controlPage.browserApiManifest, browserApiManifest);
|
||||
assert.deepEqual(shellViewModel.controlPage.browserApiMethods, createControlPageBrowserApiMethods());
|
||||
assert.deepEqual(shellViewModel.controlPage.contract, contract);
|
||||
assert.deepEqual(shellViewModel.controlPage.readonlyStatusApiManifest, manifest);
|
||||
assert.equal(shellViewModel.controlPage.createController, createMachineSessionControlPageController);
|
||||
assert.equal(shellViewModel.controlPage.isSupportedBrowserApiManifest, isSupportedIniPanelControlPageApiManifest);
|
||||
assert.equal(shellViewModel.controlPage.loadSessionState, loadMachineSessionForControlPageWorkflow);
|
||||
assert.equal(shellViewModel.controlPage.readStatus, readMachineSessionReadonlyStatus);
|
||||
assert.equal(shellViewModel.controlPage.refresh, refreshMachineSessionControlPage);
|
||||
@@ -86,8 +125,14 @@ for (const fieldName of contract.readonlyStatusApiStatusFields) {
|
||||
}
|
||||
|
||||
assert.match(shellText, /\bexport function createIniPanelShellControlPageContract\b/);
|
||||
assert.match(shellText, /\bexport function createControlPageBrowserApiManifest\b/);
|
||||
assert.match(shellText, /\bexport function createControlPageBrowserApiMethods\b/);
|
||||
assert.match(shellText, /\bexport function createControlPageBrowserApiSchema\b/);
|
||||
assert.match(shellText, /\bexport function isSupportedIniPanelControlPageApiManifest\b/);
|
||||
assert.match(docText, /\bcreateIniPanelShellControlPageContract\b/);
|
||||
assert.match(docText, /\bcreateControlPageBrowserApiManifest\b/);
|
||||
assert.match(docText, /\bcreateControlPageBrowserApiSchema\b/);
|
||||
assert.match(docText, /\bisSupportedIniPanelControlPageApiManifest\b/);
|
||||
assert.match(docText, /\bcontrolPage\.contract\b/);
|
||||
assert.doesNotMatch(shellText, /\bparseGcode\b|\bcanonicalEvents\b|\btoolTable\b|\bplanner\b/);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
6
wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.sh
Executable file
6
wasm-port/tests/ui/node/verify_ini_panel_shell_integration_manifest.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
node "$ROOT_DIR/tests/ui/node/verify_ini_panel_shell_integration_manifest.mjs"
|
||||
@@ -3,6 +3,9 @@ import assert from "node:assert/strict";
|
||||
import {
|
||||
createIniPanelStateSummary,
|
||||
createMachineSessionControlView,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createMachineSessionStateSnapshot,
|
||||
createMachineSessionStateBundle,
|
||||
createMachineSessionStateReport,
|
||||
@@ -68,6 +71,12 @@ const expectedEmptyRunReadinessReport = {
|
||||
iniWasmPath: null,
|
||||
};
|
||||
|
||||
const sessionReadinessFixture = {
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
missing: [],
|
||||
};
|
||||
|
||||
const expectedWorkflowOverview = {
|
||||
phase: "ran",
|
||||
missing: [],
|
||||
@@ -120,6 +129,7 @@ const summary = createIniPanelStateSummary({
|
||||
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||
workflow: "save-session-snapshot",
|
||||
},
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
sessionLoad: {
|
||||
iniOpfsPath: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||
iniWasmPath: "/work/session-machine.ini",
|
||||
@@ -152,6 +162,7 @@ assert.deepEqual(summary, {
|
||||
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||
workflow: "save-session-snapshot",
|
||||
},
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
sessionLoad: {
|
||||
iniOpfsPath: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||
iniWasmPath: "/work/session-machine.ini",
|
||||
@@ -175,6 +186,7 @@ assert.deepEqual(
|
||||
machine: null,
|
||||
machineFiles: null,
|
||||
sessionSnapshot: null,
|
||||
sessionReadiness: null,
|
||||
sessionLoad: null,
|
||||
runReadiness: null,
|
||||
run: null,
|
||||
@@ -195,6 +207,7 @@ assert.deepEqual(
|
||||
sessionSnapshot: {
|
||||
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||
},
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
sessionLoad: {
|
||||
iniWasmPath: "/work/session-machine.ini",
|
||||
},
|
||||
@@ -237,6 +250,7 @@ assert.deepEqual(
|
||||
sessionSnapshot: {
|
||||
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||
},
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
sessionLoad: {
|
||||
iniWasmPath: "/work/session-machine.ini",
|
||||
},
|
||||
@@ -311,6 +325,7 @@ assert.deepEqual(
|
||||
parameterWasmPath: "/work/session-linuxcnc.var",
|
||||
toolTableWasmPath: "/work/session-tool.tbl",
|
||||
},
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
runReadiness: runReadinessFixture,
|
||||
selectedProgram: {
|
||||
source: "snapshot",
|
||||
@@ -344,6 +359,7 @@ assert.deepEqual(
|
||||
toolTable: "/work/session-tool.tbl",
|
||||
gcode: "linuxcnc/gcode/ui-session.ngc",
|
||||
},
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
loaded: {
|
||||
iniWasmPath: "/work/session-machine.ini",
|
||||
parameterWasmPath: "/work/session-linuxcnc.var",
|
||||
@@ -383,6 +399,7 @@ const expectedExportReport = {
|
||||
toolTable: null,
|
||||
gcode: null,
|
||||
},
|
||||
sessionReadiness: null,
|
||||
loaded: {
|
||||
iniWasmPath: "/work/session-machine.ini",
|
||||
parameterWasmPath: null,
|
||||
@@ -446,6 +463,7 @@ assert.deepEqual(
|
||||
runWorkflow: "save-session-snapshot",
|
||||
},
|
||||
runReadiness: runReadinessFixture,
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
run: {
|
||||
runStatus: "ok",
|
||||
canonicalEventCount: 2,
|
||||
@@ -492,6 +510,7 @@ assert.deepEqual(
|
||||
},
|
||||
},
|
||||
runReadiness: expectedRunReadinessReport,
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
lastAction: "run-gcode",
|
||||
error: null,
|
||||
session: {
|
||||
@@ -525,6 +544,7 @@ const expectedBundleReport = {
|
||||
toolTable: null,
|
||||
gcode: "linuxcnc/gcode/ui-session.ngc",
|
||||
},
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
loaded: {
|
||||
iniWasmPath: null,
|
||||
parameterWasmPath: null,
|
||||
@@ -549,6 +569,145 @@ const expectedBundleReport = {
|
||||
},
|
||||
};
|
||||
|
||||
const readyPersistenceSummary = createMachineSessionPersistenceSummary({
|
||||
state: {
|
||||
badges: {
|
||||
ini: "INI WASM: ready",
|
||||
interp: "Interpreter WASM: ready",
|
||||
opfs: "OPFS: ready",
|
||||
},
|
||||
machineFiles: {
|
||||
iniOpfsPath: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||
parameterOpfsPath: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||
toolTableOpfsPath: "linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||
gcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||
},
|
||||
fields: {
|
||||
sessionSnapshot: "ui-machine-session/ui-machine-session.json",
|
||||
sessionIni: "/work/session-machine.ini",
|
||||
sessionParameters: "/work/session-linuxcnc.var",
|
||||
sessionToolTable: "/work/session-tool.tbl",
|
||||
sessionGcode: "linuxcnc/gcode/ui-session.ngc",
|
||||
},
|
||||
sessionLoad: {
|
||||
iniWasmPath: "/work/session-machine.ini",
|
||||
parameterWasmPath: "/work/session-linuxcnc.var",
|
||||
toolTableWasmPath: "/work/session-tool.tbl",
|
||||
},
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
},
|
||||
readonlyStatus: {
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
},
|
||||
handoffWorkflowSummary: {
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
readyPersistenceSummary,
|
||||
{
|
||||
apiName: "machine-session-persistence-summary",
|
||||
summaryVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
checks: {
|
||||
opfs: true,
|
||||
machineFiles: true,
|
||||
sessionSnapshot: true,
|
||||
sessionReadiness: true,
|
||||
sessionLoad: true,
|
||||
},
|
||||
missing: [],
|
||||
counts: {
|
||||
machineFiles: 4,
|
||||
missing: 0,
|
||||
},
|
||||
rows: [
|
||||
{
|
||||
id: "opfs",
|
||||
label: "OPFS",
|
||||
value: "OPFS: ready",
|
||||
},
|
||||
{
|
||||
id: "machine-files",
|
||||
label: "Machine files",
|
||||
value: "ini, parameters, tool-table, g-code",
|
||||
},
|
||||
{
|
||||
id: "session-snapshot",
|
||||
label: "Session snapshot",
|
||||
value: "ui-machine-session/ui-machine-session.json",
|
||||
},
|
||||
{
|
||||
id: "session-readiness",
|
||||
label: "Session readiness",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "session-load",
|
||||
label: "Session load",
|
||||
value: "loaded: /work/session-machine.ini",
|
||||
},
|
||||
{
|
||||
id: "readonly-status",
|
||||
label: "Readonly status",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "handoff-readiness",
|
||||
label: "Handoff readiness",
|
||||
value: "ready",
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
createMachineSessionPersistenceSummary().missing,
|
||||
["opfs", "machineFiles", "sessionSnapshot", "sessionReadiness", "sessionLoad"],
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
createMachineSessionPersistenceDisplayViewModel(readyPersistenceSummary),
|
||||
{
|
||||
phase: "ready",
|
||||
title: "OPFS/session persistence",
|
||||
statusText: "Ready",
|
||||
detailText: "No missing persistence requirements",
|
||||
rows: readyPersistenceSummary.rows,
|
||||
},
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
createMachineSessionPersistenceRenderState(
|
||||
createMachineSessionPersistenceDisplayViewModel(readyPersistenceSummary),
|
||||
),
|
||||
{
|
||||
apiName: "machine-session-persistence-render-state",
|
||||
renderStateVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
title: "OPFS/session persistence",
|
||||
statusText: "Ready",
|
||||
detailText: "No missing persistence requirements",
|
||||
statusLine: "Ready: No missing persistence requirements",
|
||||
rows: readyPersistenceSummary.rows,
|
||||
dataset: {
|
||||
persistencePhase: "ready",
|
||||
persistenceReady: "true",
|
||||
persistenceScope: "opfs-session",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
createMachineSessionPersistenceRenderState().statusLine,
|
||||
"Waiting: opfs, machineFiles, sessionSnapshot, sessionReadiness, sessionLoad",
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
createMachineSessionWorkflowOverview(expectedBundleReport),
|
||||
expectedWorkflowOverview,
|
||||
@@ -569,6 +728,7 @@ assert.deepEqual(
|
||||
runWorkflow: "save-session-snapshot",
|
||||
},
|
||||
runReadiness: runReadinessFixture,
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
run: {
|
||||
runStatus: "ok",
|
||||
canonicalEventCount: 2,
|
||||
@@ -593,6 +753,7 @@ assert.deepEqual(
|
||||
opfs: "OPFS: ready",
|
||||
},
|
||||
overview: expectedWorkflowOverview,
|
||||
sessionReadiness: sessionReadinessFixture,
|
||||
lastAction: "run-gcode",
|
||||
error: null,
|
||||
runReadiness: expectedRunReadinessReport,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@ ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_readonly_status_api.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_entry_manifest.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_launch_api_manifest.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_shell_integration_manifest.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_ui_shell.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_shell_control_page_contract.sh"
|
||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_entry_docs.sh"
|
||||
|
||||
Reference in New Issue
Block a user