@usebruno/js 0.4.0 → 0.5.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 +1 -1
- package/src/bru.js +14 -11
- package/src/bruno-request.js +5 -1
- package/src/bruno-response.js +1 -1
- package/src/runtime/assert-runtime.js +63 -31
- package/src/runtime/script-runtime.js +41 -10
- package/src/runtime/test-runtime.js +22 -8
- package/src/runtime/vars-runtime.js +4 -4
- package/src/test.js +5 -5
- package/src/utils.js +45 -26
package/package.json
CHANGED
package/src/bru.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
2
1
|
class Bru {
|
|
3
2
|
constructor(envVariables, collectionVariables) {
|
|
4
|
-
this.
|
|
5
|
-
this.
|
|
3
|
+
this.envVariables = envVariables;
|
|
4
|
+
this.collectionVariables = collectionVariables;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
getEnvName() {
|
|
8
|
+
return this.envVariables.__name__;
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
getProcessEnv(key) {
|
|
@@ -10,32 +13,32 @@ class Bru {
|
|
|
10
13
|
}
|
|
11
14
|
|
|
12
15
|
getEnvVar(key) {
|
|
13
|
-
return this.
|
|
16
|
+
return this.envVariables[key];
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
setEnvVar(key, value) {
|
|
17
|
-
if(!key) {
|
|
20
|
+
if (!key) {
|
|
18
21
|
throw new Error('Key is required');
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
// gracefully ignore if key is not present in environment
|
|
22
|
-
if(!this.
|
|
23
|
-
|
|
25
|
+
if (!this.envVariables.hasOwnProperty(key)) {
|
|
26
|
+
return;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
this.
|
|
29
|
+
this.envVariables[key] = value;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
setVar(key, value) {
|
|
30
|
-
if(!key) {
|
|
33
|
+
if (!key) {
|
|
31
34
|
throw new Error('Key is required');
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
this.
|
|
37
|
+
this.collectionVariables[key] = value;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
getVar(key) {
|
|
38
|
-
return this.
|
|
41
|
+
return this.collectionVariables[key];
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
|
package/src/bruno-request.js
CHANGED
package/src/bruno-response.js
CHANGED
|
@@ -12,11 +12,7 @@ chai.use(function (chai, utils) {
|
|
|
12
12
|
const obj = this._obj;
|
|
13
13
|
const isJson = typeof obj === 'object' && obj !== null && !Array.isArray(obj) && obj.constructor === Object;
|
|
14
14
|
|
|
15
|
-
this.assert(
|
|
16
|
-
isJson,
|
|
17
|
-
`expected ${utils.inspect(obj)} to be JSON`,
|
|
18
|
-
`expected ${utils.inspect(obj)} not to be JSON`
|
|
19
|
-
);
|
|
15
|
+
this.assert(isJson, `expected ${utils.inspect(obj)} to be JSON`, `expected ${utils.inspect(obj)} not to be JSON`);
|
|
20
16
|
});
|
|
21
17
|
});
|
|
22
18
|
|
|
@@ -25,7 +21,7 @@ chai.use(function (chai, utils) {
|
|
|
25
21
|
chai.Assertion.addMethod('match', function (regex) {
|
|
26
22
|
const obj = this._obj;
|
|
27
23
|
let match = false;
|
|
28
|
-
if(obj === undefined) {
|
|
24
|
+
if (obj === undefined) {
|
|
29
25
|
match = false;
|
|
30
26
|
} else {
|
|
31
27
|
match = regex.test(obj);
|
|
@@ -41,7 +37,7 @@ chai.use(function (chai, utils) {
|
|
|
41
37
|
|
|
42
38
|
/**
|
|
43
39
|
* Assertion operators
|
|
44
|
-
*
|
|
40
|
+
*
|
|
45
41
|
* eq : equal to
|
|
46
42
|
* neq : not equal to
|
|
47
43
|
* gt : greater than
|
|
@@ -70,7 +66,7 @@ chai.use(function (chai, utils) {
|
|
|
70
66
|
* isBoolean : is boolean
|
|
71
67
|
*/
|
|
72
68
|
const parseAssertionOperator = (str = '') => {
|
|
73
|
-
if(!str || typeof str !== 'string' || !str.length) {
|
|
69
|
+
if (!str || typeof str !== 'string' || !str.length) {
|
|
74
70
|
return {
|
|
75
71
|
operator: 'eq',
|
|
76
72
|
value: str
|
|
@@ -78,27 +74,58 @@ const parseAssertionOperator = (str = '') => {
|
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
const operators = [
|
|
81
|
-
'eq',
|
|
82
|
-
'
|
|
83
|
-
'
|
|
84
|
-
'
|
|
77
|
+
'eq',
|
|
78
|
+
'neq',
|
|
79
|
+
'gt',
|
|
80
|
+
'gte',
|
|
81
|
+
'lt',
|
|
82
|
+
'lte',
|
|
83
|
+
'in',
|
|
84
|
+
'notIn',
|
|
85
|
+
'contains',
|
|
86
|
+
'notContains',
|
|
87
|
+
'length',
|
|
88
|
+
'matches',
|
|
89
|
+
'notMatches',
|
|
90
|
+
'startsWith',
|
|
91
|
+
'endsWith',
|
|
92
|
+
'between',
|
|
93
|
+
'isEmpty',
|
|
94
|
+
'isNull',
|
|
95
|
+
'isUndefined',
|
|
96
|
+
'isDefined',
|
|
97
|
+
'isTruthy',
|
|
98
|
+
'isFalsy',
|
|
99
|
+
'isJson',
|
|
100
|
+
'isNumber',
|
|
101
|
+
'isString',
|
|
102
|
+
'isBoolean'
|
|
85
103
|
];
|
|
86
104
|
|
|
87
105
|
const unaryOperators = [
|
|
88
|
-
'isEmpty',
|
|
106
|
+
'isEmpty',
|
|
107
|
+
'isNull',
|
|
108
|
+
'isUndefined',
|
|
109
|
+
'isDefined',
|
|
110
|
+
'isTruthy',
|
|
111
|
+
'isFalsy',
|
|
112
|
+
'isJson',
|
|
113
|
+
'isNumber',
|
|
114
|
+
'isString',
|
|
115
|
+
'isBoolean'
|
|
89
116
|
];
|
|
90
117
|
|
|
91
118
|
const [operator, ...rest] = str.trim().split(' ');
|
|
92
119
|
const value = rest.join(' ');
|
|
93
120
|
|
|
94
|
-
if(unaryOperators.includes(operator)) {
|
|
121
|
+
if (unaryOperators.includes(operator)) {
|
|
95
122
|
return {
|
|
96
123
|
operator,
|
|
97
124
|
value: ''
|
|
98
125
|
};
|
|
99
126
|
}
|
|
100
127
|
|
|
101
|
-
if(operators.includes(operator)) {
|
|
128
|
+
if (operators.includes(operator)) {
|
|
102
129
|
return {
|
|
103
130
|
operator,
|
|
104
131
|
value
|
|
@@ -113,34 +140,43 @@ const parseAssertionOperator = (str = '') => {
|
|
|
113
140
|
|
|
114
141
|
const isUnaryOperator = (operator) => {
|
|
115
142
|
const unaryOperators = [
|
|
116
|
-
'isEmpty',
|
|
143
|
+
'isEmpty',
|
|
144
|
+
'isNull',
|
|
145
|
+
'isUndefined',
|
|
146
|
+
'isDefined',
|
|
147
|
+
'isTruthy',
|
|
148
|
+
'isFalsy',
|
|
149
|
+
'isJson',
|
|
150
|
+
'isNumber',
|
|
151
|
+
'isString',
|
|
152
|
+
'isBoolean'
|
|
117
153
|
];
|
|
118
154
|
|
|
119
155
|
return unaryOperators.includes(operator);
|
|
120
156
|
};
|
|
121
157
|
|
|
122
158
|
const evaluateRhsOperand = (rhsOperand, operator, context) => {
|
|
123
|
-
if(isUnaryOperator(operator)) {
|
|
159
|
+
if (isUnaryOperator(operator)) {
|
|
124
160
|
return;
|
|
125
161
|
}
|
|
126
162
|
|
|
127
163
|
// gracefully allow both a,b as well as [a, b]
|
|
128
|
-
if(operator === 'in' || operator === 'notIn') {
|
|
129
|
-
if(rhsOperand.startsWith('[') && rhsOperand.endsWith(']')) {
|
|
164
|
+
if (operator === 'in' || operator === 'notIn') {
|
|
165
|
+
if (rhsOperand.startsWith('[') && rhsOperand.endsWith(']')) {
|
|
130
166
|
rhsOperand = rhsOperand.substring(1, rhsOperand.length - 1);
|
|
131
167
|
}
|
|
132
168
|
|
|
133
169
|
return rhsOperand.split(',').map((v) => evaluateJsTemplateLiteral(v.trim(), context));
|
|
134
170
|
}
|
|
135
171
|
|
|
136
|
-
if(operator === 'between') {
|
|
172
|
+
if (operator === 'between') {
|
|
137
173
|
const [lhs, rhs] = rhsOperand.split(',').map((v) => evaluateJsTemplateLiteral(v.trim(), context));
|
|
138
174
|
return [lhs, rhs];
|
|
139
175
|
}
|
|
140
176
|
|
|
141
177
|
// gracefully allow both ^[a-Z] as well as /^[a-Z]/
|
|
142
|
-
if(operator === 'matches' || operator === 'notMatches') {
|
|
143
|
-
if(rhsOperand.startsWith('/') && rhsOperand.endsWith('/')) {
|
|
178
|
+
if (operator === 'matches' || operator === 'notMatches') {
|
|
179
|
+
if (rhsOperand.startsWith('/') && rhsOperand.endsWith('/')) {
|
|
144
180
|
rhsOperand = rhsOperand.substring(1, rhsOperand.length - 1);
|
|
145
181
|
}
|
|
146
182
|
|
|
@@ -153,7 +189,7 @@ const evaluateRhsOperand = (rhsOperand, operator, context) => {
|
|
|
153
189
|
class AssertRuntime {
|
|
154
190
|
runAssertions(assertions, request, response, envVariables, collectionVariables, collectionPath) {
|
|
155
191
|
const enabledAssertions = _.filter(assertions, (a) => a.enabled);
|
|
156
|
-
if(!enabledAssertions.length) {
|
|
192
|
+
if (!enabledAssertions.length) {
|
|
157
193
|
return [];
|
|
158
194
|
}
|
|
159
195
|
|
|
@@ -171,7 +207,7 @@ class AssertRuntime {
|
|
|
171
207
|
...envVariables,
|
|
172
208
|
...collectionVariables,
|
|
173
209
|
...bruContext
|
|
174
|
-
}
|
|
210
|
+
};
|
|
175
211
|
|
|
176
212
|
const assertionResults = [];
|
|
177
213
|
|
|
@@ -179,16 +215,13 @@ class AssertRuntime {
|
|
|
179
215
|
for (const v of enabledAssertions) {
|
|
180
216
|
const lhsExpr = v.name;
|
|
181
217
|
const rhsExpr = v.value;
|
|
182
|
-
const {
|
|
183
|
-
operator,
|
|
184
|
-
value: rhsOperand
|
|
185
|
-
} = parseAssertionOperator(rhsExpr);
|
|
218
|
+
const { operator, value: rhsOperand } = parseAssertionOperator(rhsExpr);
|
|
186
219
|
|
|
187
220
|
try {
|
|
188
221
|
const lhs = evaluateJsExpression(lhsExpr, context);
|
|
189
222
|
const rhs = evaluateRhsOperand(rhsOperand, operator, context);
|
|
190
223
|
|
|
191
|
-
switch(operator) {
|
|
224
|
+
switch (operator) {
|
|
192
225
|
case 'eq':
|
|
193
226
|
expect(lhs).to.equal(rhs);
|
|
194
227
|
break;
|
|
@@ -281,8 +314,7 @@ class AssertRuntime {
|
|
|
281
314
|
operator,
|
|
282
315
|
status: 'pass'
|
|
283
316
|
});
|
|
284
|
-
}
|
|
285
|
-
catch (err) {
|
|
317
|
+
} catch (err) {
|
|
286
318
|
assertionResults.push({
|
|
287
319
|
uid: nanoid(),
|
|
288
320
|
lhsExpr,
|
|
@@ -10,6 +10,7 @@ const punycode = require('punycode');
|
|
|
10
10
|
const Bru = require('../bru');
|
|
11
11
|
const BrunoRequest = require('../bruno-request');
|
|
12
12
|
const BrunoResponse = require('../bruno-response');
|
|
13
|
+
const { cleanJson } = require('../utils');
|
|
13
14
|
|
|
14
15
|
// Inbuilt Library Support
|
|
15
16
|
const atob = require('atob');
|
|
@@ -23,16 +24,31 @@ const fetch = require('node-fetch');
|
|
|
23
24
|
const CryptoJS = require('crypto-js');
|
|
24
25
|
|
|
25
26
|
class ScriptRuntime {
|
|
26
|
-
constructor() {
|
|
27
|
-
}
|
|
27
|
+
constructor() {}
|
|
28
28
|
|
|
29
|
-
async runRequestScript(script, request, envVariables, collectionVariables, collectionPath){
|
|
29
|
+
async runRequestScript(script, request, envVariables, collectionVariables, collectionPath, onConsoleLog) {
|
|
30
30
|
const bru = new Bru(envVariables, collectionVariables);
|
|
31
31
|
const req = new BrunoRequest(request);
|
|
32
|
+
|
|
32
33
|
const context = {
|
|
33
34
|
bru,
|
|
34
35
|
req
|
|
35
36
|
};
|
|
37
|
+
|
|
38
|
+
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
|
39
|
+
const customLogger = (type) => {
|
|
40
|
+
return (...args) => {
|
|
41
|
+
onConsoleLog(type, cleanJson(args));
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
context.console = {
|
|
45
|
+
log: customLogger('log'),
|
|
46
|
+
info: customLogger('info'),
|
|
47
|
+
warn: customLogger('warn'),
|
|
48
|
+
error: customLogger('error')
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
36
52
|
const vm = new NodeVM({
|
|
37
53
|
sandbox: context,
|
|
38
54
|
require: {
|
|
@@ -62,16 +78,16 @@ class ScriptRuntime {
|
|
|
62
78
|
}
|
|
63
79
|
}
|
|
64
80
|
});
|
|
65
|
-
const asyncVM = vm.run(`module.exports = async () => { ${script} }`,
|
|
81
|
+
const asyncVM = vm.run(`module.exports = async () => { ${script} }`, path.join(collectionPath, 'vm.js'));
|
|
66
82
|
await asyncVM();
|
|
67
83
|
return {
|
|
68
84
|
request,
|
|
69
|
-
envVariables,
|
|
70
|
-
collectionVariables
|
|
85
|
+
envVariables: cleanJson(envVariables),
|
|
86
|
+
collectionVariables: cleanJson(collectionVariables)
|
|
71
87
|
};
|
|
72
88
|
}
|
|
73
89
|
|
|
74
|
-
async runResponseScript(script, request, response, envVariables, collectionVariables, collectionPath) {
|
|
90
|
+
async runResponseScript(script, request, response, envVariables, collectionVariables, collectionPath, onConsoleLog) {
|
|
75
91
|
const bru = new Bru(envVariables, collectionVariables);
|
|
76
92
|
const req = new BrunoRequest(request);
|
|
77
93
|
const res = new BrunoResponse(response);
|
|
@@ -81,6 +97,21 @@ class ScriptRuntime {
|
|
|
81
97
|
req,
|
|
82
98
|
res
|
|
83
99
|
};
|
|
100
|
+
|
|
101
|
+
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
|
102
|
+
const customLogger = (type) => {
|
|
103
|
+
return (...args) => {
|
|
104
|
+
onConsoleLog(type, cleanJson(args));
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
context.console = {
|
|
108
|
+
log: customLogger('log'),
|
|
109
|
+
info: customLogger('info'),
|
|
110
|
+
warn: customLogger('warn'),
|
|
111
|
+
error: customLogger('error')
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
84
115
|
const vm = new NodeVM({
|
|
85
116
|
sandbox: context,
|
|
86
117
|
require: {
|
|
@@ -101,13 +132,13 @@ class ScriptRuntime {
|
|
|
101
132
|
}
|
|
102
133
|
});
|
|
103
134
|
|
|
104
|
-
const asyncVM = vm.run(`module.exports = async () => { ${script} }`,
|
|
135
|
+
const asyncVM = vm.run(`module.exports = async () => { ${script} }`, path.join(collectionPath, 'vm.js'));
|
|
105
136
|
await asyncVM();
|
|
106
137
|
|
|
107
138
|
return {
|
|
108
139
|
response,
|
|
109
|
-
envVariables,
|
|
110
|
-
collectionVariables
|
|
140
|
+
envVariables: cleanJson(envVariables),
|
|
141
|
+
collectionVariables: cleanJson(collectionVariables)
|
|
111
142
|
};
|
|
112
143
|
}
|
|
113
144
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const { NodeVM } = require('vm2');
|
|
2
|
-
const chai = require('chai');
|
|
2
|
+
const chai = require('chai');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const Bru = require('../bru');
|
|
5
5
|
const BrunoRequest = require('../bruno-request');
|
|
6
6
|
const BrunoResponse = require('../bruno-response');
|
|
7
7
|
const Test = require('../test');
|
|
8
8
|
const TestResults = require('../test-results');
|
|
9
|
+
const { cleanJson } = require('../utils');
|
|
9
10
|
|
|
10
11
|
// Inbuilt Library Support
|
|
11
12
|
const atob = require('atob');
|
|
@@ -17,10 +18,9 @@ const nanoid = require('nanoid');
|
|
|
17
18
|
const CryptoJS = require('crypto-js');
|
|
18
19
|
|
|
19
20
|
class TestRuntime {
|
|
20
|
-
constructor() {
|
|
21
|
-
}
|
|
21
|
+
constructor() {}
|
|
22
22
|
|
|
23
|
-
runTests(testsFile, request, response, envVariables, collectionVariables, collectionPath) {
|
|
23
|
+
runTests(testsFile, request, response, envVariables, collectionVariables, collectionPath, onConsoleLog) {
|
|
24
24
|
const bru = new Bru(envVariables, collectionVariables);
|
|
25
25
|
const req = new BrunoRequest(request);
|
|
26
26
|
const res = new BrunoResponse(response);
|
|
@@ -28,7 +28,7 @@ class TestRuntime {
|
|
|
28
28
|
const __brunoTestResults = new TestResults();
|
|
29
29
|
const test = Test(__brunoTestResults, chai);
|
|
30
30
|
|
|
31
|
-
if(!testsFile || !testsFile.length) {
|
|
31
|
+
if (!testsFile || !testsFile.length) {
|
|
32
32
|
return {
|
|
33
33
|
request,
|
|
34
34
|
envVariables,
|
|
@@ -47,6 +47,20 @@ class TestRuntime {
|
|
|
47
47
|
__brunoTestResults: __brunoTestResults
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
+
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
|
51
|
+
const customLogger = (type) => {
|
|
52
|
+
return (...args) => {
|
|
53
|
+
onConsoleLog(type, cleanJson(args));
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
context.console = {
|
|
57
|
+
log: customLogger('log'),
|
|
58
|
+
info: customLogger('info'),
|
|
59
|
+
warn: customLogger('warn'),
|
|
60
|
+
error: customLogger('error')
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
const vm = new NodeVM({
|
|
51
65
|
sandbox: context,
|
|
52
66
|
require: {
|
|
@@ -69,9 +83,9 @@ class TestRuntime {
|
|
|
69
83
|
|
|
70
84
|
return {
|
|
71
85
|
request,
|
|
72
|
-
envVariables,
|
|
73
|
-
collectionVariables,
|
|
74
|
-
results: __brunoTestResults.getResults()
|
|
86
|
+
envVariables: cleanJson(envVariables),
|
|
87
|
+
collectionVariables: cleanJson(collectionVariables),
|
|
88
|
+
results: cleanJson(__brunoTestResults.getResults())
|
|
75
89
|
};
|
|
76
90
|
}
|
|
77
91
|
}
|
|
@@ -6,7 +6,7 @@ const { evaluateJsTemplateLiteral, evaluateJsExpression, createResponseParser }
|
|
|
6
6
|
class VarsRuntime {
|
|
7
7
|
runPreRequestVars(vars, request, envVariables, collectionVariables, collectionPath) {
|
|
8
8
|
const enabledVars = _.filter(vars, (v) => v.enabled);
|
|
9
|
-
if(!enabledVars.length) {
|
|
9
|
+
if (!enabledVars.length) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@ class VarsRuntime {
|
|
|
22
22
|
...envVariables,
|
|
23
23
|
...collectionVariables,
|
|
24
24
|
...bruContext
|
|
25
|
-
}
|
|
25
|
+
};
|
|
26
26
|
|
|
27
27
|
_.each(enabledVars, (v) => {
|
|
28
28
|
const value = evaluateJsTemplateLiteral(v.value, context);
|
|
@@ -36,7 +36,7 @@ class VarsRuntime {
|
|
|
36
36
|
|
|
37
37
|
runPostResponseVars(vars, request, response, envVariables, collectionVariables, collectionPath) {
|
|
38
38
|
const enabledVars = _.filter(vars, (v) => v.enabled);
|
|
39
|
-
if(!enabledVars.length) {
|
|
39
|
+
if (!enabledVars.length) {
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -54,7 +54,7 @@ class VarsRuntime {
|
|
|
54
54
|
...envVariables,
|
|
55
55
|
...collectionVariables,
|
|
56
56
|
...bruContext
|
|
57
|
-
}
|
|
57
|
+
};
|
|
58
58
|
|
|
59
59
|
_.each(enabledVars, (v) => {
|
|
60
60
|
const value = evaluateJsExpression(v.value, context);
|
package/src/test.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const Test = (__brunoTestResults, chai) => (description, callback) => {
|
|
2
2
|
try {
|
|
3
3
|
callback();
|
|
4
|
-
__brunoTestResults.addResult({ description, status:
|
|
4
|
+
__brunoTestResults.addResult({ description, status: 'pass' });
|
|
5
5
|
} catch (error) {
|
|
6
6
|
console.log(chai.AssertionError);
|
|
7
7
|
if (error instanceof chai.AssertionError) {
|
|
8
8
|
const { message, actual, expected } = error;
|
|
9
9
|
__brunoTestResults.addResult({
|
|
10
10
|
description,
|
|
11
|
-
status:
|
|
11
|
+
status: 'fail',
|
|
12
12
|
error: message,
|
|
13
13
|
actual,
|
|
14
14
|
expected
|
|
@@ -16,12 +16,12 @@ const Test = (__brunoTestResults, chai) => (description, callback) => {
|
|
|
16
16
|
} else {
|
|
17
17
|
__brunoTestResults.addResult({
|
|
18
18
|
description,
|
|
19
|
-
status:
|
|
19
|
+
status: 'fail',
|
|
20
20
|
error: error.message || 'An unexpected error occurred.'
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
console.log(error);
|
|
24
24
|
}
|
|
25
|
-
}
|
|
25
|
+
};
|
|
26
26
|
|
|
27
|
-
module.exports = Test;
|
|
27
|
+
module.exports = Test;
|
package/src/utils.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
const jsonQuery = require('json-query');
|
|
2
|
-
const { get } = require(
|
|
2
|
+
const { get } = require('@usebruno/query');
|
|
3
3
|
|
|
4
4
|
const JS_KEYWORDS = `
|
|
5
5
|
break case catch class const continue debugger default delete do
|
|
6
6
|
else export extends false finally for function if import in instanceof
|
|
7
7
|
new null return super switch this throw true try typeof var void while with
|
|
8
8
|
undefined let static yield arguments of
|
|
9
|
-
|
|
9
|
+
`
|
|
10
|
+
.split(/\s+/)
|
|
11
|
+
.filter((word) => word.length > 0);
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Creates a function from a Javascript expression
|
|
13
|
-
*
|
|
15
|
+
*
|
|
14
16
|
* When the function is called, the variables used in this expression are picked up from the context
|
|
15
|
-
*
|
|
17
|
+
*
|
|
16
18
|
* ```js
|
|
17
19
|
* res.data.pets.map(pet => pet.name.toUpperCase())
|
|
18
|
-
*
|
|
20
|
+
*
|
|
19
21
|
* function(context) {
|
|
20
22
|
* const { res, pet } = context;
|
|
21
23
|
* return res.data.pets.map(pet => pet.name.toUpperCase())
|
|
@@ -29,25 +31,23 @@ const compileJsExpression = (expr) => {
|
|
|
29
31
|
// get valid js identifiers (foo, bar)
|
|
30
32
|
const vars = new Set(
|
|
31
33
|
matches
|
|
32
|
-
.filter(match => /^[a-zA-Z$_]/.test(match))
|
|
33
|
-
.map(match => match.split('.')[0])
|
|
34
|
-
.filter(name => !JS_KEYWORDS.includes(name))
|
|
34
|
+
.filter((match) => /^[a-zA-Z$_]/.test(match)) // starts with valid js identifier (foo.bar)
|
|
35
|
+
.map((match) => match.split('.')[0]) // top level identifier (foo)
|
|
36
|
+
.filter((name) => !JS_KEYWORDS.includes(name)) // exclude js keywords
|
|
35
37
|
);
|
|
36
38
|
|
|
37
39
|
// globals such as Math
|
|
38
|
-
const globals = [...vars].filter(name => name in globalThis);
|
|
40
|
+
const globals = [...vars].filter((name) => name in globalThis);
|
|
39
41
|
|
|
40
42
|
const code = {
|
|
41
|
-
vars: [...vars].join(
|
|
43
|
+
vars: [...vars].join(', '),
|
|
42
44
|
// pick global from context or globalThis
|
|
43
|
-
globals: globals
|
|
44
|
-
.map(name => ` ${name} = ${name} ?? globalThis.${name};`)
|
|
45
|
-
.join('')
|
|
45
|
+
globals: globals.map((name) => ` ${name} = ${name} ?? globalThis.${name};`).join('')
|
|
46
46
|
};
|
|
47
47
|
|
|
48
48
|
const body = `let { ${code.vars} } = context; ${code.globals}; return ${expr}`;
|
|
49
49
|
|
|
50
|
-
return new Function(
|
|
50
|
+
return new Function('context', body);
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
const internalExpressionCache = new Map();
|
|
@@ -55,47 +55,47 @@ const internalExpressionCache = new Map();
|
|
|
55
55
|
const evaluateJsExpression = (expression, context) => {
|
|
56
56
|
let fn = internalExpressionCache.get(expression);
|
|
57
57
|
if (fn == null) {
|
|
58
|
-
internalExpressionCache.set(expression, fn = compileJsExpression(expression));
|
|
58
|
+
internalExpressionCache.set(expression, (fn = compileJsExpression(expression)));
|
|
59
59
|
}
|
|
60
60
|
return fn(context);
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
const evaluateJsTemplateLiteral = (templateLiteral, context) => {
|
|
64
|
-
if(!templateLiteral || !templateLiteral.length || typeof templateLiteral !== 'string') {
|
|
64
|
+
if (!templateLiteral || !templateLiteral.length || typeof templateLiteral !== 'string') {
|
|
65
65
|
return templateLiteral;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
templateLiteral = templateLiteral.trim();
|
|
69
69
|
|
|
70
|
-
if(templateLiteral === 'true') {
|
|
70
|
+
if (templateLiteral === 'true') {
|
|
71
71
|
return true;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
if(templateLiteral === 'false') {
|
|
74
|
+
if (templateLiteral === 'false') {
|
|
75
75
|
return false;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
if(templateLiteral === 'null') {
|
|
78
|
+
if (templateLiteral === 'null') {
|
|
79
79
|
return null;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
if(templateLiteral === 'undefined') {
|
|
82
|
+
if (templateLiteral === 'undefined') {
|
|
83
83
|
return undefined;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
if(templateLiteral.startsWith('"') && templateLiteral.endsWith('"')) {
|
|
86
|
+
if (templateLiteral.startsWith('"') && templateLiteral.endsWith('"')) {
|
|
87
87
|
return templateLiteral.slice(1, -1);
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
if(templateLiteral.startsWith("'") && templateLiteral.endsWith("'")) {
|
|
90
|
+
if (templateLiteral.startsWith("'") && templateLiteral.endsWith("'")) {
|
|
91
91
|
return templateLiteral.slice(1, -1);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
if(!isNaN(templateLiteral)) {
|
|
94
|
+
if (!isNaN(templateLiteral)) {
|
|
95
95
|
return Number(templateLiteral);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
templateLiteral =
|
|
98
|
+
templateLiteral = '`' + templateLiteral + '`';
|
|
99
99
|
|
|
100
100
|
return evaluateJsExpression(templateLiteral, context);
|
|
101
101
|
};
|
|
@@ -118,9 +118,28 @@ const createResponseParser = (response = {}) => {
|
|
|
118
118
|
return res;
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Objects that are created inside vm2 execution context result in an serilaization error when sent to the renderer process
|
|
123
|
+
* Error sending from webFrameMain: Error: Failed to serialize arguments
|
|
124
|
+
* at s.send (node:electron/js2c/browser_init:169:631)
|
|
125
|
+
* at g.send (node:electron/js2c/browser_init:165:2156)
|
|
126
|
+
* How to reproduce
|
|
127
|
+
* Remove the cleanJson fix and execute the below post response script
|
|
128
|
+
* bru.setVar("a", {b:3});
|
|
129
|
+
* Todo: Find a better fix
|
|
130
|
+
*/
|
|
131
|
+
const cleanJson = (data) => {
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(JSON.stringify(data));
|
|
134
|
+
} catch (e) {
|
|
135
|
+
return data;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
121
139
|
module.exports = {
|
|
122
140
|
evaluateJsExpression,
|
|
123
141
|
evaluateJsTemplateLiteral,
|
|
124
142
|
createResponseParser,
|
|
125
|
-
internalExpressionCache
|
|
126
|
-
|
|
143
|
+
internalExpressionCache,
|
|
144
|
+
cleanJson
|
|
145
|
+
};
|