@quenty/nevermore-cli 4.50.0 → 4.50.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/CHANGELOG.md +6 -0
- package/dist/commands/tools-command/ci-post-test-results.d.ts.map +1 -1
- package/dist/commands/tools-command/ci-post-test-results.js +7 -3
- package/dist/commands/tools-command/ci-post-test-results.js.map +1 -1
- package/dist/utils/batch/changed-packages-utils.d.ts +8 -0
- package/dist/utils/batch/changed-packages-utils.d.ts.map +1 -1
- package/dist/utils/batch/changed-packages-utils.js +9 -11
- package/dist/utils/batch/changed-packages-utils.js.map +1 -1
- package/dist/utils/testing/parsers/annotation-workspace.d.ts +17 -0
- package/dist/utils/testing/parsers/annotation-workspace.d.ts.map +1 -0
- package/dist/utils/testing/parsers/annotation-workspace.js +47 -0
- package/dist/utils/testing/parsers/annotation-workspace.js.map +1 -0
- package/dist/utils/testing/parsers/index.d.ts +0 -1
- package/dist/utils/testing/parsers/index.d.ts.map +1 -1
- package/dist/utils/testing/parsers/index.js +0 -1
- package/dist/utils/testing/parsers/index.js.map +1 -1
- package/dist/utils/testing/parsers/jest-lua-parser.d.ts +14 -29
- package/dist/utils/testing/parsers/jest-lua-parser.d.ts.map +1 -1
- package/dist/utils/testing/parsers/jest-lua-parser.js +85 -100
- package/dist/utils/testing/parsers/jest-lua-parser.js.map +1 -1
- package/dist/utils/testing/parsers/jest-lua-parser.test.js +359 -56
- package/dist/utils/testing/parsers/jest-lua-parser.test.js.map +1 -1
- package/dist/utils/testing/parsers/roblox-path-resolver.d.ts +23 -17
- package/dist/utils/testing/parsers/roblox-path-resolver.d.ts.map +1 -1
- package/dist/utils/testing/parsers/roblox-path-resolver.js +89 -54
- package/dist/utils/testing/parsers/roblox-path-resolver.js.map +1 -1
- package/dist/utils/testing/parsers/roblox-path-resolver.test.js +76 -36
- package/dist/utils/testing/parsers/roblox-path-resolver.test.js.map +1 -1
- package/dist/utils/testing/runner/combined-project-generator.d.ts.map +1 -1
- package/dist/utils/testing/runner/combined-project-generator.js +5 -6
- package/dist/utils/testing/runner/combined-project-generator.js.map +1 -1
- package/dist/utils/testing/runner/test-project-slug.d.ts +11 -0
- package/dist/utils/testing/runner/test-project-slug.d.ts.map +1 -0
- package/dist/utils/testing/runner/test-project-slug.js +11 -0
- package/dist/utils/testing/runner/test-project-slug.js.map +1 -0
- package/package.json +6 -6
- package/src/commands/tools-command/ci-post-test-results.ts +7 -2
- package/src/utils/batch/changed-packages-utils.ts +21 -12
- package/src/utils/testing/parsers/annotation-workspace.ts +74 -0
- package/src/utils/testing/parsers/index.ts +0 -1
- package/src/utils/testing/parsers/jest-lua-parser.test.ts +433 -65
- package/src/utils/testing/parsers/jest-lua-parser.ts +131 -109
- package/src/utils/testing/parsers/roblox-path-resolver.test.ts +144 -61
- package/src/utils/testing/parsers/roblox-path-resolver.ts +117 -56
- package/src/utils/testing/runner/combined-project-generator.ts +9 -13
- package/src/utils/testing/runner/test-project-slug.ts +18 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
1
|
+
import { beforeAll, describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import type { PathOrFileDescriptor } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
2
4
|
import { parseJestLuaOutput } from './jest-lua-parser.js';
|
|
3
5
|
import { SourcemapResolver } from '../../sourcemap/index.js';
|
|
4
6
|
import type { SourcemapNode } from '../../sourcemap/index.js';
|
|
7
|
+
import {
|
|
8
|
+
type AnnotationWorkspace,
|
|
9
|
+
tryLoadAnnotationWorkspaceAsync,
|
|
10
|
+
} from './annotation-workspace.js';
|
|
11
|
+
|
|
12
|
+
// Pass-through, so spec searches read the real repo unless a test stubs them.
|
|
13
|
+
vi.mock('fs', async (importOriginal) => {
|
|
14
|
+
const actual = await importOriginal<typeof import('fs')>();
|
|
15
|
+
return { ...actual, readFileSync: vi.fn(actual.readFileSync) };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const REPO_ROOT = fileURLToPath(new URL('../../../../../../', import.meta.url));
|
|
5
19
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}),
|
|
11
|
-
}));
|
|
20
|
+
let workspace: AnnotationWorkspace | undefined;
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
workspace = await tryLoadAnnotationWorkspaceAsync(REPO_ROOT);
|
|
23
|
+
}, 30000);
|
|
12
24
|
|
|
13
25
|
describe('parseJestLuaOutput', () => {
|
|
14
26
|
beforeEach(() => {
|
|
@@ -28,7 +40,9 @@ describe('parseJestLuaOutput', () => {
|
|
|
28
40
|
].join('\n');
|
|
29
41
|
|
|
30
42
|
const result = parseJestLuaOutput(input, {
|
|
31
|
-
packageName: 'observablecollection',
|
|
43
|
+
packageName: '@quenty/observablecollection',
|
|
44
|
+
repoRoot: REPO_ROOT,
|
|
45
|
+
workspace,
|
|
32
46
|
});
|
|
33
47
|
|
|
34
48
|
expect(result).toHaveLength(1);
|
|
@@ -51,22 +65,29 @@ describe('parseJestLuaOutput', () => {
|
|
|
51
65
|
' Expected: 1',
|
|
52
66
|
' Received: 2',
|
|
53
67
|
'',
|
|
54
|
-
' at ServerScriptService.
|
|
68
|
+
' at ServerScriptService.observablecollection.Shared.ObservableList.spec:10',
|
|
55
69
|
'',
|
|
56
70
|
' ● Suite > test two',
|
|
57
71
|
'',
|
|
58
72
|
' Expected: true',
|
|
59
73
|
' Received: false',
|
|
60
74
|
'',
|
|
61
|
-
' at ServerScriptService.
|
|
75
|
+
' at ServerScriptService.observablecollection.Shared.ObservableMap.spec:20',
|
|
62
76
|
].join('\n');
|
|
63
77
|
|
|
64
|
-
const result = parseJestLuaOutput(input, {
|
|
78
|
+
const result = parseJestLuaOutput(input, {
|
|
79
|
+
packageName: '@quenty/observablecollection',
|
|
80
|
+
repoRoot: REPO_ROOT,
|
|
81
|
+
workspace,
|
|
82
|
+
});
|
|
65
83
|
|
|
66
84
|
expect(result).toHaveLength(2);
|
|
67
85
|
expect(result[0].title).toBe('Suite > test one');
|
|
68
86
|
expect(result[0].line).toBe(10);
|
|
69
87
|
expect(result[1].title).toBe('Suite > test two');
|
|
88
|
+
expect(result[1].file).toBe(
|
|
89
|
+
'src/observablecollection/src/Shared/ObservableMap.spec.lua'
|
|
90
|
+
);
|
|
70
91
|
expect(result[1].line).toBe(20);
|
|
71
92
|
});
|
|
72
93
|
|
|
@@ -76,10 +97,14 @@ describe('parseJestLuaOutput', () => {
|
|
|
76
97
|
'',
|
|
77
98
|
' failure message',
|
|
78
99
|
'',
|
|
79
|
-
' at ServerScriptService.
|
|
100
|
+
' at ServerScriptService.maid.Shared.MaidTaskUtils.spec:99',
|
|
80
101
|
].join('\n');
|
|
81
102
|
|
|
82
|
-
const result = parseJestLuaOutput(input, {
|
|
103
|
+
const result = parseJestLuaOutput(input, {
|
|
104
|
+
packageName: '@quenty/maid',
|
|
105
|
+
repoRoot: REPO_ROOT,
|
|
106
|
+
workspace,
|
|
107
|
+
});
|
|
83
108
|
|
|
84
109
|
expect(result).toHaveLength(1);
|
|
85
110
|
expect(result[0].title).toBe(
|
|
@@ -87,7 +112,7 @@ describe('parseJestLuaOutput', () => {
|
|
|
87
112
|
);
|
|
88
113
|
});
|
|
89
114
|
|
|
90
|
-
it('
|
|
115
|
+
it('emits no file when there is no location', () => {
|
|
91
116
|
const input = [
|
|
92
117
|
' ● Suite > test without location',
|
|
93
118
|
'',
|
|
@@ -96,40 +121,72 @@ describe('parseJestLuaOutput', () => {
|
|
|
96
121
|
'Test Suites: 1 failed, 1 total',
|
|
97
122
|
].join('\n');
|
|
98
123
|
|
|
99
|
-
const result = parseJestLuaOutput(input, {
|
|
124
|
+
const result = parseJestLuaOutput(input, {
|
|
125
|
+
packageName: '@quenty/maid',
|
|
126
|
+
repoRoot: REPO_ROOT,
|
|
127
|
+
workspace,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(result).toHaveLength(1);
|
|
131
|
+
expect(result[0].file).toBeUndefined();
|
|
132
|
+
expect(result[0].line).toBeUndefined();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('emits no file when the location does not exist in the repo', () => {
|
|
136
|
+
const input = [
|
|
137
|
+
' ● Suite > test one',
|
|
138
|
+
'',
|
|
139
|
+
' Expected: 1',
|
|
140
|
+
'',
|
|
141
|
+
' at ServerScriptService.unknownpkg.Shared.Bar.spec:10',
|
|
142
|
+
].join('\n');
|
|
143
|
+
|
|
144
|
+
const result = parseJestLuaOutput(input, {
|
|
145
|
+
packageName: '@quenty/unknownpkg',
|
|
146
|
+
repoRoot: REPO_ROOT,
|
|
147
|
+
workspace,
|
|
148
|
+
});
|
|
100
149
|
|
|
101
150
|
expect(result).toHaveLength(1);
|
|
102
|
-
expect(result[0].
|
|
103
|
-
expect(result[0].file).toBe('src/mypkg/src');
|
|
151
|
+
expect(result[0].file).toBeUndefined();
|
|
104
152
|
});
|
|
105
153
|
|
|
106
154
|
it('searches spec file for it() call when at location has no line number', async () => {
|
|
155
|
+
const actualFs = await vi.importActual<typeof import('fs')>('fs');
|
|
107
156
|
const { readFileSync } = await import('fs');
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
157
|
+
const specContent = [
|
|
158
|
+
'local require = require(script.Parent.loader).load(script)',
|
|
159
|
+
'',
|
|
160
|
+
'describe("Suite", function()',
|
|
161
|
+
' it("my test name", function()',
|
|
162
|
+
' -- test body',
|
|
163
|
+
' end)',
|
|
164
|
+
'end)',
|
|
165
|
+
].join('\n');
|
|
166
|
+
vi.mocked(readFileSync).mockImplementation(((
|
|
167
|
+
filePath: PathOrFileDescriptor,
|
|
168
|
+
options?: BufferEncoding
|
|
169
|
+
) =>
|
|
170
|
+
String(filePath).endsWith('MaidTaskUtils.spec.lua')
|
|
171
|
+
? specContent
|
|
172
|
+
: actualFs.readFileSync(filePath, options)) as typeof readFileSync);
|
|
120
173
|
|
|
121
174
|
const input = [
|
|
122
|
-
' ● Suite
|
|
175
|
+
' ● Suite › my test name',
|
|
123
176
|
'',
|
|
124
177
|
' assertion failed',
|
|
125
178
|
'',
|
|
126
|
-
' at ServerScriptService.
|
|
179
|
+
' at ServerScriptService.maid.Shared.MaidTaskUtils.spec',
|
|
127
180
|
].join('\n');
|
|
128
181
|
|
|
129
|
-
const result = parseJestLuaOutput(input, {
|
|
182
|
+
const result = parseJestLuaOutput(input, {
|
|
183
|
+
packageName: '@quenty/maid',
|
|
184
|
+
repoRoot: REPO_ROOT,
|
|
185
|
+
workspace,
|
|
186
|
+
});
|
|
130
187
|
|
|
131
188
|
expect(result).toHaveLength(1);
|
|
132
|
-
expect(result[0].file).toBe('src/
|
|
189
|
+
expect(result[0].file).toBe('src/maid/src/Shared/MaidTaskUtils.spec.lua');
|
|
133
190
|
expect(result[0].line).toBe(4);
|
|
134
191
|
});
|
|
135
192
|
|
|
@@ -137,15 +194,19 @@ describe('parseJestLuaOutput', () => {
|
|
|
137
194
|
const input = [
|
|
138
195
|
'Error: attempt to index nil with "Connect"',
|
|
139
196
|
'Stack Begin',
|
|
140
|
-
"Script 'ServerScriptService.maid.Shared.
|
|
197
|
+
"Script 'ServerScriptService.maid.Shared.MaidTaskUtils', Line 23",
|
|
141
198
|
'Stack End',
|
|
142
199
|
].join('\n');
|
|
143
200
|
|
|
144
|
-
const result = parseJestLuaOutput(input, {
|
|
201
|
+
const result = parseJestLuaOutput(input, {
|
|
202
|
+
packageName: '@quenty/maid',
|
|
203
|
+
repoRoot: REPO_ROOT,
|
|
204
|
+
workspace,
|
|
205
|
+
});
|
|
145
206
|
|
|
146
207
|
expect(result).toHaveLength(1);
|
|
147
208
|
expect(result[0]).toEqual({
|
|
148
|
-
file: 'src/maid/src/Shared/
|
|
209
|
+
file: 'src/maid/src/Shared/MaidTaskUtils.lua',
|
|
149
210
|
line: 23,
|
|
150
211
|
severity: 'error',
|
|
151
212
|
title: 'Runtime error',
|
|
@@ -161,20 +222,26 @@ describe('parseJestLuaOutput', () => {
|
|
|
161
222
|
' Expected: 1',
|
|
162
223
|
' Received: 2',
|
|
163
224
|
'',
|
|
164
|
-
' at ServerScriptService.
|
|
225
|
+
' at ServerScriptService.maid.Shared.MaidTaskUtils.spec:10',
|
|
165
226
|
'',
|
|
166
227
|
'Error: attempt to call nil value',
|
|
167
228
|
'Stack Begin',
|
|
168
|
-
"Script 'ServerScriptService.
|
|
229
|
+
"Script 'ServerScriptService.maid.Shared.Maid', Line 5",
|
|
169
230
|
'Stack End',
|
|
170
231
|
].join('\n');
|
|
171
232
|
|
|
172
|
-
const result = parseJestLuaOutput(input, {
|
|
233
|
+
const result = parseJestLuaOutput(input, {
|
|
234
|
+
packageName: '@quenty/maid',
|
|
235
|
+
repoRoot: REPO_ROOT,
|
|
236
|
+
workspace,
|
|
237
|
+
});
|
|
173
238
|
|
|
174
239
|
expect(result).toHaveLength(2);
|
|
175
240
|
expect(result[0].title).toBe('Suite > assertion test');
|
|
176
241
|
expect(result[0].source).toBe('jest-lua');
|
|
177
242
|
expect(result[1].title).toBe('Runtime error');
|
|
243
|
+
expect(result[1].message).toBe('Error: attempt to call nil value');
|
|
244
|
+
expect(result[1].file).toBe('src/maid/src/Shared/Maid.lua');
|
|
178
245
|
expect(result[1].line).toBe(5);
|
|
179
246
|
});
|
|
180
247
|
|
|
@@ -189,7 +256,7 @@ describe('parseJestLuaOutput', () => {
|
|
|
189
256
|
'Tests: 2 passed, 2 total',
|
|
190
257
|
].join('\n');
|
|
191
258
|
|
|
192
|
-
const result = parseJestLuaOutput(input, { packageName: 'maid' });
|
|
259
|
+
const result = parseJestLuaOutput(input, { packageName: '@quenty/maid' });
|
|
193
260
|
|
|
194
261
|
expect(result).toEqual([]);
|
|
195
262
|
});
|
|
@@ -201,10 +268,14 @@ describe('parseJestLuaOutput', () => {
|
|
|
201
268
|
' \x1b[31mExpected: 1\x1b[39m',
|
|
202
269
|
' \x1b[32mReceived: 2\x1b[39m',
|
|
203
270
|
'',
|
|
204
|
-
' at ServerScriptService.
|
|
271
|
+
' at ServerScriptService.maid.Shared.MaidTaskUtils.spec:10',
|
|
205
272
|
].join('\n');
|
|
206
273
|
|
|
207
|
-
const result = parseJestLuaOutput(input, {
|
|
274
|
+
const result = parseJestLuaOutput(input, {
|
|
275
|
+
packageName: '@quenty/maid',
|
|
276
|
+
repoRoot: REPO_ROOT,
|
|
277
|
+
workspace,
|
|
278
|
+
});
|
|
208
279
|
|
|
209
280
|
expect(result).toHaveLength(1);
|
|
210
281
|
expect(result[0].title).toBe('Suite > test');
|
|
@@ -221,33 +292,291 @@ describe('parseJestLuaOutput', () => {
|
|
|
221
292
|
// No at-line, no Stack Begin, just ends
|
|
222
293
|
].join('\n');
|
|
223
294
|
|
|
224
|
-
const result = parseJestLuaOutput(input, { packageName: '
|
|
295
|
+
const result = parseJestLuaOutput(input, { packageName: '@quenty/maid' });
|
|
225
296
|
|
|
226
|
-
// Should still emit a diagnostic with fallback location
|
|
227
297
|
expect(result).toHaveLength(1);
|
|
228
298
|
expect(result[0].title).toBe('Suite > incomplete test');
|
|
229
|
-
expect(result[0].
|
|
299
|
+
expect(result[0].file).toBeUndefined();
|
|
230
300
|
});
|
|
231
301
|
|
|
232
302
|
it('handles empty input', () => {
|
|
233
|
-
const result = parseJestLuaOutput('', { packageName: '
|
|
303
|
+
const result = parseJestLuaOutput('', { packageName: '@quenty/maid' });
|
|
234
304
|
expect(result).toEqual([]);
|
|
235
305
|
});
|
|
236
306
|
|
|
237
|
-
it('
|
|
307
|
+
it('finds an aliased it() call by its quoted test name', () => {
|
|
238
308
|
const input = [
|
|
239
|
-
'
|
|
240
|
-
'
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
'
|
|
309
|
+
' ● Brine.serialize / Brine.deserialize roundtrip › propagates a tag added after subscribe',
|
|
310
|
+
'',
|
|
311
|
+
' assertion failed',
|
|
312
|
+
'',
|
|
313
|
+
' at ServerScriptService.brine.Shared.Brine.spec',
|
|
244
314
|
].join('\n');
|
|
245
315
|
|
|
246
|
-
const result = parseJestLuaOutput(input, {
|
|
316
|
+
const result = parseJestLuaOutput(input, {
|
|
317
|
+
packageName: '@quenty/brine',
|
|
318
|
+
repoRoot: REPO_ROOT,
|
|
319
|
+
workspace,
|
|
320
|
+
});
|
|
247
321
|
|
|
248
|
-
expect(result).
|
|
249
|
-
expect(result[0].
|
|
250
|
-
|
|
322
|
+
expect(result[0].file).toBe('src/brine/src/Shared/Brine.spec.lua');
|
|
323
|
+
expect(result[0].line).toBe(495);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('falls back to the enclosing describe for a generated test name', () => {
|
|
327
|
+
const input = [
|
|
328
|
+
' ● EllipticCurveCryptography Performance › should efficiently process 500 payloads of 100 bytes',
|
|
329
|
+
'',
|
|
330
|
+
' assertion failed',
|
|
331
|
+
'',
|
|
332
|
+
' at ServerScriptService.ellipticcurvecryptography.Shared.EllipticCurveCryptographyBenchmark.spec',
|
|
333
|
+
].join('\n');
|
|
334
|
+
|
|
335
|
+
const result = parseJestLuaOutput(input, {
|
|
336
|
+
packageName: '@quenty/ellipticcurvecryptography',
|
|
337
|
+
repoRoot: REPO_ROOT,
|
|
338
|
+
workspace,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
expect(result[0].file).toBe(
|
|
342
|
+
'src/ellipticcurvecryptography/src/Shared/EllipticCurveCryptographyBenchmark.spec.lua'
|
|
343
|
+
);
|
|
344
|
+
expect(result[0].line).toBe(20);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('emits the file with no line when no test name matches', () => {
|
|
348
|
+
const input = [
|
|
349
|
+
' ● should efficiently process 500 payloads of 100 bytes',
|
|
350
|
+
'',
|
|
351
|
+
' assertion failed',
|
|
352
|
+
'',
|
|
353
|
+
' at ServerScriptService.ellipticcurvecryptography.Shared.EllipticCurveCryptographyBenchmark.spec',
|
|
354
|
+
].join('\n');
|
|
355
|
+
|
|
356
|
+
const result = parseJestLuaOutput(input, {
|
|
357
|
+
packageName: '@quenty/ellipticcurvecryptography',
|
|
358
|
+
repoRoot: REPO_ROOT,
|
|
359
|
+
workspace,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
expect(result[0].file).toBe(
|
|
363
|
+
'src/ellipticcurvecryptography/src/Shared/EllipticCurveCryptographyBenchmark.spec.lua'
|
|
364
|
+
);
|
|
365
|
+
expect(result[0].line).toBeUndefined();
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
describe('real CI logs', () => {
|
|
369
|
+
it('unwraps chunk names and picks the innermost package frame of a runtime error', () => {
|
|
370
|
+
const input = [
|
|
371
|
+
`[string "ServerScriptService.saveslot.Server.HasSaveSlotsDataStore"]:1668: attempt to call missing method 'GetSubStore' of table`,
|
|
372
|
+
' Stack Begin',
|
|
373
|
+
` Script '[string "ServerScriptService.saveslot.Server.HasSaveSlotsDataStore"]', Line 1668 - function _stagePlaytime`,
|
|
374
|
+
` Script '[string "ServerScriptService.saveslot.Server.HasSaveSlotsDataStore"]', Line 1647 - function _flushPlaytime`,
|
|
375
|
+
` Script '[string "ServerScriptService.saveslot.Server.HasSaveSlotsDataStore"]', Line 1678 - function _endPlaySession`,
|
|
376
|
+
` Script '[string "ServerScriptService.saveslot.Server.HasSaveSlotsDataStore"]', Line 1609`,
|
|
377
|
+
` Script '[string "ServerScriptService.saveslot.node_modules.@quenty.adorneedata.node_modules.@quenty.attributeutils.node_modules.@quenty.baseobject.node_modules.@quenty.maid.Shared.Maid"]', Line 267 - function DoCleaning`,
|
|
378
|
+
` Script '[string "ServerScriptService.saveslot.node_modules.@quenty.adorneedata.node_modules.@quenty.attributeutils.node_modules.@quenty.baseobject.Shared.BaseObject"]', Line 40 - function Destroy`,
|
|
379
|
+
` Script '[string "ServerScriptService.saveslot.node_modules.@quenty.binder.Shared.Binder"]', Line 752 - function _remove`,
|
|
380
|
+
` Script '[string "ServerScriptService.saveslot.node_modules.@quenty.binder.Shared.Binder"]', Line 224`,
|
|
381
|
+
' Stack End',
|
|
382
|
+
].join('\n');
|
|
383
|
+
|
|
384
|
+
const result = parseJestLuaOutput(input, {
|
|
385
|
+
packageName: '@quenty/saveslot',
|
|
386
|
+
repoRoot: REPO_ROOT,
|
|
387
|
+
workspace,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
expect(result).toHaveLength(1);
|
|
391
|
+
expect(result[0].file).toBe(
|
|
392
|
+
'src/saveslot/src/Server/HasSaveSlotsDataStore.lua'
|
|
393
|
+
);
|
|
394
|
+
expect(result[0].line).toBe(1668);
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it('reads past frames from other service roots', () => {
|
|
398
|
+
const input = [
|
|
399
|
+
' ● GameProductCmdrService › registers commands',
|
|
400
|
+
'',
|
|
401
|
+
' Cannot provide template "CmdrExecutionTemplate"',
|
|
402
|
+
'',
|
|
403
|
+
' ServerScriptService.gameproduct.node_modules.@quenty.cmdrservice.node_modules.@quenty.templateprovider.Shared.TemplateProvider:726 function CloneTemplate',
|
|
404
|
+
' ReplicatedStorage.Packages.CmdrClient:5',
|
|
405
|
+
' ServerScriptService.gameproduct.Server.Cmdr.GameProductCmdrService:59 function _registerCommands',
|
|
406
|
+
'',
|
|
407
|
+
].join('\n');
|
|
408
|
+
|
|
409
|
+
const result = parseJestLuaOutput(input, {
|
|
410
|
+
packageName: '@quenty/gameproductservice',
|
|
411
|
+
repoRoot: REPO_ROOT,
|
|
412
|
+
workspace,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
expect(result[0].file).toBe(
|
|
416
|
+
'src/gameproductservice/src/Server/Cmdr/GameProductCmdrService.lua'
|
|
417
|
+
);
|
|
418
|
+
expect(result[0].line).toBe(59);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('reads Jest frames without an `at` prefix', () => {
|
|
422
|
+
const input = [
|
|
423
|
+
'\x1b[1m\x1b[31m \x1b[1m● \x1b[22m\x1b[1msave slot playtime › persists accrued playtime on the save that triggered the flush\x1b[39m\x1b[22m',
|
|
424
|
+
'',
|
|
425
|
+
" attempt to call missing method 'GetSubStore' of table",
|
|
426
|
+
'\x1b[2m\x1b[22m',
|
|
427
|
+
'\x1b[2m ServerScriptService.saveslot.Server.HasSaveSlotsDataStore:1668\x1b[22m',
|
|
428
|
+
'',
|
|
429
|
+
'\x1b[1m\x1b[31m \x1b[1m● \x1b[22m\x1b[1msave slot playtime › keeps the replicated attribute and the stored value in step\x1b[39m\x1b[22m',
|
|
430
|
+
'',
|
|
431
|
+
" attempt to call missing method 'GetSubStore' of table",
|
|
432
|
+
'\x1b[2m\x1b[22m',
|
|
433
|
+
'\x1b[2m ServerScriptService.saveslot.Server.HasSaveSlotsDataStore:1668\x1b[22m',
|
|
434
|
+
'',
|
|
435
|
+
].join('\n');
|
|
436
|
+
|
|
437
|
+
const result = parseJestLuaOutput(input, {
|
|
438
|
+
packageName: '@quenty/saveslot',
|
|
439
|
+
repoRoot: REPO_ROOT,
|
|
440
|
+
workspace,
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
expect(result).toHaveLength(2);
|
|
444
|
+
expect(result.map((d) => [d.file, d.line, d.title])).toEqual([
|
|
445
|
+
[
|
|
446
|
+
'src/saveslot/src/Server/HasSaveSlotsDataStore.lua',
|
|
447
|
+
1668,
|
|
448
|
+
'save slot playtime › persists accrued playtime on the save that triggered the flush',
|
|
449
|
+
],
|
|
450
|
+
[
|
|
451
|
+
'src/saveslot/src/Server/HasSaveSlotsDataStore.lua',
|
|
452
|
+
1668,
|
|
453
|
+
'save slot playtime › keeps the replicated attribute and the stored value in step',
|
|
454
|
+
],
|
|
455
|
+
]);
|
|
456
|
+
expect(result[0].message).toBe(
|
|
457
|
+
"attempt to call missing method 'GetSubStore' of table"
|
|
458
|
+
);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('prefers the package under test over dependency frames', () => {
|
|
462
|
+
const input = [
|
|
463
|
+
`[string "ServerScriptService.gameproduct.node_modules.@quenty.cmdrservice.node_modules.@quenty.templateprovider.Shared.TemplateProvider"]:726: [TemplateProvider.CmdrTemplateProviderServer.CloneTemplate] - Cannot provide template "CmdrExecutionTemplate"`,
|
|
464
|
+
' Stack Begin',
|
|
465
|
+
` Script '[string "ServerScriptService.gameproduct.node_modules.@quenty.cmdrservice.node_modules.@quenty.templateprovider.Shared.TemplateProvider"]', Line 726 - function CloneTemplate`,
|
|
466
|
+
` Script '[string "ServerScriptService.gameproduct.node_modules.@quenty.cmdrservice.Server.CmdrService"]', Line 144 - function RegisterCommand`,
|
|
467
|
+
` Script '[string "ServerScriptService.gameproduct.Server.Cmdr.GameProductCmdrService"]', Line 59 - function _registerCommands`,
|
|
468
|
+
` Script '[string "ServerScriptService.gameproduct.Server.Cmdr.GameProductCmdrService"]', Line 55 - function Start`,
|
|
469
|
+
` Script '[string "ServerScriptService.gameproduct.node_modules.@quenty.avatareditorutils.node_modules.@quenty.observablecollection.node_modules.@quenty.blend.node_modules.@quenty.playerthumbnailutils.node_modules.@quenty.playermock.node_modules.@quenty.servic..."]', Line 217`,
|
|
470
|
+
' Stack End',
|
|
471
|
+
].join('\n');
|
|
472
|
+
|
|
473
|
+
const result = parseJestLuaOutput(input, {
|
|
474
|
+
packageName: '@quenty/gameproductservice',
|
|
475
|
+
repoRoot: REPO_ROOT,
|
|
476
|
+
workspace,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
expect(result).toHaveLength(1);
|
|
480
|
+
expect(result[0].file).toBe(
|
|
481
|
+
'src/gameproductservice/src/Server/Cmdr/GameProductCmdrService.lua'
|
|
482
|
+
);
|
|
483
|
+
expect(result[0].line).toBe(59);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('does not let a sibling package frame land in the package under test', () => {
|
|
487
|
+
const input = [
|
|
488
|
+
`[string "ServerScriptService.saveslot.Server.HasSaveSlotsDataStore"]:1668: attempt to call missing method 'GetSubStore' of table`,
|
|
489
|
+
' Stack Begin',
|
|
490
|
+
` Script '[string "ServerScriptService.othersibling.Server.SaveSlotService"]', Line 10`,
|
|
491
|
+
` Script '[string "ServerScriptService.saveslot.Server.HasSaveSlotsDataStore"]', Line 1668 - function _stagePlaytime`,
|
|
492
|
+
' Stack End',
|
|
493
|
+
].join('\n');
|
|
494
|
+
|
|
495
|
+
const result = parseJestLuaOutput(input, {
|
|
496
|
+
packageName: '@quenty/saveslot',
|
|
497
|
+
repoRoot: REPO_ROOT,
|
|
498
|
+
workspace,
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
expect(result).toHaveLength(1);
|
|
502
|
+
expect(result[0].file).toBe(
|
|
503
|
+
'src/saveslot/src/Server/HasSaveSlotsDataStore.lua'
|
|
504
|
+
);
|
|
505
|
+
expect(result[0].line).toBe(1668);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('maps dependency frames back to the dependency source package', () => {
|
|
509
|
+
const input = [
|
|
510
|
+
`[string "ServerScriptService.gameproduct.node_modules.@quenty.cmdrservice.node_modules.@quenty.templateprovider.Shared.TemplateProvider"]:726: [TemplateProvider.CmdrTemplateProviderServer.CloneTemplate] - Cannot provide template "CmdrExecutionTemplate"`,
|
|
511
|
+
' Stack Begin',
|
|
512
|
+
` Script '[string "ServerScriptService.gameproduct.node_modules.@quenty.cmdrservice.node_modules.@quenty.templateprovider.Shared.TemplateProvider"]', Line 726 - function CloneTemplate`,
|
|
513
|
+
` Script '[string "ServerScriptService.gameproduct.node_modules.@quenty.cmdrservice.Server.CmdrService"]', Line 144 - function RegisterCommand`,
|
|
514
|
+
` Script '[string "ServerScriptService.gameproduct.node_modules.@quenty.gameconfig.Server.Cmdr.GameConfigCommandService"]', Line 48 - function _registerCommands`,
|
|
515
|
+
` Script '[string "ServerScriptService.gameproduct.node_modules.@quenty.gameconfig.Server.Cmdr.GameConfigCommandService"]', Line 37 - function Start`,
|
|
516
|
+
` Script '[string "ServerScriptService.gameproduct.node_modules.@quenty.avatareditorutils.node_modules.@quenty.observablecollection.node_modules.@quenty.blend.node_modules.@quenty.playerthumbnailutils.node_modules.@quenty.playermock.node_modules.@quenty.servic..."]', Line 217`,
|
|
517
|
+
' Stack End',
|
|
518
|
+
].join('\n');
|
|
519
|
+
|
|
520
|
+
const result = parseJestLuaOutput(input, {
|
|
521
|
+
packageName: '@quenty/gameproductservice',
|
|
522
|
+
repoRoot: REPO_ROOT,
|
|
523
|
+
workspace,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
expect(result).toHaveLength(1);
|
|
527
|
+
expect(result[0].file).toBe(
|
|
528
|
+
'src/templateprovider/src/Shared/TemplateProvider.lua'
|
|
529
|
+
);
|
|
530
|
+
expect(result[0].line).toBe(726);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
const studioBridgeHeader = [
|
|
534
|
+
'\x1b[0m\x1b[7m\x1b[1m\x1b[31m FAIL \x1b[39m\x1b[22m\x1b[27m\x1b[0m \x1b[2mstudiobridge/Shared/\x1b[22m\x1b[1mActionRouter.spec\x1b[22m ',
|
|
535
|
+
' \x1b[1m● \x1b[22mTest suite failed to run',
|
|
536
|
+
'',
|
|
537
|
+
];
|
|
538
|
+
const studioBridgeFrames = [
|
|
539
|
+
' ServerScriptService.studiobridge.node_modules.@quenty.nevermore-test-runner.node_modules.@quentystudios.Jest._Index.jsdotlua_jest-runtime@3.10.0.jest-runtime:1263 function requireModule',
|
|
540
|
+
' ServerScriptService.studiobridge.node_modules.@quenty.nevermore-test-runner.node_modules.@quentystudios.Jest._Index.jsdotlua_jest-circus@3.10.0.jest-circus.circus.legacy-code-todo-rewrite.jestAdapter:113',
|
|
541
|
+
' ServerScriptService.studiobridge.node_modules.@quenty.nevermore-test-runner.node_modules.@quentystudios.Jest._Index.jsdotlua_promise@3.5.2.promise:172 function runExecutor',
|
|
542
|
+
'',
|
|
543
|
+
'\x1b[0m\x1b[7m\x1b[1m\x1b[31m FAIL \x1b[39m\x1b[22m\x1b[27m\x1b[0m \x1b[2mstudiobridge/Shared/\x1b[22m\x1b[1mBuiltInActions.spec\x1b[22m ',
|
|
544
|
+
];
|
|
545
|
+
|
|
546
|
+
it('falls back to a chunk location inside the message', () => {
|
|
547
|
+
const input = [
|
|
548
|
+
...studioBridgeHeader,
|
|
549
|
+
` ServerScriptService.studiobridge.node_modules.@quenty.nevermore-test-runner.node_modules.@quentystudios.Jest._Index.jsdotlua_jest-runtime@3.10.0.jest-runtime:1263: [string "ServerScriptService.studiobridge.node_modules.@quenty.nevermore-test-runner.node_modules.@quenty.loader"]:214: [Loader] - "Jest" is not available. Please`,
|
|
550
|
+
'',
|
|
551
|
+
...studioBridgeFrames,
|
|
552
|
+
].join('\n');
|
|
553
|
+
|
|
554
|
+
const result = parseJestLuaOutput(input, {
|
|
555
|
+
packageName: '@quenty/studio-bridge',
|
|
556
|
+
repoRoot: REPO_ROOT,
|
|
557
|
+
workspace,
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
expect(result).toHaveLength(1);
|
|
561
|
+
expect(result[0].title).toBe('Test suite failed to run');
|
|
562
|
+
expect(result[0].file).toBe('src/loader/src/init.lua');
|
|
563
|
+
expect(result[0].line).toBe(214);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('emits no file when only vendored frames are present', () => {
|
|
567
|
+
const input = [...studioBridgeHeader, ...studioBridgeFrames].join('\n');
|
|
568
|
+
|
|
569
|
+
const result = parseJestLuaOutput(input, {
|
|
570
|
+
packageName: '@quenty/studio-bridge',
|
|
571
|
+
repoRoot: REPO_ROOT,
|
|
572
|
+
workspace,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
expect(result).toHaveLength(1);
|
|
576
|
+
expect(result[0].title).toBe('Test suite failed to run');
|
|
577
|
+
expect(result[0].file).toBeUndefined();
|
|
578
|
+
expect(result[0].line).toBeUndefined();
|
|
579
|
+
});
|
|
251
580
|
});
|
|
252
581
|
|
|
253
582
|
describe('with sourcemap resolver', () => {
|
|
@@ -284,6 +613,24 @@ describe('parseJestLuaOutput', () => {
|
|
|
284
613
|
},
|
|
285
614
|
],
|
|
286
615
|
},
|
|
616
|
+
{
|
|
617
|
+
name: 'binder',
|
|
618
|
+
className: 'Folder',
|
|
619
|
+
filePaths: [`${repoRoot}/src/binder/default.project.json`],
|
|
620
|
+
children: [
|
|
621
|
+
{
|
|
622
|
+
name: 'Shared',
|
|
623
|
+
className: 'Folder',
|
|
624
|
+
children: [
|
|
625
|
+
{
|
|
626
|
+
name: 'Binder',
|
|
627
|
+
className: 'ModuleScript',
|
|
628
|
+
filePaths: [`${repoRoot}/src/binder/src/Shared/Binder.lua`],
|
|
629
|
+
},
|
|
630
|
+
],
|
|
631
|
+
},
|
|
632
|
+
],
|
|
633
|
+
},
|
|
287
634
|
],
|
|
288
635
|
};
|
|
289
636
|
|
|
@@ -300,7 +647,7 @@ describe('parseJestLuaOutput', () => {
|
|
|
300
647
|
].join('\n');
|
|
301
648
|
|
|
302
649
|
const result = parseJestLuaOutput(input, {
|
|
303
|
-
packageName: 'mypkg',
|
|
650
|
+
packageName: '@quenty/mypkg',
|
|
304
651
|
sourcemapResolver: resolver,
|
|
305
652
|
});
|
|
306
653
|
|
|
@@ -309,16 +656,16 @@ describe('parseJestLuaOutput', () => {
|
|
|
309
656
|
expect(result[0].line).toBe(10);
|
|
310
657
|
});
|
|
311
658
|
|
|
312
|
-
it('uses sourcemap resolver for runtime error paths', () => {
|
|
659
|
+
it('uses sourcemap resolver for chunk-wrapped runtime error paths', () => {
|
|
313
660
|
const input = [
|
|
314
661
|
'Error: attempt to index nil',
|
|
315
662
|
'Stack Begin',
|
|
316
|
-
|
|
663
|
+
`Script '[string "ServerScriptService.mypkg.Shared.Foo.Foo.spec"]', Line 5`,
|
|
317
664
|
'Stack End',
|
|
318
665
|
].join('\n');
|
|
319
666
|
|
|
320
667
|
const result = parseJestLuaOutput(input, {
|
|
321
|
-
packageName: 'mypkg',
|
|
668
|
+
packageName: '@quenty/mypkg',
|
|
322
669
|
sourcemapResolver: resolver,
|
|
323
670
|
});
|
|
324
671
|
|
|
@@ -327,24 +674,45 @@ describe('parseJestLuaOutput', () => {
|
|
|
327
674
|
expect(result[0].line).toBe(5);
|
|
328
675
|
});
|
|
329
676
|
|
|
330
|
-
it('
|
|
677
|
+
it('looks up dependency frames by their source package', () => {
|
|
678
|
+
const input = [
|
|
679
|
+
'Error: attempt to index nil',
|
|
680
|
+
' Stack Begin',
|
|
681
|
+
` Script '[string "ServerScriptService.mypkg.node_modules.@quenty.binder.Shared.Binder"]', Line 752 - function _remove`,
|
|
682
|
+
' Stack End',
|
|
683
|
+
].join('\n');
|
|
684
|
+
|
|
685
|
+
const result = parseJestLuaOutput(input, {
|
|
686
|
+
packageName: '@quenty/mypkg',
|
|
687
|
+
sourcemapResolver: resolver,
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
expect(result).toHaveLength(1);
|
|
691
|
+
expect(result[0].file).toBe('src/binder/src/Shared/Binder.lua');
|
|
692
|
+
expect(result[0].line).toBe(752);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('falls back to the repo layout when sourcemap has no mapping', () => {
|
|
331
696
|
const input = [
|
|
332
697
|
' ● Suite > test one',
|
|
333
698
|
'',
|
|
334
699
|
' Expected: 1',
|
|
335
700
|
' Received: 2',
|
|
336
701
|
'',
|
|
337
|
-
' at ServerScriptService.
|
|
702
|
+
' at ServerScriptService.observablecollection.Shared.ObservableList.spec:10',
|
|
338
703
|
].join('\n');
|
|
339
704
|
|
|
340
705
|
const result = parseJestLuaOutput(input, {
|
|
341
|
-
packageName: '
|
|
706
|
+
packageName: '@quenty/observablecollection',
|
|
342
707
|
sourcemapResolver: resolver,
|
|
708
|
+
repoRoot: REPO_ROOT,
|
|
709
|
+
workspace,
|
|
343
710
|
});
|
|
344
711
|
|
|
345
712
|
expect(result).toHaveLength(1);
|
|
346
|
-
|
|
347
|
-
|
|
713
|
+
expect(result[0].file).toBe(
|
|
714
|
+
'src/observablecollection/src/Shared/ObservableList.spec.lua'
|
|
715
|
+
);
|
|
348
716
|
});
|
|
349
717
|
});
|
|
350
718
|
});
|