@usebruno/js 0.35.0 → 0.37.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.35.0",
3
+ "version": "0.37.0",
4
4
  "license": "MIT",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -16,7 +16,7 @@
16
16
  "prepack": "npm run test"
17
17
  },
18
18
  "dependencies": {
19
- "@usebruno/common": "0.9.0",
19
+ "@usebruno/common": "0.11.0",
20
20
  "@usebruno/crypto-js": "^3.1.9",
21
21
  "@usebruno/query": "0.1.0",
22
22
  "ajv": "^8.12.0",
@@ -44,8 +44,6 @@
44
44
  "@rollup/plugin-commonjs": "^23.0.2",
45
45
  "@rollup/plugin-node-resolve": "^15.0.1",
46
46
  "rollup": "3.29.5",
47
- "rollup-plugin-terser": "^7.0.2",
48
- "stream": "^0.0.2",
49
- "util": "^0.12.5"
47
+ "rollup-plugin-terser": "^7.0.2"
50
48
  }
51
- }
49
+ }
package/src/bru.js CHANGED
@@ -2,6 +2,7 @@ const { cloneDeep } = require('lodash');
2
2
  const { uuid } = require('./utils');
3
3
  const { interpolate: _interpolate } = require('@usebruno/common');
4
4
  const { sendRequest } = require('@usebruno/requests').scripting;
5
+ const { jar: createCookieJar } = require('@usebruno/requests').cookies;
5
6
 
6
7
  const variableNameRegex = /^[\w-.]*$/;
7
8
 
