pinokiod 7.3.14 → 7.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/kernel/api/index.js +111 -15
- package/kernel/api/script/index.js +10 -0
- package/kernel/autolaunch.js +111 -0
- package/kernel/environment.js +89 -1
- package/kernel/git.js +9 -19
- package/kernel/index.js +142 -43
- package/kernel/launch_requirements.js +1115 -0
- package/kernel/ready.js +231 -0
- package/kernel/script.js +16 -0
- package/kernel/shells.js +9 -1
- package/kernel/workspace_status.js +111 -45
- package/package.json +1 -1
- package/server/autolaunch.js +725 -0
- package/server/index.js +244 -411
- package/server/public/logs.js +15 -1
- package/server/public/style.css +99 -31
- package/server/views/app.ejs +263 -159
- package/server/views/autolaunch.ejs +363 -75
- package/server/views/index.ejs +550 -26
- package/server/views/logs.ejs +14 -12
- package/server/views/partials/app_autolaunch_dependency_events.ejs +99 -0
- package/server/views/partials/app_autolaunch_dependency_save.ejs +10 -0
- package/server/views/partials/app_autolaunch_dependency_styles.ejs +357 -0
- package/server/views/partials/app_autolaunch_modal_helpers.ejs +347 -0
- package/server/views/partials/autolaunch_dependency_helpers.ejs +204 -0
- package/server/views/partials/autolaunch_dependency_save.ejs +13 -0
- package/server/views/partials/autolaunch_dependency_styles.ejs +347 -0
- package/server/views/partials/home_action_modal.ejs +4 -1
- package/server/views/partials/launch_requirements_status_client.ejs +271 -0
- package/server/views/partials/launch_requirements_status_styles.ejs +171 -0
- package/server/views/partials/launch_settings_dependency_save_factory.ejs +45 -0
- package/server/views/partials/launch_settings_dependency_script_loader_factory.ejs +25 -0
- package/server/views/terminal.ejs +196 -2
- package/test/home-autolaunch-live-ui.test.js +455 -0
- package/test/launch-requirements-browser.test.js +579 -0
- package/test/launch-requirements-contract-coverage.test.js +627 -0
- package/test/launch-requirements-real-browser.js +625 -0
- package/test/launch-requirements-status-client.test.js +132 -0
- package/test/launch-requirements.test.js +1806 -0
- package/test/launch-settings-ui.test.js +370 -0
- package/test/ready-state.test.js +49 -0
- package/test/server-autolaunch.test.js +1052 -0
- package/test/startup-git-index-benchmark.js +409 -0
- package/test/startup-git-index-browser.js +320 -0
- package/test/startup-git-index-performance.test.js +380 -0
- package/test/startup-git-index-refactor.test.js +450 -0
- package/test/startup-git-index-route.test.js +588 -0
- package/test/universal-launcher.smoke.spec.js +10 -9
- package/test/workspace-gitignore-benchmark.js +815 -0
- package/test/workspace-gitignore-path-scoped.test.js +256 -0
- package/spec/INSTRUCTION_SYNC.md +0 -432
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
const assert = require("node:assert/strict")
|
|
2
|
+
const fs = require("node:fs/promises")
|
|
3
|
+
const path = require("node:path")
|
|
4
|
+
const test = require("node:test")
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(__dirname, "..")
|
|
7
|
+
|
|
8
|
+
const TEST_FILES = [
|
|
9
|
+
"test/launch-requirements.test.js",
|
|
10
|
+
"test/server-autolaunch.test.js",
|
|
11
|
+
"test/launch-settings-ui.test.js",
|
|
12
|
+
"test/launch-requirements-browser.test.js",
|
|
13
|
+
"test/home-autolaunch-live-ui.test.js"
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
const implementationGateCoverage = [
|
|
17
|
+
{
|
|
18
|
+
id: "IG-01",
|
|
19
|
+
spec: "Keep the implementation strictly additive.",
|
|
20
|
+
tests: [
|
|
21
|
+
"launch requirements do not run for non-configured explicit app scripts",
|
|
22
|
+
"launch requirements do not run when no launch script and no requirements are configured",
|
|
23
|
+
"api.process bypasses launch requirement plumbing when no launch env exists",
|
|
24
|
+
"api.process bypasses launch requirement plumbing when launch script exists without requirements",
|
|
25
|
+
"kernel lifecycle hooks do not call launch requirement hooks without active requirement runtime",
|
|
26
|
+
"kernel launch requirement config gate requires both configured launch script and requirements",
|
|
27
|
+
"autolaunch route preserves configured script when startup is disabled"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "IG-02",
|
|
32
|
+
spec: "Do not change existing Pinokio script execution unless the requested script is the app's configured launch script and requirements are configured.",
|
|
33
|
+
tests: [
|
|
34
|
+
"launch requirements run when requested script is configured launch script regardless of filename",
|
|
35
|
+
"launch requirements do not run for non-configured explicit app scripts",
|
|
36
|
+
"api.process bypasses launch requirement plumbing when no launch env exists",
|
|
37
|
+
"launch requirements do not run when no launch script and no requirements are configured",
|
|
38
|
+
"api.process bypasses launch requirement plumbing when launch script exists without requirements",
|
|
39
|
+
"kernel lifecycle hooks do not call launch requirement hooks without active requirement runtime",
|
|
40
|
+
"kernel launch requirement config gate requires both configured launch script and requirements"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "IG-03",
|
|
45
|
+
spec: "Do not use script filenames, route shape, DOM attributes, query parameters, or `default: true` to decide requirement behavior.",
|
|
46
|
+
tests: [
|
|
47
|
+
"launch requirements are not controlled by DOM markers or query parameters",
|
|
48
|
+
"launch requirements run when requested script is configured launch script regardless of filename"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "IG-04",
|
|
53
|
+
spec: "Do not emit terminal/session `disconnect` from requirement preparation.",
|
|
54
|
+
tests: ["api.process does not emit terminal disconnect for requirement control results"]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "IG-05",
|
|
58
|
+
spec: "Do not emit terminal/session `disconnect` while the target is waiting for requirements.",
|
|
59
|
+
tests: ["api.process does not emit terminal disconnect for requirement control results"]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "IG-06",
|
|
63
|
+
spec: "Do not emit terminal/session `disconnect` when requirements are blocked.",
|
|
64
|
+
tests: ["api.process does not emit terminal disconnect for requirement control results"]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "IG-07",
|
|
68
|
+
spec: "Do not emit terminal/session `disconnect` when another real active launch operation is already handling the same target launch.",
|
|
69
|
+
tests: [
|
|
70
|
+
"api.process does not emit terminal disconnect for requirement control results",
|
|
71
|
+
"active launch ownership prevents duplicate manual target launch"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "IG-08",
|
|
76
|
+
spec: "Do not encode normal requirement control flow as thrown pseudo-errors that generic script execution converts into terminal events.",
|
|
77
|
+
tests: ["api.process does not emit terminal disconnect for requirement control results"]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "IG-09",
|
|
81
|
+
spec: "`api.process` must continue into normal script execution after requirements resolve.",
|
|
82
|
+
tests: ["api.process continues into normal script execution after requirements are ready"]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "IG-10",
|
|
86
|
+
spec: "`api.process` must return a structured `handled` result and emit no terminal event.",
|
|
87
|
+
tests: [
|
|
88
|
+
"api.process does not emit terminal disconnect for requirement control results",
|
|
89
|
+
"active launch ownership prevents duplicate manual target launch"
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "IG-11",
|
|
94
|
+
spec: "Do not add fallback reads from legacy dependency keys.",
|
|
95
|
+
tests: ["launch requirements ignore legacy autolaunch dependency key"]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "IG-12",
|
|
99
|
+
spec: "Do not silently select a default launch script.",
|
|
100
|
+
tests: [
|
|
101
|
+
"launch settings only checks a script when it is persisted",
|
|
102
|
+
"launch settings opens dependency script picker before saving a new dependency"
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: "IG-13",
|
|
107
|
+
spec: "Do not make app-page requirement status from saved config alone.",
|
|
108
|
+
tests: [
|
|
109
|
+
"app page does not fabricate launch requirement status from saved config",
|
|
110
|
+
"static guard: open without launching is not wired to launch requirement status",
|
|
111
|
+
"app page launch requirement status client is inert until launch config exists"
|
|
112
|
+
]
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: "IG-14",
|
|
116
|
+
spec: "Do not make home live updates depend on initial HTML status rows.",
|
|
117
|
+
tests: [
|
|
118
|
+
"static guard: home autolaunch live status discovers rows after initial render",
|
|
119
|
+
"home live status renders waiting state on an initially idle row"
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "IG-15",
|
|
124
|
+
spec: "Do not make startup rows begin with empty `dependencies` or `waiting_for` when configured requirements exist.",
|
|
125
|
+
tests: [
|
|
126
|
+
"autolaunch scheduler seeds startup requirements before launching roots",
|
|
127
|
+
"home startup state renders configured requirement waiting before runtime status"
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "IG-16",
|
|
132
|
+
spec: "Do not redesign home buttons/status while repairing behavior.",
|
|
133
|
+
tests: [
|
|
134
|
+
"home running buttons only show spinner while startup label is active",
|
|
135
|
+
"home live status transitions from preparing to normal stop button"
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: "IG-17",
|
|
140
|
+
spec: "Startup/home display rows are output only and must not control launch ownership.",
|
|
141
|
+
tests: [
|
|
142
|
+
"display-only startup row does not prevent manual target launch",
|
|
143
|
+
"display-only startup row for offline required app still starts required app"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: "IG-18",
|
|
148
|
+
spec: "Stale script progress must not render as current progress without active or running launch.",
|
|
149
|
+
tests: ["stale script progress is hidden when there is no active or running launch"]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "IG-19",
|
|
153
|
+
spec: "Do not keep code only because current tests depend on it; every existing-file change needs a changed-file reason.",
|
|
154
|
+
processGate: "diff audit and changed-file reason list"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "IG-20",
|
|
158
|
+
spec: "With no launch-related env vars, Pinokio behavior is unchanged.",
|
|
159
|
+
tests: [
|
|
160
|
+
"launch requirements do not run when no launch script and no requirements are configured",
|
|
161
|
+
"api.process bypasses launch requirement plumbing when no launch env exists",
|
|
162
|
+
"api.process bypasses launch requirement plumbing when launch script exists without requirements",
|
|
163
|
+
"kernel lifecycle hooks do not call launch requirement hooks without active requirement runtime",
|
|
164
|
+
"kernel launch requirement config gate requires both configured launch script and requirements",
|
|
165
|
+
"app page launch requirement status client is inert until launch config exists",
|
|
166
|
+
"manual explicit script is not blocked by requirement-only status without a launch path"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: "IG-21",
|
|
171
|
+
spec: "Open without launching disables all page-load script selection paths.",
|
|
172
|
+
tests: [
|
|
173
|
+
"static guard: open without launching is not wired to launch requirement status",
|
|
174
|
+
"static guard: open without launching disables page-load script frame selection",
|
|
175
|
+
"app page launch requirement status client is inert until launch config exists"
|
|
176
|
+
]
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: "IG-22",
|
|
180
|
+
spec: "Requirement runtime status is ephemeral launch-attempt state, not durable app state.",
|
|
181
|
+
tests: [
|
|
182
|
+
"launch requirements clear blocked status on explicit cancel",
|
|
183
|
+
"launch requirements clears startup status on stop instead of persisting stopped state"
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: "IG-23",
|
|
188
|
+
spec: "Requirement runtime status uses only pending, waiting, starting, ready, and blocked.",
|
|
189
|
+
tests: ["launch requirements runtime state model has no failed timeout or persisted stopped states"]
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: "IG-24",
|
|
193
|
+
spec: "There is no automatic readiness timeout path.",
|
|
194
|
+
tests: ["launch requirements have no automatic readiness timeout path"]
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: "IG-25",
|
|
198
|
+
spec: "There is no second launch-script environment variable, hidden launch-script state, or migration.",
|
|
199
|
+
tests: [
|
|
200
|
+
"launch requirements use no second launch script environment key",
|
|
201
|
+
"autolaunch scheduler must not migrate startup script into a second launch key"
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: "IG-26",
|
|
206
|
+
spec: "Selected launch script and startup enabled are separate state facts.",
|
|
207
|
+
tests: [
|
|
208
|
+
"autolaunch route preserves configured script when startup is disabled",
|
|
209
|
+
"autolaunch route rejects script save without explicit startup enabled state",
|
|
210
|
+
"autolaunch scheduler skips configured scripts when startup is disabled",
|
|
211
|
+
"disable all startup launch preserves configured scripts",
|
|
212
|
+
"launch settings UI sends selected script with explicit startup enabled state"
|
|
213
|
+
]
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
id: "IG-27",
|
|
217
|
+
spec: "Script-save requests must not infer startup enabled from a script value.",
|
|
218
|
+
tests: ["autolaunch route rejects script save without explicit startup enabled state"]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: "IG-28",
|
|
222
|
+
spec: "App ENVIRONMENT generation preserves existing custom or unsupported keys instead of silently cleaning them up.",
|
|
223
|
+
tests: ["generated app environment preserves unsupported custom keys without cleanup"]
|
|
224
|
+
}
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
const forbiddenRegressionCoverage = [
|
|
228
|
+
{
|
|
229
|
+
id: "FR-01",
|
|
230
|
+
spec: "home waits several seconds before showing startup/autolaunch status",
|
|
231
|
+
tests: [
|
|
232
|
+
"home startup state renders configured requirement waiting before runtime status",
|
|
233
|
+
"home live status renders waiting state on an initially idle row"
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: "FR-02",
|
|
238
|
+
spec: "home requires manual refresh before startup/autolaunch status changes",
|
|
239
|
+
tests: [
|
|
240
|
+
"static guard: home autolaunch live status discovers rows after initial render",
|
|
241
|
+
"home live status transitions from preparing to normal stop button"
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
id: "FR-03",
|
|
246
|
+
spec: "a startup root with requirements says only `Starting <script>` when it should say `Waiting for <required app>`",
|
|
247
|
+
tests: ["home startup state renders configured requirement waiting before runtime status"]
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: "FR-04",
|
|
251
|
+
spec: "the startup scheduler seeds root rows with empty `dependencies` or `waiting_for` when configured requirements exist",
|
|
252
|
+
tests: ["autolaunch scheduler seeds startup requirements before launching roots"]
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
id: "FR-05",
|
|
256
|
+
spec: "a script that is not the configured launch script is intercepted by requirement logic",
|
|
257
|
+
tests: [
|
|
258
|
+
"launch requirements do not run for non-configured explicit app scripts",
|
|
259
|
+
"manual explicit script is not blocked by requirement-only status without a launch path"
|
|
260
|
+
]
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: "FR-06",
|
|
264
|
+
spec: "requirement logic emits a terminal/session `disconnect` event",
|
|
265
|
+
tests: ["api.process does not emit terminal disconnect for requirement control results"]
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
id: "FR-07",
|
|
269
|
+
spec: "requirement logic reroutes a concrete script request away from the script the user requested",
|
|
270
|
+
tests: ["launch requirements do not run for non-configured explicit app scripts"]
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
id: "FR-08",
|
|
274
|
+
spec: "home stop buttons or status labels are redesigned instead of preserving the dependency-aware home behavior",
|
|
275
|
+
tests: [
|
|
276
|
+
"home running buttons only show spinner while startup label is active",
|
|
277
|
+
"home live status transitions from preparing to normal stop button"
|
|
278
|
+
]
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: "FR-09",
|
|
282
|
+
spec: "startup on/off, launch script, and requirements become tangled",
|
|
283
|
+
tests: [
|
|
284
|
+
"autolaunch route preserves configured script when startup is disabled",
|
|
285
|
+
"autolaunch route rejects script save without explicit startup enabled state",
|
|
286
|
+
"autolaunch route rejects clearing launch script while requirements exist",
|
|
287
|
+
"disable all startup launch preserves configured scripts",
|
|
288
|
+
"launch settings UI sends selected script with explicit startup enabled state"
|
|
289
|
+
]
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
id: "FR-10",
|
|
293
|
+
spec: "startup/home display status is treated as launch ownership",
|
|
294
|
+
tests: [
|
|
295
|
+
"display-only startup row does not prevent manual target launch",
|
|
296
|
+
"display-only startup row for offline required app still starts required app"
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
id: "FR-11",
|
|
301
|
+
spec: "stale progress is displayed as current launch progress",
|
|
302
|
+
tests: [
|
|
303
|
+
"stale script progress is hidden when there is no active or running launch",
|
|
304
|
+
"launch requirement status does not surface stale progress without an active launch"
|
|
305
|
+
]
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
id: "FR-12",
|
|
309
|
+
spec: "explicitly stopping a required app clears dependent launch requirement state",
|
|
310
|
+
tests: ["explicitly stopping a required app clears dependent launch requirement state"]
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: "FR-13",
|
|
314
|
+
spec: "open without launching starts a script through persisted selection, preselected links, default selection, iframe creation, or requirement status",
|
|
315
|
+
tests: ["static guard: open without launching disables page-load script frame selection"]
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
id: "FR-14",
|
|
319
|
+
spec: "no-env app behavior changes because launch-requirement code runs anyway",
|
|
320
|
+
tests: [
|
|
321
|
+
"launch requirements do not run when no launch script and no requirements are configured",
|
|
322
|
+
"api.process bypasses launch requirement plumbing when no launch env exists",
|
|
323
|
+
"app page launch requirement status client is inert until launch config exists",
|
|
324
|
+
"manual explicit script is not blocked by requirement-only status without a launch path"
|
|
325
|
+
]
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
id: "FR-15",
|
|
329
|
+
spec: "new code changes existing routes, frame selection, terminal events, or concrete script execution without a launch-related environment gate",
|
|
330
|
+
tests: [
|
|
331
|
+
"static guard: open without launching disables page-load script frame selection",
|
|
332
|
+
"api.process does not emit terminal disconnect for requirement control results",
|
|
333
|
+
"api.process bypasses launch requirement plumbing when no launch env exists",
|
|
334
|
+
"api.process bypasses launch requirement plumbing when launch script exists without requirements",
|
|
335
|
+
"kernel lifecycle hooks do not call launch requirement hooks without active requirement runtime",
|
|
336
|
+
"app page launch requirement status client is inert until launch config exists",
|
|
337
|
+
"launch requirements do not run when no launch script and no requirements are configured"
|
|
338
|
+
]
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: "FR-16",
|
|
342
|
+
spec: "requirement runtime states include failed, timeout, persisted cancelled, or persisted stopped",
|
|
343
|
+
tests: ["launch requirements runtime state model has no failed timeout or persisted stopped states"]
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
id: "FR-17",
|
|
347
|
+
spec: "blocked/setup-needed state survives as stale app state instead of being recomputed for the current launch attempt",
|
|
348
|
+
tests: [
|
|
349
|
+
"launch requirements clear blocked status on explicit cancel",
|
|
350
|
+
"manual launch ignores display-only blocked startup row and evaluates current config"
|
|
351
|
+
]
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
id: "FR-18",
|
|
355
|
+
spec: "automatic readiness timeout path changes waiting semantics",
|
|
356
|
+
tests: ["launch requirements have no automatic readiness timeout path"]
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: "FR-19",
|
|
360
|
+
spec: "dependency save silently drops bad ids and persists a partial requirement list",
|
|
361
|
+
tests: ["autolaunch dependencies route rejects invalid dependency ids atomically"]
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
|
|
365
|
+
const requiredResolverCoverage = [
|
|
366
|
+
{
|
|
367
|
+
id: "RT-01",
|
|
368
|
+
spec: "recursive ordering",
|
|
369
|
+
tests: ["launch requirements resolve recursive app fixtures in ancestor-first order"]
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: "RT-02",
|
|
373
|
+
spec: "cycle preflight",
|
|
374
|
+
tests: ["launch requirements preflight cycles before starting anything"]
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
id: "RT-03",
|
|
378
|
+
spec: "parallel independent requirements",
|
|
379
|
+
tests: ["launch requirements start independent requirements in parallel"]
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
id: "RT-04",
|
|
383
|
+
spec: "already ready requirement",
|
|
384
|
+
tests: ["launch requirements do not restart an already ready requirement"]
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
id: "RT-05",
|
|
388
|
+
spec: "already starting or already running requirement",
|
|
389
|
+
tests: [
|
|
390
|
+
"launch requirements wait for an already running requirement instead of duplicating it",
|
|
391
|
+
"active launch ownership prevents duplicate manual target launch"
|
|
392
|
+
]
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
id: "RT-06",
|
|
396
|
+
spec: "concurrent shared requirement",
|
|
397
|
+
tests: ["launch requirements dedupe one required app shared by concurrent targets"]
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
id: "RT-07",
|
|
401
|
+
spec: "cancel waiting target",
|
|
402
|
+
tests: ["launch requirements cancel a waiting target without stopping its requirement"]
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
id: "RT-07A",
|
|
406
|
+
spec: "startup reentry remains cancelled while the cancelled launch operation is still active",
|
|
407
|
+
tests: ["launch requirements startup reentry after cancel does not erase cancellation"]
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
id: "RT-07B",
|
|
411
|
+
spec: "app-page default launch after a cancelled operation unwinds clears old cancellation",
|
|
412
|
+
tests: ["launch requirements app-page default launch after cancel clears old cancellation"]
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
id: "RT-08",
|
|
416
|
+
spec: "configured launch script resolves requirements regardless of filename or entry point",
|
|
417
|
+
tests: ["launch requirements run when requested script is configured launch script regardless of filename"]
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
id: "RT-09",
|
|
421
|
+
spec: "concrete script without saved launch script runs directly",
|
|
422
|
+
tests: [
|
|
423
|
+
"launch requirements do not run when no launch script and no requirements are configured",
|
|
424
|
+
"manual explicit script is not blocked by requirement-only status without a launch path"
|
|
425
|
+
]
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
id: "RT-10",
|
|
429
|
+
spec: "concrete non-launch scripts run directly",
|
|
430
|
+
tests: ["launch requirements do not run for non-configured explicit app scripts"]
|
|
431
|
+
}
|
|
432
|
+
]
|
|
433
|
+
|
|
434
|
+
const requiredIntegrationCoverage = [
|
|
435
|
+
{
|
|
436
|
+
id: "IT-01",
|
|
437
|
+
spec: "startup home status appears immediately",
|
|
438
|
+
tests: [
|
|
439
|
+
"home startup state renders configured requirement waiting before runtime status",
|
|
440
|
+
"home live status renders waiting state on an initially idle row"
|
|
441
|
+
]
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
id: "IT-02",
|
|
445
|
+
spec: "startup root waits visibly for requirement",
|
|
446
|
+
tests: [
|
|
447
|
+
"home startup state renders configured requirement waiting before runtime status",
|
|
448
|
+
"home live status renders waiting state on an initially idle row"
|
|
449
|
+
]
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
id: "IT-03",
|
|
453
|
+
spec: "startup dependency starts first",
|
|
454
|
+
tests: [
|
|
455
|
+
"autolaunch scheduler seeds startup requirements before launching roots",
|
|
456
|
+
"startup launch mirrors requirement-only apps into startup status"
|
|
457
|
+
]
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
id: "IT-04",
|
|
461
|
+
spec: "home live updates without refresh",
|
|
462
|
+
tests: [
|
|
463
|
+
"home live status clears a running row when it disappears from runtime status",
|
|
464
|
+
"home live status transitions from preparing to normal stop button"
|
|
465
|
+
]
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
id: "IT-05",
|
|
469
|
+
spec: "manual launch waits for requirement",
|
|
470
|
+
tests: [
|
|
471
|
+
"launch requirements run when requested script is configured launch script regardless of filename",
|
|
472
|
+
"launch requirements wait for an already running requirement instead of duplicating it"
|
|
473
|
+
]
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
id: "IT-06",
|
|
477
|
+
spec: "startup root with requirement-only app",
|
|
478
|
+
tests: ["startup launch mirrors requirement-only apps into startup status"]
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
id: "IT-07",
|
|
482
|
+
spec: "requirements require an explicit configured script",
|
|
483
|
+
tests: [
|
|
484
|
+
"autolaunch dependencies route rejects requirements without owning launch script",
|
|
485
|
+
"autolaunch route preserves configured script when startup is disabled"
|
|
486
|
+
]
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
id: "IT-08",
|
|
490
|
+
spec: "startup enabled and configured launch script are separate settings",
|
|
491
|
+
tests: [
|
|
492
|
+
"autolaunch route preserves configured script when startup is disabled",
|
|
493
|
+
"disable all startup launch preserves configured scripts"
|
|
494
|
+
]
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
id: "IT-09",
|
|
498
|
+
spec: "missing required launch script blocks visibly",
|
|
499
|
+
tests: [
|
|
500
|
+
"launch requirements expose missing requirement script in target status",
|
|
501
|
+
"startup launch exposes blocked requirement status for app page",
|
|
502
|
+
"launch requirements clear blocked status on explicit cancel"
|
|
503
|
+
]
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
id: "IT-10",
|
|
507
|
+
spec: "add requirement with missing launch script",
|
|
508
|
+
tests: [
|
|
509
|
+
"launch settings opens dependency script picker before saving a new dependency",
|
|
510
|
+
"launch settings dependency script loader is shared"
|
|
511
|
+
]
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
id: "IT-11",
|
|
515
|
+
spec: "Open without launching",
|
|
516
|
+
tests: [
|
|
517
|
+
"app page does not fabricate launch requirement status from saved config",
|
|
518
|
+
"static guard: open without launching is not wired to launch requirement status"
|
|
519
|
+
]
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
id: "IT-12",
|
|
523
|
+
spec: "stale status cleanup",
|
|
524
|
+
tests: [
|
|
525
|
+
"launch requirements clear status related to edited app",
|
|
526
|
+
"home live status clears startup row when runtime status disappears after stop",
|
|
527
|
+
"explicitly stopping a required app clears dependent launch requirement state",
|
|
528
|
+
"launch requirements clear blocked status on explicit cancel",
|
|
529
|
+
"launch requirements clears startup status on stop instead of persisting stopped state"
|
|
530
|
+
]
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
id: "IT-13",
|
|
534
|
+
spec: "concrete non-launch script survives stale requirement blocker",
|
|
535
|
+
tests: [
|
|
536
|
+
"launch requirements do not block concrete scripts when stale requirements have no owning launch script",
|
|
537
|
+
"manual explicit script is not blocked by requirement-only status without a launch path"
|
|
538
|
+
]
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
id: "IT-14",
|
|
542
|
+
spec: "real browser verification",
|
|
543
|
+
processGate: "must be completed outside node --test with saved screenshot or recording"
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
id: "IT-15",
|
|
547
|
+
spec: "Open without launching must not restore persisted selection, selected rows, default scripts, or script iframes on page load.",
|
|
548
|
+
tests: ["static guard: open without launching disables page-load script frame selection"]
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
id: "IT-16",
|
|
552
|
+
spec: "blocked setup-needed UI is actionable",
|
|
553
|
+
tests: ["browser: blocked setup status exposes script selection and stop actions"],
|
|
554
|
+
processGate: "real Browser/Computer verification against the actual running app"
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
id: "IT-17",
|
|
558
|
+
spec: "dependency script confirmation is an explicit command",
|
|
559
|
+
tests: ["browser: adding a requirement with an existing script still requires script confirmation"],
|
|
560
|
+
processGate: "real Browser/Computer verification against the actual running app"
|
|
561
|
+
},
|
|
562
|
+
{
|
|
563
|
+
id: "IT-18",
|
|
564
|
+
spec: "dependency saves are atomic",
|
|
565
|
+
tests: ["autolaunch dependencies route rejects invalid dependency ids atomically"]
|
|
566
|
+
}
|
|
567
|
+
]
|
|
568
|
+
|
|
569
|
+
const allCoverage = [
|
|
570
|
+
...implementationGateCoverage,
|
|
571
|
+
...forbiddenRegressionCoverage,
|
|
572
|
+
...requiredResolverCoverage,
|
|
573
|
+
...requiredIntegrationCoverage
|
|
574
|
+
]
|
|
575
|
+
|
|
576
|
+
async function loadTestNames() {
|
|
577
|
+
const names = new Set()
|
|
578
|
+
for (const file of TEST_FILES) {
|
|
579
|
+
const text = await fs.readFile(path.resolve(root, file), "utf8")
|
|
580
|
+
for (const match of text.matchAll(/(?:test|browserTest)\("([^"]+)"/g)) {
|
|
581
|
+
names.add(match[1])
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return names
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
test("launch requirements contract coverage has no unmapped enforced items", async () => {
|
|
588
|
+
const testNames = await loadTestNames()
|
|
589
|
+
|
|
590
|
+
assert.equal(implementationGateCoverage.length, 28)
|
|
591
|
+
assert.equal(forbiddenRegressionCoverage.length, 19)
|
|
592
|
+
assert.equal(requiredResolverCoverage.length, 12)
|
|
593
|
+
assert.equal(requiredIntegrationCoverage.length, 18)
|
|
594
|
+
|
|
595
|
+
for (const item of allCoverage) {
|
|
596
|
+
assert.ok(
|
|
597
|
+
item.spec && typeof item.spec === "string",
|
|
598
|
+
`${item.id} has no spec label`
|
|
599
|
+
)
|
|
600
|
+
assert.ok(
|
|
601
|
+
(Array.isArray(item.tests) && item.tests.length > 0) || item.processGate,
|
|
602
|
+
`${item.id} has no automated test or explicit process gate`
|
|
603
|
+
)
|
|
604
|
+
for (const testName of item.tests || []) {
|
|
605
|
+
assert.ok(testNames.has(testName), `${item.id} references missing test: ${testName}`)
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
test("launch requirements contract process-only gates are explicit and limited", () => {
|
|
611
|
+
const processOnly = allCoverage.filter((item) => item.processGate && (!item.tests || item.tests.length === 0))
|
|
612
|
+
assert.deepEqual(processOnly.map((item) => item.id).sort(), ["IG-19", "IT-14"])
|
|
613
|
+
})
|
|
614
|
+
|
|
615
|
+
test("fixture browser tests cannot count as real browser verification by themselves", async () => {
|
|
616
|
+
const browserText = await fs.readFile(path.resolve(root, "test/launch-requirements-browser.test.js"), "utf8")
|
|
617
|
+
assert.doesNotMatch(browserText, /test\.skip|skip\(/)
|
|
618
|
+
assert.match(browserText, /const browserTest = test/)
|
|
619
|
+
assert.match(browserText, /Playwright is required\. Run: npx -y -p playwright node --test test\/launch-requirements-browser\.test\.js/)
|
|
620
|
+
assert.match(browserText, /startFixtureServer/)
|
|
621
|
+
|
|
622
|
+
const fixtureBrowserOnly = allCoverage.filter((item) => {
|
|
623
|
+
const tests = Array.isArray(item.tests) ? item.tests : []
|
|
624
|
+
return tests.some((testName) => testName.startsWith("browser:")) && !item.processGate
|
|
625
|
+
})
|
|
626
|
+
assert.deepEqual(fixtureBrowserOnly, [])
|
|
627
|
+
})
|