@unotest/viewer 0.19.0 → 0.21.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/CHANGELOG.md CHANGED
@@ -1,5 +1,129 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.21.0] - 2026-08-25
4
+
5
+ ### Minor Changes
6
+
7
+ - 174d27e: feat: environments are first-class in the viewer — a switcher, per-env run history
8
+
9
+ **Run artifacts are laid out per environment, as folders.** Each
10
+ environment's history lives in its own root, mirroring the env-file
11
+ scheme: `unotest/.runs` (base), `unotest/.runs.<env>` (e.g.
12
+ `.runs.staging` for `--env staging` / `UNOTEST_ENV=staging`); the axes
13
+ compose — `.runs-mobile.staging`. Indexes (`_day.jsonl`, `_latest.json`)
14
+ are per-env automatically: switching environments is a change of root,
15
+ not a filter and not a rebuild. Old runs stay in `.runs` (= base), no
16
+ migration needed. `runsDirFor` / `projectRunsRoot` / `projectRunDirFor`
17
+ gained an optional `envName` parameter; the run manifest carries `env`
18
+ for self-description. Make sure your `.gitignore` uses the wildcard
19
+ form — `unotest/.runs*/` (`init` already writes it that way for new
20
+ projects).
21
+
22
+ **The viewer knows about environments.** A new switcher sits at the top
23
+ of the Variables panel and in the status bar: base plus every
24
+ environment discovered from `unotest/.env.<name>` / `.secrets.<name>`
25
+ (`.env.example` and other templates do not count). The active
26
+ environment is server-side (`GET/POST /api/environments`), and all tabs
27
+ converge via the `env:changed` WS message:
28
+
29
+ - the Variables panel shows layers WITH the active environment's
30
+ overlay (previously base only); values coming from `.env.<name>`
31
+ carry a badge; an edit goes to the file where the key is defined; new
32
+ variables go to base;
33
+ - Overview and the Runs list show only the active environment — tiles
34
+ are colored by the latest run in that environment;
35
+ - a run started from the viewer gets the active environment's
36
+ `UNOTEST_ENV` (`RunRequest.env` is a per-request override);
37
+ - a run opened from another environment still resolves by runId across
38
+ all `.runs*` roots.
39
+
40
+ MCP: `run_test {env}` already switched the child process's environment;
41
+ its artifacts are now correctly found by the server in `.runs.<env>`
42
+ (inspect/step/attach/list_runtimes scan all roots).
43
+
44
+ Four fixes uncovered while shaking this down:
45
+
46
+ - **The `--env` overlay actually reaches the run.** Long-lived hosts
47
+ (viewer, MCP server) flattened the base `.env` into their own
48
+ `process.env` on `loadConfig`; children inherited it as ambient
49
+ (ambient beats files) — the overlay's `APP_BASE_URL` was silently
50
+ clobbered by the base value. The viewer now rolls its env back after
51
+ loading the config; MCP spawns children from a clean pre-flatten
52
+ snapshot.
53
+ - **Pause/Abort from the viewer work again.** Debug commands were
54
+ written to the flat `<root>/<runId>/commands.jsonl`, while runs have
55
+ lived in date shards since 0.19 — the write 404'd and abort was
56
+ silently ignored. Order fixed too: SIGTERM to the own process first
57
+ (tests AND collections), then the command file.
58
+ - **A collection no longer "dies" in the UI after 30 seconds.** The
59
+ parent run wrote no heartbeat — the monitor declared it interrupted,
60
+ the row vanished from ACTIVE and the tail detached. The orchestrator
61
+ now maintains the heartbeat (shared machinery in `@unotest/core`),
62
+ and the viewer additionally treats a flowing steps.jsonl as a sign of
63
+ life (compatibility with older runners).
64
+ - **No phantom `failed` after a clean exit.** The "child exited without
65
+ artifacts" check looked at the flat path and fired bogus
66
+ `run-finished: failed` events plus an error toast on every exit.
67
+
68
+ ### Patch Changes
69
+
70
+ - Updated dependencies [174d27e]
71
+ - @unotest/protocol@0.21.0
72
+ - @unotest/dsl@0.21.0
73
+
74
+ ## [0.20.0] - 2026-08-24
75
+
76
+ ### Minor Changes
77
+
78
+ - 7957469: A status of its own for tests that are supposed to fail, and tiles that fade with age.
79
+
80
+ **`// @expect-fail`.** A scenario can declare that failing IS its assertion.
81
+ The marker lives in the comment block before the first code — with the
82
+ mandatory header (right after the colour line) or without one. The parser is
83
+ `parseScenarioAnnotations` in `@unotest/protocol`; `ScenarioMeta` gains an
84
+ `expectFail` field.
85
+
86
+ Such a scenario carries its own status in the viewer (`expected`, purple):
87
+ it stays out of the FAILED counter, out of the "failing now" badge and out
88
+ of the "Failing now" section. The inverse case — a marked test that
89
+ **passed** — turns red: whatever it was guarding stopped breaking and nobody
90
+ found out. One rule (`scenarioHealthStatus`) drives the dashboard, the
91
+ scenario tree and the badge, so the three cannot drift apart.
92
+
93
+ The runner does not interpret the marker: exit code and run status are
94
+ unchanged. The run really did fail — it is the test itself that is fine.
95
+
96
+ **Fading.** An overview tile dims as its last result ages: four steps (hour /
97
+ day / week / older) set the strength of the fill itself — 46 / 30 / 19 / 11%
98
+ of the status colour. The 11% floor keeps an old red still red. The time of
99
+ the last run is now part of the tile's accessible name — colour is the one
100
+ channel a screen reader does not have.
101
+
102
+ ### Patch Changes
103
+
104
+ - aab65a5: A failure in the first test of a file is no longer lost, and the dashboard no longer waits for F5.
105
+
106
+ **One run, one `run:finished`.** The event was written by the executor's
107
+ per-entry tap, so a file with four `test_*()` functions left four terminal
108
+ events in `steps.jsonl`. Every consumer reads the last one — so `collection`
109
+ reported a scenario green whenever the final function passed and the first
110
+ one failed. The runner now writes the event once, folding the outcomes of
111
+ every entry: `failed` > `interrupted` > `aborted` > `completed`. A run that
112
+ never got started (no entry function, browser failed to launch) writes a
113
+ terminal event too — its artifact used to read as "the process died".
114
+
115
+ **The viewer asks for the index again.** The first `load()` could land while
116
+ the server was still rebuilding the run index: the answer was "no runs", and
117
+ nobody asked again — WS only reports a run starting and finishing. On a
118
+ project with history the whole overview showed "never run" until a page
119
+ reload. `indexReady: false` now schedules a retry with a 0.5s → 30s backoff,
120
+ until the first ready answer.
121
+
122
+ - Updated dependencies [7957469]
123
+ - Updated dependencies [c6efd92]
124
+ - @unotest/protocol@0.20.0
125
+ - @unotest/dsl@0.20.0
126
+
3
127
  ## [0.19.0] - 2026-08-23
4
128
 
5
129
  ### Minor Changes
@@ -23,6 +23,9 @@ interface StartViewerOptions {
23
23
  /** Target id selected at boot (`web`/`mobile`/…). Defaults to the first
24
24
  * available runner. */
25
25
  activeTarget?: string;
26
+ /** Environment name selected at boot (M-11 dot axis, `--env` /
27
+ * `UNOTEST_ENV` of the launcher). Blank/unset = base. */
28
+ activeEnv?: string;
26
29
  /** Port to bind. `0` = OS picks a free port. Default 0. */
27
30
  port?: number;
28
31
  /** Host to bind. Default 'localhost' (loopback only). */