@@ -21,6 +22,51 @@ class Bru {
21
22
  this.setVisualizations = setVisualizations;
22
23
  this.collectionName = collectionName;
23
24
  this.sendRequest = sendRequest;
25
+ this.cookies = {
26
+ jar: () => {
27
+ const cookieJar = createCookieJar();
28
+
29
+ return {
30
+ getCookie: (url, cookieName, callback) => {
31
+ const interpolatedUrl = this.interpolate(url);
32
+ return cookieJar.getCookie(interpolatedUrl, cookieName, callback);
33
+ },
34
+
35
+ getCookies: (url, callback) => {
36
+ const interpolatedUrl = this.interpolate(url);
37
+ return cookieJar.getCookies(interpolatedUrl, callback);
38
+ },
39
+
40
+ setCookie: (url, nameOrCookieObj, valueOrCallback, maybeCallback) => {
41
+ const interpolatedUrl = this.interpolate(url);
42
+ return cookieJar.setCookie(interpolatedUrl, nameOrCookieObj, valueOrCallback, maybeCallback);
43
+ },
44
+
45
+ setCookies: (url, cookiesArray, callback) => {
46
+ const interpolatedUrl = this.interpolate(url);
47
+ return cookieJar.setCookies(interpolatedUrl, cookiesArray, callback);
48
+ },
49
+
50
+ // Clear entire cookie jar
51
+ clear: (callback) => {
52
+ return cookieJar.clear(callback);
53
+ },
54
+
55
+ // Delete cookies for a specific URL/domain
56
+ deleteCookies: (url, callback) => {
57
+ const interpolatedUrl = this.interpolate(url);
58
+ return cookieJar.deleteCookies(interpolatedUrl, callback);
59
+ },
60
+
61
+ deleteCookie: (url, cookieName, callback) => {
62
+ const interpolatedUrl = this.interpolate(url);
63
+ return cookieJar.deleteCookie(interpolatedUrl, cookieName, callback);
64
+ }
65
+ };
66
+ }
67
+ };
68
+ // Holds variables that are marked as persistent by scripts
69
+ this.persistentEnvVariables = {};
24
70
  this.runner = {
25
71
  skipRequest: () => {
26
72
  this.skipRequest = true;
@@ -83,10 +129,21 @@ class Bru {
83
129
  return this.interpolate(this.envVariables[key]);
84
130
  }
85
131
 
86
- setEnvVar(key, value) {
132
+ setEnvVar(key, value, options = {}) {
87
133
  if (!key) {
88
134
  throw new Error('Creating a env variable without specifying a name is not allowed.');
89
135
  }
136
+
137
+ if (variableNameRegex.test(key) === false) {
138
+ throw new Error(
139
+ `Variable name: "${key}" contains invalid characters! Names must only contain alpha-numeric characters, "-", "_", "."`
140
+ );
141
+ }
142
+
143
+ // When persist is true, only string values are allowed
144
+ if (options?.persist && typeof value !== 'string') {
145
+ throw new Error(`Persistent environment variables must be strings. Received ${typeof value} for key "${key}".`);
146
+ }
90
147
 
91
148
  if (this.historyLogger) {
92
149
  this.historyLogger({
@@ -98,6 +155,14 @@ class Bru {
98
155
  }
99
156
 
100
157
  this.envVariables[key] = value;
158
+
159
+ if (options?.persist) {
160
+ this.persistentEnvVariables[key] = value;
161
+ } else {
162
+ if (this.persistentEnvVariables[key]) {
163
+ delete this.persistentEnvVariables[key];
164
+ }
165
+ }
101
166
  }
102
167
 
103
168
  deleteEnvVar(key) {
@@ -9,6 +9,7 @@ class BrunoResponse {
9
9
  this.headers = res ? res.headers : null;
10
10
  this.body = res ? res.data : null;
11
11
  this.responseTime = res ? res.responseTime : null;
12
+ this.url = res?.request ? res.request.protocol + '//' + res.request.host + res.request.path : null;
12
13
 
13
14
  // Make the instance callable
14
15
  const callable = (...args) => get(this.body, ...args);
@@ -42,6 +43,10 @@ class BrunoResponse {
42
43
  return this.res ? this.res.responseTime : null;
43
44
  }
44
45
 
46
+ getUrl() {
47
+ return this.res ? this.url : null;
48
+ }
49
+
45
50
  setBody(data) {
46
51
  if (!this.res) {
47
52
  return;
@@ -131,6 +131,7 @@ class ScriptRuntime {
131
131
  envVariables: cleanJson(envVariables),
132
132
  runtimeVariables: cleanJson(runtimeVariables),
133
133
  visualizations,
134
+ persistentEnvVariables: bru.persistentEnvVariables,
134
135
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
135
136
  results: cleanJson(__brunoTestResults.getResults()),
136
137
  nextRequestName: bru.nextRequest,
@@ -187,6 +188,7 @@ class ScriptRuntime {
187
188
  envVariables: cleanJson(envVariables),
188
189
  runtimeVariables: cleanJson(runtimeVariables),
189
190
  visualizations,
191
+ persistentEnvVariables: bru.persistentEnvVariables,
190
192
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
191
193
  results: cleanJson(__brunoTestResults.getResults()),
192
194
  nextRequestName: bru.nextRequest,
@@ -285,6 +287,7 @@ class ScriptRuntime {
285
287
  return {
286
288
  response,
287
289
  envVariables: cleanJson(envVariables),
290
+ persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
288
291
  runtimeVariables: cleanJson(runtimeVariables),
289
292
  visualizations,
290
293
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
@@ -341,6 +344,7 @@ class ScriptRuntime {
341
344
  return {
342
345
  response,
343
346
  envVariables: cleanJson(envVariables),
347
+ persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
344
348
  runtimeVariables: cleanJson(runtimeVariables),
345
349
  visualizations,
346
350
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
@@ -197,6 +197,7 @@ class TestRuntime {
197
197
  envVariables: cleanJson(envVariables),
198
198
  runtimeVariables: cleanJson(runtimeVariables),
199
199
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
200
+ persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
200
201
  results: cleanJson(__brunoTestResults.getResults()),
201
202
  nextRequestName: bru.nextRequest
202
203
  };
@@ -76,6 +76,7 @@ class VarsRuntime {
76
76
  envVariables,
77
77
  runtimeVariables,
78
78
  globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
79
+ persistentEnvVariables: cleanJson(bru.persistentEnvVariables),
79
80
  error
80
81
  };
81
82
  }
@@ -48,8 +48,8 @@ const addBruShimToContext = (vm, bru) => {
48
48
  vm.setProp(bruObject, 'getEnvVar', getEnvVar);
49
49
  getEnvVar.dispose();
50
50
 
51
- let setEnvVar = vm.newFunction('setEnvVar', function (key, value) {
52
- bru.setEnvVar(vm.dump(key), vm.dump(value));
51
+ let setEnvVar = vm.newFunction('setEnvVar', function (key, value, options = {}) {
52
+ bru.setEnvVar(vm.dump(key), vm.dump(value), vm.dump(options));
53
53
  });
54
54
  vm.setProp(bruObject, 'setEnvVar', setEnvVar);
55
55
  setEnvVar.dispose();
@@ -173,6 +173,9 @@ const addBruShimToContext = (vm, bru) => {
173
173
  vm.setProp(bruRunnerIterationDataObject, 'stringify', runnerStringifyIterationData);
174
174
  runnerStringifyIterationData.dispose();
175
175
 
176
+ vm.setProp(bruRunnerObject, 'iterationData', bruRunnerIterationDataObject);
177
+ bruRunnerIterationDataObject.dispose();
178
+
176
179
  let visualize = vm.newFunction('visualize', function (type, data) {
177
180
  bru.visualize(vm.dump(type), vm.dump(data));
178
181
  });
@@ -300,8 +303,139 @@ const addBruShimToContext = (vm, bru) => {
300
303
  });
301
304
  sleep.consume((handle) => vm.setProp(bruObject, 'sleep', handle));
302
305
 
303
- vm.setProp(bruRunnerObject, 'iterationData', bruRunnerIterationDataObject);
306
+ let bruCookiesObject = vm.newObject();
307
+
308
+ const _jarFn = vm.newFunction('_jar', () => {
309
+ const nativeJar = bru.cookies.jar();
310
+ const jarObj = vm.newObject();
311
+
312
+ const _getCookieFn = vm.newFunction('_getCookie', (url, cookieName) => {
313
+ const promise = vm.newPromise();
314
+ nativeJar.getCookie(vm.dump(url), vm.dump(cookieName), (err, cookie) => {
315
+ if (err) {
316
+ promise.reject(marshallToVm(cleanJson(err), vm));
317
+ } else {
318
+ promise.resolve(marshallToVm(cleanCircularJson(cookie), vm));
319
+ }
320
+ });
321
+ promise.settled.then(vm.runtime.executePendingJobs);
322
+ return promise.handle;
323
+ });
324
+ _getCookieFn.consume((handle) => vm.setProp(jarObj, '_getCookie', handle));
325
+
326
+ const _getCookiesFn = vm.newFunction('_getCookies', (url) => {
327
+ const promise = vm.newPromise();
328
+ nativeJar.getCookies(vm.dump(url), (err, cookies) => {
329
+ if (err) {
330
+ promise.reject(marshallToVm(cleanJson(err), vm));
331
+ } else {
332
+ promise.resolve(marshallToVm(cleanCircularJson(cookies), vm));
333
+ }
334
+ });
335
+ promise.settled.then(vm.runtime.executePendingJobs);
336
+ return promise.handle;
337
+ });
338
+ _getCookiesFn.consume((handle) => vm.setProp(jarObj, '_getCookies', handle));
339
+
340
+ const _setCookieFn = vm.newFunction('_setCookie', (url, nameOrCookieObj, value) => {
341
+ const promise = vm.newPromise();
342
+ const dumpedUrl = vm.dump(url);
343
+ const dumpedNameOrObj = vm.dump(nameOrCookieObj);
344
+
345
+ // Check if the second argument is an object (cookie object case)
346
+ if (typeof dumpedNameOrObj === 'object' && dumpedNameOrObj !== null) {
347
+ // Cookie object case: setCookie(url, cookieObject, callback)
348
+ nativeJar.setCookie(dumpedUrl, dumpedNameOrObj, (err) => {
349
+ if (err) {
350
+ promise.reject(marshallToVm(cleanJson(err), vm));
351
+ } else {
352
+ promise.resolve(vm.undefined);
353
+ }
354
+ });
355
+ } else {
356
+ // Name/value case: setCookie(url, name, value, callback)
357
+ const dumpedValue = value ? vm.dump(value) : '';
358
+ nativeJar.setCookie(dumpedUrl, dumpedNameOrObj, dumpedValue, (err) => {
359
+ if (err) {
360
+ promise.reject(marshallToVm(cleanJson(err), vm));
361
+ } else {
362
+ promise.resolve(vm.undefined);
363
+ }
364
+ });
365
+ }
366
+
367
+ promise.settled.then(vm.runtime.executePendingJobs);
368
+ return promise.handle;
369
+ });
370
+ _setCookieFn.consume((handle) => vm.setProp(jarObj, '_setCookie', handle));
371
+
372
+ const _setCookiesFn = vm.newFunction('_setCookies', (url, cookiesArray) => {
373
+ const promise = vm.newPromise();
374
+
375
+ nativeJar.setCookies(vm.dump(url), vm.dump(cookiesArray), (err) => {
376
+ if (err) {
377
+ promise.reject(marshallToVm(cleanJson(err), vm));
378
+ } else {
379
+ promise.resolve(vm.undefined);
380
+ }
381
+ });
382
+ promise.settled.then(vm.runtime.executePendingJobs);
383
+ return promise.handle;
384
+ });
385
+ _setCookiesFn.consume((handle) => vm.setProp(jarObj, '_setCookies', handle));
386
+
387
+ const _clearFn = vm.newFunction('_clear', () => {
388
+ const promise = vm.newPromise();
389
+ nativeJar.clear((err) => {
390
+ if (err) {
391
+ promise.reject(marshallToVm(cleanJson(err), vm));
392
+ } else {
393
+ promise.resolve(vm.undefined);
394
+ }
395
+ });
396
+ promise.settled.then(vm.runtime.executePendingJobs);
397
+ return promise.handle;
398
+ });
399
+ _clearFn.consume((handle) => vm.setProp(jarObj, '_clear', handle));
400
+
401
+ const _deleteCookiesFn = vm.newFunction('_deleteCookies', (url) => {
402
+ const promise = vm.newPromise();
403
+ nativeJar.deleteCookies(vm.dump(url), (err) => {
404
+ if (err) {
405
+ promise.reject(marshallToVm(cleanJson(err), vm));
406
+ } else {
407
+ promise.resolve(vm.undefined);
408
+ }
409
+ });
410
+ promise.settled.then(vm.runtime.executePendingJobs);
411
+ return promise.handle;
412
+ });
413
+ _deleteCookiesFn.consume((handle) => vm.setProp(jarObj, '_deleteCookies', handle));
414
+
415
+ const _deleteCookieFn = vm.newFunction('_deleteCookie', (url, cookieName) => {
416
+ const promise = vm.newPromise();
417
+ nativeJar.deleteCookie(vm.dump(url), vm.dump(cookieName), (err) => {
418
+ if (err) {
419
+ promise.reject(marshallToVm(cleanJson(err), vm));
420
+ } else {
421
+ promise.resolve(vm.undefined);
422
+ }
423
+ });
424
+ promise.settled.then(vm.runtime.executePendingJobs);
425
+ return promise.handle;
426
+ });
427
+ _deleteCookieFn.consume((handle) => vm.setProp(jarObj, '_deleteCookie', handle));
428
+
429
+ return jarObj;
430
+ });
431
+ _jarFn.consume((handle) => vm.setProp(bruCookiesObject, '_jar', handle));
432
+
433
+ vm.setProp(bruObject, 'cookies', bruCookiesObject);
434
+ bruCookiesObject.dispose();
435
+
304
436
  vm.setProp(bruObject, 'runner', bruRunnerObject);
437
+ bruRunnerObject.dispose();
438
+
305
439
  vm.setProp(vm.global, 'bru', bruObject);
306
440
  bruObject.dispose();
307
441
 
@@ -325,7 +459,41 @@ const addBruShimToContext = (vm, bru) => {
325
459
  return Promise.reject(err);
326
460
  }
327
461
  }
328
- }
462
+ };
463
+
464
+ globalThis.bru.cookies.jar = () => {
465
+ const _jar = globalThis.bru.cookies._jar();
466
+
467
+ const callWithCallback = async (promiseFn, callback) => {
468
+ if (!callback) return await promiseFn();
469
+ try {
470
+ const result = await promiseFn();
471
+ try { await callback(null, result); } catch(cbErr) { return Promise.reject(cbErr); }
472
+ } catch(err) {
473
+ try { await callback(err, null); } catch(cbErr) { return Promise.reject(cbErr); }
474
+ }
475
+ };
476
+
477
+ return {
478
+ getCookie: (url, name, cb) => callWithCallback(() => _jar._getCookie(url, name), cb),
479
+ getCookies: (url, cb) => callWithCallback(() => _jar._getCookies(url), cb),
480
+ setCookie: (url, nameOrCookieObj, valueOrCallback, maybeCallback) => {
481
+ if (typeof nameOrCookieObj === 'object' && nameOrCookieObj !== null) {
482
+ const callback = typeof valueOrCallback === 'function' ? valueOrCallback : undefined;
483
+ return callWithCallback(() => _jar._setCookie(url, nameOrCookieObj), callback);
484
+ } else {
485
+ const value = typeof valueOrCallback === 'string' ? valueOrCallback : '';
486
+ const callback = typeof maybeCallback === 'function' ? maybeCallback :
487
+ (typeof valueOrCallback === 'function' ? valueOrCallback : undefined);
488
+ return callWithCallback(() => _jar._setCookie(url, nameOrCookieObj, value), callback);
489
+ }
490
+ },
491
+ setCookies: (url, cookiesArray, cb) => callWithCallback(() => _jar._setCookies(url, cookiesArray), cb),
492
+ clear: (cb) => callWithCallback(() => _jar._clear(), cb),
493
+ deleteCookies: (url, cb) => callWithCallback(() => _jar._deleteCookies(url), cb),
494
+ deleteCookie: (url, name, cb) => callWithCallback(() => _jar._deleteCookie(url, name), cb)
495
+ };
496
+ };
329
497
  `);
330
498
  };
331
499
 
@@ -10,17 +10,20 @@ const addBrunoResponseShimToContext = (vm, res) => {
10
10
  const headers = marshallToVm(res?.headers, vm);
11
11
  const body = marshallToVm(res?.body, vm);
12
12
  const responseTime = marshallToVm(res?.responseTime, vm);
13
+ const url = marshallToVm(res?.url, vm);
13
14
 
14
15
  vm.setProp(resFn, 'status', status);
15
16
  vm.setProp(resFn, 'statusText', statusText);
16
17
  vm.setProp(resFn, 'headers', headers);
17
18
  vm.setProp(resFn, 'body', body);
18
19
  vm.setProp(resFn, 'responseTime', responseTime);
20
+ vm.setProp(resFn, 'url', url);
19
21
 
20
22
  status.dispose();
21
23
  headers.dispose();
22
24
  body.dispose();
23
25
  responseTime.dispose();
26
+ url.dispose();
24
27
  statusText.dispose();
25
28
 
26
29
  let getStatusText = vm.newFunction('getStatusText', function () {
@@ -59,6 +62,12 @@ const addBrunoResponseShimToContext = (vm, res) => {
59
62
  vm.setProp(resFn, 'getResponseTime', getResponseTime);
60
63
  getResponseTime.dispose();
61
64
 
65
+ let getUrl = vm.newFunction('getUrl', function () {
66
+ return marshallToVm(res.getUrl(), vm);
67
+ });
68
+ vm.setProp(resFn, 'getUrl', getUrl);
69
+ getUrl.dispose();
70
+
62
71
  let setBody = vm.newFunction('setBody', function (data) {
63
72
  res.setBody(vm.dump(data));
64
73
  });
package/src/utils.js CHANGED
@@ -118,6 +118,7 @@ const createResponseParser = (response = {}) => {
118
118
  res.headers = response.headers;
119
119
  res.body = response.data;
120
120
  res.responseTime = response.responseTime;
121
+ res.url = response.request ? response.request.protocol + '//' + response.request.host + response.request.path : null;
121
122
 
122
123
  res.jq = (expr) => {
123
124
  const output = jsonQuery(expr, { data: response.data });