@isentinel/jest-roblox 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/jest-roblox.js +4 -0
- package/dist/cli.d.mts +8 -0
- package/dist/cli.mjs +281 -0
- package/dist/game-output-BKBGosEI.mjs +1362 -0
- package/dist/index.d.mts +243 -0
- package/dist/index.mjs +3 -0
- package/dist/schema-ryuVGD35.d.mts +826 -0
- package/package.json +80 -0
- package/plugin/JestRobloxRunner.rbxm +0 -0
- package/plugin/plugin.project.json +6 -0
- package/plugin/sourcemap.json +1 -0
- package/plugin/src/init.server.luau +193 -0
- package/plugin/src/test-in-run-mode.server.luau +36 -0
- package/plugin/src/test-runner.luau +145 -0
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { t as CliOptions } from "./schema-ryuVGD35.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/cli.d.ts
|
|
4
|
+
declare function main(): Promise<void>;
|
|
5
|
+
declare function parseArgs(args: Array<string>): CliOptions;
|
|
6
|
+
declare function run(args: Array<string>): Promise<number>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { main, parseArgs, run };
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { g as createOpenCloudBackend, i as execute, m as createStudioBackend, n as parseGameOutput, o as writeJsonFile, r as writeGameOutput, t as formatGameOutputNotice, u as loadConfig, y as LuauScriptError } from "./game-output-BKBGosEI.mjs";
|
|
2
|
+
import * as path$1 from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { parseArgs as parseArgs$1 } from "node:util";
|
|
5
|
+
import * as fs from "node:fs";
|
|
6
|
+
|
|
7
|
+
//#region package.json
|
|
8
|
+
var version = "0.0.1";
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/utils/glob.ts
|
|
12
|
+
function globSync(pattern, options = {}) {
|
|
13
|
+
const cwd = options.cwd ?? process.cwd();
|
|
14
|
+
return walkDirectory(cwd, cwd).filter((file) => matchesGlobPattern(file, pattern));
|
|
15
|
+
}
|
|
16
|
+
function matchesGlobPattern(filePath, pattern) {
|
|
17
|
+
const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{DOUBLESTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{DOUBLESTAR\}\}/g, ".*");
|
|
18
|
+
return new RegExp(`^${regexPattern}$`).test(filePath);
|
|
19
|
+
}
|
|
20
|
+
function walkDirectory(directoryPath, baseDirectory) {
|
|
21
|
+
const results = [];
|
|
22
|
+
try {
|
|
23
|
+
const entries = fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const fullPath = path$1.join(directoryPath, entry.name);
|
|
26
|
+
const relativePath = path$1.relative(baseDirectory, fullPath).replace(/\\/g, "/");
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
if (!entry.name.startsWith(".") && entry.name !== "node_modules") results.push(...walkDirectory(fullPath, baseDirectory));
|
|
29
|
+
} else results.push(relativePath);
|
|
30
|
+
}
|
|
31
|
+
} catch {}
|
|
32
|
+
return results;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/cli.ts
|
|
37
|
+
const HELP_TEXT = `
|
|
38
|
+
Usage: jest-roblox [options] [files...]
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
--backend <type> Backend: "open-cloud" or "studio" (default: open-cloud)
|
|
42
|
+
--port <number> WebSocket port for studio backend (default: 3001)
|
|
43
|
+
--config <path> Path to config file
|
|
44
|
+
--testPathPattern <regex> Filter test files by path pattern
|
|
45
|
+
-t, --testNamePattern <regex> Filter tests by name pattern
|
|
46
|
+
--json Output results as JSON
|
|
47
|
+
--compact Token-efficient output for AI consumption
|
|
48
|
+
--compactMaxFailures <n> Max failures in compact mode (default: 10)
|
|
49
|
+
--outputFile <path> Write results to file
|
|
50
|
+
--gameOutput <path> Write game output (print/warn/error) to file
|
|
51
|
+
--sourceMap Map Luau stack traces to TypeScript source
|
|
52
|
+
--rojoProject <path> Path to rojo project file (auto-detected if not set)
|
|
53
|
+
--verbose Show individual test results
|
|
54
|
+
--silent Suppress output
|
|
55
|
+
--no-color Disable colored output
|
|
56
|
+
--no-cache Force re-upload place file (skip cache)
|
|
57
|
+
--pollInterval <ms> Open Cloud poll interval in ms (default: 500)
|
|
58
|
+
--projects <path...> DataModel paths to search for tests
|
|
59
|
+
--setupFiles <path...> DataModel paths to setup scripts
|
|
60
|
+
--no-show-luau Hide Luau code in failure output
|
|
61
|
+
--help Show this help message
|
|
62
|
+
--version Show version number
|
|
63
|
+
|
|
64
|
+
Environment Variables (open-cloud backend only):
|
|
65
|
+
ROBLOX_OPEN_CLOUD_API_KEY API key for Roblox Open Cloud
|
|
66
|
+
ROBLOX_UNIVERSE_ID Target universe ID
|
|
67
|
+
ROBLOX_PLACE_ID Target place ID
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
jest-roblox Run all tests (open-cloud)
|
|
71
|
+
jest-roblox --backend studio Run tests via Studio plugin
|
|
72
|
+
jest-roblox src/player.spec.ts Run specific test file
|
|
73
|
+
jest-roblox -t "should spawn" Run tests matching pattern
|
|
74
|
+
jest-roblox --json --outputFile results.json
|
|
75
|
+
`;
|
|
76
|
+
async function main() {
|
|
77
|
+
const exitCode = await run(process.argv.slice(2));
|
|
78
|
+
process.exit(exitCode);
|
|
79
|
+
}
|
|
80
|
+
function parseArgs(args) {
|
|
81
|
+
const { positionals, values } = parseArgs$1({
|
|
82
|
+
allowPositionals: true,
|
|
83
|
+
args,
|
|
84
|
+
options: {
|
|
85
|
+
"backend": { type: "string" },
|
|
86
|
+
"cache": { type: "boolean" },
|
|
87
|
+
"color": { type: "boolean" },
|
|
88
|
+
"compact": { type: "boolean" },
|
|
89
|
+
"compactMaxFailures": { type: "string" },
|
|
90
|
+
"config": { type: "string" },
|
|
91
|
+
"gameOutput": { type: "string" },
|
|
92
|
+
"help": {
|
|
93
|
+
default: false,
|
|
94
|
+
type: "boolean"
|
|
95
|
+
},
|
|
96
|
+
"json": { type: "boolean" },
|
|
97
|
+
"no-cache": { type: "boolean" },
|
|
98
|
+
"no-color": { type: "boolean" },
|
|
99
|
+
"no-show-luau": { type: "boolean" },
|
|
100
|
+
"outputFile": { type: "string" },
|
|
101
|
+
"pollInterval": { type: "string" },
|
|
102
|
+
"port": { type: "string" },
|
|
103
|
+
"projects": {
|
|
104
|
+
multiple: true,
|
|
105
|
+
type: "string"
|
|
106
|
+
},
|
|
107
|
+
"rojoProject": { type: "string" },
|
|
108
|
+
"setupFiles": {
|
|
109
|
+
multiple: true,
|
|
110
|
+
type: "string"
|
|
111
|
+
},
|
|
112
|
+
"showLuau": { type: "boolean" },
|
|
113
|
+
"silent": { type: "boolean" },
|
|
114
|
+
"sourceMap": { type: "boolean" },
|
|
115
|
+
"testNamePattern": {
|
|
116
|
+
short: "t",
|
|
117
|
+
type: "string"
|
|
118
|
+
},
|
|
119
|
+
"testPathPattern": { type: "string" },
|
|
120
|
+
"timeout": { type: "string" },
|
|
121
|
+
"verbose": { type: "boolean" },
|
|
122
|
+
"version": {
|
|
123
|
+
default: false,
|
|
124
|
+
type: "boolean"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
strict: true
|
|
128
|
+
});
|
|
129
|
+
const compactMaxFailures = values.compactMaxFailures !== void 0 ? Number.parseInt(values.compactMaxFailures, 10) : void 0;
|
|
130
|
+
const pollInterval = values.pollInterval !== void 0 ? Number.parseInt(values.pollInterval, 10) : void 0;
|
|
131
|
+
const port = values.port !== void 0 ? Number.parseInt(values.port, 10) : void 0;
|
|
132
|
+
const timeout = values.timeout !== void 0 ? Number.parseInt(values.timeout, 10) : void 0;
|
|
133
|
+
return {
|
|
134
|
+
backend: values.backend,
|
|
135
|
+
cache: values["no-cache"] === true ? false : values.cache,
|
|
136
|
+
color: values["no-color"] === true ? false : values.color,
|
|
137
|
+
compact: values.compact,
|
|
138
|
+
compactMaxFailures,
|
|
139
|
+
config: values.config,
|
|
140
|
+
files: positionals.length > 0 ? positionals : void 0,
|
|
141
|
+
gameOutput: values.gameOutput,
|
|
142
|
+
help: values.help,
|
|
143
|
+
json: values.json,
|
|
144
|
+
outputFile: values.outputFile,
|
|
145
|
+
pollInterval,
|
|
146
|
+
port,
|
|
147
|
+
projects: values.projects,
|
|
148
|
+
rojoProject: values.rojoProject,
|
|
149
|
+
setupFiles: values.setupFiles,
|
|
150
|
+
showLuau: values["no-show-luau"] === true ? false : values.showLuau,
|
|
151
|
+
silent: values.silent,
|
|
152
|
+
sourceMap: values.sourceMap,
|
|
153
|
+
testNamePattern: values.testNamePattern,
|
|
154
|
+
testPathPattern: values.testPathPattern,
|
|
155
|
+
timeout,
|
|
156
|
+
verbose: values.verbose,
|
|
157
|
+
version: values.version
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async function run(args) {
|
|
161
|
+
try {
|
|
162
|
+
return await runInner(args);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
printError(err);
|
|
165
|
+
return 2;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function printError(err) {
|
|
169
|
+
if (err instanceof LuauScriptError) {
|
|
170
|
+
const hint = getLuauErrorHint(err.message);
|
|
171
|
+
console.error(`\n Luau script error: ${err.message}`);
|
|
172
|
+
if (hint !== void 0) console.error(` Hint: ${hint}`);
|
|
173
|
+
console.error();
|
|
174
|
+
} else if (err instanceof Error) console.error(`Error: ${err.message}`);
|
|
175
|
+
else console.error("An unknown error occurred");
|
|
176
|
+
}
|
|
177
|
+
async function runInner(args) {
|
|
178
|
+
const cli = parseArgs(args);
|
|
179
|
+
if (cli.help === true) {
|
|
180
|
+
console.log(HELP_TEXT);
|
|
181
|
+
return 0;
|
|
182
|
+
}
|
|
183
|
+
if (cli.version === true) {
|
|
184
|
+
console.log(version);
|
|
185
|
+
return 0;
|
|
186
|
+
}
|
|
187
|
+
const config = mergeCliWithConfig(cli, await loadConfig(cli.config));
|
|
188
|
+
const discovery = discoverTestFiles(config, cli.files);
|
|
189
|
+
if (discovery.files.length === 0) {
|
|
190
|
+
console.error("No test files found");
|
|
191
|
+
return 2;
|
|
192
|
+
}
|
|
193
|
+
if (!config.silent && !config.compact && !config.json && discovery.files.length !== discovery.totalFiles) process.stderr.write(`Running ${discovery.files.length} of ${discovery.totalFiles} test files\n`);
|
|
194
|
+
const result = await execute({
|
|
195
|
+
backend: config.backend === "studio" ? createStudioBackend({
|
|
196
|
+
port: config.port,
|
|
197
|
+
timeout: config.timeout
|
|
198
|
+
}) : createOpenCloudBackend(),
|
|
199
|
+
config,
|
|
200
|
+
testFiles: discovery.files,
|
|
201
|
+
version
|
|
202
|
+
});
|
|
203
|
+
if (result.output) console.log(result.output);
|
|
204
|
+
if (config.outputFile !== void 0) await writeJsonFile(result.result, config.outputFile);
|
|
205
|
+
writeGameOutputIfConfigured(config, result.gameOutput);
|
|
206
|
+
return result.exitCode;
|
|
207
|
+
}
|
|
208
|
+
function writeGameOutputIfConfigured(config, gameOutput) {
|
|
209
|
+
if (config.gameOutput === void 0) return;
|
|
210
|
+
const entries = parseGameOutput(gameOutput);
|
|
211
|
+
writeGameOutput(config.gameOutput, entries);
|
|
212
|
+
if (!config.silent) {
|
|
213
|
+
const notice = formatGameOutputNotice(config.gameOutput, entries.length);
|
|
214
|
+
if (notice) console.error(notice);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const LUAU_ERROR_HINTS = [
|
|
218
|
+
[/Failed to find Jest instance in ReplicatedStorage/, "Set \"jestPath\" in your config to specify the Jest module location, e.g. \"ReplicatedStorage/rbxts_include/node_modules/@rbxts/jest/src\""],
|
|
219
|
+
[/Failed to find Jest instance at path/, "The configured jestPath does not resolve to a valid instance. Verify the path matches your Rojo project tree."],
|
|
220
|
+
[/Failed to find service/, "The first segment of jestPath must be a valid Roblox service name (e.g. ReplicatedStorage, ServerScriptService)."],
|
|
221
|
+
[/No projects configured/, "Set \"projects\" in jest.config.ts (e.g. [\"ReplicatedStorage/client\", \"ServerScriptService/server\"]) or pass --projects."]
|
|
222
|
+
];
|
|
223
|
+
function discoverTestFiles(config, cliFiles) {
|
|
224
|
+
if (cliFiles && cliFiles.length > 0) {
|
|
225
|
+
const files = cliFiles.map((file) => path$1.resolve(config.rootDir, file));
|
|
226
|
+
return {
|
|
227
|
+
files,
|
|
228
|
+
totalFiles: files.length
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
const allFiles = [];
|
|
232
|
+
for (const pattern of config.testMatch) {
|
|
233
|
+
const matches = globSync(pattern, { cwd: config.rootDir });
|
|
234
|
+
allFiles.push(...matches);
|
|
235
|
+
}
|
|
236
|
+
const ignoredPatterns = config.testPathIgnorePatterns.map((pat) => new RegExp(pat));
|
|
237
|
+
const baseFiles = allFiles.filter((file) => {
|
|
238
|
+
return !ignoredPatterns.some((pattern) => pattern.test(file));
|
|
239
|
+
});
|
|
240
|
+
const totalFiles = new Set(baseFiles).size;
|
|
241
|
+
let filtered = baseFiles;
|
|
242
|
+
if (config.testPathPattern !== void 0) {
|
|
243
|
+
const pathPattern = new RegExp(config.testPathPattern);
|
|
244
|
+
filtered = filtered.filter((file) => pathPattern.test(file));
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
files: [...new Set(filtered)],
|
|
248
|
+
totalFiles
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function getLuauErrorHint(message) {
|
|
252
|
+
for (const [pattern, hint] of LUAU_ERROR_HINTS) if (pattern.test(message)) return hint;
|
|
253
|
+
}
|
|
254
|
+
function mergeCliWithConfig(cli, config) {
|
|
255
|
+
return {
|
|
256
|
+
...config,
|
|
257
|
+
backend: cli.backend ?? config.backend,
|
|
258
|
+
cache: cli.cache ?? config.cache,
|
|
259
|
+
color: cli.color ?? config.color,
|
|
260
|
+
compact: cli.compact ?? config.compact,
|
|
261
|
+
compactMaxFailures: cli.compactMaxFailures ?? config.compactMaxFailures,
|
|
262
|
+
gameOutput: cli.gameOutput ?? config.gameOutput,
|
|
263
|
+
json: cli.json ?? config.json,
|
|
264
|
+
outputFile: cli.outputFile ?? config.outputFile,
|
|
265
|
+
pollInterval: cli.pollInterval ?? config.pollInterval,
|
|
266
|
+
port: cli.port ?? config.port,
|
|
267
|
+
projects: cli.projects ?? config.projects,
|
|
268
|
+
rojoProject: cli.rojoProject ?? config.rojoProject,
|
|
269
|
+
setupFiles: cli.setupFiles ?? config.setupFiles,
|
|
270
|
+
showLuau: cli.showLuau ?? config.showLuau,
|
|
271
|
+
silent: cli.silent ?? config.silent,
|
|
272
|
+
sourceMap: cli.sourceMap ?? config.sourceMap,
|
|
273
|
+
testNamePattern: cli.testNamePattern ?? config.testNamePattern,
|
|
274
|
+
testPathPattern: cli.testPathPattern ?? config.testPathPattern,
|
|
275
|
+
timeout: cli.timeout ?? config.timeout,
|
|
276
|
+
verbose: cli.verbose ?? config.verbose
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
//#endregion
|
|
281
|
+
export { main, parseArgs, run };
|