@isentinel/jest-roblox 0.0.2 → 0.0.3

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/dist/cli.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { t as CliOptions } from "./schema-CEsyg-FX.mjs";
1
+ import { t as CliOptions } from "./schema-hhbEFVXy.mjs";
2
2
 
3
3
  //#region src/cli.d.ts
4
4
  declare function main(): Promise<void>;
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
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-BDatGA9S.mjs";
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--EpqHGEr.mjs";
2
2
  import * as path$1 from "node:path";
3
3
  import process from "node:process";
4
4
  import { parseArgs as parseArgs$1 } from "node:util";
@@ -6,7 +6,7 @@ import { WebSocketServer } from "ws";
6
6
  import * as fs from "node:fs";
7
7
 
8
8
  //#region package.json
9
- var version = "0.0.2";
9
+ var version = "0.0.3";
10
10
 
11
11
  //#endregion
12
12
  //#region src/backends/auto.ts
@@ -162,6 +162,7 @@ Options:
162
162
  --pollInterval <ms> Open Cloud poll interval in ms (default: 500)
163
163
  --projects <path...> DataModel paths to search for tests
164
164
  --setupFiles <path...> DataModel paths to setup scripts
165
+ --setupFilesAfterEnv <path...> DataModel paths to post-env setup scripts
165
166
  --no-show-luau Hide Luau code in failure output
166
167
  --help Show this help message
167
168
  --version Show version number
@@ -214,6 +215,10 @@ function parseArgs(args) {
214
215
  multiple: true,
215
216
  type: "string"
216
217
  },
218
+ "setupFilesAfterEnv": {
219
+ multiple: true,
220
+ type: "string"
221
+ },
217
222
  "showLuau": { type: "boolean" },
218
223
  "silent": { type: "boolean" },
219
224
  "sourceMap": { type: "boolean" },
