@isentinel/jest-roblox 0.3.7 → 0.3.9
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/README.md +79 -13
- package/bin/jest-roblox.js +8 -9
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +13 -3
- package/dist/index.d.mts +256 -3
- package/dist/index.mjs +52 -2
- package/dist/{run-Bh8G33J1.mjs → run-BM5sqclu.mjs} +1264 -375
- package/dist/{schema-CCF2E3k_.d.mts → schema-i-pZhLCV.d.mts} +23 -3
- package/loaders/luau-raw.d.mts +4 -4
- package/loaders/luau-raw.mjs +5 -5
- package/package.json +5 -5
- package/plugin/JestRobloxRunner.rbxm +0 -0
- package/plugin/src/init.server.luau +11 -4
- package/plugin/src/test-in-run-mode.server.luau +73 -20
package/README.md
CHANGED
|
@@ -15,10 +15,13 @@ Run your roblox-ts and Luau tests inside Roblox, get results in your terminal.
|
|
|
15
15
|
- roblox-ts and pure Luau
|
|
16
16
|
- Source-mapped errors (Luau line numbers back to `.ts` files)
|
|
17
17
|
- Code coverage (via [Lute](https://github.com/luau-lang/lute) instrumentation)
|
|
18
|
-
-
|
|
18
|
+
- Three backends: Open Cloud (remote), Studio (attached, local), and Studio CLI
|
|
19
|
+
(self-launched headless Studio, local)
|
|
19
20
|
- Multiple output formatters (human, agent, JSON, GitHub Actions)
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
<!-- prettier-ignore -->
|
|
23
|
+
> [!NOTE]
|
|
24
|
+
> roblox-ts projects currently require
|
|
22
25
|
> [@isentinel/roblox-ts](https://npmx.dev/package/@isentinel/roblox-ts) for
|
|
23
26
|
> source maps and coverage support.
|
|
24
27
|
|
|
@@ -68,6 +71,15 @@ Then run:
|
|
|
68
71
|
jest-roblox
|
|
69
72
|
```
|
|
70
73
|
|
|
74
|
+
<!-- prettier-ignore -->
|
|
75
|
+
> [!NOTE]
|
|
76
|
+
> `projects` is optional. With it omitted, the CLI derives one project per
|
|
77
|
+
> `luauRoots` mount (the compiled-output dirs your Rojo project mounts,
|
|
78
|
+
> auto-detected from your tsconfig `outDir`), generating each project's
|
|
79
|
+
> `jest.config` stub for you. Set `projects` explicitly when you need
|
|
80
|
+
> per-project overrides (a distinct `displayName`, `setupFiles`, `include`,
|
|
81
|
+
> etc.) or a tighter project layout than the luau roots imply.
|
|
82
|
+
|
|
71
83
|
## Usage
|
|
72
84
|
|
|
73
85
|
```bash
|
|
@@ -88,6 +100,7 @@ jest-roblox --testPathPattern="modifiers|define\\.spec|triggers"
|
|
|
88
100
|
# Use a specific backend (default "auto" picks Studio if the plugin is
|
|
89
101
|
# connected, else Open Cloud if credentials are set — see Backends below)
|
|
90
102
|
jest-roblox --backend studio
|
|
103
|
+
jest-roblox --backend studio-cli
|
|
91
104
|
jest-roblox --backend open-cloud
|
|
92
105
|
|
|
93
106
|
# Collect coverage
|
|
@@ -133,7 +146,7 @@ per-package declarations error loudly.
|
|
|
133
146
|
|
|
134
147
|
| Field | What it does | Default |
|
|
135
148
|
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
136
|
-
| `backend` | `"auto"`, `"open-cloud"`, or `"studio"`
|
|
149
|
+
| `backend` | `"auto"`, `"open-cloud"`, `"studio"`, or `"studio-cli"` | `"auto"` |
|
|
137
150
|
| `color` | Use ANSI colors in console output | `true` |
|
|
138
151
|
| `formatters` | Output formatters (`"default"`, `"agent"`, `"json"`, `"github-actions"`) | `["default"]` |
|
|
139
152
|
| `gameOutput` | Write Game Output to a file — a path, or `true` for `game-output.log` under the root. In `--workspace` mode this is one grouped aggregate file across every package | — |
|
|
@@ -144,6 +157,7 @@ per-package declarations error loudly.
|
|
|
144
157
|
| `placeId` | Open Cloud place ID | — |
|
|
145
158
|
| `port` | WebSocket port for Studio backend | `3001` |
|
|
146
159
|
| `silent` | Suppress console output | `false` |
|
|
160
|
+
| `studioPath` | Roblox Studio executable for the `studio-cli` backend (auto-detected if unset; also `--studioPath` / `JEST_ROBLOX_STUDIO_PATH`) | — |
|
|
147
161
|
| `universeId` | Open Cloud universe ID | — |
|
|
148
162
|
|
|
149
163
|
#### Per-package fields
|
|
@@ -169,7 +183,7 @@ Put these under `test: { ... }`.
|
|
|
169
183
|
|
|
170
184
|
| Field | What it does | Default |
|
|
171
185
|
| ------------------------ | ------------------------------------------------ | ------------------------------------ |
|
|
172
|
-
| `projects` | Where to look for tests in the DataModel |
|
|
186
|
+
| `projects` | Where to look for tests in the DataModel | one project per `luauRoots` mount |
|
|
173
187
|
| `testMatch` | Glob patterns that find test files | `**/*.spec.ts`, `**/*.test.ts`, etc. |
|
|
174
188
|
| `testPathIgnorePatterns` | Patterns to skip | `/node_modules/`, `/dist/`, `/out/` |
|
|
175
189
|
| `setupFiles` | Scripts to run before the test environment loads | — |
|
|
@@ -181,7 +195,9 @@ Put these under `test: { ... }`.
|
|
|
181
195
|
|
|
182
196
|
Put these under `test: { ... }`.
|
|
183
197
|
|
|
184
|
-
|
|
198
|
+
<!-- prettier-ignore -->
|
|
199
|
+
> [!IMPORTANT]
|
|
200
|
+
> Coverage requires [Lute](https://github.com/luau-lang/lute) to be
|
|
185
201
|
> installed and on your `PATH`. Lute parses Luau ASTs so the CLI can insert
|
|
186
202
|
> coverage probes.
|
|
187
203
|
|
|
@@ -194,7 +210,9 @@ Put these under `test: { ... }`.
|
|
|
194
210
|
| `coveragePathIgnorePatterns` | Files to leave out of coverage | test files, `node_modules`, `rbxts_include` |
|
|
195
211
|
| `collectCoverageFrom` | Globs for files to include in coverage | — |
|
|
196
212
|
|
|
197
|
-
|
|
213
|
+
<!-- prettier-ignore -->
|
|
214
|
+
> [!NOTE]
|
|
215
|
+
> Coverage uses vitest `all` / Istanbul semantics: every instrumented
|
|
198
216
|
> file matching the include globs is reported, so a source file with no test
|
|
199
217
|
> shows **0%** (and fails `coverageThreshold`) instead of being silently
|
|
200
218
|
> omitted. When `collectCoverageFrom` is unset for a multi-project run, the
|
|
@@ -258,7 +276,7 @@ export default defineConfig({
|
|
|
258
276
|
|
|
259
277
|
## Backends
|
|
260
278
|
|
|
261
|
-
|
|
279
|
+
Three ways to run tests, plus an auto-pick:
|
|
262
280
|
|
|
263
281
|
### Auto (default)
|
|
264
282
|
|
|
@@ -320,7 +338,9 @@ Connects to Roblox Studio over WebSocket. Faster than Open Cloud (no upload
|
|
|
320
338
|
step), but Studio must be open with the plugin running. Studio doesn't expose
|
|
321
339
|
which place is open, so multiple concurrent projects aren't supported yet.
|
|
322
340
|
|
|
323
|
-
|
|
341
|
+
<!-- prettier-ignore -->
|
|
342
|
+
> [!NOTE]
|
|
343
|
+
> For `--coverage`, prefer `--backend open-cloud` since the coverage
|
|
324
344
|
> output is built to a separate output under `.jest-roblox/coverage/` that is
|
|
325
345
|
> likely not the studio place being served.
|
|
326
346
|
|
|
@@ -342,12 +362,54 @@ Or download `JestRobloxRunner.rbxm` from the
|
|
|
342
362
|
[latest release](https://github.com/christopher-buss/jest-roblox-cli/releases)
|
|
343
363
|
and drop it into your Studio plugins folder.
|
|
344
364
|
|
|
365
|
+
### Studio CLI (self-launched, local)
|
|
366
|
+
|
|
367
|
+
`--backend studio-cli` owns the whole Studio lifecycle: it builds its own place,
|
|
368
|
+
launches Roblox Studio headless via Studio's `--task RunScript` interface,
|
|
369
|
+
drives the installed plugin's Run mode, reads the result from Studio's output
|
|
370
|
+
log, and quits Studio. No API key, no upload, no pre-opened editor — you just
|
|
371
|
+
need Studio installed (logged in) with the jest plugin. It spawns its own
|
|
372
|
+
isolated Studio instance, so any editor you already have open is untouched.
|
|
373
|
+
|
|
374
|
+
It is selected only when you ask for it explicitly — `auto` never launches a
|
|
375
|
+
Studio process on its own. Studio is auto-discovered per-OS; override the
|
|
376
|
+
executable with `studioPath` (config key), `--studioPath`, or
|
|
377
|
+
`JEST_ROBLOX_STUDIO_PATH`. The backend is serial: `--parallel > 1` errors.
|
|
378
|
+
|
|
379
|
+
Pass `--headed` to show the Studio window during the run instead of the default
|
|
380
|
+
hidden one — useful for watching a slow run or a hang (Studio still self-quits
|
|
381
|
+
when tests finish, so a fast run just flashes). It is a per-run debugging flag,
|
|
382
|
+
CLI-only and inert on every other backend.
|
|
383
|
+
|
|
384
|
+
Once the result lands, Studio is shut down gracefully: it is allowed to close
|
|
385
|
+
the place — running any edit-mode plugin `BindToClose` handlers and freeing the
|
|
386
|
+
place lock — and is then killed the instant the lock releases, skipping Studio's
|
|
387
|
+
slow telemetry teardown. The shutdown is decoupled from the result, so pass/fail
|
|
388
|
+
prints immediately and the process exits once teardown finishes.
|
|
389
|
+
|
|
390
|
+
Unlike the attached `studio` backend, `--coverage` works here: studio-cli opens
|
|
391
|
+
the Coverage-Instrumented Place instead of the Clean Place, so the report
|
|
392
|
+
universe, thresholds, reporters, and exclusions behave identically to the
|
|
393
|
+
open-cloud backend (including the all-files semantics where an untested included
|
|
394
|
+
file reports **0%** and fails `coverageThreshold`).
|
|
395
|
+
|
|
396
|
+
<!-- prettier-ignore -->
|
|
397
|
+
> [!NOTE]
|
|
398
|
+
> studio-cli is a local-developer convenience backend (it needs a logged-in
|
|
399
|
+
> Studio and the installed plugin), not a CI path.
|
|
400
|
+
|
|
345
401
|
## Workspace mode
|
|
346
402
|
|
|
347
403
|
Run tests across multiple packages in a pnpm workspace in a single invocation.
|
|
348
|
-
Open Cloud
|
|
349
|
-
|
|
350
|
-
|
|
404
|
+
Works on every backend: Open Cloud (fans packages across parallel tasks),
|
|
405
|
+
`studio-cli` (one self-launched Studio process drives every package, no
|
|
406
|
+
sharding), and the attached `studio` backend (runs the workspace inside an open
|
|
407
|
+
Studio — handy for debugging the flow). `studio-cli` is serial, so
|
|
408
|
+
`--parallel > 1` is rejected with `--workspace`.
|
|
409
|
+
|
|
410
|
+
<!-- prettier-ignore -->
|
|
411
|
+
> [!NOTE]
|
|
412
|
+
> Package discovery uses one of two sources. By default it reads
|
|
351
413
|
> `pnpm-workspace.yaml` at the workspace root. Alternatively, declare a
|
|
352
414
|
> `workspace` block in your jest config (see
|
|
353
415
|
> [Workspaces without pnpm](#workspaces-without-pnpm)) to enumerate packages by
|
|
@@ -440,8 +502,10 @@ project) under `.jest-roblox/output/`.
|
|
|
440
502
|
|
|
441
503
|
| Flag | What it does |
|
|
442
504
|
| -------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
443
|
-
| `--backend <type>` | Choose `auto`, `open-cloud`, or `studio`
|
|
505
|
+
| `--backend <type>` | Choose `auto`, `open-cloud`, `studio`, or `studio-cli` |
|
|
444
506
|
| `--port <n>` | WebSocket port for Studio |
|
|
507
|
+
| `--studioPath <path>` | Roblox Studio executable for `studio-cli` (auto-detected if unset) |
|
|
508
|
+
| `--headed` | Show the Studio window during the run (`studio-cli` only; default: hidden) |
|
|
445
509
|
| `--config <path>` | Path to config file |
|
|
446
510
|
| `--testPathPattern <regex>` | Filter test files by path |
|
|
447
511
|
| `-t, --testNamePattern <regex>` | Filter tests by name |
|
|
@@ -486,7 +550,9 @@ project) under `.jest-roblox/output/`.
|
|
|
486
550
|
5. Maps Luau line numbers to TypeScript via source maps (roblox-ts only)
|
|
487
551
|
6. Prints results
|
|
488
552
|
|
|
489
|
-
|
|
553
|
+
<!-- prettier-ignore -->
|
|
554
|
+
> [!NOTE]
|
|
555
|
+
> Coverage adds extra steps: copy Luau files, insert tracking probes,
|
|
490
556
|
> build a separate place file, then map hit counts back to source. For
|
|
491
557
|
> roblox-ts, this goes through source maps to report TypeScript lines.
|
|
492
558
|
|
package/bin/jest-roblox.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
|
+
import { registerHooks } from "node:module";
|
|
3
4
|
import { dirname, resolve } from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
|
|
7
|
+
import { load, resolve as resolveLuau } from "../loaders/luau-raw.mjs";
|
|
8
|
+
|
|
6
9
|
const sourceEntry = resolve(dirname(fileURLToPath(import.meta.url)), "../src/cli.ts");
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
register("../loaders/luau-raw.mjs", import.meta.url);
|
|
11
|
+
registerHooks({ load, resolve: resolveLuau });
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
await
|
|
14
|
-
|
|
15
|
-
const { main } = await import("../dist/cli.mjs");
|
|
16
|
-
await main();
|
|
17
|
-
}
|
|
13
|
+
const { main } = existsSync(sourceEntry)
|
|
14
|
+
? await import("../src/cli.ts")
|
|
15
|
+
: await import("../dist/cli.mjs");
|
|
16
|
+
await main();
|
package/dist/cli.d.mts
CHANGED
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { B as parseGameOutput, J as formatBanner, N as outputMultiResult, P as outputSingleResult, bt as version, ct as loadConfig, ht as VALID_BACKENDS, n as runJestRoblox, st as mergeCliWithConfig, tt as LuauScriptError, v as formatMissingScopes, vt as isValidBackend, y as walkErrorChain, yt as ConfigError } from "./run-BM5sqclu.mjs";
|
|
2
2
|
import { OpenCloudError } from "@bedrock-rbx/ocale";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { parseArgs as parseArgs$1 } from "node:util";
|
|
@@ -9,8 +9,13 @@ const HELP_TEXT = `
|
|
|
9
9
|
Usage: jest-roblox [options] [files...]
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
|
-
--backend <type> Backend: "auto", "open-cloud",
|
|
12
|
+
--backend <type> Backend: "auto", "open-cloud", "studio", or
|
|
13
|
+
"studio-cli" (default: auto)
|
|
13
14
|
--port <number> WebSocket port for studio backend (default: 3001)
|
|
15
|
+
--studioPath <path> Roblox Studio executable for the studio-cli
|
|
16
|
+
backend (auto-detected if not set)
|
|
17
|
+
--headed Show the Studio window during the run
|
|
18
|
+
(studio-cli backend only; default: hidden)
|
|
14
19
|
--config <path> Path to config file
|
|
15
20
|
--testPathPattern <regex> Filter test files by path pattern
|
|
16
21
|
-t, --testNamePattern <regex> Filter tests by name pattern
|
|
@@ -96,6 +101,7 @@ function parseArgs(args) {
|
|
|
96
101
|
type: "string"
|
|
97
102
|
},
|
|
98
103
|
"gameOutput": { type: "string" },
|
|
104
|
+
"headed": { type: "boolean" },
|
|
99
105
|
"help": {
|
|
100
106
|
default: false,
|
|
101
107
|
type: "boolean"
|
|
@@ -126,6 +132,7 @@ function parseArgs(args) {
|
|
|
126
132
|
"showLuau": { type: "boolean" },
|
|
127
133
|
"silent": { type: "boolean" },
|
|
128
134
|
"sourceMap": { type: "boolean" },
|
|
135
|
+
"studioPath": { type: "string" },
|
|
129
136
|
"testNamePattern": {
|
|
130
137
|
short: "t",
|
|
131
138
|
type: "string"
|
|
@@ -166,6 +173,7 @@ function parseArgs(args) {
|
|
|
166
173
|
files: positionals.length > 0 ? positionals : void 0,
|
|
167
174
|
formatters: values.formatters,
|
|
168
175
|
gameOutput: values.gameOutput,
|
|
176
|
+
headed: values.headed,
|
|
169
177
|
help: values.help,
|
|
170
178
|
outputFile: values.outputFile,
|
|
171
179
|
packages: values.packages,
|
|
@@ -180,6 +188,7 @@ function parseArgs(args) {
|
|
|
180
188
|
showLuau: values["no-show-luau"] === true ? false : values.showLuau,
|
|
181
189
|
silent: values.silent,
|
|
182
190
|
sourceMap: values.sourceMap,
|
|
191
|
+
studioPath: values.studioPath,
|
|
183
192
|
testNamePattern: values.testNamePattern,
|
|
184
193
|
testPathPattern: values.testPathPattern,
|
|
185
194
|
timeout,
|
|
@@ -206,6 +215,7 @@ async function main() {
|
|
|
206
215
|
process.exitCode = await run(process.argv.slice(2));
|
|
207
216
|
}
|
|
208
217
|
const PARALLEL_FLAG = "--parallel";
|
|
218
|
+
const IntegerLikePattern = /^-?\d+$/;
|
|
209
219
|
function normalizeParallelFlag(args) {
|
|
210
220
|
const out = [];
|
|
211
221
|
for (let index = 0; index < args.length; index++) {
|
|
@@ -215,7 +225,7 @@ function normalizeParallelFlag(args) {
|
|
|
215
225
|
continue;
|
|
216
226
|
}
|
|
217
227
|
const next = args[index + 1];
|
|
218
|
-
if (next !== void 0 && !next.startsWith("-") && (next === "auto" ||
|
|
228
|
+
if (next !== void 0 && !next.startsWith("-") && (next === "auto" || IntegerLikePattern.test(next))) {
|
|
219
229
|
out.push(PARALLEL_FLAG, next);
|
|
220
230
|
index += 1;
|
|
221
231
|
} else out.push(PARALLEL_FLAG, "auto");
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as SnapshotFormatOptions, a as DisplayName, b as defineProject, c as GlobalTestConfig, d as ProjectEntry, f as ProjectTestConfig, g as SharedTestConfig, h as SHARED_TEST_KEYS, i as DEFAULT_CONFIG, l as InlineProjectConfig, m as ResolvedConfig, n as Config, o as FormatterEntry, p as ROOT_CLI_KEYS, r as ConfigInput, s as GLOBAL_TEST_KEYS, t as CliOptions, u as JEST_ARGV_EXCLUDED_KEYS, v as WorkspaceConfig, x as TypecheckConfig, y as defineConfig } from "./schema-
|
|
1
|
+
import { _ as SnapshotFormatOptions, a as DisplayName, b as defineProject, c as GlobalTestConfig, d as ProjectEntry, f as ProjectTestConfig, g as SharedTestConfig, h as SHARED_TEST_KEYS, i as DEFAULT_CONFIG, l as InlineProjectConfig, m as ResolvedConfig, n as Config, o as FormatterEntry, p as ROOT_CLI_KEYS, r as ConfigInput, s as GLOBAL_TEST_KEYS, t as CliOptions, u as JEST_ARGV_EXCLUDED_KEYS, v as WorkspaceConfig, x as TypecheckConfig, y as defineConfig } from "./schema-i-pZhLCV.mjs";
|
|
2
2
|
import { type } from "arktype";
|
|
3
3
|
import { StorageClient } from "@bedrock-rbx/ocale/storage";
|
|
4
4
|
import { WebSocket, WebSocketServer } from "ws";
|
|
@@ -106,6 +106,44 @@ interface CoverageArtifacts {
|
|
|
106
106
|
}
|
|
107
107
|
declare function readBuildManifest(filePath: string, options?: ReadBuildManifestOptions): ReadBuildManifestResult;
|
|
108
108
|
//#endregion
|
|
109
|
+
//#region src/artifacts/build-coverage-place.d.ts
|
|
110
|
+
/**
|
|
111
|
+
* Everything a caller (a Node-only "Machine A") needs after producing the
|
|
112
|
+
* coverage-instrumented place offline, without executing any suite: the built
|
|
113
|
+
* place (`coveragePlace.path` + content hash) and the paths of the sibling
|
|
114
|
+
* manifests it shares a `buildId` with. The place is left on disk for the caller
|
|
115
|
+
* to copy to the run machine — it is never cleaned.
|
|
116
|
+
*/
|
|
117
|
+
interface CoveragePlaceBundle {
|
|
118
|
+
buildId: string;
|
|
119
|
+
/** The always-on build record's path (`build-manifest.json`). */
|
|
120
|
+
buildManifestPath: string;
|
|
121
|
+
/** The coverage-data sibling manifest's path (`coverage-manifest.json`). */
|
|
122
|
+
coverageManifestPath: string;
|
|
123
|
+
/** The instrumented place: cwd-relative path + SHA-256 of its bytes. */
|
|
124
|
+
coveragePlace: BuildManifestArtifact;
|
|
125
|
+
/** Per-project DataModel paths baked into the place. */
|
|
126
|
+
projects: Array<BuildManifestProject>;
|
|
127
|
+
/** `false` on the incremental no-change reuse path (place was not rebuilt). */
|
|
128
|
+
rebuilt: boolean;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build the coverage-instrumented place **without running it** — the offline
|
|
132
|
+
* half of the split producer. Instruments (incrementally) and rojo-builds the
|
|
133
|
+
* place, bakes each project's `jest.config` stub into it so any runner that
|
|
134
|
+
* opens the place can discover and run the suite unaided, publishes the sibling
|
|
135
|
+
* Build + Coverage manifests, and hands back the place. No backend is resolved,
|
|
136
|
+
* no suite executes, nothing hits the network. Coverage collection is forced on
|
|
137
|
+
* regardless of the input config.
|
|
138
|
+
*
|
|
139
|
+
* The counterpart to `prepareArtifacts`, minus the run and the Clean Place —
|
|
140
|
+
* the entry point for a machine that cannot execute Roblox at all. It shares the
|
|
141
|
+
* `prepareBakedCoverage` seam with the run path but always bakes stubs (the run
|
|
142
|
+
* path skips baking for studio-cli, which injects configs at runtime), because a
|
|
143
|
+
* place handed to a foreign runner must be self-contained.
|
|
144
|
+
*/
|
|
145
|
+
declare function buildCoveragePlace(config: ResolvedConfig): Promise<CoveragePlaceBundle>;
|
|
146
|
+
//#endregion
|
|
109
147
|
//#region src/coverage-pipeline/types.d.ts
|
|
110
148
|
/**
|
|
111
149
|
* Raw hit counts for a single file, keyed by statement/function index.
|
|
@@ -352,7 +390,16 @@ interface Backend {
|
|
|
352
390
|
readonly kind: BackendKind;
|
|
353
391
|
runTests(options: BackendOptions): Promise<BackendResult>;
|
|
354
392
|
}
|
|
355
|
-
type BackendKind = "open-cloud" | "studio";
|
|
393
|
+
type BackendKind = "open-cloud" | "studio" | "studio-cli";
|
|
394
|
+
/**
|
|
395
|
+
* Whether this is a workspace (multi-package) run. Workspace jobs each carry
|
|
396
|
+
* their owning package name (`pkg`); single-/multi-project jobs never do, and
|
|
397
|
+
* the run layer builds them all-or-none — so any job with `pkg` means the whole
|
|
398
|
+
* run is a workspace run. The Studio backends key off this to drive the plugin's
|
|
399
|
+
* staged-materializer dispatch (`workspace.entries`) instead of the configs
|
|
400
|
+
* path. `buildWorkspaceEntries` then fails fast if a job is missing `pkg`, so a
|
|
401
|
+
* malformed (mixed) array surfaces as a clear error rather than a bad payload.
|
|
402
|
+
*/
|
|
356
403
|
//#endregion
|
|
357
404
|
//#region packages/roblox-runner/dist/index.d.mts
|
|
358
405
|
//#endregion
|
|
@@ -423,6 +470,154 @@ declare class OpenCloudBackend implements Backend {
|
|
|
423
470
|
}
|
|
424
471
|
declare function createOpenCloudBackend(credentials: OpenCloudCredentials): OpenCloudBackend;
|
|
425
472
|
//#endregion
|
|
473
|
+
//#region src/staging/synthesizer.d.ts
|
|
474
|
+
interface StubMount {
|
|
475
|
+
absStubPath: string;
|
|
476
|
+
dataModelPath: string;
|
|
477
|
+
}
|
|
478
|
+
interface CoverageRoot {
|
|
479
|
+
/** Path relative to `packageDirectory` that points at the original luau root. */
|
|
480
|
+
luauRoot: string;
|
|
481
|
+
/** Absolute path to the instrumented shadow directory for the same root. */
|
|
482
|
+
shadowDir: string;
|
|
483
|
+
}
|
|
484
|
+
interface PackageDescriptor {
|
|
485
|
+
name: string;
|
|
486
|
+
/**
|
|
487
|
+
* Per-package `coverageCache` opt-out, forwarded to
|
|
488
|
+
* `prepareWorkspaceCoverage` so the cache gate honors each package's own
|
|
489
|
+
* declaration. Not read by synthesis itself.
|
|
490
|
+
*/
|
|
491
|
+
coverageCache?: boolean;
|
|
492
|
+
/**
|
|
493
|
+
* Per-package coverage ignore patterns, forwarded to
|
|
494
|
+
* `prepareWorkspaceCoverage` so the matcher reflects the merged pkgConfig
|
|
495
|
+
* instead of the workspace-root default. Not read by synthesis itself.
|
|
496
|
+
*/
|
|
497
|
+
coveragePathIgnorePatterns?: Array<string>;
|
|
498
|
+
/**
|
|
499
|
+
* When set, $path entries that fall inside any listed `luauRoot` are
|
|
500
|
+
* redirected to the corresponding `shadowDir` so the synthesized place
|
|
501
|
+
* picks up instrumented sources for this package only. Packages without
|
|
502
|
+
* `coverageRoots` use their original $path entries unchanged.
|
|
503
|
+
*/
|
|
504
|
+
coverageRoots?: Array<CoverageRoot>;
|
|
505
|
+
/**
|
|
506
|
+
* Per-package `luauRoots`, forwarded to `prepareWorkspaceCoverage` so the
|
|
507
|
+
* short-circuit honors the user-listed source dirs. Not read by synthesis
|
|
508
|
+
* itself.
|
|
509
|
+
*/
|
|
510
|
+
luauRoots?: Array<string>;
|
|
511
|
+
packageDirectory: string;
|
|
512
|
+
rojoProjectPath: string;
|
|
513
|
+
stubMounts?: Array<StubMount>;
|
|
514
|
+
}
|
|
515
|
+
//#endregion
|
|
516
|
+
//#region src/staging/place-builder.d.ts
|
|
517
|
+
interface BuildPlaceOptions {
|
|
518
|
+
/**
|
|
519
|
+
* Force `ServerScriptService.LoadStringEnabled = true` on the built place.
|
|
520
|
+
* Used by studio-cli's Clean Place, whose Run-mode runner gates on
|
|
521
|
+
* LoadString. Forwarded verbatim to {@link synthesize}.
|
|
522
|
+
*/
|
|
523
|
+
loadStringEnabled?: boolean;
|
|
524
|
+
packages: Array<PackageDescriptor>;
|
|
525
|
+
placeFile: string;
|
|
526
|
+
projectFile: string;
|
|
527
|
+
wrap?: boolean;
|
|
528
|
+
}
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/backends/studio-cli.d.ts
|
|
531
|
+
interface StudioCliLaunchRequest {
|
|
532
|
+
/** Full Studio CLI argument vector (already absolute paths). */
|
|
533
|
+
args: Array<string>;
|
|
534
|
+
/**
|
|
535
|
+
* Show the Studio window during the run (`--headed`) instead of the default
|
|
536
|
+
* hidden window. Maps to `windowsHide: !headed` in {@link spawnStudio}.
|
|
537
|
+
*/
|
|
538
|
+
headed: boolean;
|
|
539
|
+
/**
|
|
540
|
+
* Absolute path of the place Studio opens. Used only to clear a stale
|
|
541
|
+
* `<place>.lock` a previously killed Studio could not remove itself.
|
|
542
|
+
*/
|
|
543
|
+
placeFile: string;
|
|
544
|
+
/** Absolute path to the Studio executable. */
|
|
545
|
+
studioPath: string;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* A launched Studio the host can kill once the result arrives (or on timeout).
|
|
549
|
+
* The injected seam: unit tests return a fake that drives a canned result frame
|
|
550
|
+
* over the mock WebSocket server instead of launching Studio.
|
|
551
|
+
*/
|
|
552
|
+
interface StudioCliProcess {
|
|
553
|
+
/**
|
|
554
|
+
* Terminate Studio immediately (`TerminateProcess`), skipping graceful
|
|
555
|
+
* shutdown. Used on every hung/error/timeout path.
|
|
556
|
+
*/
|
|
557
|
+
kill: () => void;
|
|
558
|
+
/**
|
|
559
|
+
* Graceful teardown: wait for Studio to release
|
|
560
|
+
* `<place>.lock` — which happens only after every edit-mode `BindToClose`
|
|
561
|
+
* handler ran and `ClosePlace` finished — then terminate it, skipping
|
|
562
|
+
* Studio's ~30s post-close telemetry drain. Hard-kills anyway after
|
|
563
|
+
* `graceCapMs` (a long-yielding handler). Returns immediately; the watch runs
|
|
564
|
+
* in the background, keeping node's event loop alive (the child handle + a
|
|
565
|
+
* poll timer) until it fires, so the CLI prints results now and the process
|
|
566
|
+
* exits once teardown completes. The caller closes the result server first,
|
|
567
|
+
* which is what lets the bootstrap return and `--quitAfterExecution` begin
|
|
568
|
+
* the graceful close.
|
|
569
|
+
*/
|
|
570
|
+
killOnLockRelease: (graceCapMs: number) => void;
|
|
571
|
+
/** Subscribe to a spawn failure (e.g. a bad `studioPath`). */
|
|
572
|
+
onError: (listener: (error: Error) => void) => void;
|
|
573
|
+
}
|
|
574
|
+
/** Spawns Studio and returns the handle the host kills. */
|
|
575
|
+
type StudioCliLauncher = (request: StudioCliLaunchRequest) => StudioCliProcess;
|
|
576
|
+
interface StudioCliOptions {
|
|
577
|
+
/** Place Builder seam; defaults to the real {@link defaultBuildPlace}. */
|
|
578
|
+
buildPlace?: (options: BuildPlaceOptions) => BuildManifestArtifact;
|
|
579
|
+
/** Result-server factory seam; defaults to an ephemeral-port `ws` server. */
|
|
580
|
+
createServer?: () => WebSocketServer;
|
|
581
|
+
/** Studio-executable resolver seam; defaults to {@link discoverStudioPath}. */
|
|
582
|
+
discover?: (override: string | undefined) => string;
|
|
583
|
+
/**
|
|
584
|
+
* Backstop for the graceful teardown: hard-kill if `<place>.lock` isn't
|
|
585
|
+
* released within this many ms. Defaults to {@link GRACEFUL_SHUTDOWN_CAP_MS}.
|
|
586
|
+
*/
|
|
587
|
+
gracefulShutdownTimeout?: number;
|
|
588
|
+
/**
|
|
589
|
+
* Show the Studio window during the run (`--headed`). CLI-only — never read
|
|
590
|
+
* from config. Defaults to false (hidden window).
|
|
591
|
+
*/
|
|
592
|
+
headed?: boolean;
|
|
593
|
+
/** Process launcher seam; defaults to the real {@link spawnStudio}. */
|
|
594
|
+
launch?: StudioCliLauncher;
|
|
595
|
+
/** Explicit Studio executable path (override from config / CLI / env). */
|
|
596
|
+
studioPath?: string;
|
|
597
|
+
/** Run timeout in milliseconds. Defaults to 300000. */
|
|
598
|
+
timeout?: number;
|
|
599
|
+
}
|
|
600
|
+
declare class StudioCliBackend implements Backend {
|
|
601
|
+
private readonly buildPlace;
|
|
602
|
+
private readonly createServer;
|
|
603
|
+
private readonly discover;
|
|
604
|
+
private readonly gracefulShutdownTimeout;
|
|
605
|
+
private readonly headed;
|
|
606
|
+
private readonly launch;
|
|
607
|
+
private readonly studioPath?;
|
|
608
|
+
private readonly timeout;
|
|
609
|
+
readonly kind = "studio-cli";
|
|
610
|
+
constructor(options?: StudioCliOptions);
|
|
611
|
+
runTests(options: BackendOptions): Promise<BackendResult>;
|
|
612
|
+
/**
|
|
613
|
+
* Build the Clean Place for a normal (non-coverage) run and return its path.
|
|
614
|
+
* `loadStringEnabled` is forced on so the Run-mode runner's LoadString gate
|
|
615
|
+
* passes. Coverage runs skip this and open the instrumented place instead.
|
|
616
|
+
*/
|
|
617
|
+
private buildCleanPlace;
|
|
618
|
+
}
|
|
619
|
+
declare function createStudioCliBackend(options?: StudioCliOptions): StudioCliBackend;
|
|
620
|
+
//#endregion
|
|
426
621
|
//#region src/backends/studio.d.ts
|
|
427
622
|
interface PreConnected {
|
|
428
623
|
server: WebSocketServer;
|
|
@@ -604,6 +799,40 @@ type ReadManifestResult = ParsedManifest<CoverageManifest>;
|
|
|
604
799
|
declare const manifestSchema: type<CoverageManifest>;
|
|
605
800
|
declare function readManifest(filePath: string): ReadManifestResult;
|
|
606
801
|
//#endregion
|
|
802
|
+
//#region src/coverage-pipeline/merge-raw-coverage.d.ts
|
|
803
|
+
/**
|
|
804
|
+
* Additively merge two raw coverage datasets. Overlapping files have their
|
|
805
|
+
* hit counts summed (matching istanbul-lib-coverage's semantics).
|
|
806
|
+
*/
|
|
807
|
+
declare function mergeRawCoverage(target: RawCoverageData | undefined, source: RawCoverageData | undefined): RawCoverageData | undefined;
|
|
808
|
+
//#endregion
|
|
809
|
+
//#region src/coverage-pipeline/raw-coverage.d.ts
|
|
810
|
+
/**
|
|
811
|
+
* Normalize a raw coverage table into typed {@link RawCoverageData}. The input
|
|
812
|
+
* is the per-file hit table the coverage probes accumulate at runtime — the
|
|
813
|
+
* `_G.__jest_roblox_cov` global, or the `_coverage` field of a run envelope —
|
|
814
|
+
* keyed by the stable per-file join key (`fileKey`). Luau serializes the
|
|
815
|
+
* `s`/`f` counters as 1-based arrays and `b` as an array of arrays; this
|
|
816
|
+
* canonicalizes them to string-keyed records while leaving the fileKey verbatim
|
|
817
|
+
* (it is the byte-identical join key the static maps are also keyed to). Returns
|
|
818
|
+
* `undefined` when the input is not an object or carries no file with a
|
|
819
|
+
* statement map.
|
|
820
|
+
*/
|
|
821
|
+
declare function normalizeRawCoverage(coverage: unknown): RawCoverageData | undefined;
|
|
822
|
+
/**
|
|
823
|
+
* Extract raw coverage from a completed run's result envelope — the companion
|
|
824
|
+
* seam for a run this CLI did not launch. Accepts the plugin's `jestOutput`
|
|
825
|
+
* (a JSON string or an already-parsed object), or the bare `_G.__jest_roblox_cov`
|
|
826
|
+
* table read straight off the run. When an object carries a `_coverage` field it
|
|
827
|
+
* is used; otherwise the object is treated as the hit table itself. Returns
|
|
828
|
+
* `undefined` for malformed JSON or an envelope with no coverage.
|
|
829
|
+
*
|
|
830
|
+
* A multi-project result (`{ entries: [{ jestOutput }, …] }`) carries one
|
|
831
|
+
* envelope per project; parse each `entries[i].jestOutput` and combine with
|
|
832
|
+
* `mergeRawCoverage`.
|
|
833
|
+
*/
|
|
834
|
+
declare function parseCoverageEnvelope(output: unknown): RawCoverageData | undefined;
|
|
835
|
+
//#endregion
|
|
607
836
|
//#region src/coverage-pipeline/attribution.d.ts
|
|
608
837
|
interface AttributionResult {
|
|
609
838
|
/** Per Luau file: statement id → ids of the tests that covered it. */
|
|
@@ -942,6 +1171,16 @@ interface MappedCoverageResult {
|
|
|
942
1171
|
files: Record<string, MappedFileCoverage>;
|
|
943
1172
|
}
|
|
944
1173
|
//#endregion
|
|
1174
|
+
//#region src/coverage-pipeline/agent-table-filter.d.ts
|
|
1175
|
+
/**
|
|
1176
|
+
* Decides whether a single coverage-universe source file is shown in the agent
|
|
1177
|
+
* **text table**. Receives a normalized-absolute source path (see
|
|
1178
|
+
* {@link narrowMappedForAgentTable}). This is a display-only narrowing layered
|
|
1179
|
+
* on top of `filterCoverageUniverse`: thresholds, the totals line, and the
|
|
1180
|
+
* lcov/html/json artifacts always keep the full universe.
|
|
1181
|
+
*/
|
|
1182
|
+
type CoverageDisplayPredicate = (normalizedAbsolutePath: string) => boolean;
|
|
1183
|
+
//#endregion
|
|
945
1184
|
//#region src/run/types.d.ts
|
|
946
1185
|
type RunMode = "multi" | "single" | "workspace";
|
|
947
1186
|
interface ProjectResult {
|
|
@@ -961,6 +1200,13 @@ interface SingleRunResult {
|
|
|
961
1200
|
* place set it has.
|
|
962
1201
|
*/
|
|
963
1202
|
coverageArtifacts?: CoverageArtifacts;
|
|
1203
|
+
/**
|
|
1204
|
+
* Narrows the agent coverage **text table** to the directly-filtered source
|
|
1205
|
+
* files on a filtered run (single file / `--testPathPattern`). Display-only:
|
|
1206
|
+
* thresholds, totals, and lcov/html/json keep the full universe. Undefined on
|
|
1207
|
+
* a full run.
|
|
1208
|
+
*/
|
|
1209
|
+
coverageDisplayFilter?: CoverageDisplayPredicate;
|
|
964
1210
|
mode: "single";
|
|
965
1211
|
preCoverageMs: number;
|
|
966
1212
|
runtimeResult?: ExecuteResult;
|
|
@@ -971,6 +1217,13 @@ interface MultiRunResult {
|
|
|
971
1217
|
collectCoverageFrom?: Array<string>;
|
|
972
1218
|
/** Producer record for the entry point to emit a Build Manifest from. */
|
|
973
1219
|
coverageArtifacts?: CoverageArtifacts;
|
|
1220
|
+
/**
|
|
1221
|
+
* Narrows the agent coverage **text table** to the directly-filtered source
|
|
1222
|
+
* files on a filtered run (positional files / `--testPathPattern`, or the
|
|
1223
|
+
* selected `--project` scope). Display-only: thresholds, totals, and
|
|
1224
|
+
* lcov/html/json keep the full universe. Undefined on a full run.
|
|
1225
|
+
*/
|
|
1226
|
+
coverageDisplayFilter?: CoverageDisplayPredicate;
|
|
974
1227
|
merged: MultiProjectMerged;
|
|
975
1228
|
mode: "multi";
|
|
976
1229
|
preCoverageMs: number;
|
|
@@ -1427,4 +1680,4 @@ declare function visitExpression(expression: AstExpr, visitor: LuauVisitor): voi
|
|
|
1427
1680
|
declare function visitStatement(statement: AstStat, visitor: LuauVisitor): void;
|
|
1428
1681
|
declare function visitBlock(block: AstStatBlock, visitor: LuauVisitor): void; //#endregion
|
|
1429
1682
|
//#endregion
|
|
1430
|
-
export { type ArtifactBundle, type AstExpr, type AstExprBinary, type AstExprCall, type AstExprFunction, type AstStat, type AstStatBlock, BUILD_MANIFEST_VERSION, type Backend, type BackendOptions, type BuildManifest, type BuildManifestArtifact, type BuildManifestFileRecord, type BuildManifestProject, type CliOptions, type Config, type ConfigInput, type CoverageManifest, DEFAULT_CONFIG, type DisplayName, type ExecuteResult, type FormatOutputOptions, type FormatterEntry, GLOBAL_TEST_KEYS, type GameOutputEntry, type GitHubActionsFormatterOptions, type GlobalTestConfig, type InlineProjectConfig, type InstrumentedFileRecord, JEST_ARGV_EXCLUDED_KEYS, type JestArgv, type JestResult, type LuauSpan, type LuauVisitor, MANIFEST_VERSION, type MultiProjectMerged, type MultiRunResult, type NonInstrumentedFileRecord, OpenCloudBackend, type ProjectEntry, type ProjectInput, type ProjectResult, type ProjectTestConfig, ROOT_CLI_KEYS, type ReadBuildManifestOptions, type ReadBuildManifestResult, type ReadManifestResult as ReadCoverageManifestResult, type ResolvedConfig, type ResolvedProjectConfig, type RunMode, type RunOptions, type RunProjectsOptions, type RunProjectsResult, type RunResult, SHARED_TEST_KEYS, type SharedTestConfig, type SingleRunResult, StudioBackend, type TestCaseResult, type TestDefinition, type TestFileResult, type TestRecord, type TestStatus, type TscErrorInfo, type TypecheckOptions, type WorkspaceConfig, type WorkspaceRunResult, buildJestArgv, buildManifestSchema, manifestSchema as coverageManifestSchema, createOpenCloudBackend, createStudioBackend, defineConfig, defineProject, extractJsonFromOutput, formatAnnotations, formatExecuteOutput, formatFailure, formatGameOutputNotice, formatJobSummary, formatJson, formatResult, formatTestSummary, generateTestScript, hashFile, loadConfig, parseGameOutput, parseJestOutput, prepareArtifacts, readBuildManifest, readManifest as readCoverageManifest, resolveConfig, runJestRoblox, runProjects, runTypecheck, visitBlock, visitExpression, visitStatement, writeGameOutput, writeJsonFile };
|
|
1683
|
+
export { type ArtifactBundle, type AstExpr, type AstExprBinary, type AstExprCall, type AstExprFunction, type AstStat, type AstStatBlock, BUILD_MANIFEST_VERSION, type Backend, type BackendOptions, type BuildManifest, type BuildManifestArtifact, type BuildManifestFileRecord, type BuildManifestProject, type CliOptions, type Config, type ConfigInput, type CoverageManifest, type CoveragePlaceBundle, DEFAULT_CONFIG, type DisplayName, type ExecuteResult, type FormatOutputOptions, type FormatterEntry, GLOBAL_TEST_KEYS, type GameOutputEntry, type GitHubActionsFormatterOptions, type GlobalTestConfig, type InlineProjectConfig, type InstrumentedFileRecord, JEST_ARGV_EXCLUDED_KEYS, type JestArgv, type JestResult, type LuauSpan, type LuauVisitor, MANIFEST_VERSION, type MultiProjectMerged, type MultiRunResult, type NonInstrumentedFileRecord, OpenCloudBackend, type ProjectEntry, type ProjectInput, type ProjectResult, type ProjectTestConfig, ROOT_CLI_KEYS, type RawCoverageData, type RawFileCoverage, type ReadBuildManifestOptions, type ReadBuildManifestResult, type ReadManifestResult as ReadCoverageManifestResult, type ResolvedConfig, type ResolvedProjectConfig, type RunMode, type RunOptions, type RunProjectsOptions, type RunProjectsResult, type RunResult, SHARED_TEST_KEYS, type SharedTestConfig, type SingleRunResult, StudioBackend, StudioCliBackend, type TestCaseResult, type TestDefinition, type TestFileResult, type TestRecord, type TestStatus, type TscErrorInfo, type TypecheckOptions, type WorkspaceConfig, type WorkspaceRunResult, buildCoveragePlace, buildJestArgv, buildManifestSchema, manifestSchema as coverageManifestSchema, createOpenCloudBackend, createStudioBackend, createStudioCliBackend, defineConfig, defineProject, extractJsonFromOutput, formatAnnotations, formatExecuteOutput, formatFailure, formatGameOutputNotice, formatJobSummary, formatJson, formatResult, formatTestSummary, generateTestScript, hashFile, loadConfig, mergeRawCoverage, normalizeRawCoverage, parseCoverageEnvelope, parseGameOutput, parseJestOutput, prepareArtifacts, readBuildManifest, readManifest as readCoverageManifest, resolveConfig, runJestRoblox, runProjects, runTypecheck, visitBlock, visitExpression, visitStatement, writeGameOutput, writeJsonFile };
|