@usebruno/js 0.51.0 → 0.52.0
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/package.json +6 -6
- package/src/bru.js +15 -23
- package/src/bruno-request.js +4 -0
- package/src/grpc/bruno-grpc-request.js +52 -0
- package/src/grpc/bruno-grpc-response.js +43 -0
- package/src/grpc/grpc-message-list.js +74 -0
- package/src/grpc/grpc-message.js +20 -0
- package/src/grpc/grpc-metadata-list.js +214 -0
- package/src/grpc/grpc-metadata.js +21 -0
- package/src/grpc/grpc-script-runtime.js +302 -0
- package/src/index.js +2 -0
- package/src/interpolate-string.js +2 -1
- package/src/runtime/assert-runtime.js +1 -0
- package/src/runtime/script-runtime.js +18 -2
- package/src/runtime/test-runtime.js +12 -7
- package/src/sandbox/bundle-browser-rollup.js +32 -32
- package/src/sandbox/node-vm/cjs-loader.js +390 -39
- package/src/sandbox/node-vm/index.js +96 -65
- package/src/sandbox/node-vm/index.spec.js +616 -0
- package/src/sandbox/quickjs/index.js +101 -17
- package/src/sandbox/quickjs/shims/bruno-grpc.js +29 -0
- package/src/sandbox/quickjs/shims/grpc/bruno-grpc-request.js +47 -0
- package/src/sandbox/quickjs/shims/grpc/bruno-grpc-response.js +40 -0
- package/src/sandbox/quickjs/shims/grpc/grpc-message-list.js +29 -0
- package/src/sandbox/quickjs/shims/grpc/grpc-metadata-list.js +31 -0
- package/src/sandbox/quickjs/utils/index.js +42 -23
- package/src/utils/error-formatter.js +13 -9
- package/src/utils/error-formatter.spec.js +97 -2
- package/src/utils/results.js +30 -3
|
@@ -11,7 +11,8 @@ const {
|
|
|
11
11
|
parseErrorLocation,
|
|
12
12
|
getSourceContextFromContent,
|
|
13
13
|
adjustStackTrace,
|
|
14
|
-
buildStackFromCallSites
|
|
14
|
+
buildStackFromCallSites,
|
|
15
|
+
posixifyPath
|
|
15
16
|
} = require('./error-formatter');
|
|
16
17
|
const fs = require('fs');
|
|
17
18
|
const path = require('path');
|
|
@@ -120,6 +121,64 @@ const COLLECTION_YML = [
|
|
|
120
121
|
' });'
|
|
121
122
|
].join('\n');
|
|
122
123
|
|
|
124
|
+
// gRPC lifecycle hooks:
|
|
125
|
+
// 11: script:grpc:before-call-start { → blockStartLine = 12
|
|
126
|
+
// 16: script:grpc:after-call-end { → blockStartLine = 17
|
|
127
|
+
const GRPC_BRU = `meta {
|
|
128
|
+
name: grpc-test
|
|
129
|
+
type: grpc
|
|
130
|
+
seq: 1
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
grpc {
|
|
134
|
+
url: grpc://localhost:50051
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
script:grpc:before-call-start {
|
|
138
|
+
const token = bru.getEnvVar('token');
|
|
139
|
+
request.setHeader('authorization', token);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
script:grpc:after-call-end {
|
|
143
|
+
const data = response.data;
|
|
144
|
+
bru.setVar('userId', data.id);
|
|
145
|
+
console.log(data);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
script:grpc:before-message-send {
|
|
149
|
+
bru.setVar('outbound', bru.grpc.request.message.data.greeting);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
script:grpc:after-message-receive {
|
|
153
|
+
const message = bru.grpc.response.message;
|
|
154
|
+
bru.setVar('inbound', message.data.reply);
|
|
155
|
+
}`;
|
|
156
|
+
|
|
157
|
+
// gRPC yml fixture: blockStartLine = 8 (before-call-start), 12 (after-call-end),
|
|
158
|
+
// 16 (before-message-send), 19 (after-message-receive)
|
|
159
|
+
const GRPC_YML = [
|
|
160
|
+
'info:',
|
|
161
|
+
' name: grpc-yaml-test',
|
|
162
|
+
' version: "1"',
|
|
163
|
+
'runtime:',
|
|
164
|
+
' scripts:',
|
|
165
|
+
' - type: grpc:before-call-start',
|
|
166
|
+
' code: |-',
|
|
167
|
+
' const token = bru.getEnvVar(\'token\');',
|
|
168
|
+
' request.setHeader(\'authorization\', token);',
|
|
169
|
+
' - type: grpc:after-call-end',
|
|
170
|
+
' code: |-',
|
|
171
|
+
' const data = response.data;',
|
|
172
|
+
' bru.setVar(\'userId\', data.id);',
|
|
173
|
+
' - type: grpc:before-message-send',
|
|
174
|
+
' code: |-',
|
|
175
|
+
' bru.setVar(\'outbound\', bru.grpc.request.message.data.greeting);',
|
|
176
|
+
' - type: grpc:after-message-receive',
|
|
177
|
+
' code: |-',
|
|
178
|
+
' const message = bru.grpc.response.message;',
|
|
179
|
+
' bru.setVar(\'inbound\', message.data.reply);'
|
|
180
|
+
].join('\n');
|
|
181
|
+
|
|
123
182
|
// Wrapper offsets: QuickJS = 9 (script line 1 = VM line 10), NodeVM = 2 (script line 1 = VM line 3)
|
|
124
183
|
|
|
125
184
|
describe('Error Formatter', () => {
|
|
@@ -128,6 +187,8 @@ describe('Error Formatter', () => {
|
|
|
128
187
|
let ymlFilePath;
|
|
129
188
|
let bruWithCommentsPath;
|
|
130
189
|
let collectionYmlPath;
|
|
190
|
+
let grpcBruPath;
|
|
191
|
+
let grpcYmlPath;
|
|
131
192
|
|
|
132
193
|
beforeEach(() => {
|
|
133
194
|
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bruno-test-'));
|
|
@@ -135,10 +196,14 @@ describe('Error Formatter', () => {
|
|
|
135
196
|
ymlFilePath = path.join(testDir, 'test.yml');
|
|
136
197
|
bruWithCommentsPath = path.join(testDir, 'comments.bru');
|
|
137
198
|
collectionYmlPath = path.join(testDir, 'opencollection.yml');
|
|
199
|
+
grpcBruPath = path.join(testDir, 'grpc.bru');
|
|
200
|
+
grpcYmlPath = path.join(testDir, 'grpc.yml');
|
|
138
201
|
fs.writeFileSync(bruFilePath, MULTI_BLOCK_BRU);
|
|
139
202
|
fs.writeFileSync(ymlFilePath, MULTI_BLOCK_YML);
|
|
140
203
|
fs.writeFileSync(bruWithCommentsPath, BRU_WITH_COMMENTS);
|
|
141
204
|
fs.writeFileSync(collectionYmlPath, COLLECTION_YML);
|
|
205
|
+
fs.writeFileSync(grpcBruPath, GRPC_BRU);
|
|
206
|
+
fs.writeFileSync(grpcYmlPath, GRPC_YML);
|
|
142
207
|
});
|
|
143
208
|
|
|
144
209
|
afterEach(() => {
|
|
@@ -152,6 +217,13 @@ describe('Error Formatter', () => {
|
|
|
152
217
|
expect(findScriptBlockStartLine(bruFilePath, 'test')).toBe(25);
|
|
153
218
|
});
|
|
154
219
|
|
|
220
|
+
it('should find gRPC lifecycle hook blocks in .bru files', () => {
|
|
221
|
+
expect(findScriptBlockStartLine(grpcBruPath, 'before-call-start')).toBe(12);
|
|
222
|
+
expect(findScriptBlockStartLine(grpcBruPath, 'after-call-end')).toBe(17);
|
|
223
|
+
expect(findScriptBlockStartLine(grpcBruPath, 'before-message-send')).toBe(23);
|
|
224
|
+
expect(findScriptBlockStartLine(grpcBruPath, 'after-message-receive')).toBe(27);
|
|
225
|
+
});
|
|
226
|
+
|
|
155
227
|
it('should return null for missing block or non-.bru files', () => {
|
|
156
228
|
const noBlockPath = path.join(testDir, 'no-block.bru');
|
|
157
229
|
fs.writeFileSync(noBlockPath, 'meta {\n name: test\n}');
|
|
@@ -167,6 +239,13 @@ describe('Error Formatter', () => {
|
|
|
167
239
|
expect(findScriptBlockEndLine(bruFilePath, 'test')).toBe(30);
|
|
168
240
|
});
|
|
169
241
|
|
|
242
|
+
it('should find last content line for gRPC lifecycle hook blocks', () => {
|
|
243
|
+
expect(findScriptBlockEndLine(grpcBruPath, 'before-call-start')).toBe(13);
|
|
244
|
+
expect(findScriptBlockEndLine(grpcBruPath, 'after-call-end')).toBe(19);
|
|
245
|
+
expect(findScriptBlockEndLine(grpcBruPath, 'before-message-send')).toBe(23);
|
|
246
|
+
expect(findScriptBlockEndLine(grpcBruPath, 'after-message-receive')).toBe(28);
|
|
247
|
+
});
|
|
248
|
+
|
|
170
249
|
it('should return null for empty block', () => {
|
|
171
250
|
const emptyBlockPath = path.join(testDir, 'empty.bru');
|
|
172
251
|
fs.writeFileSync(emptyBlockPath, 'script:pre-request {\n}');
|
|
@@ -197,6 +276,13 @@ describe('Error Formatter', () => {
|
|
|
197
276
|
expect(findYmlScriptBlockStartLine(collectionYmlPath, 'test')).toBe(11);
|
|
198
277
|
});
|
|
199
278
|
|
|
279
|
+
it('should find gRPC lifecycle hook blocks in .yml files', () => {
|
|
280
|
+
expect(findYmlScriptBlockStartLine(grpcYmlPath, 'before-call-start')).toBe(8);
|
|
281
|
+
expect(findYmlScriptBlockStartLine(grpcYmlPath, 'after-call-end')).toBe(12);
|
|
282
|
+
expect(findYmlScriptBlockStartLine(grpcYmlPath, 'before-message-send')).toBe(16);
|
|
283
|
+
expect(findYmlScriptBlockStartLine(grpcYmlPath, 'after-message-receive')).toBe(19);
|
|
284
|
+
});
|
|
285
|
+
|
|
200
286
|
it('should return null for missing block or non-.yml files', () => {
|
|
201
287
|
const noRuntimePath = path.join(testDir, 'no-runtime.yml');
|
|
202
288
|
fs.writeFileSync(noRuntimePath, 'info:\n name: simple\n version: "1"\n');
|
|
@@ -216,6 +302,13 @@ describe('Error Formatter', () => {
|
|
|
216
302
|
expect(findYmlScriptBlockEndLine(collectionYmlPath, 'test')).toBe(13);
|
|
217
303
|
});
|
|
218
304
|
|
|
305
|
+
it('should find last content line for gRPC lifecycle hook blocks', () => {
|
|
306
|
+
expect(findYmlScriptBlockEndLine(grpcYmlPath, 'before-call-start')).toBe(9);
|
|
307
|
+
expect(findYmlScriptBlockEndLine(grpcYmlPath, 'after-call-end')).toBe(13);
|
|
308
|
+
expect(findYmlScriptBlockEndLine(grpcYmlPath, 'before-message-send')).toBe(16);
|
|
309
|
+
expect(findYmlScriptBlockEndLine(grpcYmlPath, 'after-message-receive')).toBe(20);
|
|
310
|
+
});
|
|
311
|
+
|
|
219
312
|
it('should return null for missing block', () => {
|
|
220
313
|
const noRuntimePath = path.join(testDir, 'no-runtime.yml');
|
|
221
314
|
fs.writeFileSync(noRuntimePath, 'info:\n name: simple\n version: "1"\n');
|
|
@@ -804,7 +897,9 @@ get {
|
|
|
804
897
|
const result = formatErrorWithContextV2(error, 'pre-request');
|
|
805
898
|
|
|
806
899
|
expect(result).not.toBeNull();
|
|
807
|
-
|
|
900
|
+
// display paths are posixified, so the Windows path.join backslashes
|
|
901
|
+
// must be normalized before comparing
|
|
902
|
+
expect(result.filePath).toBe(posixifyPath(bruFilePath));
|
|
808
903
|
});
|
|
809
904
|
});
|
|
810
905
|
|
package/src/utils/results.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const TestResults = require('../test-results');
|
|
2
2
|
const Test = require('../test');
|
|
3
3
|
|
|
4
|
-
// Calculate summary statistics for test results
|
|
5
4
|
const getResultsSummary = (results) => {
|
|
6
5
|
const summary = {
|
|
7
6
|
total: results.length,
|
|
@@ -20,12 +19,40 @@ const getResultsSummary = (results) => {
|
|
|
20
19
|
return summary;
|
|
21
20
|
};
|
|
22
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Called once per script phase - pre-request, post-response, and the Tests tab each call
|
|
24
|
+
* this separately, from their own place in runtime/script-runtime.js and
|
|
25
|
+
* runtime/test-runtime.js. Every call creates its own private `pendingTestPromises` array
|
|
26
|
+
* below, so each phase tracks and waits for only its own test() calls
|
|
27
|
+
*/
|
|
23
28
|
const createBruTestResultMethods = (bru, assertionResults, chai) => {
|
|
24
29
|
const __brunoTestResults = new TestResults();
|
|
25
|
-
const
|
|
30
|
+
const baseTest = Test(__brunoTestResults, chai);
|
|
31
|
+
|
|
32
|
+
const pendingTestPromises = [];
|
|
33
|
+
|
|
34
|
+
const test = (description, callback) => {
|
|
35
|
+
const promise = baseTest(description, callback);
|
|
36
|
+
pendingTestPromises.push(promise.catch(() => {}));
|
|
37
|
+
return promise;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Waits for every test() call registered so far to settle - including a test() called
|
|
42
|
+
* from inside another test()'s callback, after this wait has already started. Mirrors
|
|
43
|
+
* QuickJS's own waitForPendingDeferreds(). If a test() callback has its own delay (a
|
|
44
|
+
* setTimeout, a slow request), this simply waits until that delay is over
|
|
45
|
+
*/
|
|
46
|
+
const waitForPendingTests = async () => {
|
|
47
|
+
while (pendingTestPromises.length) {
|
|
48
|
+
const batch = pendingTestPromises.splice(0);
|
|
49
|
+
await Promise.all(batch);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
26
53
|
setupBruTestMethods(bru, __brunoTestResults, assertionResults);
|
|
27
54
|
|
|
28
|
-
return { __brunoTestResults, test };
|
|
55
|
+
return { __brunoTestResults, test, waitForPendingTests };
|
|
29
56
|
};
|
|
30
57
|
|
|
31
58
|
const setupBruTestMethods = (bru, __brunoTestResults, assertionResults) => {
|