@isentinel/jest-roblox 0.0.8 → 0.1.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/LICENSE.md +21 -0
- package/README.md +131 -130
- package/bin/jest-roblox.js +14 -2
- package/dist/cli.d.mts +4 -2
- package/dist/cli.mjs +920 -183
- package/dist/{schema-DcDQmTyn.d.mts → executor-DqZE3wME.d.mts} +236 -31
- package/dist/{game-output-M8du29nj.mjs → game-output-C0_-YIAY.mjs} +1095 -468
- package/dist/index.d.mts +6 -145
- package/dist/index.mjs +2 -3
- package/dist/sea/jest-roblox +0 -0
- package/dist/sea-entry.cjs +61580 -0
- package/package.json +17 -42
- package/plugin/JestRobloxRunner.rbxm +0 -0
- package/plugin/out/shared/instance-resolver.luau +3 -8
- package/plugin/out/shared/runner.luau +62 -13
- package/plugin/out/shared/snapshot-patch.luau +2 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isentinel/jest-roblox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Jest-compatible CLI for running roblox-ts tests via Roblox Open Cloud",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jest",
|
|
@@ -39,8 +39,11 @@
|
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@jridgewell/trace-mapping": "0.3.31",
|
|
42
|
+
"@roblox-ts/rojo-resolver": "1.1.0",
|
|
42
43
|
"arktype": "2.1.29",
|
|
43
|
-
"c12": "
|
|
44
|
+
"c12": "4.0.0-beta.4",
|
|
45
|
+
"confbox": "0.2.2",
|
|
46
|
+
"defu": "6.1.4",
|
|
44
47
|
"get-tsconfig": "4.13.6",
|
|
45
48
|
"highlight.js": "11.11.1",
|
|
46
49
|
"istanbul-lib-coverage": "3.2.2",
|
|
@@ -48,15 +51,18 @@
|
|
|
48
51
|
"istanbul-reports": "3.2.0",
|
|
49
52
|
"oxc-parser": "0.120.0",
|
|
50
53
|
"picomatch": "4.0.3",
|
|
54
|
+
"std-env": "4.0.0",
|
|
51
55
|
"tinyrainbow": "3.0.3",
|
|
52
56
|
"ws": "8.18.0"
|
|
53
57
|
},
|
|
54
58
|
"devDependencies": {
|
|
55
59
|
"@isentinel/eslint-config": "5.0.0-beta.9",
|
|
60
|
+
"@isentinel/tsconfig": "1.2.0",
|
|
56
61
|
"@oxc-project/types": "0.120.0",
|
|
57
62
|
"@rbxts/jest": "3.13.3-ts.1",
|
|
58
63
|
"@rbxts/types": "1.0.911",
|
|
59
64
|
"@total-typescript/shoehorn": "0.1.2",
|
|
65
|
+
"@tsdown/exe": "0.21.4",
|
|
60
66
|
"@types/istanbul-lib-coverage": "2.0.6",
|
|
61
67
|
"@types/istanbul-lib-report": "3.0.3",
|
|
62
68
|
"@types/istanbul-reports": "3.0.4",
|
|
@@ -64,64 +70,33 @@
|
|
|
64
70
|
"@types/picomatch": "4.0.2",
|
|
65
71
|
"@types/ws": "8.5.13",
|
|
66
72
|
"@typescript/native-preview": "7.0.0-dev.20260308.1",
|
|
73
|
+
"@vitest/coverage-v8": "4.1.0",
|
|
67
74
|
"@vitest/eslint-plugin": "1.6.12",
|
|
68
75
|
"better-typescript-lib": "2.12.0",
|
|
69
76
|
"bumpp": "10.4.1",
|
|
70
77
|
"eslint": "9.39.2",
|
|
71
78
|
"eslint-plugin-jest-extended": "3.0.1",
|
|
79
|
+
"eslint-plugin-n": "17.23.1",
|
|
80
|
+
"eslint-plugin-pnpm": "1.4.3",
|
|
72
81
|
"jest-extended": "7.0.0",
|
|
73
82
|
"memfs": "4.56.11",
|
|
74
83
|
"publint": "0.3.15",
|
|
75
|
-
"tsdown": "0.
|
|
84
|
+
"tsdown": "0.21.4",
|
|
76
85
|
"type-fest": "5.2.0",
|
|
86
|
+
"typescript": "5.9.3",
|
|
77
87
|
"vitest": "4.1.0"
|
|
78
88
|
},
|
|
79
89
|
"engines": {
|
|
80
|
-
"node": ">=
|
|
81
|
-
},
|
|
82
|
-
"nx": {
|
|
83
|
-
"name": "jest-roblox-cli",
|
|
84
|
-
"tags": [
|
|
85
|
-
"scope:tooling",
|
|
86
|
-
"type:cli"
|
|
87
|
-
],
|
|
88
|
-
"projectType": "application",
|
|
89
|
-
"includedScripts": [
|
|
90
|
-
"!build"
|
|
91
|
-
],
|
|
92
|
-
"targets": {
|
|
93
|
-
"build": {
|
|
94
|
-
"command": "pnpm build:bundle && tsdown && pnpm build:plugin",
|
|
95
|
-
"options": {
|
|
96
|
-
"cwd": "tools/jest-roblox-cli"
|
|
97
|
-
}
|
|
98
|
-
},
|
|
99
|
-
"lint": {
|
|
100
|
-
"command": "eslint",
|
|
101
|
-
"hasTypeAwareRules": true
|
|
102
|
-
},
|
|
103
|
-
"build:bundle": {
|
|
104
|
-
"command": "pnpm build:bundle",
|
|
105
|
-
"options": {
|
|
106
|
-
"cwd": "tools/jest-roblox-cli"
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
"test": {
|
|
110
|
-
"command": "vitest run",
|
|
111
|
-
"dependsOn": [
|
|
112
|
-
"build:bundle"
|
|
113
|
-
],
|
|
114
|
-
"options": {
|
|
115
|
-
"cwd": "tools/jest-roblox-cli"
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
90
|
+
"node": ">=24.10.0"
|
|
119
91
|
},
|
|
120
92
|
"scripts": {
|
|
121
93
|
"build": "pnpm build:bundle && tsdown && pnpm build:plugin",
|
|
122
94
|
"build:bundle": "darklua process -c .darklua-bundle.json luau/entry.luau src/test-runner.bundled.luau",
|
|
123
95
|
"build:plugin": "rm -rf plugin/out && darklua process -c .darklua-plugin.json luau/ plugin/out/shared/ && rojo build plugin/plugin.project.json -o plugin/JestRobloxRunner.rbxm",
|
|
96
|
+
"lint": "eslint --cache",
|
|
97
|
+
"lint:ci": "eslint --cache --cache-strategy content",
|
|
124
98
|
"release": "bumpp",
|
|
99
|
+
"test": "nr typecheck && vitest run --coverage",
|
|
125
100
|
"typecheck": "tsgo --build --emitDeclarationOnly",
|
|
126
101
|
"watch": "tsdown --watch"
|
|
127
102
|
}
|
|
Binary file
|
|
@@ -32,25 +32,20 @@ function module.getJest(config: { jestPath: string? }): ModuleScript
|
|
|
32
32
|
|
|
33
33
|
local jestInstance = ReplicatedStorage:FindFirstChild("Jest", true)
|
|
34
34
|
assert(jestInstance, "Failed to find Jest instance in ReplicatedStorage")
|
|
35
|
-
assert(
|
|
36
|
-
jestInstance:IsA("ModuleScript"),
|
|
37
|
-
"Jest instance in ReplicatedStorage is not a ModuleScript"
|
|
38
|
-
)
|
|
35
|
+
assert(jestInstance:IsA("ModuleScript"), "Jest instance in ReplicatedStorage is not a ModuleScript")
|
|
39
36
|
return jestInstance
|
|
40
37
|
end
|
|
41
38
|
|
|
42
39
|
function module.findRobloxShared(jestModule: ModuleScript): Instance?
|
|
43
40
|
local parent = jestModule.Parent
|
|
44
41
|
if parent then
|
|
45
|
-
local found = parent:FindFirstChild("RobloxShared")
|
|
46
|
-
or parent:FindFirstChild("jest-roblox-shared")
|
|
42
|
+
local found = parent:FindFirstChild("RobloxShared") or parent:FindFirstChild("jest-roblox-shared")
|
|
47
43
|
if found then
|
|
48
44
|
return found
|
|
49
45
|
end
|
|
50
46
|
|
|
51
47
|
if parent.Parent then
|
|
52
|
-
found = parent.Parent:FindFirstChild("RobloxShared")
|
|
53
|
-
or parent.Parent:FindFirstChild("jest-roblox-shared")
|
|
48
|
+
found = parent.Parent:FindFirstChild("RobloxShared") or parent.Parent:FindFirstChild("jest-roblox-shared")
|
|
54
49
|
if found then
|
|
55
50
|
return found
|
|
56
51
|
end
|
|
@@ -21,6 +21,28 @@ local function fail(err: string)
|
|
|
21
21
|
}
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
type CapturedMessage = { message: string, messageType: number, timestamp: number }
|
|
25
|
+
|
|
26
|
+
local function interceptWriteable(writeable: any, buffer: { CapturedMessage }, messageType: number)
|
|
27
|
+
local original = writeable._writeFn
|
|
28
|
+
if typeof(original) ~= "function" then
|
|
29
|
+
return function() end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
writeable._writeFn = function(data: string)
|
|
33
|
+
table.insert(buffer, {
|
|
34
|
+
message = data,
|
|
35
|
+
messageType = messageType,
|
|
36
|
+
timestamp = os.clock(),
|
|
37
|
+
})
|
|
38
|
+
original(data)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
return function()
|
|
42
|
+
writeable._writeFn = original
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
24
46
|
local module = {}
|
|
25
47
|
|
|
26
48
|
function module.run(callingScript: LuaSourceContainer, config: Config): (string, string)
|
|
@@ -37,8 +59,7 @@ function module.run(callingScript: LuaSourceContainer, config: Config): (string,
|
|
|
37
59
|
return HttpService:JSONEncode(LogService:GetLogHistory())
|
|
38
60
|
end)
|
|
39
61
|
|
|
40
|
-
return HttpService:JSONEncode(fail(findValue :: any)),
|
|
41
|
-
if logSuccess then logHistory else "[]"
|
|
62
|
+
return HttpService:JSONEncode(fail(findValue :: any)), if logSuccess then logHistory else "[]"
|
|
42
63
|
end
|
|
43
64
|
|
|
44
65
|
LogService:ClearOutput()
|
|
@@ -53,6 +74,28 @@ function module.run(callingScript: LuaSourceContainer, config: Config): (string,
|
|
|
53
74
|
local Jest = (require :: any)(findValue)
|
|
54
75
|
local t_requireJest = os.clock()
|
|
55
76
|
|
|
77
|
+
-- Intercept Jest's stdout/stderr to capture output synchronously.
|
|
78
|
+
-- Jest writes via process.stdout/stderr (Writeable objects whose _writeFn
|
|
79
|
+
-- defaults to print). Wrapping _writeFn captures messages like
|
|
80
|
+
-- "No tests found" that are printed just before exit(1) throws.
|
|
81
|
+
local capturedMessages: { CapturedMessage } = {}
|
|
82
|
+
local restoreStdout: (() -> ())?
|
|
83
|
+
local restoreStderr: (() -> ())?
|
|
84
|
+
|
|
85
|
+
local interceptOk = pcall(function()
|
|
86
|
+
local nodeModules = findValue.Parent.Parent.Parent :: any
|
|
87
|
+
local RobloxShared = (require :: any)(nodeModules["@rbxts-js"].RobloxShared)
|
|
88
|
+
local process = RobloxShared.nodeUtils.process
|
|
89
|
+
|
|
90
|
+
restoreStdout = interceptWriteable(process.stdout, capturedMessages, 0)
|
|
91
|
+
restoreStderr = interceptWriteable(process.stderr, capturedMessages, 1)
|
|
92
|
+
end)
|
|
93
|
+
|
|
94
|
+
if not interceptOk then
|
|
95
|
+
restoreStdout = nil
|
|
96
|
+
restoreStderr = nil
|
|
97
|
+
end
|
|
98
|
+
|
|
56
99
|
local function runTests()
|
|
57
100
|
local t_resolveProjects0 = os.clock()
|
|
58
101
|
local projects = {}
|
|
@@ -142,17 +185,15 @@ function module.run(callingScript: LuaSourceContainer, config: Config): (string,
|
|
|
142
185
|
end)
|
|
143
186
|
|
|
144
187
|
local infiniteYieldMessage: string? = nil
|
|
145
|
-
local watchdogConnection = LogService.MessageOut:Connect(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
infiniteYieldMessage = message
|
|
153
|
-
end
|
|
188
|
+
local watchdogConnection = LogService.MessageOut:Connect(function(message: string, messageType: Enum.MessageType)
|
|
189
|
+
if
|
|
190
|
+
messageType == Enum.MessageType.MessageWarning
|
|
191
|
+
and string.find(message, "Infinite yield possible")
|
|
192
|
+
and not infiniteYieldMessage
|
|
193
|
+
then
|
|
194
|
+
infiniteYieldMessage = message
|
|
154
195
|
end
|
|
155
|
-
)
|
|
196
|
+
end)
|
|
156
197
|
|
|
157
198
|
while not jestDone and not infiniteYieldMessage do
|
|
158
199
|
task.wait(0.1)
|
|
@@ -160,6 +201,14 @@ function module.run(callingScript: LuaSourceContainer, config: Config): (string,
|
|
|
160
201
|
|
|
161
202
|
watchdogConnection:Disconnect()
|
|
162
203
|
|
|
204
|
+
if restoreStdout then
|
|
205
|
+
restoreStdout()
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
if restoreStderr then
|
|
209
|
+
restoreStderr()
|
|
210
|
+
end
|
|
211
|
+
|
|
163
212
|
if not jestDone and infiniteYieldMessage then
|
|
164
213
|
runSuccess = false
|
|
165
214
|
runValue = "Infinite yield detected, aborting tests: " .. infiniteYieldMessage
|
|
@@ -175,7 +224,7 @@ function module.run(callingScript: LuaSourceContainer, config: Config): (string,
|
|
|
175
224
|
end
|
|
176
225
|
|
|
177
226
|
local logSuccess, logHistory = pcall(function()
|
|
178
|
-
return HttpService:JSONEncode(
|
|
227
|
+
return HttpService:JSONEncode(capturedMessages)
|
|
179
228
|
end)
|
|
180
229
|
|
|
181
230
|
return jestResult, if logSuccess then logHistory else "[]"
|
|
@@ -46,8 +46,7 @@ function module.patch(jestModule: ModuleScript, snapshotWrites: { [string]: stri
|
|
|
46
46
|
|
|
47
47
|
local getDataModelServiceChild = robloxSharedInstance:FindFirstChild("getDataModelService")
|
|
48
48
|
|
|
49
|
-
local jestRuntimeModule =
|
|
50
|
-
InstanceResolver.findSiblingPackage(jestModule, "JestRuntime", "jest-runtime")
|
|
49
|
+
local jestRuntimeModule = InstanceResolver.findSiblingPackage(jestModule, "JestRuntime", "jest-runtime")
|
|
51
50
|
if not jestRuntimeModule then
|
|
52
51
|
warn("Could not find JestRuntime; snapshot interception unavailable")
|
|
53
52
|
return {
|
|
@@ -63,11 +62,7 @@ function module.patch(jestModule: ModuleScript, snapshotWrites: { [string]: stri
|
|
|
63
62
|
|
|
64
63
|
Runtime.requireInternalModule = function(self: any, from: any, to: any, ...): any
|
|
65
64
|
local target = if to ~= nil then to else from
|
|
66
|
-
if
|
|
67
|
-
getDataModelServiceChild
|
|
68
|
-
and typeof(target) == "Instance"
|
|
69
|
-
and target == getDataModelServiceChild
|
|
70
|
-
then
|
|
65
|
+
if getDataModelServiceChild and typeof(target) == "Instance" and target == getDataModelServiceChild then
|
|
71
66
|
return mockGetDataModelService
|
|
72
67
|
end
|
|
73
68
|
|