@isentinel/jest-roblox 0.3.0 → 0.3.2
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 +2 -1
- package/dist/cli.mjs +6 -25
- package/dist/index.d.mts +36 -0
- package/dist/index.mjs +1 -1
- package/dist/{run-BEUPi80L.mjs → run-CyHhajiY.mjs} +516 -246
- package/dist/sea-entry.cjs +522 -266
- package/package.json +4 -4
package/dist/sea-entry.cjs
CHANGED
|
@@ -686,7 +686,7 @@ function C$4({ force: e } = {}) {
|
|
|
686
686
|
var y$3 = C$4();
|
|
687
687
|
//#endregion
|
|
688
688
|
//#region package.json
|
|
689
|
-
var version = "0.3.
|
|
689
|
+
var version = "0.3.2";
|
|
690
690
|
//#endregion
|
|
691
691
|
//#region src/config/errors.ts
|
|
692
692
|
var ConfigError = class extends Error {
|
|
@@ -5332,6 +5332,11 @@ async function findWorkspaceDir(id = process.cwd(), options = {}) {
|
|
|
5332
5332
|
throw new Error(`Cannot detect workspace root from ${id}`);
|
|
5333
5333
|
}
|
|
5334
5334
|
//#endregion
|
|
5335
|
+
//#region \0sea-stub:giget
|
|
5336
|
+
var require__sea_stub_giget = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
5337
|
+
module.exports = {};
|
|
5338
|
+
}));
|
|
5339
|
+
//#endregion
|
|
5335
5340
|
//#region node_modules/.pnpm/c12@4.0.0-beta.5_jiti@2.7.0_magicast@0.5.3/node_modules/c12/dist/_chunks/libs/ohash.mjs
|
|
5336
5341
|
var ohash_exports = /* @__PURE__ */ __exportAll$1({
|
|
5337
5342
|
n: () => dist_exports,
|
|
@@ -11261,7 +11266,7 @@ async function resolveConfig$1(source, options, sourceOptions = {}) {
|
|
|
11261
11266
|
const customProviderKeys = Object.keys(sourceOptions.giget?.providers || {}).map((key) => `${key}:`);
|
|
11262
11267
|
const gigetPrefixes = customProviderKeys.length > 0 ? [...new Set([...customProviderKeys, ...GIGET_PREFIXES])] : GIGET_PREFIXES;
|
|
11263
11268
|
if (options.giget !== false && gigetPrefixes.some((prefix) => source.startsWith(prefix))) {
|
|
11264
|
-
const { downloadTemplate } = await
|
|
11269
|
+
const { downloadTemplate } = await Promise.resolve().then(() => /* @__PURE__ */ __toESM(require__sea_stub_giget(), 1)).catch((error) => {
|
|
11265
11270
|
throw new Error(`Extending config from \`${source}\` requires \`giget\` peer dependency to be installed.\n\nInstall it with: \`npx nypm i giget\``, { cause: error });
|
|
11266
11271
|
});
|
|
11267
11272
|
const { digest } = await Promise.resolve().then(() => (init_ohash(), ohash_exports)).then((n) => n.n);
|
|
@@ -20145,12 +20150,35 @@ function mapBranchArmLocations(traceMap, locations, sourceMapDirectory) {
|
|
|
20145
20150
|
tsPath
|
|
20146
20151
|
};
|
|
20147
20152
|
}
|
|
20153
|
+
/**
|
|
20154
|
+
* Detects a phantom branch arm produced by a source-less synthetic statement
|
|
20155
|
+
* `if` (e.g. a roblox-ts Array polyfill like `.filter`/`.includes`). The
|
|
20156
|
+
* synthetic `if` has no source map entry, so trace-mapping's greatest-lower-
|
|
20157
|
+
* bound bias snaps both arms onto the nearest preceding segment — the then-
|
|
20158
|
+
* arm's own start — yielding a zero-width arm that coincides with another
|
|
20159
|
+
* arm's start and can never be covered.
|
|
20160
|
+
*
|
|
20161
|
+
* A genuine statement `if` is safe: roblox-ts always renders it multi-line, so
|
|
20162
|
+
* the then-body (generated line `if+1`) and the implicit-else arm (generated
|
|
20163
|
+
* line `if`) carry distinct source-map segments and never collapse. This is
|
|
20164
|
+
* gated to `type === "if"` by the caller: a single-line `expr-if` (ternary)
|
|
20165
|
+
* legitimately collapses to one column-0 segment and must NOT be dropped.
|
|
20166
|
+
*/
|
|
20167
|
+
function hasCollapsedPhantomArm(locations) {
|
|
20168
|
+
return locations.some((arm, index) => {
|
|
20169
|
+
if (!(arm.start.line === arm.end.line && arm.start.column === arm.end.column)) return false;
|
|
20170
|
+
return locations.some((other, otherIndex) => {
|
|
20171
|
+
return otherIndex !== index && other.start.line === arm.start.line && other.start.column === arm.start.column;
|
|
20172
|
+
});
|
|
20173
|
+
});
|
|
20174
|
+
}
|
|
20148
20175
|
function mapFileBranches(resources, fileCoverage, pendingBranches) {
|
|
20149
20176
|
if (resources.coverageMap.branchMap === void 0) return;
|
|
20150
20177
|
for (const [branchId, entry] of Object.entries(resources.coverageMap.branchMap)) {
|
|
20151
20178
|
const armHitCounts = fileCoverage.b?.[branchId] ?? [];
|
|
20152
20179
|
const result = mapBranchArmLocations(resources.traceMap, entry.locations, resources.sourceMapDirectory);
|
|
20153
20180
|
if (result === void 0) continue;
|
|
20181
|
+
if (entry.type === "if" && hasCollapsedPhantomArm(result.locations)) continue;
|
|
20154
20182
|
let fileBranches = pendingBranches.get(result.tsPath);
|
|
20155
20183
|
if (fileBranches === void 0) {
|
|
20156
20184
|
fileBranches = [];
|
|
@@ -24412,6 +24440,7 @@ function convertToLuau(filePath) {
|
|
|
24412
24440
|
const RbxPathParent = Symbol("Parent");
|
|
24413
24441
|
var RojoResolver = class RojoResolver {
|
|
24414
24442
|
rbxPath = new Array();
|
|
24443
|
+
realpathCache = /* @__PURE__ */ new Map();
|
|
24415
24444
|
walkedConfigFilesInternal = /* @__PURE__ */ new Set();
|
|
24416
24445
|
walkedDirectoriesInternal = /* @__PURE__ */ new Set();
|
|
24417
24446
|
filePathToRbxPathMap = /* @__PURE__ */ new Map();
|
|
@@ -24422,12 +24451,12 @@ var RojoResolver = class RojoResolver {
|
|
|
24422
24451
|
static findRojoConfigFilePath(projectPath) {
|
|
24423
24452
|
const warnings = new Array();
|
|
24424
24453
|
const defaultPath = node_path.default.join(projectPath, ROJO_DEFAULT_NAME);
|
|
24425
|
-
if (
|
|
24454
|
+
if (node_fs.existsSync(defaultPath)) return {
|
|
24426
24455
|
path: defaultPath,
|
|
24427
24456
|
warnings
|
|
24428
24457
|
};
|
|
24429
24458
|
const candidates = new Array();
|
|
24430
|
-
for (const fileName of
|
|
24459
|
+
for (const fileName of node_fs.readdirSync(projectPath)) if (fileName !== ROJO_DEFAULT_NAME && (fileName === ROJO_OLD_NAME || ROJO_FILE_REGEX.test(fileName))) candidates.push(node_path.default.join(projectPath, fileName));
|
|
24431
24460
|
if (candidates.length > 1) warnings.push(`Multiple *.project.json files found, using ${candidates[0]}`);
|
|
24432
24461
|
return {
|
|
24433
24462
|
path: candidates[0],
|
|
@@ -24563,34 +24592,42 @@ var RojoResolver = class RojoResolver {
|
|
|
24563
24592
|
get walkedDirectories() {
|
|
24564
24593
|
return this.walkedDirectoriesInternal;
|
|
24565
24594
|
}
|
|
24595
|
+
cachedRealpath(targetPath) {
|
|
24596
|
+
let resolved = this.realpathCache.get(targetPath);
|
|
24597
|
+
if (resolved === void 0) {
|
|
24598
|
+
resolved = node_fs.realpathSync(targetPath);
|
|
24599
|
+
this.realpathCache.set(targetPath, resolved);
|
|
24600
|
+
}
|
|
24601
|
+
return resolved;
|
|
24602
|
+
}
|
|
24566
24603
|
getContainer(from, rbxPath) {
|
|
24567
24604
|
if (this.isGame && rbxPath) {
|
|
24568
24605
|
for (const container of from) if (arrayStartsWith(rbxPath, container)) return container;
|
|
24569
24606
|
}
|
|
24570
24607
|
}
|
|
24571
24608
|
parseConfig(rojoConfigFilePath, doNotPush = false) {
|
|
24572
|
-
if (!
|
|
24609
|
+
if (!node_fs.existsSync(rojoConfigFilePath)) {
|
|
24573
24610
|
this.warn(`RojoResolver: Path does not exist "${rojoConfigFilePath}"`);
|
|
24574
24611
|
return;
|
|
24575
24612
|
}
|
|
24576
|
-
const realPath =
|
|
24613
|
+
const realPath = this.cachedRealpath(rojoConfigFilePath);
|
|
24577
24614
|
this.walkedConfigFilesInternal.add(realPath);
|
|
24578
24615
|
let configJson;
|
|
24579
24616
|
try {
|
|
24580
|
-
configJson = JSON.parse(
|
|
24617
|
+
configJson = JSON.parse(node_fs.readFileSync(realPath, "utf8"));
|
|
24581
24618
|
} catch {}
|
|
24582
24619
|
if (isValidRojoConfig(configJson)) this.parseTree(node_path.default.dirname(rojoConfigFilePath), configJson.name, configJson.tree, doNotPush);
|
|
24583
24620
|
else this.warn("RojoResolver: Invalid configuration!");
|
|
24584
24621
|
}
|
|
24585
24622
|
parsePath(itemPath) {
|
|
24586
24623
|
const luauPath = convertToLuau(itemPath);
|
|
24587
|
-
const realPath =
|
|
24624
|
+
const realPath = node_fs.existsSync(luauPath) ? this.cachedRealpath(luauPath) : luauPath;
|
|
24588
24625
|
const extension = node_path.default.extname(luauPath);
|
|
24589
24626
|
if (ROJO_MODULE_EXTS.has(extension)) this.filePathToRbxPathMap.set(luauPath, [...this.rbxPath]);
|
|
24590
24627
|
else {
|
|
24591
|
-
const isDirectory =
|
|
24628
|
+
const isDirectory = node_fs.existsSync(realPath) && node_fs.statSync(realPath).isDirectory();
|
|
24592
24629
|
if (isDirectory) this.walkedDirectoriesInternal.add(realPath);
|
|
24593
|
-
if (isDirectory &&
|
|
24630
|
+
if (isDirectory && node_fs.readdirSync(realPath).includes(ROJO_DEFAULT_NAME)) this.parseConfig(node_path.default.join(luauPath, ROJO_DEFAULT_NAME), true);
|
|
24594
24631
|
else {
|
|
24595
24632
|
this.partitions.unshift({
|
|
24596
24633
|
fsPath: luauPath,
|
|
@@ -24607,26 +24644,40 @@ var RojoResolver = class RojoResolver {
|
|
|
24607
24644
|
for (const childName of Object.keys(tree).filter((value) => !value.startsWith("$"))) this.parseTree(basePath, childName, tree[childName]);
|
|
24608
24645
|
if (!doNotPush) this.rbxPath.pop();
|
|
24609
24646
|
}
|
|
24610
|
-
searchChildren(directory,
|
|
24611
|
-
|
|
24612
|
-
|
|
24613
|
-
|
|
24614
|
-
|
|
24615
|
-
|
|
24616
|
-
|
|
24617
|
-
if (
|
|
24647
|
+
searchChildren(directory, directoryEntries) {
|
|
24648
|
+
const projectFiles = new Array();
|
|
24649
|
+
const subDirectories = new Array();
|
|
24650
|
+
for (const entry of directoryEntries) {
|
|
24651
|
+
const childPath = node_path.default.join(directory, entry.name);
|
|
24652
|
+
let isFile = entry.isFile();
|
|
24653
|
+
let isDirectory = entry.isDirectory();
|
|
24654
|
+
if (!isFile && !isDirectory) try {
|
|
24655
|
+
const stat = node_fs.statSync(this.cachedRealpath(childPath));
|
|
24656
|
+
isFile = stat.isFile();
|
|
24657
|
+
isDirectory = stat.isDirectory();
|
|
24658
|
+
} catch (err) {
|
|
24659
|
+
this.warn(`RojoResolver: Failed to resolve "${childPath}" (${err.message})`);
|
|
24660
|
+
continue;
|
|
24661
|
+
}
|
|
24662
|
+
if (isFile && ROJO_FILE_REGEX.test(entry.name)) projectFiles.push(childPath);
|
|
24663
|
+
else if (isDirectory) subDirectories.push({
|
|
24664
|
+
name: entry.name,
|
|
24665
|
+
path: childPath
|
|
24666
|
+
});
|
|
24618
24667
|
}
|
|
24668
|
+
for (const childPath of projectFiles) this.parseConfig(childPath);
|
|
24669
|
+
for (const { name, path: childPath } of subDirectories) this.searchDirectory(childPath, name);
|
|
24619
24670
|
}
|
|
24620
24671
|
searchDirectory(directory, item) {
|
|
24621
|
-
const realPath =
|
|
24672
|
+
const realPath = this.cachedRealpath(directory);
|
|
24622
24673
|
this.walkedDirectoriesInternal.add(realPath);
|
|
24623
|
-
const
|
|
24624
|
-
if (
|
|
24674
|
+
const directoryEntries = node_fs.readdirSync(directory, { withFileTypes: true });
|
|
24675
|
+
if (directoryEntries.some((entry) => entry.name === ROJO_DEFAULT_NAME)) {
|
|
24625
24676
|
this.parseConfig(node_path.default.join(directory, ROJO_DEFAULT_NAME));
|
|
24626
24677
|
return;
|
|
24627
24678
|
}
|
|
24628
24679
|
if (item !== void 0) this.rbxPath.push(item);
|
|
24629
|
-
this.searchChildren(directory,
|
|
24680
|
+
this.searchChildren(directory, directoryEntries);
|
|
24630
24681
|
if (item !== void 0) this.rbxPath.pop();
|
|
24631
24682
|
}
|
|
24632
24683
|
warn(str) {
|
|
@@ -25891,6 +25942,7 @@ function findMapping(filePath, mappings, key = "outDir") {
|
|
|
25891
25942
|
}
|
|
25892
25943
|
function replacePrefix(filePath, from, to) {
|
|
25893
25944
|
if (filePath === from) return to;
|
|
25945
|
+
if (from === ".") return `${to}/${filePath.startsWith("./") ? filePath.slice(2) : filePath}`;
|
|
25894
25946
|
if (filePath.startsWith(`${from}/`)) return `${to}${filePath.slice(from.length)}`;
|
|
25895
25947
|
return filePath;
|
|
25896
25948
|
}
|
|
@@ -26730,7 +26782,7 @@ var core_default = (/* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exp
|
|
|
26730
26782
|
}
|
|
26731
26783
|
});
|
|
26732
26784
|
};
|
|
26733
|
-
var MODES =
|
|
26785
|
+
var MODES = /*#__PURE__*/ Object.freeze({
|
|
26734
26786
|
__proto__: null,
|
|
26735
26787
|
APOS_STRING_MODE,
|
|
26736
26788
|
BACKSLASH_ESCAPE,
|
|
@@ -29027,7 +29079,7 @@ function formatTestSummary(result, timing, styles, options) {
|
|
|
29027
29079
|
}
|
|
29028
29080
|
function formatResult(result, timing, options) {
|
|
29029
29081
|
const styles = createStyles(options.color, options.slowTestThreshold);
|
|
29030
|
-
const lines = [
|
|
29082
|
+
const lines = [""];
|
|
29031
29083
|
for (const file of result.testResults) {
|
|
29032
29084
|
if (options.failuresOnly === true && file.numFailingTests === 0 && !hasExecError(file)) continue;
|
|
29033
29085
|
lines.push(formatFileSummary(file, options, styles));
|
|
@@ -29201,7 +29253,7 @@ function formatMultiProjectResult(projects, timing, options) {
|
|
|
29201
29253
|
result,
|
|
29202
29254
|
styles
|
|
29203
29255
|
}));
|
|
29204
|
-
const lines = [
|
|
29256
|
+
const lines = ["", sections.join("\n\n")];
|
|
29205
29257
|
const mergedResult = mergeJestResults(projects.map((project) => project.result));
|
|
29206
29258
|
lines.push("", formatTestSummary(mergedResult, timing, styles, {
|
|
29207
29259
|
snapshotWriteFailures: options.snapshotWriteFailures,
|
|
@@ -30008,6 +30060,15 @@ function hasFormatter(formatters, name) {
|
|
|
30008
30060
|
function usesAgentFormatter(formatters, verbose = false) {
|
|
30009
30061
|
return hasFormatter(formatters, "agent") && !verbose;
|
|
30010
30062
|
}
|
|
30063
|
+
/**
|
|
30064
|
+
* Whether human-facing progress output (the run header, the "Running X of Y"
|
|
30065
|
+
* notice, the workspace streaming lines) should be written: not silent and not
|
|
30066
|
+
* a machine-readable formatter (json, or non-verbose agent). The single source
|
|
30067
|
+
* of truth so these sinks can't drift apart.
|
|
30068
|
+
*/
|
|
30069
|
+
function isDefaultHumanFormatter(options) {
|
|
30070
|
+
return options.silent !== true && !usesAgentFormatter(options.formatters, options.verbose) && !hasFormatter(options.formatters, "json");
|
|
30071
|
+
}
|
|
30011
30072
|
//#endregion
|
|
30012
30073
|
//#region src/snapshot/path-resolver.ts
|
|
30013
30074
|
function createSnapshotPathResolver(config) {
|
|
@@ -30020,7 +30081,7 @@ function createSnapshotPathResolver(config) {
|
|
|
30020
30081
|
const result = `${basePath}/${normalized.slice(prefix.length + 1)}`;
|
|
30021
30082
|
const mapping = findMapping(result, tsconfigMappings);
|
|
30022
30083
|
if (mapping !== void 0) return {
|
|
30023
|
-
filePath: replacePrefix(result, mapping.outDir, mapping.rootDir)
|
|
30084
|
+
filePath: replacePrefix(result, mapping.outDir, mapping.rootDir),
|
|
30024
30085
|
mapping
|
|
30025
30086
|
};
|
|
30026
30087
|
return { filePath: result };
|
|
@@ -30040,6 +30101,96 @@ function buildMappings(tree, prefix) {
|
|
|
30040
30101
|
return mappings;
|
|
30041
30102
|
}
|
|
30042
30103
|
//#endregion
|
|
30104
|
+
//#region src/timing/orchestration-collector.ts
|
|
30105
|
+
/**
|
|
30106
|
+
* A buffered span-tree profiler for a single, sequential host run. Nesting is
|
|
30107
|
+
* tracked with one shared stack, so spans must open and close in LIFO order:
|
|
30108
|
+
* profile a phase, and any spans it opens nest under it. It is NOT safe to run
|
|
30109
|
+
* two `profile` / `profileAsync` calls concurrently on the same collector (e.g.
|
|
30110
|
+
* `Promise.all`) — interleaved opens/closes would corrupt the stack. Create one
|
|
30111
|
+
* collector per run; `flushTimingReport` empties it so a second flush is a
|
|
30112
|
+
* no-op.
|
|
30113
|
+
*/
|
|
30114
|
+
function createTimingCollector(options = {}) {
|
|
30115
|
+
const clock = options.clock ?? { now: () => node_perf_hooks.performance.now() };
|
|
30116
|
+
const sink = options.sink ?? ((line) => void node_process.default.stderr.write(`${line}\n`));
|
|
30117
|
+
const enabled = options.enabled ?? node_process.default.env["TIMING"] !== void 0;
|
|
30118
|
+
const roots = /* @__PURE__ */ new Map();
|
|
30119
|
+
const stack = [];
|
|
30120
|
+
function open(name) {
|
|
30121
|
+
const top = stack.at(-1);
|
|
30122
|
+
const node = childOf(top === void 0 ? roots : top.children, name);
|
|
30123
|
+
stack.push(node);
|
|
30124
|
+
const start = clock.now();
|
|
30125
|
+
return () => {
|
|
30126
|
+
node.elapsedMs += clock.now() - start;
|
|
30127
|
+
stack.pop();
|
|
30128
|
+
};
|
|
30129
|
+
}
|
|
30130
|
+
function profile(name, func) {
|
|
30131
|
+
if (!enabled) return func();
|
|
30132
|
+
const close = open(name);
|
|
30133
|
+
try {
|
|
30134
|
+
return func();
|
|
30135
|
+
} finally {
|
|
30136
|
+
close();
|
|
30137
|
+
}
|
|
30138
|
+
}
|
|
30139
|
+
async function profileAsync(name, func) {
|
|
30140
|
+
if (!enabled) return func();
|
|
30141
|
+
const close = open(name);
|
|
30142
|
+
try {
|
|
30143
|
+
return await func();
|
|
30144
|
+
} finally {
|
|
30145
|
+
close();
|
|
30146
|
+
}
|
|
30147
|
+
}
|
|
30148
|
+
function record(name, elapsedMs) {
|
|
30149
|
+
if (!enabled) return;
|
|
30150
|
+
const top = stack.at(-1);
|
|
30151
|
+
const node = childOf(top === void 0 ? roots : top.children, name);
|
|
30152
|
+
node.elapsedMs += elapsedMs;
|
|
30153
|
+
}
|
|
30154
|
+
function emit(node, depth) {
|
|
30155
|
+
sink(`[TIMING] ${" ".repeat(depth)}${node.name}: ${String(Math.round(node.elapsedMs))}ms`);
|
|
30156
|
+
for (const child of node.children.values()) emit(child, depth + 1);
|
|
30157
|
+
}
|
|
30158
|
+
function flushTimingReport() {
|
|
30159
|
+
if (!enabled || roots.size === 0) return;
|
|
30160
|
+
let total = 0;
|
|
30161
|
+
for (const node of roots.values()) {
|
|
30162
|
+
emit(node, 0);
|
|
30163
|
+
total += Math.round(node.elapsedMs);
|
|
30164
|
+
}
|
|
30165
|
+
sink(`[TIMING] TOTAL (host): ${String(total)}ms`);
|
|
30166
|
+
roots.clear();
|
|
30167
|
+
}
|
|
30168
|
+
return {
|
|
30169
|
+
flushTimingReport,
|
|
30170
|
+
profile,
|
|
30171
|
+
profileAsync,
|
|
30172
|
+
record
|
|
30173
|
+
};
|
|
30174
|
+
}
|
|
30175
|
+
/**
|
|
30176
|
+
* Shared disabled collector for callers that thread a profiler through their
|
|
30177
|
+
* signatures but are invoked outside a profiled workspace run (single-mode
|
|
30178
|
+
* coverage, the `instrument` subcommand, tests). Every method is a no-op.
|
|
30179
|
+
*/
|
|
30180
|
+
const NOOP_TIMING_COLLECTOR = createTimingCollector({ enabled: false });
|
|
30181
|
+
function childOf(parent, name) {
|
|
30182
|
+
let node = parent.get(name);
|
|
30183
|
+
if (node === void 0) {
|
|
30184
|
+
node = {
|
|
30185
|
+
name,
|
|
30186
|
+
children: /* @__PURE__ */ new Map(),
|
|
30187
|
+
elapsedMs: 0
|
|
30188
|
+
};
|
|
30189
|
+
parent.set(name, node);
|
|
30190
|
+
}
|
|
30191
|
+
return node;
|
|
30192
|
+
}
|
|
30193
|
+
//#endregion
|
|
30043
30194
|
//#region src/types/rojo.ts
|
|
30044
30195
|
const rojoProjectSchema = type({
|
|
30045
30196
|
"name": "string",
|
|
@@ -30189,38 +30340,48 @@ function formatExecuteOutput(options) {
|
|
|
30189
30340
|
* config.
|
|
30190
30341
|
*/
|
|
30191
30342
|
async function runProjects(options) {
|
|
30192
|
-
const
|
|
30193
|
-
const
|
|
30194
|
-
|
|
30195
|
-
|
|
30196
|
-
|
|
30197
|
-
|
|
30198
|
-
|
|
30343
|
+
const timing = options.timing ?? NOOP_TIMING_COLLECTOR;
|
|
30344
|
+
const jobs = timing.profile("buildJobs", () => {
|
|
30345
|
+
return options.projects.map((project) => buildProjectJob(project, timing));
|
|
30346
|
+
});
|
|
30347
|
+
const { rawResults, timing: backendTiming } = await timing.profileAsync("backend.runTests", async () => {
|
|
30348
|
+
const result = await options.backend.runTests({
|
|
30349
|
+
jobs,
|
|
30350
|
+
parallel: options.parallel,
|
|
30351
|
+
scriptOverride: options.scriptOverride,
|
|
30352
|
+
streaming: options.streaming,
|
|
30353
|
+
workStealing: options.workStealing
|
|
30354
|
+
});
|
|
30355
|
+
recordBackendTimingSpans(timing, result.timing);
|
|
30356
|
+
return result;
|
|
30199
30357
|
});
|
|
30200
30358
|
if (rawResults.length !== jobs.length) throw new Error(`Backend returned ${rawResults.length.toString()} results for ${jobs.length.toString()} jobs — rawResults must be parallel to jobs`);
|
|
30201
30359
|
return {
|
|
30202
30360
|
backendTiming,
|
|
30203
|
-
results:
|
|
30204
|
-
|
|
30205
|
-
|
|
30206
|
-
|
|
30207
|
-
|
|
30208
|
-
|
|
30209
|
-
|
|
30210
|
-
|
|
30211
|
-
|
|
30212
|
-
|
|
30213
|
-
|
|
30214
|
-
|
|
30215
|
-
|
|
30216
|
-
|
|
30217
|
-
|
|
30218
|
-
|
|
30219
|
-
|
|
30220
|
-
|
|
30221
|
-
|
|
30222
|
-
|
|
30223
|
-
|
|
30361
|
+
results: timing.profile("processResults", () => {
|
|
30362
|
+
return rawResults.map((raw, index) => {
|
|
30363
|
+
const job = jobs[index];
|
|
30364
|
+
try {
|
|
30365
|
+
return processProjectResult(buildProjectResult(raw.entry, job, raw.fallbackGameOutput), {
|
|
30366
|
+
backendTiming,
|
|
30367
|
+
config: job.config,
|
|
30368
|
+
deferFormatting: options.deferFormatting,
|
|
30369
|
+
startTime: options.startTime,
|
|
30370
|
+
timing,
|
|
30371
|
+
version: options.version
|
|
30372
|
+
});
|
|
30373
|
+
} catch (err) {
|
|
30374
|
+
if (!(err instanceof LuauScriptError)) throw err;
|
|
30375
|
+
return buildExecutionErrorResult({
|
|
30376
|
+
backendTiming,
|
|
30377
|
+
config: job.config,
|
|
30378
|
+
deferFormatting: options.deferFormatting,
|
|
30379
|
+
error: err,
|
|
30380
|
+
startTime: options.startTime,
|
|
30381
|
+
version: options.version
|
|
30382
|
+
});
|
|
30383
|
+
}
|
|
30384
|
+
});
|
|
30224
30385
|
})
|
|
30225
30386
|
};
|
|
30226
30387
|
}
|
|
@@ -30261,6 +30422,10 @@ function parseTsconfigMappings(options) {
|
|
|
30261
30422
|
rootDir: normalizeDirectoryPath(options.rootDir ?? "src")
|
|
30262
30423
|
}];
|
|
30263
30424
|
}
|
|
30425
|
+
function recordBackendTimingSpans(timing, backendTiming) {
|
|
30426
|
+
if (backendTiming.uploadMs !== void 0) timing.record("uploadMs", backendTiming.uploadMs);
|
|
30427
|
+
timing.record("executionMs", backendTiming.executionMs);
|
|
30428
|
+
}
|
|
30264
30429
|
const EXIT_CODE_MESSAGE$1 = /^Exited with code: \d+$/;
|
|
30265
30430
|
/**
|
|
30266
30431
|
* Compose the human-readable failure message for an exec-error file
|
|
@@ -30461,7 +30626,7 @@ function writeSnapshots(snapshotWrites, config, tsconfigMappings) {
|
|
|
30461
30626
|
node_fs.writeFileSync(absolutePath, content);
|
|
30462
30627
|
const { filePath, mapping } = resolved;
|
|
30463
30628
|
if (mapping !== void 0) {
|
|
30464
|
-
const outPath = mapping.
|
|
30629
|
+
const outPath = replacePrefix(filePath, mapping.rootDir, mapping.outDir);
|
|
30465
30630
|
const absoluteOutPath = node_path.resolve(config.rootDir, outPath);
|
|
30466
30631
|
node_fs.mkdirSync(node_path.dirname(absoluteOutPath), { recursive: true });
|
|
30467
30632
|
node_fs.writeFileSync(absoluteOutPath, content);
|
|
@@ -30490,19 +30655,23 @@ function writeSnapshots(snapshotWrites, config, tsconfigMappings) {
|
|
|
30490
30655
|
* formatter output. Called once per job.
|
|
30491
30656
|
*/
|
|
30492
30657
|
function processProjectResult(entry, options) {
|
|
30493
|
-
const { backendTiming, config, deferFormatting, startTime, version } = options;
|
|
30658
|
+
const { backendTiming, config, deferFormatting, startTime, timing, version } = options;
|
|
30494
30659
|
const { coverageData, gameOutput, luauTiming, result, setupMs, snapshotWrites } = entry;
|
|
30495
|
-
const tsconfigMappings =
|
|
30496
|
-
|
|
30660
|
+
const tsconfigMappings = timing.profile("resolveTsconfigMappings", () => {
|
|
30661
|
+
return resolveAllTsconfigMappings(config.rootDir);
|
|
30662
|
+
});
|
|
30663
|
+
const writeCounts = snapshotWrites !== void 0 ? timing.profile("writeSnapshots", () => {
|
|
30664
|
+
return writeSnapshots(snapshotWrites, config, tsconfigMappings);
|
|
30665
|
+
}) : {
|
|
30497
30666
|
attempted: 0,
|
|
30498
30667
|
failed: 0,
|
|
30499
30668
|
written: 0
|
|
30500
30669
|
};
|
|
30501
30670
|
const testsMs = calculateTestsMs(result.testResults);
|
|
30502
|
-
const sourceMapper = config.sourceMap ? buildSourceMapper(config, tsconfigMappings) : void 0;
|
|
30671
|
+
const sourceMapper = config.sourceMap ? timing.profile("buildSourceMapper", () => buildSourceMapper(config, tsconfigMappings)) : void 0;
|
|
30503
30672
|
resolveTestFilePaths(result, sourceMapper);
|
|
30504
30673
|
const totalMs = Date.now() - startTime;
|
|
30505
|
-
const
|
|
30674
|
+
const resultTiming = {
|
|
30506
30675
|
executionMs: backendTiming.executionMs,
|
|
30507
30676
|
setupMs,
|
|
30508
30677
|
startTime,
|
|
@@ -30515,7 +30684,7 @@ function processProjectResult(entry, options) {
|
|
|
30515
30684
|
result,
|
|
30516
30685
|
snapshotWriteFailures: writeCounts.failed,
|
|
30517
30686
|
sourceMapper,
|
|
30518
|
-
timing,
|
|
30687
|
+
timing: resultTiming,
|
|
30519
30688
|
version
|
|
30520
30689
|
}) : "";
|
|
30521
30690
|
if (luauTiming !== void 0) printLuauTiming(luauTiming);
|
|
@@ -30527,7 +30696,7 @@ function processProjectResult(entry, options) {
|
|
|
30527
30696
|
result,
|
|
30528
30697
|
snapshotWriteFailures: writeCounts.failed > 0 ? writeCounts.failed : void 0,
|
|
30529
30698
|
sourceMapper,
|
|
30530
|
-
timing
|
|
30699
|
+
timing: resultTiming
|
|
30531
30700
|
};
|
|
30532
30701
|
}
|
|
30533
30702
|
/**
|
|
@@ -30535,8 +30704,10 @@ function processProjectResult(entry, options) {
|
|
|
30535
30704
|
* carries its own config so the Luau runner never re-resolves or shares format
|
|
30536
30705
|
* state across projects (fixes the spike's snapshot-diff regression — C1).
|
|
30537
30706
|
*/
|
|
30538
|
-
function buildProjectJob(parameters) {
|
|
30539
|
-
const tsconfigMappings =
|
|
30707
|
+
function buildProjectJob(parameters, timing) {
|
|
30708
|
+
const tsconfigMappings = timing.profile("resolveTsconfigMappings", () => {
|
|
30709
|
+
return resolveAllTsconfigMappings(parameters.config.rootDir);
|
|
30710
|
+
});
|
|
30540
30711
|
const luauProject = isLuauProject(parameters.testFiles, tsconfigMappings);
|
|
30541
30712
|
return {
|
|
30542
30713
|
config: applySnapshotFormatDefaults(parameters.config, luauProject),
|
|
@@ -37331,7 +37502,10 @@ var OcaleRunner = class {
|
|
|
37331
37502
|
script,
|
|
37332
37503
|
timeoutSeconds,
|
|
37333
37504
|
universeId: this.credentials.universeId
|
|
37334
|
-
}, {
|
|
37505
|
+
}, {
|
|
37506
|
+
retryableTransportCodes: TRANSIENT_TRANSPORT_CODES,
|
|
37507
|
+
timeoutMs: timeout
|
|
37508
|
+
});
|
|
37335
37509
|
if (!result.success) {
|
|
37336
37510
|
if (result.err instanceof PollTimeoutError) throw new Error("Execution timed out", { cause: result.err });
|
|
37337
37511
|
throw new Error(result.err.message, { cause: result.err });
|
|
@@ -37353,7 +37527,7 @@ var OcaleRunner = class {
|
|
|
37353
37527
|
placeId: this.credentials.placeId,
|
|
37354
37528
|
universeId: this.credentials.universeId
|
|
37355
37529
|
};
|
|
37356
|
-
const result = await this.places.save(parameters);
|
|
37530
|
+
const result = await this.places.save(parameters, { retryableTransportCodes: TRANSIENT_TRANSPORT_CODES });
|
|
37357
37531
|
if (!result.success) throw new Error(`Failed to upload place: ${result.err.message}`, { cause: result.err });
|
|
37358
37532
|
return {
|
|
37359
37533
|
uploadMs: Date.now() - uploadStart,
|
|
@@ -37471,6 +37645,35 @@ function generateTestScript(options) {
|
|
|
37471
37645
|
return test_runner_bundled_default.replace("__CONFIG_JSON__", () => JSON.stringify({ configs }));
|
|
37472
37646
|
}
|
|
37473
37647
|
//#endregion
|
|
37648
|
+
//#region src/utils/error-chain.ts
|
|
37649
|
+
const MAX_DEPTH = 5;
|
|
37650
|
+
function walkErrorChain(err) {
|
|
37651
|
+
const entries = [];
|
|
37652
|
+
let current = err;
|
|
37653
|
+
while (current instanceof Error && entries.length < MAX_DEPTH) {
|
|
37654
|
+
entries.push({
|
|
37655
|
+
name: current.constructor.name,
|
|
37656
|
+
code: readStringProperty(current, "code"),
|
|
37657
|
+
errno: readStringProperty(current, "errno"),
|
|
37658
|
+
message: current.message,
|
|
37659
|
+
requiredScopes: current instanceof PermissionError ? current.requiredScopes : void 0,
|
|
37660
|
+
syscall: readStringProperty(current, "syscall")
|
|
37661
|
+
});
|
|
37662
|
+
current = current.cause;
|
|
37663
|
+
}
|
|
37664
|
+
return entries;
|
|
37665
|
+
}
|
|
37666
|
+
function formatMissingScopes(scopes) {
|
|
37667
|
+
if (scopes.length === 0) return "API key has insufficient scopes. Add via Creator Dashboard.";
|
|
37668
|
+
const joined = scopes.join(", ");
|
|
37669
|
+
return `API key missing scope${scopes.length === 1 ? "" : "s"} ${joined}. Add via Creator Dashboard.`;
|
|
37670
|
+
}
|
|
37671
|
+
function readStringProperty(err, key) {
|
|
37672
|
+
const value = Reflect.get(err, key);
|
|
37673
|
+
if (value === void 0 || value === null) return;
|
|
37674
|
+
return String(value);
|
|
37675
|
+
}
|
|
37676
|
+
//#endregion
|
|
37474
37677
|
//#region src/backends/open-cloud.ts
|
|
37475
37678
|
const PARALLEL_AUTO_CAP = 3;
|
|
37476
37679
|
const BASE_URL_ENV = "JEST_ROBLOX_OPEN_CLOUD_BASE_URL";
|
|
@@ -37606,10 +37809,7 @@ function createOpenCloudBackend(credentials) {
|
|
|
37606
37809
|
}
|
|
37607
37810
|
function describeError(err) {
|
|
37608
37811
|
const cause = err instanceof Error ? err.cause : void 0;
|
|
37609
|
-
if (cause instanceof PermissionError)
|
|
37610
|
-
const scopes = cause.requiredScopes.join(", ");
|
|
37611
|
-
return `API key missing scope${cause.requiredScopes.length === 1 ? "" : "s"} ${scopes}. Add via Creator Dashboard.`;
|
|
37612
|
-
}
|
|
37812
|
+
if (cause instanceof PermissionError) return formatMissingScopes(cause.requiredScopes);
|
|
37613
37813
|
return err instanceof Error ? err.message : String(err);
|
|
37614
37814
|
}
|
|
37615
37815
|
function warnStreamingDisabled(err, state) {
|
|
@@ -38382,6 +38582,27 @@ function narrowConfigByFiles(config, files) {
|
|
|
38382
38582
|
testPathPattern: `(${branches.join("|")})`
|
|
38383
38583
|
};
|
|
38384
38584
|
}
|
|
38585
|
+
/**
|
|
38586
|
+
* Forward an Instance-namespace `testPathPattern` to the Luau runner.
|
|
38587
|
+
*
|
|
38588
|
+
* Node-side discovery is the source of truth: the FS-namespace filter
|
|
38589
|
+
* (positional args or `--testPathPattern`) has already resolved to a concrete
|
|
38590
|
+
* file set against real paths. Drop the raw FS-shaped pattern and re-narrow by
|
|
38591
|
+
* the discovered files so Jest-on-Roblox matches the same files — its paths are
|
|
38592
|
+
* Roblox Instance names (e.g. `ServerScriptService/...`) with no `src/` prefix,
|
|
38593
|
+
* so a raw FS pattern like `src/server/foo.spec` matches zero files there.
|
|
38594
|
+
*
|
|
38595
|
+
* `filterActive` gates the rewrite: a bare run (no positionals, no
|
|
38596
|
+
* `testPathPattern`) leaves the config untouched so the Luau side runs every
|
|
38597
|
+
* `testMatch` file rather than a giant basename alternation.
|
|
38598
|
+
*/
|
|
38599
|
+
function narrowForLuauRun(config, runtimeFiles, filterActive) {
|
|
38600
|
+
if (!filterActive) return config;
|
|
38601
|
+
return narrowConfigByFiles({
|
|
38602
|
+
...config,
|
|
38603
|
+
testPathPattern: void 0
|
|
38604
|
+
}, runtimeFiles);
|
|
38605
|
+
}
|
|
38385
38606
|
function toBasenamePattern(file) {
|
|
38386
38607
|
const posix = file.replaceAll("\\", "/");
|
|
38387
38608
|
const lastSlash = posix.lastIndexOf("/");
|
|
@@ -38915,89 +39136,6 @@ function buildWithRojo(projectPath, outputPath) {
|
|
|
38915
39136
|
}
|
|
38916
39137
|
}
|
|
38917
39138
|
//#endregion
|
|
38918
|
-
//#region src/timing/orchestration-collector.ts
|
|
38919
|
-
/**
|
|
38920
|
-
* A buffered span-tree profiler for a single, sequential host run. Nesting is
|
|
38921
|
-
* tracked with one shared stack, so spans must open and close in LIFO order:
|
|
38922
|
-
* profile a phase, and any spans it opens nest under it. It is NOT safe to run
|
|
38923
|
-
* two `profile` / `profileAsync` calls concurrently on the same collector (e.g.
|
|
38924
|
-
* `Promise.all`) — interleaved opens/closes would corrupt the stack. Create one
|
|
38925
|
-
* collector per run; `flushTimingReport` empties it so a second flush is a
|
|
38926
|
-
* no-op.
|
|
38927
|
-
*/
|
|
38928
|
-
function createTimingCollector(options = {}) {
|
|
38929
|
-
const clock = options.clock ?? { now: () => node_perf_hooks.performance.now() };
|
|
38930
|
-
const sink = options.sink ?? ((line) => void node_process.default.stderr.write(`${line}\n`));
|
|
38931
|
-
const enabled = options.enabled ?? node_process.default.env["TIMING"] !== void 0;
|
|
38932
|
-
const roots = /* @__PURE__ */ new Map();
|
|
38933
|
-
const stack = [];
|
|
38934
|
-
function open(name) {
|
|
38935
|
-
const top = stack.at(-1);
|
|
38936
|
-
const node = childOf(top === void 0 ? roots : top.children, name);
|
|
38937
|
-
stack.push(node);
|
|
38938
|
-
const start = clock.now();
|
|
38939
|
-
return () => {
|
|
38940
|
-
node.elapsedMs += clock.now() - start;
|
|
38941
|
-
stack.pop();
|
|
38942
|
-
};
|
|
38943
|
-
}
|
|
38944
|
-
function profile(name, func) {
|
|
38945
|
-
if (!enabled) return func();
|
|
38946
|
-
const close = open(name);
|
|
38947
|
-
try {
|
|
38948
|
-
return func();
|
|
38949
|
-
} finally {
|
|
38950
|
-
close();
|
|
38951
|
-
}
|
|
38952
|
-
}
|
|
38953
|
-
async function profileAsync(name, func) {
|
|
38954
|
-
if (!enabled) return func();
|
|
38955
|
-
const close = open(name);
|
|
38956
|
-
try {
|
|
38957
|
-
return await func();
|
|
38958
|
-
} finally {
|
|
38959
|
-
close();
|
|
38960
|
-
}
|
|
38961
|
-
}
|
|
38962
|
-
function emit(node, depth) {
|
|
38963
|
-
sink(`[TIMING] ${" ".repeat(depth)}${node.name}: ${String(Math.round(node.elapsedMs))}ms`);
|
|
38964
|
-
for (const child of node.children.values()) emit(child, depth + 1);
|
|
38965
|
-
}
|
|
38966
|
-
function flushTimingReport() {
|
|
38967
|
-
if (!enabled || roots.size === 0) return;
|
|
38968
|
-
let total = 0;
|
|
38969
|
-
for (const node of roots.values()) {
|
|
38970
|
-
emit(node, 0);
|
|
38971
|
-
total += Math.round(node.elapsedMs);
|
|
38972
|
-
}
|
|
38973
|
-
sink(`[TIMING] TOTAL (host): ${String(total)}ms`);
|
|
38974
|
-
roots.clear();
|
|
38975
|
-
}
|
|
38976
|
-
return {
|
|
38977
|
-
flushTimingReport,
|
|
38978
|
-
profile,
|
|
38979
|
-
profileAsync
|
|
38980
|
-
};
|
|
38981
|
-
}
|
|
38982
|
-
/**
|
|
38983
|
-
* Shared disabled collector for callers that thread a profiler through their
|
|
38984
|
-
* signatures but are invoked outside a profiled workspace run (single-mode
|
|
38985
|
-
* coverage, the `instrument` subcommand, tests). Every method is a no-op.
|
|
38986
|
-
*/
|
|
38987
|
-
const NOOP_TIMING_COLLECTOR = createTimingCollector({ enabled: false });
|
|
38988
|
-
function childOf(parent, name) {
|
|
38989
|
-
let node = parent.get(name);
|
|
38990
|
-
if (node === void 0) {
|
|
38991
|
-
node = {
|
|
38992
|
-
name,
|
|
38993
|
-
children: /* @__PURE__ */ new Map(),
|
|
38994
|
-
elapsedMs: 0
|
|
38995
|
-
};
|
|
38996
|
-
parent.set(name, node);
|
|
38997
|
-
}
|
|
38998
|
-
return node;
|
|
38999
|
-
}
|
|
39000
|
-
//#endregion
|
|
39001
39139
|
//#region src/utils/hash.ts
|
|
39002
39140
|
function hashBuffer(data) {
|
|
39003
39141
|
return (0, node_crypto.createHash)("sha256").update(data).digest("hex");
|
|
@@ -40472,15 +40610,47 @@ function classifyTestFiles(files, config) {
|
|
|
40472
40610
|
typeTestFiles
|
|
40473
40611
|
};
|
|
40474
40612
|
}
|
|
40613
|
+
function resolveAllSetupFilePaths(configs) {
|
|
40614
|
+
const resolvers = /* @__PURE__ */ new Map();
|
|
40615
|
+
for (const config of configs) {
|
|
40616
|
+
if (config.setupFiles === void 0 && config.setupFilesAfterEnv === void 0) continue;
|
|
40617
|
+
const rojoConfigPath = node_path.resolve(config.rootDir, config.rojoProject ?? DEFAULT_ROJO_PROJECT$1);
|
|
40618
|
+
const key = JSON.stringify([config.rootDir, rojoConfigPath]);
|
|
40619
|
+
let resolve = resolvers.get(key);
|
|
40620
|
+
if (resolve === void 0) {
|
|
40621
|
+
resolve = createSetupResolver({
|
|
40622
|
+
configDirectory: config.rootDir,
|
|
40623
|
+
rojoConfigPath
|
|
40624
|
+
});
|
|
40625
|
+
resolvers.set(key, resolve);
|
|
40626
|
+
}
|
|
40627
|
+
if (config.setupFiles !== void 0) config.setupFiles = config.setupFiles.map(resolve);
|
|
40628
|
+
if (config.setupFilesAfterEnv !== void 0) config.setupFilesAfterEnv = config.setupFilesAfterEnv.map(resolve);
|
|
40629
|
+
}
|
|
40630
|
+
}
|
|
40475
40631
|
function resolveSetupFilePaths(config) {
|
|
40476
|
-
|
|
40477
|
-
|
|
40478
|
-
|
|
40479
|
-
|
|
40480
|
-
|
|
40481
|
-
|
|
40482
|
-
|
|
40483
|
-
|
|
40632
|
+
resolveAllSetupFilePaths([config]);
|
|
40633
|
+
}
|
|
40634
|
+
//#endregion
|
|
40635
|
+
//#region src/run/run-header.ts
|
|
40636
|
+
/**
|
|
40637
|
+
* Print the ` RUN vX.Y <rootDir>` header to stdout at the moment a run begins
|
|
40638
|
+
* (right before the backend uploads), so the CLI doesn't look stalled while it
|
|
40639
|
+
* waits for remote results. The end-of-run formatters no longer emit it.
|
|
40640
|
+
*
|
|
40641
|
+
* Self-gates to the default human formatter: nothing is written under
|
|
40642
|
+
* `--silent`, `--formatters json`, or `--formatters agent` (without
|
|
40643
|
+
* `--verbose`), which produce machine-readable output that must stay clean.
|
|
40644
|
+
*/
|
|
40645
|
+
function emitRunHeader(input) {
|
|
40646
|
+
if (!isDefaultHumanFormatter(input)) return;
|
|
40647
|
+
node_process.default.stdout.write(formatRunHeader({
|
|
40648
|
+
collectCoverage: input.collectCoverage,
|
|
40649
|
+
color: input.color,
|
|
40650
|
+
rootDir: input.rootDir,
|
|
40651
|
+
verbose: input.verbose ?? false,
|
|
40652
|
+
version: input.version
|
|
40653
|
+
}));
|
|
40484
40654
|
}
|
|
40485
40655
|
//#endregion
|
|
40486
40656
|
//#region src/run/multi.ts
|
|
@@ -40488,30 +40658,61 @@ const DEFAULT_ROJO_PROJECT = "default.project.json";
|
|
|
40488
40658
|
const VERSION$3 = version;
|
|
40489
40659
|
async function runMultiProject(options) {
|
|
40490
40660
|
const { cli, config: rootConfig, rawProjects } = options;
|
|
40491
|
-
const
|
|
40492
|
-
|
|
40493
|
-
const
|
|
40661
|
+
const timing = options.timing ?? NOOP_TIMING_COLLECTOR;
|
|
40662
|
+
const rojoTree = timing.profile("loadRojoTree", () => loadRojoTree(rootConfig));
|
|
40663
|
+
const allProjects = await timing.profileAsync("resolveAllProjects", async () => {
|
|
40664
|
+
return resolveAllProjects(rawProjects, rootConfig, rojoTree, rootConfig.rootDir);
|
|
40665
|
+
});
|
|
40666
|
+
timing.profile("resolveSetupFilePaths", () => {
|
|
40667
|
+
resolveAllSetupFilePaths(allProjects.map((project) => project.config));
|
|
40668
|
+
});
|
|
40669
|
+
const { filesByProject, projects } = timing.profile("selectProjects", () => {
|
|
40670
|
+
return selectProjects(allProjects, cli.project, cli.files, rootConfig.rootDir);
|
|
40671
|
+
});
|
|
40494
40672
|
const cacheRoot = node_path.resolve(rootConfig.rootDir, ".jest-roblox", "cache");
|
|
40495
|
-
const cleaned = cleanLeftoverStubs
|
|
40673
|
+
const cleaned = timing.profile("cleanLeftoverStubs", () => {
|
|
40674
|
+
return cleanLeftoverStubs(projects, rootConfig.rootDir);
|
|
40675
|
+
});
|
|
40496
40676
|
if (cleaned.length > 0) node_process.default.stderr.write(`jest-roblox: cleaned ${String(cleaned.length)} leftover stub(s):\n${cleaned.map((stubPath) => ` ${stubPath}\n`).join("")}`);
|
|
40497
|
-
generateProjectStubs
|
|
40498
|
-
|
|
40499
|
-
|
|
40677
|
+
timing.profile("generateProjectStubs", () => {
|
|
40678
|
+
generateProjectStubs(projects, rootConfig.rootDir, cacheRoot);
|
|
40679
|
+
});
|
|
40680
|
+
const { effectiveConfig, preCoverageMs } = timing.profile("prepareCoverage", () => {
|
|
40681
|
+
return prepareMultiProjectCoverage(rootConfig, projects, cacheRoot);
|
|
40682
|
+
});
|
|
40683
|
+
const backend = await timing.profileAsync("resolveBackend", async () => {
|
|
40684
|
+
return resolveBackend(cli, effectiveConfig);
|
|
40685
|
+
});
|
|
40500
40686
|
const parallel = effectiveParallelForBackend(effectiveConfig.parallel, backend);
|
|
40501
|
-
if (!rootConfig.collectCoverage && backend.kind === "open-cloud") buildOpenCloudPlace
|
|
40502
|
-
|
|
40503
|
-
cliFiles: cli.files,
|
|
40504
|
-
effectivePlaceFile: effectiveConfig.placeFile,
|
|
40505
|
-
filesByProject,
|
|
40506
|
-
projects,
|
|
40507
|
-
rootConfig
|
|
40687
|
+
if (!rootConfig.collectCoverage && backend.kind === "open-cloud") timing.profile("buildOpenCloudPlace", () => {
|
|
40688
|
+
buildOpenCloudPlace(rootConfig, projects, cacheRoot);
|
|
40508
40689
|
});
|
|
40509
|
-
const
|
|
40510
|
-
|
|
40511
|
-
|
|
40512
|
-
|
|
40690
|
+
const { allTypeTestFiles, pendingJobs } = timing.profile("collectPendingJobs", () => {
|
|
40691
|
+
return collectPendingJobs({
|
|
40692
|
+
cliFiles: cli.files,
|
|
40693
|
+
effectivePlaceFile: effectiveConfig.placeFile,
|
|
40694
|
+
filesByProject,
|
|
40695
|
+
projects,
|
|
40696
|
+
rootConfig
|
|
40697
|
+
});
|
|
40698
|
+
});
|
|
40699
|
+
if (pendingJobs.length > 0) emitRunHeader({
|
|
40700
|
+
collectCoverage: rootConfig.collectCoverage,
|
|
40701
|
+
color: rootConfig.color,
|
|
40702
|
+
formatters: rootConfig.formatters,
|
|
40513
40703
|
rootDir: rootConfig.rootDir,
|
|
40514
|
-
|
|
40704
|
+
silent: rootConfig.silent,
|
|
40705
|
+
verbose: rootConfig.verbose,
|
|
40706
|
+
version: VERSION$3
|
|
40707
|
+
});
|
|
40708
|
+
const projectResults = await runJobs(backend, pendingJobs, parallel, timing);
|
|
40709
|
+
const uniqueTypeTestFiles = [...new Set(allTypeTestFiles)];
|
|
40710
|
+
const typecheckResult = uniqueTypeTestFiles.length > 0 ? timing.profile("runTypecheck", () => {
|
|
40711
|
+
return runTypecheck({
|
|
40712
|
+
files: uniqueTypeTestFiles,
|
|
40713
|
+
rootDir: rootConfig.rootDir,
|
|
40714
|
+
tsconfig: rootConfig.typecheckTsconfig
|
|
40715
|
+
});
|
|
40515
40716
|
}) : void 0;
|
|
40516
40717
|
if (projectResults.length === 0 && typecheckResult === void 0) {
|
|
40517
40718
|
if (rootConfig.passWithNoTests) return {
|
|
@@ -40575,10 +40776,11 @@ function collectPendingJobs(arguments_) {
|
|
|
40575
40776
|
testMatch: project.include
|
|
40576
40777
|
};
|
|
40577
40778
|
const { runtimeFiles, typeTestFiles } = classifyTestFiles(discoverTestFiles(discoveryConfig, projectCliFiles).files, rootConfig);
|
|
40578
|
-
const
|
|
40779
|
+
const filterActive = (projectCliFiles?.length ?? 0) > 0 || discoveryConfig.testPathPattern !== void 0;
|
|
40780
|
+
const projConfig = narrowForLuauRun({
|
|
40579
40781
|
...discoveryConfig,
|
|
40580
40782
|
testMatch: project.testMatch
|
|
40581
|
-
},
|
|
40783
|
+
}, runtimeFiles, filterActive);
|
|
40582
40784
|
allTypeTestFiles.push(...typeTestFiles);
|
|
40583
40785
|
if (runtimeFiles.length === 0) continue;
|
|
40584
40786
|
const runtimeInjectionPaths = [];
|
|
@@ -40599,28 +40801,31 @@ function collectPendingJobs(arguments_) {
|
|
|
40599
40801
|
pendingJobs
|
|
40600
40802
|
};
|
|
40601
40803
|
}
|
|
40602
|
-
async function runJobs(backend, pendingJobs, parallel) {
|
|
40804
|
+
async function runJobs(backend, pendingJobs, parallel, timing) {
|
|
40603
40805
|
if (pendingJobs.length === 0) {
|
|
40604
40806
|
await backend.close?.();
|
|
40605
40807
|
return [];
|
|
40606
40808
|
}
|
|
40607
40809
|
let runResult;
|
|
40608
40810
|
try {
|
|
40609
|
-
runResult = await runProjects({
|
|
40610
|
-
|
|
40611
|
-
|
|
40612
|
-
|
|
40613
|
-
|
|
40614
|
-
|
|
40615
|
-
|
|
40616
|
-
|
|
40617
|
-
|
|
40618
|
-
|
|
40619
|
-
|
|
40620
|
-
|
|
40621
|
-
|
|
40622
|
-
|
|
40623
|
-
|
|
40811
|
+
runResult = await timing.profileAsync("runProjects", async () => {
|
|
40812
|
+
return runProjects({
|
|
40813
|
+
backend,
|
|
40814
|
+
deferFormatting: true,
|
|
40815
|
+
parallel,
|
|
40816
|
+
projects: pendingJobs.map((pending) => {
|
|
40817
|
+
return {
|
|
40818
|
+
config: pending.config,
|
|
40819
|
+
displayColor: pending.displayColor,
|
|
40820
|
+
displayName: pending.displayName,
|
|
40821
|
+
runtimeInjectionPaths: pending.runtimeInjectionPaths,
|
|
40822
|
+
testFiles: pending.runtimeFiles
|
|
40823
|
+
};
|
|
40824
|
+
}),
|
|
40825
|
+
startTime: Date.now(),
|
|
40826
|
+
timing,
|
|
40827
|
+
version: VERSION$3
|
|
40828
|
+
});
|
|
40624
40829
|
});
|
|
40625
40830
|
} finally {
|
|
40626
40831
|
await backend.close?.();
|
|
@@ -40697,11 +40902,16 @@ function selectProjects(allProjects, projectNames, cliFiles, rootDirectory) {
|
|
|
40697
40902
|
const VERSION$2 = version;
|
|
40698
40903
|
async function runSingleProject(options) {
|
|
40699
40904
|
const { cli } = options;
|
|
40700
|
-
const
|
|
40701
|
-
|
|
40702
|
-
|
|
40905
|
+
const timing = options.timing ?? NOOP_TIMING_COLLECTOR;
|
|
40906
|
+
const baseConfig = { ...options.config };
|
|
40907
|
+
timing.profile("resolveSetupFilePaths", () => {
|
|
40908
|
+
resolveSetupFilePaths(baseConfig);
|
|
40909
|
+
});
|
|
40910
|
+
const discovery = timing.profile("discoverTestFiles", () => {
|
|
40911
|
+
return discoverTestFiles(baseConfig, cli.files);
|
|
40912
|
+
});
|
|
40703
40913
|
if (discovery.files.length === 0) {
|
|
40704
|
-
if (
|
|
40914
|
+
if (baseConfig.passWithNoTests) return {
|
|
40705
40915
|
mode: "single",
|
|
40706
40916
|
preCoverageMs: 0
|
|
40707
40917
|
};
|
|
@@ -40712,7 +40922,13 @@ async function runSingleProject(options) {
|
|
|
40712
40922
|
validationExitCode: 2
|
|
40713
40923
|
};
|
|
40714
40924
|
}
|
|
40715
|
-
const { runtimeFiles, typeTestFiles } = classifyTestFiles
|
|
40925
|
+
const { runtimeFiles, typeTestFiles } = timing.profile("classifyTestFiles", () => {
|
|
40926
|
+
return classifyTestFiles(discovery.files, baseConfig);
|
|
40927
|
+
});
|
|
40928
|
+
const filterActive = (cli.files?.length ?? 0) > 0 || baseConfig.testPathPattern !== void 0;
|
|
40929
|
+
const config = timing.profile("narrowForLuauRun", () => {
|
|
40930
|
+
return narrowForLuauRun(baseConfig, runtimeFiles, filterActive);
|
|
40931
|
+
});
|
|
40716
40932
|
if (typeTestFiles.length === 0 && runtimeFiles.length === 0) {
|
|
40717
40933
|
if (config.passWithNoTests) return {
|
|
40718
40934
|
mode: "single",
|
|
@@ -40729,19 +40945,27 @@ async function runSingleProject(options) {
|
|
|
40729
40945
|
let effectiveConfig = config;
|
|
40730
40946
|
if (config.collectCoverage && !config.typecheckOnly && runtimeFiles.length > 0) {
|
|
40731
40947
|
const preCoverageStart = Date.now();
|
|
40732
|
-
const { placeFile } = prepareCoverage(config);
|
|
40948
|
+
const { placeFile } = timing.profile("prepareCoverage", () => prepareCoverage(config));
|
|
40733
40949
|
preCoverageMs = Date.now() - preCoverageStart;
|
|
40734
40950
|
effectiveConfig = {
|
|
40735
40951
|
...config,
|
|
40736
40952
|
placeFile
|
|
40737
40953
|
};
|
|
40738
40954
|
}
|
|
40739
|
-
const typecheckResult = typeTestFiles.length > 0 ? runTypecheck({
|
|
40740
|
-
|
|
40741
|
-
|
|
40742
|
-
|
|
40955
|
+
const typecheckResult = typeTestFiles.length > 0 ? timing.profile("runTypecheck", () => {
|
|
40956
|
+
return runTypecheck({
|
|
40957
|
+
files: typeTestFiles,
|
|
40958
|
+
rootDir: effectiveConfig.rootDir,
|
|
40959
|
+
tsconfig: effectiveConfig.typecheckTsconfig
|
|
40960
|
+
});
|
|
40961
|
+
}) : void 0;
|
|
40962
|
+
const runtimeResult = runtimeFiles.length > 0 ? await executeRuntimeTests({
|
|
40963
|
+
cli,
|
|
40964
|
+
config: effectiveConfig,
|
|
40965
|
+
testFiles: runtimeFiles,
|
|
40966
|
+
timing,
|
|
40967
|
+
totalFiles: discovery.totalFiles
|
|
40743
40968
|
}) : void 0;
|
|
40744
|
-
const runtimeResult = runtimeFiles.length > 0 ? await executeRuntimeTests(options, effectiveConfig, runtimeFiles, discovery.totalFiles) : void 0;
|
|
40745
40969
|
return {
|
|
40746
40970
|
mode: "single",
|
|
40747
40971
|
preCoverageMs,
|
|
@@ -40749,19 +40973,35 @@ async function runSingleProject(options) {
|
|
|
40749
40973
|
typecheckResult
|
|
40750
40974
|
};
|
|
40751
40975
|
}
|
|
40752
|
-
async function executeRuntimeTests(options
|
|
40753
|
-
|
|
40754
|
-
const
|
|
40976
|
+
async function executeRuntimeTests(options) {
|
|
40977
|
+
const { cli, config, testFiles, timing, totalFiles } = options;
|
|
40978
|
+
const useDefaultFormatter = isDefaultHumanFormatter(config);
|
|
40979
|
+
emitRunHeader({
|
|
40980
|
+
collectCoverage: config.collectCoverage,
|
|
40981
|
+
color: config.color,
|
|
40982
|
+
formatters: config.formatters,
|
|
40983
|
+
rootDir: config.rootDir,
|
|
40984
|
+
silent: config.silent,
|
|
40985
|
+
verbose: config.verbose,
|
|
40986
|
+
version: VERSION$2
|
|
40987
|
+
});
|
|
40988
|
+
if (useDefaultFormatter && testFiles.length !== totalFiles) node_process.default.stderr.write(`Running ${String(testFiles.length)} of ${String(totalFiles)} test files\n`);
|
|
40989
|
+
const backend = await timing.profileAsync("resolveBackend", async () => {
|
|
40990
|
+
return resolveBackend(cli, config);
|
|
40991
|
+
});
|
|
40755
40992
|
try {
|
|
40756
|
-
const { results } = await runProjects({
|
|
40757
|
-
|
|
40758
|
-
|
|
40759
|
-
|
|
40760
|
-
|
|
40761
|
-
|
|
40762
|
-
|
|
40763
|
-
|
|
40764
|
-
|
|
40993
|
+
const { results } = await timing.profileAsync("runProjects", async () => {
|
|
40994
|
+
return runProjects({
|
|
40995
|
+
backend,
|
|
40996
|
+
deferFormatting: true,
|
|
40997
|
+
projects: [{
|
|
40998
|
+
config,
|
|
40999
|
+
testFiles
|
|
41000
|
+
}],
|
|
41001
|
+
startTime: Date.now(),
|
|
41002
|
+
timing,
|
|
41003
|
+
version: VERSION$2
|
|
41004
|
+
});
|
|
40765
41005
|
});
|
|
40766
41006
|
return results[0];
|
|
40767
41007
|
} finally {
|
|
@@ -41497,12 +41737,7 @@ const SYNTHESIZED_PLACE_FILE = "synthesized.rbxl";
|
|
|
41497
41737
|
const WORKSPACE_CACHE_DIRECTORY = node_path.join(".jest-roblox", "workspace");
|
|
41498
41738
|
const ROJO_PROJECT_DEFAULT = "test.project.json";
|
|
41499
41739
|
async function runWorkspace(options) {
|
|
41500
|
-
|
|
41501
|
-
try {
|
|
41502
|
-
return await runWorkspaceProfiled(options, timing);
|
|
41503
|
-
} finally {
|
|
41504
|
-
timing.flushTimingReport();
|
|
41505
|
-
}
|
|
41740
|
+
return runWorkspaceProfiled(options, options.timing ?? NOOP_TIMING_COLLECTOR);
|
|
41506
41741
|
}
|
|
41507
41742
|
function buildCoverageMap(entries) {
|
|
41508
41743
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -41616,6 +41851,7 @@ async function runWorkspaceProfiled(options, timing) {
|
|
|
41616
41851
|
};
|
|
41617
41852
|
}),
|
|
41618
41853
|
startTime,
|
|
41854
|
+
timing,
|
|
41619
41855
|
version,
|
|
41620
41856
|
...dispatchSpec
|
|
41621
41857
|
});
|
|
@@ -41682,13 +41918,33 @@ async function prepareWorkspaceDispatch(input) {
|
|
|
41682
41918
|
}
|
|
41683
41919
|
return { scriptOverride: generateMaterializerScript(inputs) };
|
|
41684
41920
|
}
|
|
41921
|
+
/**
|
|
41922
|
+
* Resolve a `--testPathPattern` against this package's files Node-side, then
|
|
41923
|
+
* forward an Instance-namespace basename pattern (see {@link narrowForLuauRun}).
|
|
41924
|
+
*
|
|
41925
|
+
* A pattern that matches no file in this package simply targets a different
|
|
41926
|
+
* package: keep the (zero-matching) raw pattern so Jest-on-Roblox runs nothing,
|
|
41927
|
+
* and set `passWithNoTests` so it doesn't `exit(1)`. The raw pattern is
|
|
41928
|
+
* load-bearing here — clearing it would drop the filter entirely and make the
|
|
41929
|
+
* Luau side fall back to `testMatch`, running the whole package.
|
|
41930
|
+
*/
|
|
41931
|
+
function narrowPackageTestPathPattern(packageConfig) {
|
|
41932
|
+
if (packageConfig.testPathPattern === void 0) return packageConfig;
|
|
41933
|
+
const { files } = discoverTestFiles(packageConfig);
|
|
41934
|
+
const { runtimeFiles } = classifyTestFiles(files, packageConfig);
|
|
41935
|
+
if (runtimeFiles.length === 0) return {
|
|
41936
|
+
...packageConfig,
|
|
41937
|
+
passWithNoTests: true
|
|
41938
|
+
};
|
|
41939
|
+
return narrowForLuauRun(packageConfig, runtimeFiles, true);
|
|
41940
|
+
}
|
|
41685
41941
|
async function loadPackages(input) {
|
|
41686
41942
|
const { cli, packageInfos, timing } = input;
|
|
41687
41943
|
const loaded = [];
|
|
41688
41944
|
for (const info of packageInfos) {
|
|
41689
|
-
const packageConfig = mergeCliWithConfig(cli, await timing.profileAsync(`load-config:${info.name}`, async () => {
|
|
41945
|
+
const packageConfig = narrowPackageTestPathPattern(mergeCliWithConfig(cli, await timing.profileAsync(`load-config:${info.name}`, async () => {
|
|
41690
41946
|
return loadConfig(void 0, info.packageDirectory);
|
|
41691
|
-
}));
|
|
41947
|
+
})));
|
|
41692
41948
|
const rojoProject = packageConfig.rojoProject ?? ROJO_PROJECT_DEFAULT;
|
|
41693
41949
|
const hasExplicitIgnore = packageConfig.coveragePathIgnorePatterns !== DEFAULT_CONFIG.coveragePathIgnorePatterns;
|
|
41694
41950
|
const hasExplicitCoverageCache = packageConfig.coverageCache !== DEFAULT_CONFIG.coverageCache;
|
|
@@ -42215,7 +42471,7 @@ const EMPTY_RESULT = {
|
|
|
42215
42471
|
preCoverageMs: 0,
|
|
42216
42472
|
projectResults: []
|
|
42217
42473
|
};
|
|
42218
|
-
async function runWorkspaceMode(cli, workspace) {
|
|
42474
|
+
async function runWorkspaceMode(cli, workspace, timing) {
|
|
42219
42475
|
const basicValidation = validateBasicWorkspaceFlags(cli);
|
|
42220
42476
|
if (!basicValidation.ok) return {
|
|
42221
42477
|
...EMPTY_RESULT,
|
|
@@ -42281,6 +42537,14 @@ async function runWorkspaceMode(cli, workspace) {
|
|
|
42281
42537
|
}
|
|
42282
42538
|
let runtimeResults;
|
|
42283
42539
|
try {
|
|
42540
|
+
emitRunHeader({
|
|
42541
|
+
color: runOptions.color,
|
|
42542
|
+
formatters: runOptions.formatters,
|
|
42543
|
+
rootDir: workspaceRoot,
|
|
42544
|
+
silent: runOptions.silent,
|
|
42545
|
+
verbose: cli.verbose,
|
|
42546
|
+
version: VERSION$1
|
|
42547
|
+
});
|
|
42284
42548
|
const onStreamingResult = resolveStreamingProgressSink(runOptions, cli);
|
|
42285
42549
|
runtimeResults = await runWorkspace({
|
|
42286
42550
|
backend,
|
|
@@ -42288,6 +42552,7 @@ async function runWorkspaceMode(cli, workspace) {
|
|
|
42288
42552
|
...onStreamingResult !== void 0 ? { onStreamingResult } : {},
|
|
42289
42553
|
packageInfos,
|
|
42290
42554
|
runOptions,
|
|
42555
|
+
timing,
|
|
42291
42556
|
version: VERSION$1,
|
|
42292
42557
|
workspaceRoot,
|
|
42293
42558
|
workStealingCredentials
|
|
@@ -42377,8 +42642,11 @@ function composeWorkspaceDisplayName(package_, project) {
|
|
|
42377
42642
|
* either break the structured output or be silenced anyway.
|
|
42378
42643
|
*/
|
|
42379
42644
|
function resolveStreamingProgressSink(runOptions, cli) {
|
|
42380
|
-
if (
|
|
42381
|
-
|
|
42645
|
+
if (!isDefaultHumanFormatter({
|
|
42646
|
+
formatters: runOptions.formatters,
|
|
42647
|
+
silent: runOptions.silent,
|
|
42648
|
+
verbose: cli.verbose
|
|
42649
|
+
})) return;
|
|
42382
42650
|
return (entry) => {
|
|
42383
42651
|
const line = formatStreamingProgressLine(entry, { color: runOptions.color });
|
|
42384
42652
|
node_process.default.stdout.write(`${line}\n`);
|
|
@@ -42390,41 +42658,25 @@ function isWorkspaceInvocation(cli) {
|
|
|
42390
42658
|
return cli.workspace === true || cli.packages !== void 0 || cli.affectedSince !== void 0;
|
|
42391
42659
|
}
|
|
42392
42660
|
async function runJestRoblox(cli, config) {
|
|
42393
|
-
|
|
42394
|
-
|
|
42395
|
-
|
|
42396
|
-
|
|
42397
|
-
|
|
42398
|
-
|
|
42399
|
-
|
|
42400
|
-
|
|
42401
|
-
|
|
42402
|
-
|
|
42403
|
-
config: merged
|
|
42404
|
-
});
|
|
42405
|
-
}
|
|
42406
|
-
//#endregion
|
|
42407
|
-
//#region src/utils/error-chain.ts
|
|
42408
|
-
const MAX_DEPTH = 5;
|
|
42409
|
-
function walkErrorChain(err) {
|
|
42410
|
-
const entries = [];
|
|
42411
|
-
let current = err;
|
|
42412
|
-
while (current instanceof Error && entries.length < MAX_DEPTH) {
|
|
42413
|
-
entries.push({
|
|
42414
|
-
name: current.constructor.name,
|
|
42415
|
-
code: readStringProperty(current, "code"),
|
|
42416
|
-
errno: readStringProperty(current, "errno"),
|
|
42417
|
-
message: current.message,
|
|
42418
|
-
syscall: readStringProperty(current, "syscall")
|
|
42661
|
+
const timing = createTimingCollector();
|
|
42662
|
+
try {
|
|
42663
|
+
if (isWorkspaceInvocation(cli)) return await runWorkspaceMode(cli, config.workspace, timing);
|
|
42664
|
+
const merged = mergeCliWithConfig(cli, config);
|
|
42665
|
+
const rawProjects = merged.projects;
|
|
42666
|
+
if (rawProjects !== void 0 && rawProjects.length > 0) return await runMultiProject({
|
|
42667
|
+
cli,
|
|
42668
|
+
config: merged,
|
|
42669
|
+
rawProjects,
|
|
42670
|
+
timing
|
|
42419
42671
|
});
|
|
42420
|
-
|
|
42672
|
+
return await runSingleProject({
|
|
42673
|
+
cli,
|
|
42674
|
+
config: merged,
|
|
42675
|
+
timing
|
|
42676
|
+
});
|
|
42677
|
+
} finally {
|
|
42678
|
+
timing.flushTimingReport();
|
|
42421
42679
|
}
|
|
42422
|
-
return entries;
|
|
42423
|
-
}
|
|
42424
|
-
function readStringProperty(err, key) {
|
|
42425
|
-
const value = Reflect.get(err, key);
|
|
42426
|
-
if (value === void 0 || value === null) return;
|
|
42427
|
-
return String(value);
|
|
42428
42680
|
}
|
|
42429
42681
|
//#endregion
|
|
42430
42682
|
//#region src/cli.ts
|
|
@@ -42448,6 +42700,7 @@ Options:
|
|
|
42448
42700
|
--no-color Disable colored output
|
|
42449
42701
|
-u, --updateSnapshot Update snapshot files
|
|
42450
42702
|
--coverage Enable coverage collection
|
|
42703
|
+
--no-coverage Disable coverage for this run (overrides config)
|
|
42451
42704
|
--collectCoverageFrom <glob> Globs for files to include in coverage (repeatable)
|
|
42452
42705
|
--coverageDirectory <path> Directory for coverage output (default: coverage)
|
|
42453
42706
|
--coverageReporters <r...> Coverage reporters (default: text, lcov)
|
|
@@ -42491,6 +42744,7 @@ Examples:
|
|
|
42491
42744
|
jest-roblox -t "should spawn" Run tests matching pattern
|
|
42492
42745
|
jest-roblox --formatters json Output JSON to file
|
|
42493
42746
|
jest-roblox --coverage Run tests with coverage instrumentation
|
|
42747
|
+
jest-roblox --no-coverage Skip coverage instrumentation for this run
|
|
42494
42748
|
`;
|
|
42495
42749
|
function parseArgs(args) {
|
|
42496
42750
|
const { positionals, values } = (0, node_util.parseArgs)({
|
|
@@ -42523,6 +42777,7 @@ function parseArgs(args) {
|
|
|
42523
42777
|
type: "boolean"
|
|
42524
42778
|
},
|
|
42525
42779
|
"no-color": { type: "boolean" },
|
|
42780
|
+
"no-coverage": { type: "boolean" },
|
|
42526
42781
|
"no-coverage-cache": { type: "boolean" },
|
|
42527
42782
|
"no-show-luau": { type: "boolean" },
|
|
42528
42783
|
"outputFile": { type: "string" },
|
|
@@ -42577,7 +42832,7 @@ function parseArgs(args) {
|
|
|
42577
42832
|
affectedSince: values["affected-since"],
|
|
42578
42833
|
apiKey: values.apiKey,
|
|
42579
42834
|
backend: validateBackend(values.backend),
|
|
42580
|
-
collectCoverage: values.coverage,
|
|
42835
|
+
collectCoverage: values["no-coverage"] === true ? false : values.coverage,
|
|
42581
42836
|
collectCoverageFrom: values.collectCoverageFrom,
|
|
42582
42837
|
color: values["no-color"] === true ? false : values.color,
|
|
42583
42838
|
config: values.config,
|
|
@@ -42689,6 +42944,7 @@ function formatBackendErrorBanner(err) {
|
|
|
42689
42944
|
const extras = formatChainExtras(entry);
|
|
42690
42945
|
const label = y$3.dim(`[${index.toString()}]`);
|
|
42691
42946
|
body.push(` ${label} ${entry.name}: ${entry.message}${extras}`);
|
|
42947
|
+
if (entry.requiredScopes !== void 0) body.push(` ${y$3.yellow(formatMissingScopes(entry.requiredScopes))}`);
|
|
42692
42948
|
}
|
|
42693
42949
|
return formatBanner({
|
|
42694
42950
|
body,
|