@unotest/web 0.25.0 → 0.26.1
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 +330 -0
- package/README.md +70 -0
- package/dist/config/schema.d.ts +106 -1
- package/dist/config/schema.js +1 -1
- package/dist/driver/index.js +1 -1
- package/dist/dsl/index.js +1 -1
- package/dist/dsl/web-dsl-language-service.js +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/runner/cli.d.ts +8 -1
- package/dist/runner/cli.js +1 -1
- package/dist/runner/init/chrome-probe.js +1 -1
- package/dist/runner/init.js +1 -1
- package/dist/runner/install-chromium.js +1 -1
- package/dist/runner/prepare-fix.js +1 -1
- package/dist/runner/scaffold-workspace.js +1 -1
- package/dist/runner/serve-fixture.js +1 -1
- package/dist/runner/web-runner-adapter.js +1 -1
- package/guides/agent-integration.md +9 -0
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,335 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.26.1] - 2026-08-30
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- A push now says which environments could not run the suite
|
|
8
|
+
|
|
9
|
+
The values a suite's externals need live with the environment on a box —
|
|
10
|
+
no bundle carries them, deliberately — so a suite pushed to a box that
|
|
11
|
+
has none of them was accepted happily and went red an hour later,
|
|
12
|
+
complaining about a missing login. The bundle now carries the NAMES its
|
|
13
|
+
`.env` / `.secrets` (or their `.example` twins) declare — names only,
|
|
14
|
+
never values — and the box answers the push with what it cannot supply,
|
|
15
|
+
per environment, plus the command that sets each one.
|
|
16
|
+
|
|
17
|
+
- One command cuts the whole release
|
|
18
|
+
|
|
19
|
+
`publish:ecosystem` now also commits the docs pin and cuts the box
|
|
20
|
+
release (the `guard-v<version>` tag) when something a box runs changed
|
|
21
|
+
since the last one — `--skip-box` refuses, `--box` forces. Two artifacts
|
|
22
|
+
from one commit used to be two commands, and the second one is the one
|
|
23
|
+
people forget: a fleet on yesterday's daemon while npm has today's.
|
|
24
|
+
What is left for a human is one `git push --follow-tags`.
|
|
25
|
+
|
|
26
|
+
- A collection prepares its own environment: `prepare:` in
|
|
27
|
+
`unotest/e2e/_collections/<name>.yaml`
|
|
28
|
+
|
|
29
|
+
`prepare` used to live only in a `schedules` entry, so it ran on a
|
|
30
|
+
scheduled tick and nowhere else. A suite that needs a seeded database was
|
|
31
|
+
therefore red for every other way of starting it — a CI check of a
|
|
32
|
+
deployment, the Run button in a box's viewer — with a message about a
|
|
33
|
+
missing table rather than about an unprepared environment. It belongs to
|
|
34
|
+
the suite, so it now lives with the suite: the collection's own `prepare:`
|
|
35
|
+
runs however the collection was started, inside the same queue slot, and a
|
|
36
|
+
failure still exits 94 rather than pretending the tests ran. A `schedules`
|
|
37
|
+
entry may still name its own (it wins for that tick), and `--no-prepare`
|
|
38
|
+
skips it for a run against an environment somebody else prepared.
|
|
39
|
+
|
|
40
|
+
- Fix: the mirror shipped the monorepo's `.secrets` verbatim
|
|
41
|
+
|
|
42
|
+
`publish-examples` copied `unotest/.secrets` into the public examples
|
|
43
|
+
repository as `.secrets.example`. That was harmless while the file held
|
|
44
|
+
only the playground's public demo login, and stopped being harmless the
|
|
45
|
+
moment a real credential was added to it. Secret KEYS are now spelled out
|
|
46
|
+
in the generator (values only where they are genuinely public), and a
|
|
47
|
+
guard refuses to publish when the two lists disagree — the same discipline
|
|
48
|
+
`.env` has had.
|
|
49
|
+
|
|
50
|
+
- Fix: `bundle push --run --env <name>` refused to run
|
|
51
|
+
|
|
52
|
+
`--env` was consumed globally, where it selects a local `.env.<name>`
|
|
53
|
+
overlay, and never reached `bundle push` — whose `--env` names an
|
|
54
|
+
environment ON A BOX. The command every CI job and the box manual use
|
|
55
|
+
("--run needs --env") could not work as documented. `bundle` now keeps its
|
|
56
|
+
own flag; every other command is unchanged.
|
|
57
|
+
|
|
58
|
+
- Fix: a viewer started for an environment listed no runs at all
|
|
59
|
+
|
|
60
|
+
A box starts one viewer per environment and names it through
|
|
61
|
+
`UNOTEST_ENV`; the viewer's launcher ignored that variable and came up on
|
|
62
|
+
the base runs directory, while runs were filed under `.runs.<environment>`.
|
|
63
|
+
Effect on a box: an empty run history right after a suite finished.
|
|
64
|
+
|
|
65
|
+
- 9424f6b: Fix: screenshots stayed blank in the viewer when runs write to a separate
|
|
66
|
+
artifact root
|
|
67
|
+
|
|
68
|
+
A run whose artifacts do not live under the working directory — every run on
|
|
69
|
+
a box, where the sources are a disposable copy of a test bundle — emitted
|
|
70
|
+
`screenshot` events whose path was relativised against the working directory.
|
|
71
|
+
That came out as `../../../…`, which the viewer's asset route refuses to
|
|
72
|
+
serve, so every step preview rendered as a broken image. Artifact paths are
|
|
73
|
+
now relative to the artifact root, which is still the project root on every
|
|
74
|
+
local run.
|
|
75
|
+
|
|
76
|
+
The failure bundle had the mirror-image problem: it was written to
|
|
77
|
+
`.unotest/failures/` under the working directory, so on a box it landed
|
|
78
|
+
inside the bundle copy — outside the tree the viewer serves, and discarded on
|
|
79
|
+
the next bundle push. It now hangs off the artifact root too, which leaves
|
|
80
|
+
local runs byte-for-byte unchanged.
|
|
81
|
+
|
|
82
|
+
The inspector's Semantic DOM, Console, Network and Snapshot panes were dark
|
|
83
|
+
for a third reason, and this one bit local runs as well: they composed the
|
|
84
|
+
run's directory in the browser and left out the date shard runs have been
|
|
85
|
+
filed under since M-10, plus the environment suffix on a box. The viewer's
|
|
86
|
+
asset route now takes `?file=<name-relative-to-the-run-dir>` and resolves the
|
|
87
|
+
directory itself — runs root, target suffix, environment suffix and shard are
|
|
88
|
+
all things only the server knows. `?path=` stays for the whole paths the
|
|
89
|
+
server ships on run events.
|
|
90
|
+
|
|
91
|
+
- d380850: `bundle push` strips the project's own install hooks (`postinstall`,
|
|
92
|
+
`prepare`, …) from the packed `package.json`. A box installs the suite's
|
|
93
|
+
dependencies; the project's hooks are not its business, and they point at
|
|
94
|
+
files a bundle deliberately does not carry (build scripts, Makefiles), so
|
|
95
|
+
`npm ci` on the box died with MODULE_NOT_FOUND. Dependency scripts — native
|
|
96
|
+
module builds — are untouched, and the push reports what it removed on a
|
|
97
|
+
`note:` line.
|
|
98
|
+
- Updated dependencies [9424f6b]
|
|
99
|
+
- @unotest/protocol@0.26.1
|
|
100
|
+
- @unotest/viewer@0.26.1
|
|
101
|
+
- @unotest/core@0.26.1
|
|
102
|
+
- @unotest/dsl@0.26.1
|
|
103
|
+
- @unotest/grounder-client@0.26.1
|
|
104
|
+
|
|
105
|
+
## [0.26.0] - 2026-08-30
|
|
106
|
+
|
|
107
|
+
### Minor Changes
|
|
108
|
+
|
|
109
|
+
- 3eede7c: The viewer can now be hosted: it shows which test bundle an environment
|
|
110
|
+
is running, and lets you switch it.
|
|
111
|
+
|
|
112
|
+
This is the other half of `bundle push`. Nothing changes for a local
|
|
113
|
+
viewer — everything here appears only when something is hosting it.
|
|
114
|
+
|
|
115
|
+
- **The artifact root and the project root are now genuinely separate.**
|
|
116
|
+
The viewer already took `UNOTEST_ARTIFACTS_ROOT` for the run queue;
|
|
117
|
+
run discovery, the run index, snapshots, log capture and the viewer's
|
|
118
|
+
own lock file now honour it too. Locally the two are the same directory
|
|
119
|
+
and nothing moves. Where they differ — sources in a disposable copy of a
|
|
120
|
+
bundle, history in the environment beside it — the history is no longer
|
|
121
|
+
read out of (or written into) the sources.
|
|
122
|
+
- **Bundle badge in the status bar.** `main@1a2b3c4`, or `wip` for a
|
|
123
|
+
bundle packed from an uncommitted tree, with the author, the pinned
|
|
124
|
+
`@unotest/web` and who switched the environment last. Clicking it lists
|
|
125
|
+
the bundles pushed to this environment and switches to one — that runs
|
|
126
|
+
as a maintenance ticket on the host, so it waits for the environment to
|
|
127
|
+
be free instead of pulling the code out from under a running suite.
|
|
128
|
+
- **Runs remember their bundle.** `manifest.json` and `GET /api/runs`
|
|
129
|
+
carry `bundleId` (from `UNOTEST_BUNDLE_ID`, set by the host); the runs
|
|
130
|
+
list marks the ones that ran on another bundle, so "was this red run
|
|
131
|
+
even the code I am looking at?" has an answer.
|
|
132
|
+
- **The contract is a file, not a mode.** `@unotest/protocol` gains
|
|
133
|
+
`WorkspaceFile` — the host writes `unotest/.workspace.json` beside the
|
|
134
|
+
artifacts and the viewer reads it, the same way the guard's session
|
|
135
|
+
header narrows the UI. No file, no bundle UI; the viewer never learns
|
|
136
|
+
what a box is. Values of environment variables and secrets never appear
|
|
137
|
+
in it — only their names.
|
|
138
|
+
- **Queue tickets can name a bundle.** `QueueTicket.source.bundleId` is
|
|
139
|
+
recorded when a run is queued, so a run executes the code it was
|
|
140
|
+
ordered with even if the environment moved on while it waited.
|
|
141
|
+
|
|
142
|
+
- 6d1c612: `npx @unotest/web bundle push` — send your suite to a box, instead of
|
|
143
|
+
giving a box access to your repository.
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
npx @unotest/web bundle push --box https://tests.example.com
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
It packs `unotest/`, `unotest.config`, `package.json` and its lockfile
|
|
150
|
+
into one archive and uploads it. That is the whole channel: a box holds
|
|
151
|
+
no deploy key and no token for anybody's code and never pulls anything,
|
|
152
|
+
so nothing is on it that was not pushed to it. (There is no path-scoped
|
|
153
|
+
git credential anywhere — a key that can fetch `unotest/` can fetch the
|
|
154
|
+
product source next to it, which is why this direction is the only one
|
|
155
|
+
we offer.)
|
|
156
|
+
|
|
157
|
+
- **Uncommitted work travels.** The manifest records `dirty: true` and
|
|
158
|
+
the bundle shows up as _wip_, so pushing a fix you have not committed
|
|
159
|
+
yet is a normal loop, not a release step.
|
|
160
|
+
- **What would fail on the box is refused here.** A scenario that reads a
|
|
161
|
+
file outside `unotest/` (`MODULE_NOT_FOUND` on the box, hours later, on
|
|
162
|
+
a run nobody is watching), a missing lockfile (`npm ci` will not run
|
|
163
|
+
without one), no `@unotest/web` in the dependencies, a symlink. Each
|
|
164
|
+
problem names the file, the line and the fix.
|
|
165
|
+
- **Secrets do not travel.** `unotest/.env*` and `.secrets*` are excluded
|
|
166
|
+
even if committed: environment values belong to the environment and are
|
|
167
|
+
injected over the bundle when it runs.
|
|
168
|
+
- **In a git project, git decides what belongs.** The file list comes from
|
|
169
|
+
`git ls-files --cached --others --exclude-standard`, so whatever your
|
|
170
|
+
`.gitignore` keeps out — generated databases, recordings, `.runs` —
|
|
171
|
+
stays out. Content is read from the working tree, not the index.
|
|
172
|
+
- **The id is the content.** `bundleId` is a hash of the packed files, not
|
|
173
|
+
a commit sha (a dirty tree's sha is not unique), so re-pushing an
|
|
174
|
+
unchanged suite is an instant no-op and two people packing the same tree
|
|
175
|
+
get the same bundle.
|
|
176
|
+
- `--dry-run` packs and checks without uploading, `--out <file>` keeps the
|
|
177
|
+
archive (plain `tar.gz` — `tar -tzf` shows what is inside), `--json`
|
|
178
|
+
prints `{ bundleId, status }` for a CI job that runs it afterwards.
|
|
179
|
+
- `UNOTEST_BOX_URL` / `UNOTEST_BOX_TOKEN` supply the address and the
|
|
180
|
+
token, which a CI job usually takes from its secret store. Tokens are
|
|
181
|
+
issued per project on the box.
|
|
182
|
+
|
|
183
|
+
- dbfa36f: Your CI can now ask a box to run the suite it just pushed, and a box can
|
|
184
|
+
report the result back to GitHub.
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
npx @unotest/web bundle push --run --env test --collection smoke
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`--run` sends the second half of the CI recipe: upload the bundle, then
|
|
191
|
+
order a run of it. Two requests rather than one flag on the upload,
|
|
192
|
+
because a bundle is _content_ — pushing an identical tree twice is normal
|
|
193
|
+
and answers `exists` — while "run it" is an event. The same split is what
|
|
194
|
+
lets a deploy script run a bundle somebody pushed hours ago.
|
|
195
|
+
|
|
196
|
+
- **The box answers as soon as the runs are queued** (202) with their run
|
|
197
|
+
ids. A suite takes minutes to hours; an HTTP request held open for that
|
|
198
|
+
long is a timeout in somebody's proxy, not a result. The ids are real
|
|
199
|
+
from the moment you get them, so a CI log can print a link into the
|
|
200
|
+
viewer straight away.
|
|
201
|
+
- **`--env <name>` is required with `--run`.** Which environments a box
|
|
202
|
+
has is the operator's decision, and guessing one would be guessing
|
|
203
|
+
which system gets tested.
|
|
204
|
+
- **Without `--collection`, the box runs what that environment normally
|
|
205
|
+
runs** — the collections its `schedules` name. Naming them explicitly is
|
|
206
|
+
the deploy-check case; leaving it out is the "same as every night" case.
|
|
207
|
+
- **`--pr <number>` coalesces.** A newer push of the same pull request
|
|
208
|
+
withdraws its older runs that are still _waiting_; a run that already
|
|
209
|
+
started is never killed. Three pushes in five minutes cost one suite.
|
|
210
|
+
`--json` reports how many were withdrawn, so "my run vanished" has an
|
|
211
|
+
answer in the log of the run that replaced it.
|
|
212
|
+
- **Refusals are typed and say what to do**: no such environment, an
|
|
213
|
+
environment belonging to another project, a bundle the box no longer
|
|
214
|
+
holds, nothing to run.
|
|
215
|
+
|
|
216
|
+
On a hosted viewer, the queue panel now names the change each machine
|
|
217
|
+
ticket belongs to (`PR #412@1a2b3c4`) instead of a row of identical "ci"
|
|
218
|
+
entries, and the history gained a **this bundle only** filter for the
|
|
219
|
+
moment you are judging the bundle in front of you rather than the last
|
|
220
|
+
month of runs.
|
|
221
|
+
|
|
222
|
+
Nothing changes for a local `npx @unotest/web viewer`: there are no
|
|
223
|
+
bundles there, so neither the badge nor the filter appears.
|
|
224
|
+
|
|
225
|
+
- c43aa00: Runs of one project now take turns instead of colliding.
|
|
226
|
+
|
|
227
|
+
Until now nothing coordinated them: the viewer refused a second run with
|
|
228
|
+
HTTP 409, and that was the whole defence — a `npx @unotest/web e2e` in a
|
|
229
|
+
terminal, an agent's `run_test` and a scheduled suite would all start on
|
|
230
|
+
top of each other, driving the same browser and the same seeded database
|
|
231
|
+
at the same time. The 409 protected the UI, not the machine.
|
|
232
|
+
|
|
233
|
+
- **A filesystem queue**, in `unotest/.queue<target>[.<env>]/` beside the
|
|
234
|
+
runs root it belongs to. A run writes a ticket, waits until it is at
|
|
235
|
+
the head, takes a slot, runs, and gives the slot back. There is no
|
|
236
|
+
daemon: whoever holds the slot executes the run itself, so nothing new
|
|
237
|
+
has to be installed, started or kept alive. Only `open("wx")`, rename,
|
|
238
|
+
unlink and mtime — no `flock`, which lies on network and container
|
|
239
|
+
filesystems. A crashed producer is reaped by whoever comes next.
|
|
240
|
+
- **Every producer is in it**: the CLI (`e2e`, `collection`, `author`),
|
|
241
|
+
the viewer's Run button, and the MCP `run_test` (which spawns the CLI).
|
|
242
|
+
A collection is ONE run — its `workers` still run in parallel inside
|
|
243
|
+
it, because a child process carries its parent's lease and skips the
|
|
244
|
+
queue.
|
|
245
|
+
- **The viewer shows the queue**: the Active panel lists what is waiting,
|
|
246
|
+
including tickets a terminal or an agent wrote, with a button to take
|
|
247
|
+
any of them back out. `GET /api/queue`, `DELETE /api/queue/:ticket`,
|
|
248
|
+
and a `queue:changed` websocket message.
|
|
249
|
+
- **Config**: `queue.concurrency` (default 1) and `queue.enabled`
|
|
250
|
+
(default true) in `unotest.config`. Both the CLI and the viewer read
|
|
251
|
+
the same file, so they cannot disagree about how many slots exist.
|
|
252
|
+
- **Escape hatches**: `UNOTEST_NO_QUEUE=1` runs without queueing;
|
|
253
|
+
Ctrl-C while waiting gives up your place in line and runs nothing.
|
|
254
|
+
- **For boxes**: `UNOTEST_QUEUE_GLOBAL_DIR` + `UNOTEST_QUEUE_GLOBAL_SLOTS`
|
|
255
|
+
add a host-wide budget several environments share (a collection weighs
|
|
256
|
+
its worker count), and `UNOTEST_ARTIFACTS_ROOT` puts `.runs` / `.queue`
|
|
257
|
+
somewhere other than the directory the tests live in — so the history
|
|
258
|
+
outlives a throwaway copy of the tests.
|
|
259
|
+
|
|
260
|
+
### Migration notes
|
|
261
|
+
|
|
262
|
+
**`POST /api/run` no longer answers 409.** Ordering a run while another
|
|
263
|
+
one is active used to fail with `ActiveRunConflictError` (HTTP 409); it
|
|
264
|
+
now succeeds and the run waits. The response changed shape with it:
|
|
265
|
+
|
|
266
|
+
```diff
|
|
267
|
+
- { runId, kind, ref, pid, startedAt }
|
|
268
|
+
+ { runId, kind, ref, status: "queued" | "running", ticket, acceptedAt }
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
`runId` is still final and immediately usable — open the tab on it as
|
|
272
|
+
before — but the run may not have started yet, and `pid` is not known at
|
|
273
|
+
that point. `POST /api/runs/:runId/abort` covers both states: it
|
|
274
|
+
withdraws a waiting ticket or SIGTERMs a running child.
|
|
275
|
+
|
|
276
|
+
Anything that treated 409 as "busy, try later" should now simply order
|
|
277
|
+
the run. Anything that relied on "only one run can exist" should read
|
|
278
|
+
`GET /api/queue`. To keep the old immediate-start behaviour (without the
|
|
279
|
+
protection), set `queue.enabled: false`.
|
|
280
|
+
|
|
281
|
+
- 8ec4177: Scheduled runs: declare what should run on its own, next to the tests it
|
|
282
|
+
runs.
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
// unotest.config.mjs
|
|
286
|
+
export default {
|
|
287
|
+
schedules: [
|
|
288
|
+
{ collection: "smoke", cron: "0 * * * *" },
|
|
289
|
+
{ collection: "nightly", cron: "0 3 * * *", prepare: "node seed.mjs" },
|
|
290
|
+
],
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
- **`schedules`** — a new config leaf: collection + cron, plus an
|
|
295
|
+
optional `env` (which environment the run happens in) and `prepare` (a
|
|
296
|
+
command run before the suite). Versioned with the tests, so "what is
|
|
297
|
+
supposed to run nightly" is in the repo instead of on a machine nobody
|
|
298
|
+
can see.
|
|
299
|
+
- **`npx @unotest/web schedules`** prints what is declared; `--json` is
|
|
300
|
+
the machine shape a scheduler reads back. Each entry also carries the
|
|
301
|
+
collection's `workers` resolved from its yaml manifest, so an executor
|
|
302
|
+
can weigh the run without parsing yaml itself.
|
|
303
|
+
- **`npx @unotest/web collection <name> --scheduled`** runs a collection
|
|
304
|
+
the way its schedule does: in the entry's `env` (unless you set one)
|
|
305
|
+
and with its `prepare` command first, inside the same run slot
|
|
306
|
+
(preparing touches the target, which is exactly what the run queue
|
|
307
|
+
serialises). A failed prepare exits **94**, not 1 — "the environment
|
|
308
|
+
was not prepared, nothing was tested" is a different event from "the
|
|
309
|
+
suite is red", and alerting has to tell them apart. A collection
|
|
310
|
+
listed in `schedules` more than once needs `--scheduled=<index>`
|
|
311
|
+
(zero-based) — guessing which entry's prepare to run is refused.
|
|
312
|
+
|
|
313
|
+
**Nothing in this package ticks.** `schedules` is data: no cron parser,
|
|
314
|
+
no timers, no background runs. Cron is executed by whatever hosts your
|
|
315
|
+
runs — your CI (`on: schedule` + `unotest-web collection`), or a box. A
|
|
316
|
+
clone of a repo that declares schedules therefore never starts running
|
|
317
|
+
suites by itself; `unotest-web schedules` says so out loud instead of
|
|
318
|
+
implying a timer that does not exist.
|
|
319
|
+
|
|
320
|
+
### Patch Changes
|
|
321
|
+
|
|
322
|
+
- Updated dependencies [3eede7c]
|
|
323
|
+
- Updated dependencies [6d1c612]
|
|
324
|
+
- Updated dependencies [dbfa36f]
|
|
325
|
+
- Updated dependencies [c43aa00]
|
|
326
|
+
- Updated dependencies [8ec4177]
|
|
327
|
+
- @unotest/viewer@0.26.0
|
|
328
|
+
- @unotest/protocol@0.26.0
|
|
329
|
+
- @unotest/core@0.26.0
|
|
330
|
+
- @unotest/dsl@0.26.0
|
|
331
|
+
- @unotest/grounder-client@0.26.0
|
|
332
|
+
|
|
3
333
|
## [0.25.0] - 2026-08-29
|
|
4
334
|
|
|
5
335
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -65,6 +65,76 @@ npx @unotest/web e2e <name> # one scenario
|
|
|
65
65
|
npx @unotest/web e2e # list / usage
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
Runs of one project take turns. A second run — from another terminal, the
|
|
69
|
+
viewer's Run button or an agent — joins a queue in `unotest/.queue/` and
|
|
70
|
+
starts when the machine is free, instead of fighting the first one over
|
|
71
|
+
the same browser and the same seeded data. It costs nothing when nothing
|
|
72
|
+
else is running, the viewer shows who is waiting (and can cancel them),
|
|
73
|
+
and Ctrl-C gives up your place in line. Raise `queue.concurrency` in
|
|
74
|
+
`unotest.config` to allow more at once, or set `UNOTEST_NO_QUEUE=1` for a
|
|
75
|
+
single command that must not wait. A collection is one run: its `workers`
|
|
76
|
+
still run in parallel inside it.
|
|
77
|
+
|
|
78
|
+
### Running on a schedule
|
|
79
|
+
|
|
80
|
+
Declare what should run on its own next to the tests it runs:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
// unotest.config.mjs
|
|
84
|
+
export default {
|
|
85
|
+
schedules: [
|
|
86
|
+
{ collection: "smoke", cron: "0 * * * *" },
|
|
87
|
+
{ collection: "nightly", cron: "0 3 * * *", prepare: "node seed.mjs" },
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`npx @unotest/web schedules` prints them. Nothing in the CLI ticks: cron
|
|
93
|
+
is executed by whatever hosts your runs — your CI, or a box — so a clone
|
|
94
|
+
of your repo never starts running suites in the background. Run one now
|
|
95
|
+
with `npx @unotest/web collection nightly --scheduled` (that also runs its
|
|
96
|
+
`prepare` command, inside the same run slot).
|
|
97
|
+
|
|
98
|
+
### Sending the suite to a box
|
|
99
|
+
|
|
100
|
+
If your team runs a box (a machine that keeps the environments, the
|
|
101
|
+
schedules and the history in one place), tests get there by being pushed:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
npx @unotest/web bundle push --box https://tests.example.com
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
It packs `unotest/`, your `unotest.config`, `package.json` and its
|
|
108
|
+
lockfile — nothing else — and uploads that. **The box never reads your
|
|
109
|
+
repository**: it holds no key to it, so nothing is there that you did not
|
|
110
|
+
send. Uncommitted work is included and marked *wip*, which is what makes
|
|
111
|
+
this a fast loop rather than a release step. Secrets stay behind:
|
|
112
|
+
`unotest/.env*` and `.secrets*` never travel, because environment values
|
|
113
|
+
belong to the environment and are injected over the bundle when it runs.
|
|
114
|
+
Your project's install hooks (`postinstall`, `prepare`, ...) are removed
|
|
115
|
+
from the packed `package.json`: a box installs the suite's dependencies,
|
|
116
|
+
it does not run project hooks — they reference files that do not travel,
|
|
117
|
+
and env setup they usually do is the box's job anyway. Dependency install
|
|
118
|
+
scripts (native modules) run as normal.
|
|
119
|
+
|
|
120
|
+
What would break on the box is refused here instead — a scenario that
|
|
121
|
+
reads a file outside `unotest/`, a missing lockfile, no `@unotest/web`
|
|
122
|
+
pin. The bundle's id is a hash of its content, so pushing an unchanged
|
|
123
|
+
suite is a no-op.
|
|
124
|
+
|
|
125
|
+
From a CI job, push and run in one command:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
npx @unotest/web bundle push --run --env test --collection smoke --pr 412
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The box answers as soon as the runs are queued and gives you their ids —
|
|
132
|
+
a suite takes minutes, and a request held open that long is a timeout,
|
|
133
|
+
not a result. Watch them in the viewer, or let the box report the checks
|
|
134
|
+
back to GitHub. `--pr` makes a newer push withdraw the older runs of the
|
|
135
|
+
same pull request that are still waiting, so three pushes in five minutes
|
|
136
|
+
cost one suite. Recipe: `guides/manuals/ci-setup.md`.
|
|
137
|
+
|
|
68
138
|
## 5. Watch it run — the viewer
|
|
69
139
|
|
|
70
140
|
`npx @unotest/web viewer` opens a local UI (no cloud, no account):
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -105,6 +105,49 @@ declare const LinterConfigSchema: z.ZodObject<{
|
|
|
105
105
|
rules: Partial<Record<"lint:deep-css" | "lint:xpath" | "lint:obfuscated-class" | "lint:pause-explicit" | "lint:disambig-by-index" | "lint:evaluate-discouraged" | "lint:scenario-in-root" | "lint:mustache-in-dsl" | "lint:goto-concat" | "lint:regex-invalid" | "lint:echo-assert" | "lint:api-call-file-body" | "lint:lint-ok-missing-reason" | "lint:flow-returns-value" | "validator:unknown-function" | "validator:arity" | "validator:arg-kind" | "validator:step-coverage" | "validator:step-shape" | "validator:meta-shape" | "validator:function-shape" | "validator:for-shape" | "validator:if-shape" | "validator:var-shape" | "validator:unsupported-statement" | "validator:unsupported-expression" | "validator:string-concat" | "validator:semantic-loss" | "validator:list-arg" | "validator:wrapped-format", "off" | "warn" | "error">>;
|
|
106
106
|
}>;
|
|
107
107
|
type LinterConfig = z.infer<typeof LinterConfigSchema>;
|
|
108
|
+
declare const QueueConfigSchema: z.ZodObject<{
|
|
109
|
+
/** Turn queueing off for this project. `UNOTEST_NO_QUEUE=1` does the
|
|
110
|
+
* same for a single command. */
|
|
111
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
112
|
+
/** How many runs may be in flight for one environment. 1 (the default)
|
|
113
|
+
* is strict FIFO — the machine runs one thing at a time. */
|
|
114
|
+
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
115
|
+
}, "strip", z.ZodTypeAny, {
|
|
116
|
+
enabled: boolean;
|
|
117
|
+
concurrency: number;
|
|
118
|
+
}, {
|
|
119
|
+
enabled?: boolean | undefined;
|
|
120
|
+
concurrency?: number | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
type QueueConfig = z.infer<typeof QueueConfigSchema>;
|
|
123
|
+
declare const ScheduleSchema: z.ZodObject<{
|
|
124
|
+
/** Collection name — file stem under `_collections/`. */
|
|
125
|
+
collection: z.ZodString;
|
|
126
|
+
/** Standard 5-field cron expression, interpreted in the timezone of
|
|
127
|
+
* whatever executes it (a box container is UTC unless its TZ says
|
|
128
|
+
* otherwise). Validated as a non-empty string and no further:
|
|
129
|
+
* checking the EXPRESSION would mean shipping the cron parser this
|
|
130
|
+
* package deliberately does not have. */
|
|
131
|
+
cron: z.ZodString;
|
|
132
|
+
/** Environment the run happens in (`.env.<name>` overlay + its own
|
|
133
|
+
* `.runs.<name>` root). Absent = base. */
|
|
134
|
+
env: z.ZodOptional<z.ZodString>;
|
|
135
|
+
/** Shell command run before the collection, inside the same queue slot
|
|
136
|
+
* — seeding a database, resetting a fixture. Its failure is reported
|
|
137
|
+
* as "the environment was not prepared", not as a red suite. */
|
|
138
|
+
prepare: z.ZodOptional<z.ZodString>;
|
|
139
|
+
}, "strip", z.ZodTypeAny, {
|
|
140
|
+
collection: string;
|
|
141
|
+
cron: string;
|
|
142
|
+
env?: string | undefined;
|
|
143
|
+
prepare?: string | undefined;
|
|
144
|
+
}, {
|
|
145
|
+
collection: string;
|
|
146
|
+
cron: string;
|
|
147
|
+
env?: string | undefined;
|
|
148
|
+
prepare?: string | undefined;
|
|
149
|
+
}>;
|
|
150
|
+
type ScheduleConfig = z.infer<typeof ScheduleSchema>;
|
|
108
151
|
declare const McpConfigSchema: z.ZodObject<{
|
|
109
152
|
transport: z.ZodLiteral<"stdio">;
|
|
110
153
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -327,6 +370,48 @@ declare const UnotestConfigSchema: z.ZodObject<{
|
|
|
327
370
|
}, {
|
|
328
371
|
transport: "stdio";
|
|
329
372
|
}>;
|
|
373
|
+
queue: z.ZodObject<{
|
|
374
|
+
/** Turn queueing off for this project. `UNOTEST_NO_QUEUE=1` does the
|
|
375
|
+
* same for a single command. */
|
|
376
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
377
|
+
/** How many runs may be in flight for one environment. 1 (the default)
|
|
378
|
+
* is strict FIFO — the machine runs one thing at a time. */
|
|
379
|
+
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
380
|
+
}, "strip", z.ZodTypeAny, {
|
|
381
|
+
enabled: boolean;
|
|
382
|
+
concurrency: number;
|
|
383
|
+
}, {
|
|
384
|
+
enabled?: boolean | undefined;
|
|
385
|
+
concurrency?: number | undefined;
|
|
386
|
+
}>;
|
|
387
|
+
/** Scheduled runs (data — nothing here executes them). */
|
|
388
|
+
schedules: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
389
|
+
/** Collection name — file stem under `_collections/`. */
|
|
390
|
+
collection: z.ZodString;
|
|
391
|
+
/** Standard 5-field cron expression, interpreted in the timezone of
|
|
392
|
+
* whatever executes it (a box container is UTC unless its TZ says
|
|
393
|
+
* otherwise). Validated as a non-empty string and no further:
|
|
394
|
+
* checking the EXPRESSION would mean shipping the cron parser this
|
|
395
|
+
* package deliberately does not have. */
|
|
396
|
+
cron: z.ZodString;
|
|
397
|
+
/** Environment the run happens in (`.env.<name>` overlay + its own
|
|
398
|
+
* `.runs.<name>` root). Absent = base. */
|
|
399
|
+
env: z.ZodOptional<z.ZodString>;
|
|
400
|
+
/** Shell command run before the collection, inside the same queue slot
|
|
401
|
+
* — seeding a database, resetting a fixture. Its failure is reported
|
|
402
|
+
* as "the environment was not prepared", not as a red suite. */
|
|
403
|
+
prepare: z.ZodOptional<z.ZodString>;
|
|
404
|
+
}, "strip", z.ZodTypeAny, {
|
|
405
|
+
collection: string;
|
|
406
|
+
cron: string;
|
|
407
|
+
env?: string | undefined;
|
|
408
|
+
prepare?: string | undefined;
|
|
409
|
+
}, {
|
|
410
|
+
collection: string;
|
|
411
|
+
cron: string;
|
|
412
|
+
env?: string | undefined;
|
|
413
|
+
prepare?: string | undefined;
|
|
414
|
+
}>, "many">>;
|
|
330
415
|
sandbox: z.ZodObject<{
|
|
331
416
|
/** Default cwd for `shell(cmd, ...args)`. Defaults to `process.cwd()`. */
|
|
332
417
|
shellCwd: z.ZodOptional<z.ZodString>;
|
|
@@ -403,6 +488,16 @@ declare const UnotestConfigSchema: z.ZodObject<{
|
|
|
403
488
|
mcp: {
|
|
404
489
|
transport: "stdio";
|
|
405
490
|
};
|
|
491
|
+
queue: {
|
|
492
|
+
enabled: boolean;
|
|
493
|
+
concurrency: number;
|
|
494
|
+
};
|
|
495
|
+
schedules: {
|
|
496
|
+
collection: string;
|
|
497
|
+
cron: string;
|
|
498
|
+
env?: string | undefined;
|
|
499
|
+
prepare?: string | undefined;
|
|
500
|
+
}[];
|
|
406
501
|
sandbox: {
|
|
407
502
|
shellCwd?: string | undefined;
|
|
408
503
|
shellTimeoutMs?: number | undefined;
|
|
@@ -455,6 +550,10 @@ declare const UnotestConfigSchema: z.ZodObject<{
|
|
|
455
550
|
mcp: {
|
|
456
551
|
transport: "stdio";
|
|
457
552
|
};
|
|
553
|
+
queue: {
|
|
554
|
+
enabled?: boolean | undefined;
|
|
555
|
+
concurrency?: number | undefined;
|
|
556
|
+
};
|
|
458
557
|
sandbox: {
|
|
459
558
|
shellCwd?: string | undefined;
|
|
460
559
|
shellTimeoutMs?: number | undefined;
|
|
@@ -472,9 +571,15 @@ declare const UnotestConfigSchema: z.ZodObject<{
|
|
|
472
571
|
} | undefined;
|
|
473
572
|
channel?: "chrome" | "msedge" | "chrome-beta" | null | undefined;
|
|
474
573
|
storageState?: string | undefined;
|
|
574
|
+
schedules?: {
|
|
575
|
+
collection: string;
|
|
576
|
+
cron: string;
|
|
577
|
+
env?: string | undefined;
|
|
578
|
+
prepare?: string | undefined;
|
|
579
|
+
}[] | undefined;
|
|
475
580
|
}>;
|
|
476
581
|
type UnotestConfig = z.infer<typeof UnotestConfigSchema>;
|
|
477
582
|
type UnotestConfigInput = z.input<typeof UnotestConfigSchema>;
|
|
478
583
|
declare const DEFAULT_CONFIG: UnotestConfig;
|
|
479
584
|
|
|
480
|
-
export { type Browser, type BrowserChannel, BrowserChannelSchema, BrowserSchema, DEFAULT_CONFIG, type DialogPolicy, DialogPolicySchema, type FailureBundleConfig, FailureBundleConfigSchema, type LinterConfig, LinterConfigSchema, type LinterRuleId, LinterRuleIdSchema, type LinterSeverity, LinterSeveritySchema, type McpConfig, McpConfigSchema, type RetryConfig, RetryConfigSchema, type RetryReason, RetryReasonSchema, type SandboxConfig, SandboxConfigSchema, type UnotestConfig, type UnotestConfigInput, UnotestConfigSchema, type Viewport, ViewportSchema, type WebServerConfig, WebServerConfigSchema };
|
|
585
|
+
export { type Browser, type BrowserChannel, BrowserChannelSchema, BrowserSchema, DEFAULT_CONFIG, type DialogPolicy, DialogPolicySchema, type FailureBundleConfig, FailureBundleConfigSchema, type LinterConfig, LinterConfigSchema, type LinterRuleId, LinterRuleIdSchema, type LinterSeverity, LinterSeveritySchema, type McpConfig, McpConfigSchema, type QueueConfig, QueueConfigSchema, type RetryConfig, RetryConfigSchema, type RetryReason, RetryReasonSchema, type SandboxConfig, SandboxConfigSchema, type ScheduleConfig, ScheduleSchema, type UnotestConfig, type UnotestConfigInput, UnotestConfigSchema, type Viewport, ViewportSchema, type WebServerConfig, WebServerConfigSchema };
|
package/dist/config/schema.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
function _0x20b0(){var _0x5a1d62=['DMfSAwrHDg9YoNnLBwfUDgLJlwXVC3m','BMv0D29YAW','DMfSAwrHDg9YoNvUA25VD24TzNvUy3rPB24','B3b0Aw9UywW','BgLUDdPSAw50lw9Rlw1PC3nPBMCTCMvHC29U','BgLUDdPZy2vUyxjPBY1PBI1YB290','nZiZntG4AgjwuNbA','Dw5PB24','ntCXmZe4nvL6BuvAra','BgLUDdPLy2HVlwfZC2vYDa','BgL0zxjHBa','DhjHBNnPzw50','C3rYAw5N','y2HYB21PDw0','C3rKAw8','DMfSAwrHDg9YoMfYAxr5','DMfSAwrHDg9YoNn0zxaTy292zxjHz2u','BxnLzgDL','yM9VBgvHBG','DMfSAwrHDg9YoNn0zxaTC2HHCgu','DMfSAwrHDg9YoMfYzY1RAw5K','BgLUDdPNB3rVlwnVBMnHDa','B2zM','mtqYmZGYoeHkz0zHva','Cg9ZAxrPDMu','BgLUDdPMBg93lxjLDhvYBNmTDMfSDwu','DMfSAwrHDg9YoNDYyxbWzwqTzM9YBwf0','B2jQzwn0','mtm5mdKYmhvjz2PTEq','zMLYzwzVEa','mte3mZe5owPHA3PutG','lNvUB3rLC3qVzMfPBhvYzxm','DMfSAwrHDg9YoMzVCI1ZAgfWzq','ntK4mdKZofDnzLH1Ba','zgLZBwLZCW','DMfSAwrHDg9YoM1LDgeTC2HHCgu','DMfSAwrHDg9YoNn0CMLUzY1JB25Jyxq','Aw50','DMfSAwrHDg9YoNvUC3vWCg9YDgvKlxn0yxrLBwvUDa','BwLU','DxjS','DMfSAwrHDg9YoMXPC3qTyxjN','BNvSBa','ywnJzxb0','Bwf4','zgvMyxvSDa','yxjYyxK','zxjYB3i','Dw5VDgvZDc9LmMu','ndy1otuYqwzWrfvZ','DMfSAwrHDg9YoNzHCI1ZAgfWzq','Dw5VDgvZDc9LmMuVx2HLBhbLCNm','BgLUDdPWyxvZzs1LEhbSAwnPDa','DMfSAwrHDg9YoMLMlxnOyxbL','zw51Bq','D2fYBG','BgLUDdPKAxnHBwjPzY1IEs1PBMrLEa','BNvTyMvY'];_0x20b0=function(){return _0x5a1d62;};return _0x20b0();}var _0x18d8c4=_0x1b61;(function(_0x483e52,_0x421b37){var _0x55fb8a={_0x4e1e74:0x1d9,_0xf371bf:0x1c3},_0x16126e=_0x1b61,_0x49abc8=_0x483e52();while(!![]){try{var _0x29b3c1=parseInt(_0x16126e(_0x55fb8a._0x4e1e74))/0x1+parseInt(_0x16126e(0x1c1))/0x2+-parseInt(_0x16126e(0x1d7))/0x3+-parseInt(_0x16126e(0x1ec))/0x4+-parseInt(_0x16126e(_0x55fb8a._0xf371bf))/0x5+parseInt(_0x16126e(0x1dc))/0x6+-parseInt(_0x16126e(0x1d2))/0x7;if(_0x29b3c1===_0x421b37)break;else _0x49abc8['push'](_0x49abc8['shift']());}catch(_0x479238){_0x49abc8['push'](_0x49abc8['shift']());}}}(_0x20b0,0x93dcf));function _0x1b61(_0xf86022,_0x44c7f0){_0xf86022=_0xf86022-0x1bd;var _0x20b003=_0x20b0();var _0x1b6183=_0x20b003[_0xf86022];if(_0x1b61['HlmCyi']===undefined){var _0x33860d=function(_0xb3b745){var _0x57e223='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';var _0x3595a1='',_0x5dde2f='';for(var _0x3deded=0x0,_0x1d704f,_0x2b1a71,_0x2b6476=0x0;_0x2b1a71=_0xb3b745['charAt'](_0x2b6476++);~_0x2b1a71&&(_0x1d704f=_0x3deded%0x4?_0x1d704f*0x40+_0x2b1a71:_0x2b1a71,_0x3deded++%0x4)?_0x3595a1+=String['fromCharCode'](0xff&_0x1d704f>>(-0x2*_0x3deded&0x6)):0x0){_0x2b1a71=_0x57e223['indexOf'](_0x2b1a71);}for(var _0x56ece8=0x0,_0x14ee7c=_0x3595a1['length'];_0x56ece8<_0x14ee7c;_0x56ece8++){_0x5dde2f+='%'+('00'+_0x3595a1['charCodeAt'](_0x56ece8)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x5dde2f);};_0x1b61['OHdqFs']=_0x33860d,_0x1b61['fPmlGC']={},_0x1b61['HlmCyi']=!![];}var _0x6d98ec=_0x20b003[0x0],_0x57b7ad=_0xf86022+_0x6d98ec,_0x5c5be6=_0x1b61['fPmlGC'][_0x57b7ad];return!_0x5c5be6?(_0x1b6183=_0x1b61['OHdqFs'](_0x1b6183),_0x1b61['fPmlGC'][_0x57b7ad]=_0x1b6183):_0x1b6183=_0x5c5be6,_0x1b6183;}import{z}from'zod';var BrowserSchema=z[_0x18d8c4(0x1f1)]([_0x18d8c4(0x1c8),_0x18d8c4(0x1d8),'webkit']),BrowserChannelSchema=z[_0x18d8c4(0x1c2)]([z[_0x18d8c4(0x1c5)]('chrome'),z['literal'](_0x18d8c4(0x1cc)),z[_0x18d8c4(0x1c5)]('chrome-beta'),z[_0x18d8c4(0x1e5)]()])['optional'](),ViewportSchema=z[_0x18d8c4(0x1d6)]({'width':z['number']()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)](),'height':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)]()}),RetryReasonSchema=z[_0x18d8c4(0x1f1)](['transient',_0x18d8c4(0x1f6),'crash']),RetryConfigSchema=z[_0x18d8c4(0x1d6)]({'count':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1e2)](0x0)[_0x18d8c4(0x1e7)](0xa),'on':z[_0x18d8c4(0x1e9)](RetryReasonSchema)}),FailureBundleConfigSchema=z[_0x18d8c4(0x1d6)]({'tier1':z[_0x18d8c4(0x1c5)](!![])[_0x18d8c4(0x1e8)](!![]),'tier2':z[_0x18d8c4(0x1cd)](),'tier3':z['object']({'network':z[_0x18d8c4(0x1cd)](),'video':z[_0x18d8c4(0x1cd)]()}),'retention':z[_0x18d8c4(0x1d6)]({'runs':z[_0x18d8c4(0x1f4)]()['int']()[_0x18d8c4(0x1d3)](),'days':z['number']()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)]()}),'storageDir':z['string']()[_0x18d8c4(0x1e2)](0x1)}),DialogPolicySchema=z[_0x18d8c4(0x1f1)]([_0x18d8c4(0x1e6),_0x18d8c4(0x1dd),'manual']),LinterRuleIdSchema=z['enum'](['lint:deep-css','lint:xpath','lint:obfuscated-class',_0x18d8c4(0x1ef),_0x18d8c4(0x1f3),'lint:evaluate-discouraged',_0x18d8c4(0x1c0),'lint:mustache-in-dsl',_0x18d8c4(0x1d0),'lint:regex-invalid',_0x18d8c4(0x1c4),'lint:api-call-file-body',_0x18d8c4(0x1bf),_0x18d8c4(0x1d4),_0x18d8c4(0x1bd),_0x18d8c4(0x1ca),_0x18d8c4(0x1cf),_0x18d8c4(0x1cb),_0x18d8c4(0x1ce),_0x18d8c4(0x1de),'validator:function-shape',_0x18d8c4(0x1db),_0x18d8c4(0x1f0),_0x18d8c4(0x1ed),_0x18d8c4(0x1e1),'validator:unsupported-expression',_0x18d8c4(0x1df),_0x18d8c4(0x1f5),_0x18d8c4(0x1e4),_0x18d8c4(0x1d5)]),LinterSeveritySchema=z[_0x18d8c4(0x1f1)]([_0x18d8c4(0x1d1),'warn',_0x18d8c4(0x1ea)]),LinterConfigSchema=z[_0x18d8c4(0x1d6)]({'enabled':z[_0x18d8c4(0x1cd)](),'rules':z['record'](LinterRuleIdSchema,LinterSeveritySchema)}),QueueConfigSchema=z[_0x18d8c4(0x1d6)]({'enabled':z['boolean']()['default'](!![]),'concurrency':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)]()['default'](0x1)}),ScheduleSchema=z[_0x18d8c4(0x1d6)]({'collection':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1),'cron':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1),'env':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1)[_0x18d8c4(0x1be)](),'prepare':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1)[_0x18d8c4(0x1be)]()}),McpConfigSchema=z[_0x18d8c4(0x1d6)]({'transport':z[_0x18d8c4(0x1c5)](_0x18d8c4(0x1c9))}),SandboxConfigSchema=z[_0x18d8c4(0x1d6)]({'shellCwd':z['string']()[_0x18d8c4(0x1be)](),'shellTimeoutMs':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()['positive']()[_0x18d8c4(0x1be)](),'exportSecrets':z[_0x18d8c4(0x1e9)](z['string']())[_0x18d8c4(0x1be)](),'database':z['string']()['optional'](),'apiBaseUrl':z[_0x18d8c4(0x1c7)]()['url']()['optional'](),'uploadDir':z[_0x18d8c4(0x1c7)]()['optional']()}),WebServerConfigSchema=z['object']({'command':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1),'url':z['string']()[_0x18d8c4(0x1e3)](),'reuseExistingServer':z['boolean']()[_0x18d8c4(0x1e8)](!![]),'timeoutMs':z['number']()[_0x18d8c4(0x1e0)]()['positive']()['default'](0xea60)}),UnotestConfigSchema=z['object']({'baseUrl':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e3)]()[_0x18d8c4(0x1be)](),'webServer':WebServerConfigSchema[_0x18d8c4(0x1be)](),'browsers':z[_0x18d8c4(0x1e9)](BrowserSchema)[_0x18d8c4(0x1e2)](0x1),'channel':BrowserChannelSchema,'viewport':ViewportSchema,'retry':RetryConfigSchema,'failureBundle':FailureBundleConfigSchema,'dialogPolicy':DialogPolicySchema,'storageState':z['string']()[_0x18d8c4(0x1be)](),'testDir':z['string']()[_0x18d8c4(0x1e2)](0x1),'helpersDir':z[_0x18d8c4(0x1c7)]()[_0x18d8c4(0x1e2)](0x1),'defaultTimeoutMs':z['number']()['int']()[_0x18d8c4(0x1d3)](),'defaultNavigationTimeoutMs':z[_0x18d8c4(0x1f4)]()[_0x18d8c4(0x1e0)]()[_0x18d8c4(0x1d3)](),'linter':LinterConfigSchema,'mcp':McpConfigSchema,'queue':QueueConfigSchema,'schedules':z[_0x18d8c4(0x1e9)](ScheduleSchema)[_0x18d8c4(0x1e8)]([]),'sandbox':SandboxConfigSchema}),DEFAULT_CONFIG={'browsers':[_0x18d8c4(0x1c8)],'viewport':{'width':0x500,'height':0x2d0},'retry':{'count':0x0,'on':[_0x18d8c4(0x1c6)]},'failureBundle':{'tier1':!![],'tier2':!![],'tier3':{'network':![],'video':![]},'retention':{'runs':0x14,'days':0x7},'storageDir':_0x18d8c4(0x1da)},'dialogPolicy':_0x18d8c4(0x1e6),'testDir':_0x18d8c4(0x1eb),'helpersDir':_0x18d8c4(0x1ee),'defaultTimeoutMs':0xbb8,'defaultNavigationTimeoutMs':0x7530,'linter':{'enabled':!![],'rules':{'lint:deep-css':_0x18d8c4(0x1f2),'lint:xpath':_0x18d8c4(0x1f2),'lint:obfuscated-class':_0x18d8c4(0x1f2),'lint:pause-explicit':_0x18d8c4(0x1f2),'lint:disambig-by-index':_0x18d8c4(0x1d1),'lint:evaluate-discouraged':_0x18d8c4(0x1f2),'lint:scenario-in-root':_0x18d8c4(0x1ea),'lint:mustache-in-dsl':_0x18d8c4(0x1ea),'lint:goto-concat':_0x18d8c4(0x1f2),'lint:regex-invalid':'error','lint:echo-assert':_0x18d8c4(0x1f2),'lint:lint-ok-missing-reason':_0x18d8c4(0x1f2),'lint:flow-returns-value':_0x18d8c4(0x1f2),'validator:unknown-function':_0x18d8c4(0x1ea)}},'mcp':{'transport':_0x18d8c4(0x1c9)},'queue':{'enabled':!![],'concurrency':0x1},'schedules':[],'sandbox':{}};export{BrowserChannelSchema,BrowserSchema,DEFAULT_CONFIG,DialogPolicySchema,FailureBundleConfigSchema,LinterConfigSchema,LinterRuleIdSchema,LinterSeveritySchema,McpConfigSchema,QueueConfigSchema,RetryConfigSchema,RetryReasonSchema,SandboxConfigSchema,ScheduleSchema,UnotestConfigSchema,ViewportSchema,WebServerConfigSchema};
|