@usebruno/js 0.23.0 → 0.24.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebruno/js",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -16,6 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@usebruno/common": "0.3.0",
19
+ "@usebruno/crypto-js": "^3.1.9",
19
20
  "@usebruno/query": "0.1.0",
20
21
  "ajv": "^8.12.0",
21
22
  "ajv-formats": "^2.1.1",
@@ -25,11 +26,10 @@
25
26
  "chai": "^4.3.7",
26
27
  "chai-string": "^1.5.0",
27
28
  "crypto-js": "^4.1.1",
28
- "crypto-js-3.1.9-1": "npm:crypto-js@^3.1.9-1",
29
29
  "json-query": "^2.2.2",
30
30
  "lodash": "^4.17.21",
31
31
  "moment": "^2.29.4",
32
- "nanoid": "3.3.4",
32
+ "nanoid": "3.3.8",
33
33
  "node-fetch": "2.7.0",
34
34
  "node-vault": "^0.10.2",
35
35
  "path": "^0.12.7",
package/src/bru.js CHANGED
@@ -17,6 +17,17 @@ class Bru {
17
17
  this.collectionPath = collectionPath;
18
18
  this.historyLogger = historyLogger;
19
19
  this.setVisualizations = setVisualizations;
20
+ this.runner = {
21
+ skipRequest: () => {
22
+ this.skipRequest = true;
23
+ },
24
+ stopExecution: () => {
25
+ this.stopExecution = true;
26
+ },
27
+ setNextRequest: (nextRequest) => {
28
+ this.nextRequest = nextRequest;
29
+ }
30
+ };
20
31
  }
21
32
 