@@ -256,6 +261,7 @@ function parseArgs(args) {
256
261
  projects: values.projects,
257
262
  rojoProject: values.rojoProject,
258
263
  setupFiles: values.setupFiles,
264
+ setupFilesAfterEnv: values.setupFilesAfterEnv,
259
265
  showLuau: values["no-show-luau"] === true ? false : values.showLuau,
260
266
  silent: values.silent,
261
267
  sourceMap: values.sourceMap,
@@ -375,6 +381,7 @@ function mergeCliWithConfig(cli, config) {
375
381
  projects: cli.projects ?? config.projects,
376
382
  rojoProject: cli.rojoProject ?? config.rojoProject,
377
383
  setupFiles: cli.setupFiles ?? config.setupFiles,
384
+ setupFilesAfterEnv: cli.setupFilesAfterEnv ?? config.setupFilesAfterEnv,
378
385
  showLuau: cli.showLuau ?? config.showLuau,
379
386
  silent: cli.silent ?? config.silent,
380
387
  sourceMap: cli.sourceMap ?? config.sourceMap,
@@ -126,7 +126,7 @@ function validateJestResult(value) {
126
126
 
127
127
  //#endregion
128
128
  //#region src/test-runner.luau
129
- var test_runner_default = "local HttpService = game:GetService(\"HttpService\")\nlocal LogService = game:GetService(\"LogService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal t0 = os.clock()\nlocal baseConfig = HttpService:JSONDecode([[__CONFIG_JSON__]])\nlocal t_configDecode = os.clock()\n\ntype Config = {\n jestPath: string?,\n projects: { string }?,\n setupFiles: { string }?,\n _timing: boolean?,\n}\n\nlocal function fail(err: string)\n return {\n success = false,\n err = err,\n }\nend\n\nlocal config = table.clone(baseConfig) :: Config\nlocal timingEnabled = config._timing\n\nlocal function findInstance(path: string, class: string?): Instance\n local parts = string.split(path, \"/\")\n\n local result, current: Instance? = pcall(function()\n return game:FindService(parts[1])\n end)\n assert(result, `Failed to find service {parts[1]}: {current}`)\n\n for i = 2, #parts do\n assert(current, `Failed to find instance at {parts[i]}`)\n current = current:FindFirstChild(parts[i])\n end\n\n assert(current, `Failed to find instance at path {path}: {current}`)\n\n return current\nend\n\nlocal function getJest(): ModuleScript\n local jestPath = config.jestPath\n if jestPath then\n local instance = findInstance(jestPath)\n assert(instance, `Failed to find Jest instance at path ${jestPath}`)\n assert(instance:IsA(\"ModuleScript\"), `Instance at path ${jestPath} is not a ModuleScript`)\n return instance\n end\n\n local jestInstance = ReplicatedStorage:FindFirstChild(\"Jest\", true)\n assert(jestInstance, \"Failed to find Jest instance in ReplicatedStorage\")\n assert(\n jestInstance:IsA(\"ModuleScript\"),\n \"Jest instance in ReplicatedStorage is not a ModuleScript\"\n )\n return jestInstance\nend\n\nlocal t_findJest0 = os.clock()\nlocal findSuccess, findValue = pcall(getJest)\nlocal t_findJest = os.clock()\n\nif not findSuccess then\n local logSuccess, logHistory = pcall(function()\n return HttpService:JSONEncode(LogService:GetLogHistory())\n end)\n\n return HttpService:JSONEncode(fail(findValue :: any)), if logSuccess then logHistory else \"[]\"\nend\n\nLogService:ClearOutput()\n\n-- Snapshot support: patch jest-roblox-shared before requiring Jest\nlocal snapshotWrites: { [string]: string } = {}\n\nlocal function getInstancePath(instance: Instance): string\n local parts = {} :: { string }\n local curr: Instance? = instance\n while curr and curr ~= game do\n table.insert(parts, 1, curr.Name)\n curr = curr.Parent\n end\n\n return table.concat(parts, \"/\")\nend\n\nlocal mockFileSystemService = {\n WriteFile = function(self: any, path: string, contents: string)\n -- Rewrite .snap.lua → .snap.luau\n snapshotWrites[string.gsub(path, \"%.snap%.lua$\", \".snap.luau\")] = contents\n end,\n CreateDirectories = function(self: any, _path: string) end,\n Exists = function(self: any, path: string): boolean\n return snapshotWrites[string.gsub(path, \"%.snap%.lua$\", \".snap.luau\")] ~= nil\n end,\n Remove = function(self: any, path: string)\n snapshotWrites[string.gsub(path, \"%.snap%.lua$\", \".snap.luau\")] = nil\n end,\n IsRegularFile = function(self: any, path: string): boolean\n return snapshotWrites[string.gsub(path, \"%.snap%.lua$\", \".snap.luau\")] ~= nil\n end,\n}\n\nlocal mockCoreScriptSyncService = {}\nfunction mockCoreScriptSyncService:GetScriptFilePath(script_: Instance): string\n return getInstancePath(script_)\nend\n\n-- Use _G so the mock survives across Jest's separate module loader context.\n-- Requires getDataModelService.lua to be patched (jest-roblox patch).\n_G.__mockGetDataModelService = function(service: string): any\n if service == \"FileSystemService\" then\n return mockFileSystemService\n elseif service == \"CoreScriptSyncService\" then\n return mockCoreScriptSyncService\n end\n\n local success, result = pcall(function()\n local svc = game:GetService(service)\n local _ = svc.Name\n return svc\n end)\n\n return if success then result else nil\nend\n\nlocal t_requireJest0 = os.clock()\nlocal Jest = (require :: any)(findValue)\nlocal t_requireJest = os.clock()\n\nlocal function runTests()\n local t_resolveProjects0 = os.clock()\n local projects = {}\n\n assert(\n config.projects and #config.projects > 0,\n \"No projects configured. Set 'projects' in jest.config.ts or pass --projects.\"\n )\n\n for _, projectPath in config.projects do\n table.insert(projects, findInstance(projectPath))\n end\n\n config.projects = {}\n local t_resolveProjects = os.clock()\n\n local t_resolveSetupFiles0 = os.clock()\n if config.setupFiles and #config.setupFiles > 0 then\n local resolved = {}\n\n for _, setupPath in config.setupFiles do\n table.insert(resolved, findInstance(setupPath))\n end\n\n config.setupFiles = resolved :: any\n end\n local t_resolveSetupFiles = os.clock()\n\n config._timing = nil :: any\n\n local t_jestRunCLI0 = os.clock()\n local jestResult = Jest.runCLI(script, config, projects):expect()\n local t_jestRunCLI = os.clock()\n\n local result: { [string]: any } = {\n success = true,\n value = jestResult,\n }\n\n if timingEnabled then\n result._timing = {\n configDecode = t_configDecode - t0,\n findJest = t_findJest - t_findJest0,\n requireJest = t_requireJest - t_requireJest0,\n resolveProjects = t_resolveProjects - t_resolveProjects0,\n resolveSetupFiles = t_resolveSetupFiles - t_resolveSetupFiles0,\n jestRunCLI = t_jestRunCLI - t_jestRunCLI0,\n }\n end\n\n if next(snapshotWrites) then\n result._snapshotWrites = snapshotWrites\n end\n\n return result\nend\n\nlocal runSuccess, runValue = pcall(runTests)\n\n_G.__mockGetDataModelService = nil\n\nlocal jestResult\nif not runSuccess then\n jestResult = HttpService:JSONEncode(fail(runValue :: any))\nelse\n jestResult = HttpService:JSONEncode(runValue)\nend\n\nlocal logSuccess, logHistory = pcall(function()\n return HttpService:JSONEncode(LogService:GetLogHistory())\nend)\n\nreturn jestResult, if logSuccess then logHistory else \"[]\"\n";
129
+ var test_runner_default = "local HttpService = game:GetService(\"HttpService\")\nlocal LogService = game:GetService(\"LogService\")\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\n\nlocal t0 = os.clock()\nlocal baseConfig = HttpService:JSONDecode([[__CONFIG_JSON__]])\nlocal t_configDecode = os.clock()\n\ntype Config = {\n jestPath: string?,\n projects: { string }?,\n setupFiles: { string }?,\n setupFilesAfterEnv: { string }?,\n _timing: boolean?,\n}\n\nlocal function fail(err: string)\n return {\n success = false,\n err = err,\n }\nend\n\nlocal config = table.clone(baseConfig) :: Config\nlocal timingEnabled = config._timing\n\nlocal function findInstance(path: string, class: string?): Instance\n local parts = string.split(path, \"/\")\n\n local result, current: Instance? = pcall(function()\n return game:FindService(parts[1])\n end)\n assert(result, `Failed to find service {parts[1]}: {current}`)\n\n for i = 2, #parts do\n assert(current, `Failed to find instance at {parts[i]}`)\n current = current:FindFirstChild(parts[i])\n end\n\n assert(current, `Failed to find instance at path {path}: {current}`)\n\n return current\nend\n\nlocal function getJest(): ModuleScript\n local jestPath = config.jestPath\n if jestPath then\n local instance = findInstance(jestPath)\n assert(instance, `Failed to find Jest instance at path ${jestPath}`)\n assert(instance:IsA(\"ModuleScript\"), `Instance at path ${jestPath} is not a ModuleScript`)\n return instance\n end\n\n local jestInstance = ReplicatedStorage:FindFirstChild(\"Jest\", true)\n assert(jestInstance, \"Failed to find Jest instance in ReplicatedStorage\")\n assert(\n jestInstance:IsA(\"ModuleScript\"),\n \"Jest instance in ReplicatedStorage is not a ModuleScript\"\n )\n return jestInstance\nend\n\nlocal t_findJest0 = os.clock()\nlocal findSuccess, findValue = pcall(getJest)\nlocal t_findJest = os.clock()\n\nif not findSuccess then\n local logSuccess, logHistory = pcall(function()\n return HttpService:JSONEncode(LogService:GetLogHistory())\n end)\n\n return HttpService:JSONEncode(fail(findValue :: any)), if logSuccess then logHistory else \"[]\"\nend\n\nLogService:ClearOutput()\n\n-- Snapshot support: patch jest-roblox-shared before requiring Jest\nlocal snapshotWrites: { [string]: string } = {}\n\nlocal function getInstancePath(instance: Instance): string\n local parts = {} :: { string }\n local curr: Instance? = instance\n while curr and curr ~= game do\n table.insert(parts, 1, curr.Name)\n curr = curr.Parent\n end\n\n return table.concat(parts, \"/\")\nend\n\nlocal mockFileSystemService = {\n WriteFile = function(self: any, path: string, contents: string)\n -- Rewrite .snap.lua → .snap.luau\n snapshotWrites[string.gsub(path, \"%.snap%.lua$\", \".snap.luau\")] = contents\n end,\n CreateDirectories = function(self: any, _path: string) end,\n Exists = function(self: any, path: string): boolean\n return snapshotWrites[string.gsub(path, \"%.snap%.lua$\", \".snap.luau\")] ~= nil\n end,\n Remove = function(self: any, path: string)\n snapshotWrites[string.gsub(path, \"%.snap%.lua$\", \".snap.luau\")] = nil\n end,\n IsRegularFile = function(self: any, path: string): boolean\n return snapshotWrites[string.gsub(path, \"%.snap%.lua$\", \".snap.luau\")] ~= nil\n end,\n}\n\nlocal mockCoreScriptSyncService = {}\nfunction mockCoreScriptSyncService:GetScriptFilePath(script_: Instance): string\n return getInstancePath(script_)\nend\n\n-- Use _G so the mock survives across Jest's separate module loader context.\n-- Requires getDataModelService.lua to be patched (jest-roblox patch).\n_G.__mockGetDataModelService = function(service: string): any\n if service == \"FileSystemService\" then\n return mockFileSystemService\n elseif service == \"CoreScriptSyncService\" then\n return mockCoreScriptSyncService\n end\n\n local success, result = pcall(function()\n local svc = game:GetService(service)\n local _ = svc.Name\n return svc\n end)\n\n return if success then result else nil\nend\n\nlocal t_requireJest0 = os.clock()\nlocal Jest = (require :: any)(findValue)\nlocal t_requireJest = os.clock()\n\nlocal function runTests()\n local t_resolveProjects0 = os.clock()\n local projects = {}\n\n assert(\n config.projects and #config.projects > 0,\n \"No projects configured. Set 'projects' in jest.config.ts or pass --projects.\"\n )\n\n for _, projectPath in config.projects do\n table.insert(projects, findInstance(projectPath))\n end\n\n config.projects = {}\n local t_resolveProjects = os.clock()\n\n local t_resolveSetupFiles0 = os.clock()\n if config.setupFiles and #config.setupFiles > 0 then\n local resolved = {}\n\n for _, setupPath in config.setupFiles do\n table.insert(resolved, findInstance(setupPath))\n end\n\n config.setupFiles = resolved :: any\n end\n if config.setupFilesAfterEnv and #config.setupFilesAfterEnv > 0 then\n local resolved = {}\n\n for _, setupPath in config.setupFilesAfterEnv do\n table.insert(resolved, findInstance(setupPath))\n end\n\n config.setupFilesAfterEnv = resolved :: any\n end\n local t_resolveSetupFiles = os.clock()\n\n config._timing = nil :: any\n\n local t_jestRunCLI0 = os.clock()\n local jestResult = Jest.runCLI(script, config, projects):expect()\n local t_jestRunCLI = os.clock()\n\n local result: { [string]: any } = {\n success = true,\n value = jestResult,\n }\n\n if timingEnabled then\n result._timing = {\n configDecode = t_configDecode - t0,\n findJest = t_findJest - t_findJest0,\n requireJest = t_requireJest - t_requireJest0,\n resolveProjects = t_resolveProjects - t_resolveProjects0,\n resolveSetupFiles = t_resolveSetupFiles - t_resolveSetupFiles0,\n jestRunCLI = t_jestRunCLI - t_jestRunCLI0,\n }\n end\n\n if next(snapshotWrites) then\n result._snapshotWrites = snapshotWrites\n end\n\n return result\nend\n\nlocal runSuccess, runValue = pcall(runTests)\n\n_G.__mockGetDataModelService = nil\n\nlocal jestResult\nif not runSuccess then\n jestResult = HttpService:JSONEncode(fail(runValue :: any))\nelse\n jestResult = HttpService:JSONEncode(runValue)\nend\n\nlocal logSuccess, logHistory = pcall(function()\n return HttpService:JSONEncode(LogService:GetLogHistory())\nend)\n\nreturn jestResult, if logSuccess then logHistory else \"[]\"\n";
130
130
 
131
131
  //#endregion
132
132
  //#region src/test-script.ts
@@ -503,7 +503,7 @@ function findExpectationColumn(lineText) {
503
503
  const expectIndex = lineText.search(/\bexpect\s*\(/);
504
504
  if (expectIndex === -1) return 1;
505
505
  const afterExpect = lineText.slice(expectIndex);
506
- const matcherRegex = /\.\s*([A-Za-z_$][\w$]*)\s*(?=\()/g;
506
+ const matcherRegex = /[.:]\s*([A-Za-z_$][\w$]*)\s*(?=\()/g;
507
507
  let lastMatcher = null;
508
508
  for (const match of afterExpect.matchAll(matcherRegex)) lastMatcher = match;
509
509
  const matcherName = lastMatcher?.[1];
@@ -515,12 +515,12 @@ function findExpectationColumn(lineText) {
515
515
  //#region src/source-mapper/line-mapper.ts
516
516
  function mapLine(luauLineNumber, luauLines, tsLines) {
517
517
  const luauLine = normalize(luauLines[luauLineNumber - 1]);
518
- if (luauLine === void 0) return;
518
+ if (luauLine === void 0 || luauLine === "") return;
519
519
  for (const [index, tsLine] of tsLines.entries()) if (normalize(tsLine) === luauLine) return index + 1;
520
520
  const stripped = luauLine.replace(/\s/g, "");
521
521
  for (const [index, tsLine] of tsLines.entries()) {
522
522
  const tsNormalized = normalize(tsLine);
523
- if (tsNormalized === void 0) continue;
523
+ if (tsNormalized === void 0 || tsNormalized === "") continue;
524
524
  const tsStripped = tsNormalized.replace(/\s/g, "");
525
525
  if (tsStripped.includes(stripped) || stripped.includes(tsStripped)) return index + 1;
526
526
  }
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as Argv, i as ResolvedConfig, n as Config, r as DEFAULT_CONFIG, t as CliOptions } from "./schema-CEsyg-FX.mjs";
1
+ import { a as Argv, i as ResolvedConfig, n as Config, r as DEFAULT_CONFIG, t as CliOptions } from "./schema-hhbEFVXy.mjs";
2
2
  import { WebSocketServer } from "ws";
3
3
  import buffer from "node:buffer";
4
4
 
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { _ as buildJestArgv, a as formatJson, b as extractJsonFromOutput, c as formatResult, d as resolveConfig, f as DEFAULT_CONFIG, g as createOpenCloudBackend, h as OpenCloudBackend, i as execute, l as formatTestSummary, m as createStudioBackend, n as parseGameOutput, o as writeJsonFile, p as StudioBackend, r as writeGameOutput, s as formatFailure, t as formatGameOutputNotice, u as loadConfig, v as generateTestScript, x as parseJestOutput } from "./game-output-BDatGA9S.mjs";
1
+ import { _ as buildJestArgv, a as formatJson, b as extractJsonFromOutput, c as formatResult, d as resolveConfig, f as DEFAULT_CONFIG, g as createOpenCloudBackend, h as OpenCloudBackend, i as execute, l as formatTestSummary, m as createStudioBackend, n as parseGameOutput, o as writeJsonFile, p as StudioBackend, r as writeGameOutput, s as formatFailure, t as formatGameOutputNotice, u as loadConfig, v as generateTestScript, x as parseJestOutput } from "./game-output--EpqHGEr.mjs";
2
2
 
3
3
  export { DEFAULT_CONFIG, OpenCloudBackend, StudioBackend, buildJestArgv, createOpenCloudBackend, createStudioBackend, execute, extractJsonFromOutput, formatFailure, formatGameOutputNotice, formatJson, formatResult, formatTestSummary, generateTestScript, loadConfig, parseGameOutput, parseJestOutput, resolveConfig, writeGameOutput, writeJsonFile };
@@ -757,7 +757,7 @@ type Except<ObjectType, KeysType extends keyof ObjectType, Options extends Excep
757
757
  type _Except<ObjectType, KeysType extends keyof ObjectType, Options extends Required<ExceptOptions>> = { [KeyType in keyof ObjectType as Filter<KeyType, KeysType>]: ObjectType[KeyType] } & (Options['requireExactProps'] extends true ? Partial<Record<KeysType, never>> : {});
758
758
  //#endregion
759
759
  //#region src/config/schema.d.ts
760
- interface Config extends Except<Argv, "rootDir" | "setupFiles" | "testPathPattern"> {
760
+ interface Config extends Except<Argv, "rootDir" | "setupFiles" | "setupFilesAfterEnv" | "testPathPattern"> {
761
761
  backend?: Backend;
762
762
  cache?: boolean;
763
763
  compact?: boolean;
@@ -770,6 +770,7 @@ interface Config extends Except<Argv, "rootDir" | "setupFiles" | "testPathPatter
770
770
  rojoProject?: string;
771
771
  rootDir?: string;
772
772
  setupFiles?: Array<string>;
773
+ setupFilesAfterEnv?: Array<string>;
773
774
  showLuau?: boolean;
774
775
  sourceMap?: boolean;
775
776
  testPathPattern?: string;
@@ -815,6 +816,7 @@ interface CliOptions {
815
816
  projects?: Array<string>;
816
817
  rojoProject?: string;
817
818
  setupFiles?: Array<string>;
819
+ setupFilesAfterEnv?: Array<string>;
818
820
  showLuau?: boolean;
819
821
  silent?: boolean;
820
822
  sourceMap?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isentinel/jest-roblox",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Jest-compatible CLI for running roblox-ts tests via Roblox Open Cloud",
5
5
  "keywords": [
6
6
  "jest",
Binary file
@@ -169,6 +169,15 @@ function module.runTestsAync(
169
169
 
170
170
  config.setupFiles = resolved
171
171
  end
172
+ if config.setupFilesAfterEnv and #config.setupFilesAfterEnv > 0 then
173
+ local resolved = {}
174
+
175
+ for _, setupPath in config.setupFilesAfterEnv do
176
+ table.insert(resolved, module.findInstance(setupPath))
177
+ end
178
+
179
+ config.setupFilesAfterEnv = resolved
180
+ end
172
181
  local t_resolveSetupFiles = os.clock()
173
182
 
174
183
  config._timing = nil