22
33
  _interpolate = (str) => {
@@ -58,6 +58,7 @@ chai.use(function (chai, utils) {
58
58
  * endsWith : ends with
59
59
  * between : between
60
60
  * isEmpty : is empty
61
+ * isNotEmpty : is not empty
61
62
  * isNull : is null
62
63
  * isUndefined : is undefined
63
64
  * isDefined : is defined
@@ -95,6 +96,7 @@ const parseAssertionOperator = (str = '') => {
95
96
  'endsWith',
96
97
  'between',
97
98
  'isEmpty',
99
+ 'isNotEmpty',
98
100
  'isNull',
99
101
  'isUndefined',
100
102
  'isDefined',
@@ -109,6 +111,7 @@ const parseAssertionOperator = (str = '') => {
109
111
 
110
112
  const unaryOperators = [
111
113
  'isEmpty',
114
+ 'isNotEmpty',
112
115
  'isNull',
113
116
  'isUndefined',
114
117
  'isDefined',
@@ -147,6 +150,7 @@ const parseAssertionOperator = (str = '') => {
147
150
  const isUnaryOperator = (operator) => {
148
151
  const unaryOperators = [
149
152
  'isEmpty',
153
+ 'isNotEmpty',
150
154
  'isNull',
151
155
  'isUndefined',
152
156
  'isDefined',
@@ -348,6 +352,9 @@ class AssertRuntime {
348
352
  case 'isEmpty':
349
353
  expect(lhs).to.be.empty;
350
354
  break;
355
+ case 'isNotEmpty':
356
+ expect(lhs).to.not.be.empty;
357
+ break;
351
358
  case 'isNull':
352
359
  expect(lhs).to.be.null;
353
360
  break;
@@ -412,6 +419,7 @@ class AssertRuntime {
412
419
  createdAt: new Date().toISOString()
413
420
  });
414
421
  }
422
+ request.assertionResults = assertionResults;
415
423
 
416
424
  return assertionResults;
417
425
  }
@@ -47,7 +47,8 @@ class ScriptRuntime {
47
47
  processEnvVars,
48
48
  scriptingConfig,
49
49
  historyLogger,
50
- secretVariables
50
+ secretVariables,
51
+ runRequestByItemPathname
51
52
  ) {
52
53
  let visualizations = [];
53
54
  let setVisualizations = (data) => {
@@ -98,6 +99,10 @@ class ScriptRuntime {
98
99
  };
99
100
  }
100
101
 
102
+ if(runRequestByItemPathname) {
103
+ context.bru.runRequest = runRequestByItemPathname;
104
+ }
105
+
101
106
  if (this.runtime === 'quickjs') {
102
107
  await executeQuickJsVmAsync({
103
108
  script: script,
@@ -111,7 +116,9 @@ class ScriptRuntime {
111
116
  runtimeVariables: cleanJson(runtimeVariables),
112
117
  visualizations,
113
118
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
114
- nextRequestName: bru.nextRequest
119
+ nextRequestName: bru.nextRequest,
120
+ skipRequest: bru.skipRequest,
121
+ stopExecution: bru.stopExecution
115
122
  };
116
123
  }
117
124
 
@@ -160,7 +167,9 @@ class ScriptRuntime {
160
167
  runtimeVariables: cleanJson(runtimeVariables),
161
168
  visualizations,
162
169
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
163
- nextRequestName: bru.nextRequest
170
+ nextRequestName: bru.nextRequest,
171
+ skipRequest: bru.skipRequest,
172
+ stopExecution: bru.stopExecution
164
173
  };
165
174
  }
166
175
 
@@ -175,7 +184,8 @@ class ScriptRuntime {
175
184
  processEnvVars,
176
185
  scriptingConfig,
177
186
  historyLogger,
178
- secretVariables
187
+ secretVariables,
188
+ runRequestByItemPathname
179
189
  ) {
180
190
  let visualizations = [];
181
191
  let setVisualizations = (data) => {
@@ -223,6 +233,10 @@ class ScriptRuntime {
223
233
  };
224
234
  }
225
235
 
236
+ if(runRequestByItemPathname) {
237
+ context.bru.runRequest = runRequestByItemPathname;
238
+ }
239
+
226
240
  if (this.runtime === 'quickjs') {
227
241
  await executeQuickJsVmAsync({
228
242
  script: script,
@@ -236,7 +250,9 @@ class ScriptRuntime {
236
250
  runtimeVariables: cleanJson(runtimeVariables),
237
251
  visualizations,
238
252
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
239
- nextRequestName: bru.nextRequest
253
+ nextRequestName: bru.nextRequest,
254
+ skipRequest: bru.skipRequest,
255
+ stopExecution: bru.stopExecution
240
256
  };
241
257
  }
242
258
 
@@ -285,7 +301,9 @@ class ScriptRuntime {
285
301
  runtimeVariables: cleanJson(runtimeVariables),
286
302
  visualizations,
287
303
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
288
- nextRequestName: bru.nextRequest
304
+ nextRequestName: bru.nextRequest,
305
+ skipRequest: bru.skipRequest,
306
+ stopExecution: bru.stopExecution
289
307
  };
290
308
  }
291
309
  }
@@ -32,6 +32,25 @@ const CryptoJS = require('crypto-js');
32
32
  const NodeVault = require('node-vault');
33
33
  const { executeQuickJsVmAsync } = require('../sandbox/quickjs');
34
34
 
35
+ const getResultsSummary = (results) => {
36
+ const summary = {
37
+ total: results.length,
38
+ passed: 0,
39
+ failed: 0,
40
+ skipped: 0,
41
+ };
42
+
43
+ results.forEach((r) => {
44
+ const passed = r.status === "pass";
45
+ if (passed) summary.passed += 1;
46
+ else if (r.status === "fail") summary.failed += 1;
47
+ else summary.skipped += 1;
48
+ });
49
+
50
+ return summary;
51
+ }
52
+
53
+
35
54
  class TestRuntime {
36
55
  constructor(props) {
37
56
  this.runtime = props?.runtime || 'vm2';
@@ -48,12 +67,14 @@ class TestRuntime {
48
67
  processEnvVars,
49
68
  scriptingConfig,
50
69
  historyLogger,
51
- secretVariables
70
+ secretVariables,
71
+ runRequestByItemPathname
52
72
  ) {
53
73
  const globalEnvironmentVariables = request?.globalEnvironmentVariables || {};
54
74
  const collectionVariables = request?.collectionVariables || {};
55
75
  const folderVariables = request?.folderVariables || {};
56
76
  const requestVariables = request?.requestVariables || {};
77
+ const assertionResults = request?.assertionResults || [];
57
78
  const bru = new Bru(envVariables, runtimeVariables, processEnvVars, collectionPath, historyLogger, undefined, secretVariables, collectionVariables, folderVariables, requestVariables, globalEnvironmentVariables);
58
79
  const req = new BrunoRequest(request, historyLogger);
59
80
  const res = new BrunoResponse(response);
@@ -77,6 +98,7 @@ class TestRuntime {
77
98
  }
78
99
 
79
100
  const __brunoTestResults = new TestResults();
101
+
80
102
  const test = Test(__brunoTestResults, chai);
81
103
 
82
104
  if (!testsFile || !testsFile.length) {
@@ -90,6 +112,36 @@ class TestRuntime {
90
112
  };
91
113
  }
92
114
 
115
+ bru.getTestResults = async () => {
116
+ let results = await __brunoTestResults.getResults();
117
+ const summary = getResultsSummary(results);
118
+ return {
119
+ summary,
120
+ results: results?.map?.(r => ({
121
+ status: r?.status,
122
+ description: r?.description,
123
+ expected: r?.expected,
124
+ actual: r?.actual,
125
+ error: r?.error
126
+ }))
127
+ };
128
+ }
129
+ bru.getAssertionResults = async () => {
130
+ let results = assertionResults;
131
+ const summary = getResultsSummary(results);
132
+ return {
133
+ summary,
134
+ results: results?.map?.(r => ({
135
+ status: r?.status,
136
+ lhsExpr: r?.lhsExpr,
137
+ rhsExpr: r?.rhsExpr,
138
+ operator: r?.operator,
139
+ rhsOperand: r?.rhsOperand,
140
+ error: r?.error
141
+ }))
142
+ };
143
+ }
144
+
93
145
  const context = {
94
146
  test,
95
147
  bru,
@@ -115,6 +167,10 @@ class TestRuntime {
115
167
  };
116
168
  }
117
169
 
170
+ if(runRequestByItemPathname) {
171
+ context.bru.runRequest = runRequestByItemPathname;
172
+ }
173
+
118
174
  if (this.runtime === 'quickjs') {
119
175
  await executeQuickJsVmAsync({
120
176
  script: testsFile,
@@ -11,7 +11,7 @@ const bundleLibraries = async () => {
11
11
  import moment from "moment";
12
12
  import btoa from "btoa";
13
13
  import atob from "atob";
14
- import * as CryptoJS from "crypto-js-3.1.9-1";
14
+ import * as CryptoJS from "@usebruno/crypto-js";
15
15
  globalThis.expect = expect;
16
16
  globalThis.assert = assert;
17
17
  globalThis.moment = moment;
@@ -1,7 +1,9 @@
1
+ const { cleanJson } = require('../../../utils');
1
2
  const { marshallToVm } = require('../utils');
2
3
 
3
4
  const addBruShimToContext = (vm, bru) => {
4
5
  const bruObject = vm.newObject();
6
+ const bruRunnerObject = vm.newObject();
5
7
 
6
8
  let cwd = vm.newFunction('cwd', function () {
7
9
  return marshallToVm(bru.cwd(), vm);
@@ -93,6 +95,24 @@ const addBruShimToContext = (vm, bru) => {
93
95
  vm.setProp(bruObject, 'setNextRequest', setNextRequest);
94
96
  setNextRequest.dispose();
95
97
 
98
+ let runnerSkipRequest = vm.newFunction('skipRequest', function () {
99
+ bru?.runner?.skipRequest();
100
+ });
101
+ vm.setProp(bruRunnerObject, 'skipRequest', runnerSkipRequest);
102
+ runnerSkipRequest.dispose();
103
+
104
+ let runnerStopExecution = vm.newFunction('stopExecution', function () {
105
+ bru?.runner?.stopExecution();
106
+ });
107
+ vm.setProp(bruRunnerObject, 'stopExecution', runnerStopExecution);
108
+ runnerStopExecution.dispose();
109
+
110
+ let runnerSetNextRequest = vm.newFunction('setNextRequest', function (nextRequest) {
111
+ bru?.runner?.setNextRequest(vm.dump(nextRequest));
112
+ });
113
+ vm.setProp(bruRunnerObject, 'setNextRequest', runnerSetNextRequest);
114
+ runnerSetNextRequest.dispose();
115
+
96
116
  let visualize = vm.newFunction('visualize', function (type, data) {
97
117
  bru.visualize(vm.dump(type), vm.dump(data));
98
118
  });
@@ -123,6 +143,70 @@ const addBruShimToContext = (vm, bru) => {
123
143
  vm.setProp(bruObject, 'getCollectionVar', getCollectionVar);
124
144
  getCollectionVar.dispose();
125
145
 
146
+ let getTestResults = vm.newFunction('getTestResults', () => {
147
+ const promise = vm.newPromise();
148
+ bru.getTestResults()
149
+ .then((results) => {
150
+ promise.resolve(marshallToVm(cleanJson(results), vm));
151
+ })
152
+ .catch((err) => {
153
+ promise.resolve(
154
+ marshallToVm(
155
+ cleanJson({
156
+ message: err.message
157
+ }),
158
+ vm
159
+ )
160
+ );
161
+ });
162
+ promise.settled.then(vm.runtime.executePendingJobs);
163
+ return promise.handle;
164
+ });
165
+ getTestResults.consume((handle) => vm.setProp(bruObject, 'getTestResults', handle));
166
+
167
+ let getAssertionResults = vm.newFunction('getAssertionResults', () => {
168
+ const promise = vm.newPromise();
169
+ bru.getAssertionResults()
170
+ .then((results) => {
171
+ promise.resolve(marshallToVm(cleanJson(results), vm));
172
+ })
173
+ .catch((err) => {
174
+ promise.resolve(
175
+ marshallToVm(
176
+ cleanJson({
177
+ message: err.message
178
+ }),
179
+ vm
180
+ )
181
+ );
182
+ });
183
+ promise.settled.then(vm.runtime.executePendingJobs);
184
+ return promise.handle;
185
+ });
186
+ getAssertionResults.consume((handle) => vm.setProp(bruObject, 'getAssertionResults', handle));
187
+
188
+ let runRequestHandle = vm.newFunction('runRequest', (args) => {
189
+ const promise = vm.newPromise();
190
+ bru.runRequest(vm.dump(args))
191
+ .then((response) => {
192
+ const { status, headers, data, dataBuffer, size } = response || {};
193
+ promise.resolve(marshallToVm(cleanJson({ status, headers, data, dataBuffer, size }), vm));
194
+ })
195
+ .catch((err) => {
196
+ promise.resolve(
197
+ marshallToVm(
198
+ cleanJson({
199
+ message: err.message
200
+ }),
201
+ vm
202
+ )
203
+ );
204
+ });
205
+ promise.settled.then(vm.runtime.executePendingJobs);
206
+ return promise.handle;
207
+ });
208
+ runRequestHandle.consume((handle) => vm.setProp(bruObject, 'runRequest', handle));
209
+
126
210
  const sleep = vm.newFunction('sleep', (timer) => {
127
211
  const t = vm.getString(timer);
128
212
  const promise = vm.newPromise();
@@ -134,6 +218,7 @@ const addBruShimToContext = (vm, bru) => {
134
218
  });
135
219
  sleep.consume((handle) => vm.setProp(bruObject, 'sleep', handle));
136
220
 
221
+ vm.setProp(bruObject, 'runner', bruRunnerObject);
137
222
  vm.setProp(vm.global, 'bru', bruObject);
138
223
  bruObject.dispose();
139
224
  };
package/src/utils.js CHANGED
@@ -19,8 +19,8 @@ const JS_KEYWORDS = `
19
19
  * ```js
20
20
  * res.data.pets.map(pet => pet.name.toUpperCase())
21
21
  *
22
- * function(context) {
23
- * const { res, pet } = context;
22
+ * function(__bruno__functionInnerContext) {
23
+ * const { res, pet } = __bruno__functionInnerContext;
24
24
  * return res.data.pets.map(pet => pet.name.toUpperCase())
25
25
  * }
26
26
  * ```
@@ -46,9 +46,11 @@ const compileJsExpression = (expr) => {
46
46
  globals: globals.map((name) => ` ${name} = ${name} ?? globalThis.${name};`).join('')
47
47
  };
48
48
 
49
- const body = `let { ${code.vars} } = context; ${code.globals}; return ${expr}`;
49
+ // param name that is unlikely to show up as a var in an expression
50
+ const param = `__bruno__functionInnerContext`;
51
+ const body = `let { ${code.vars} } = ${param}; ${code.globals}; return ${expr}`;
50
52
 
51
- return new Function('context', body);
53
+ return new Function(param, body);
52
54
  };
53
55
 
54
56
  const internalExpressionCache = new Map();