node-tdd 3.0.6 → 3.2.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/README.md CHANGED
@@ -80,12 +80,13 @@ Can be used in the following ways:
80
80
 
81
81
  - `--nock-heal`: Will try to heal ordering of nock cassette recordings
82
82
  - `--nock-heal prune`: Will remove unmatched recordings from nock cassette
83
+ - `--nock-heal headers`: Will try to heal request headers of nock cassette recordings
83
84
  - `--nock-heal body`: Will try to heal bodies of nock cassette recordings
84
85
  - `--nock-heal path`: Will try to heal paths of nock cassette recordings
85
86
  - `--nock-heal response`: Will try to heal responses
86
87
  - `--nock-heal record`: Will record the next unmatched request
87
88
  - `--nock-heal stub`: Will stub the next unmatched request
88
- - `--nock-heal magic`: Shorthand for `prune,body,path,response`
89
+ - `--nock-heal magic`: Shorthand for `headers,body,path,response`
89
90
 
90
91
  Notes:
91
92
  - Different flags can be combined as e.g. `--nock-heal body,path`
@@ -131,6 +132,13 @@ Default: `false`
131
132
 
132
133
  When set to true, all headers are stripped when requests are recorded.
133
134
 
135
+ ### nockReqHeaderOverwrite
136
+
137
+ Type: `object`<br>
138
+ Default: `{}`
139
+
140
+ Can be used to overwrite `reqheaders` in recordings. Cassette files are only updated when changed.
141
+
134
142
  #### fixtureFolder
135
143
 
136
144
  Type: `string`<br>
@@ -2,7 +2,7 @@
2
2
 
3
3
  const nockCommon = require('nock/lib/common');
4
4
 
5
- const fns = ['setInterval', 'setTimeout'];
5
+ const fns = ['setInterval', 'setTimeout', 'deleteHeadersField'];
6
6
  const originals = fns.reduce((p, c) => Object.assign(p, {
7
7
  [c]: nockCommon[c]
8
8
  }), {});
@@ -10,6 +10,10 @@ module.exports = {
10
10
  patch: () => {
11
11
  fns.forEach(fn => {
12
12
  nockCommon[fn] = (...args) => {
13
+ if (fn === 'deleteHeadersField') {
14
+ return undefined;
15
+ }
16
+
13
17
  if (args[1] === 0) {
14
18
  return args[0](...args.slice(2));
15
19
  }
@@ -6,7 +6,7 @@ const http = require('http');
6
6
 
7
7
  const https = require('https');
8
8
 
9
- const common = require('nock/lib/common');
9
+ const nockCommon = require('nock/lib/common');
10
10
 
11
11
  let lastProtocol = null;
12
12
  let lastOptions = null;
@@ -21,7 +21,7 @@ const wrapper = proto => {
21
21
 
22
22
  const requestWrapper = (...args) => {
23
23
  lastProtocol = proto;
24
- lastOptions = common.normalizeClientRequestArgs(...args).options;
24
+ lastOptions = nockCommon.normalizeClientRequestArgs(...args).options;
25
25
  lastBody.length = 0;
26
26
  const result = requestOriginal.call(protocol, ...args);
27
27
  const writeOriginal = result.write;
@@ -24,4 +24,12 @@ module.exports.convertHeaders = array => {
24
24
  }
25
25
 
26
26
  return obj;
27
+ };
28
+
29
+ module.exports.rewriteHeaders = (headers, fn = (k, v) => v) => {
30
+ if (headers === undefined) {
31
+ return {};
32
+ }
33
+
34
+ return Object.fromEntries(Object.entries(headers).sort(([a], [b]) => a > b ? 1 : -1).map(([k, v]) => [k.toLowerCase(), fn(k, v)]));
27
35
  };
@@ -18,6 +18,8 @@ const cloneDeep = require('lodash.clonedeep');
18
18
 
19
19
  const compareUrls = require('compare-urls');
20
20
 
21
+ const nockCommon = require('nock/lib/common');
22
+
21
23
  const nockListener = require('./request-recorder/nock-listener');
22
24
 
23
25
  const nockMock = require('./request-recorder/nock-mock');
@@ -30,7 +32,8 @@ const {
30
32
  buildKey,
31
33
  tryParseJson,
32
34
  nullAsString,
33
- convertHeaders
35
+ convertHeaders,
36
+ rewriteHeaders
34
37
  } = require('./request-recorder/util');
35
38
 
36
39
  const requestInjector = require('./request-recorder/request-injector');
@@ -42,6 +45,7 @@ module.exports = opts => {
42
45
  Joi.assert(opts, Joi.object().keys({
43
46
  cassetteFolder: Joi.string(),
44
47
  stripHeaders: Joi.boolean(),
48
+ reqHeaderOverwrite: Joi.object().pattern(Joi.string().case('lower'), Joi.string()),
45
49
  strict: Joi.boolean(),
46
50
  heal: Joi.alternatives(Joi.boolean(), Joi.string()),
47
51
  modifiers: Joi.object().pattern(Joi.string(), Joi.alternatives(Joi.function(), Joi.link('#modifiers')))
@@ -65,6 +69,14 @@ module.exports = opts => {
65
69
  return flags.some(flag => needleFlags.includes(flag));
66
70
  };
67
71
 
72
+ const overwriteHeaders = (k, v) => {
73
+ if (k in opts.reqHeaderOverwrite) {
74
+ return opts.reqHeaderOverwrite[k];
75
+ }
76
+
77
+ return v;
78
+ };
79
+
68
80
  return {
69
81
  inject: async cassetteFile => {
70
82
  assert(nockDone === null);
@@ -101,7 +113,7 @@ module.exports = opts => {
101
113
  nockRecorder.rec({
102
114
  output_objects: true,
103
115
  dont_print: true,
104
- enable_reqheaders_recording: false
116
+ enable_reqheaders_recording: true
105
117
  });
106
118
  await new Promise(resolve => {
107
119
  options.protocol = `${protocol}:`;
@@ -123,7 +135,8 @@ module.exports = opts => {
123
135
  nockRecorder.clear();
124
136
  return recorded.map(record => Object.assign(record, {
125
137
  headers: opts.stripHeaders === true ? undefined : convertHeaders(record.rawHeaders),
126
- rawHeaders: undefined
138
+ rawHeaders: undefined,
139
+ reqheaders: rewriteHeaders(record.reqheaders, overwriteHeaders)
127
140
  }));
128
141
  });
129
142
  } else if (anyFlagPresent(['stub'])) {
@@ -133,6 +146,7 @@ module.exports = opts => {
133
146
  path: options.path,
134
147
  body: tryParseJson(body),
135
148
  status: 200,
149
+ reqheaders: rewriteHeaders(options.headers, overwriteHeaders),
136
150
  response: {},
137
151
  responseIsBinary: false
138
152
  });
@@ -143,6 +157,19 @@ module.exports = opts => {
143
157
  records.push(cloneDeep(scope));
144
158
  applyModifiers(scope, opts.modifiers); // eslint-disable-next-line no-param-reassign
145
159
 
160
+ scope.reqheaders = rewriteHeaders(scope.reqheaders, (k, valueRecording) => valueRequest => {
161
+ const match = nockCommon.matchStringOrRegexp(valueRequest, /^\^.*\$$/.test(valueRecording) ? new RegExp(valueRecording) : valueRecording);
162
+
163
+ if (!match && anyFlagPresent(['magic', 'headers'])) {
164
+ const idx = pendingMocks.findIndex(m => m.idx === scopeIdx); // overwrite existing headers
165
+
166
+ pendingMocks[idx].record.reqheaders[k] = valueRequest;
167
+ return true;
168
+ }
169
+
170
+ return match;
171
+ }); // eslint-disable-next-line no-param-reassign
172
+
146
173
  scope.filteringRequestBody = body => {
147
174
  if (anyFlagPresent(['magic', 'body'])) {
148
175
  const idx = pendingMocks.findIndex(m => m.idx === scopeIdx);
@@ -175,6 +202,14 @@ module.exports = opts => {
175
202
  scope.on('request', (req, interceptor, requestBodyString) => {
176
203
  const idx = pendingMocks.findIndex(e => e.idx === scopeIdx);
177
204
 
205
+ if (anyFlagPresent(['magic', 'headers'])) {
206
+ // add new headers
207
+ const reqheaders = { ...rewriteHeaders(req.headers),
208
+ ...rewriteHeaders(pendingMocks[idx].record.reqheaders)
209
+ };
210
+ pendingMocks[idx].record.reqheaders = rewriteHeaders(reqheaders, overwriteHeaders);
211
+ }
212
+
178
213
  if (anyFlagPresent(['magic', 'response'])) {
179
214
  const responseBody = tryParseJson([healSqsSendMessageBatch].reduce((respBody, fn) => fn(requestBodyString, respBody, scope), interceptor.body)); // eslint-disable-next-line no-param-reassign
180
215
 
@@ -193,7 +228,8 @@ module.exports = opts => {
193
228
  },
194
229
  afterRecord: recordings => JSON.stringify(recordings.map(r => ({ ...r,
195
230
  body: tryParseJson(r.body),
196
- rawHeaders: opts.stripHeaders === true ? undefined : r.rawHeaders
231
+ rawHeaders: opts.stripHeaders === true ? undefined : r.rawHeaders,
232
+ reqheaders: rewriteHeaders(r.reqheaders, overwriteHeaders)
197
233
  })), null, 2)
198
234
  }, resolve));
199
235
  requestInjector.inject();
@@ -201,10 +237,6 @@ module.exports = opts => {
201
237
  release: async () => {
202
238
  assert(nockDone !== null);
203
239
  requestInjector.release();
204
- nockDone();
205
- nockDone = null;
206
- nockListener.unsubscribeAll('no match');
207
- nockMock.unpatch();
208
240
 
209
241
  for (let idx = 0; idx < expectedCassette.length; idx += 1) {
210
242
  if (typeof expectedCassette[idx] === 'function') {
@@ -214,8 +246,13 @@ module.exports = opts => {
214
246
  }
215
247
  }
216
248
 
249
+ nockDone();
250
+ nockDone = null;
251
+ nockListener.unsubscribeAll('no match');
252
+ nockMock.unpatch();
253
+
217
254
  if (opts.heal !== false) {
218
- fs.smartWrite(cassetteFilePath, anyFlagPresent(['magic', 'prune']) ? expectedCassette : [...expectedCassette, ...pendingMocks.map(({
255
+ fs.smartWrite(cassetteFilePath, anyFlagPresent(['prune']) ? expectedCassette : [...expectedCassette, ...pendingMocks.map(({
219
256
  record
220
257
  }) => record)], {
221
258
  keepOrder: outOfOrderErrors.length === 0 && pendingMocks.length === 0
package/lib/util/desc.js CHANGED
@@ -73,6 +73,7 @@ const desc = (suiteName, optsOrTests, testsOrNull = null) => {
73
73
  const nockFolder = resolve(get(opts, 'nockFolder', '$FILENAME__cassettes'));
74
74
  const nockModifiers = get(opts, 'nockModifiers', {});
75
75
  const nockStripHeaders = get(opts, 'nockStripHeaders', false);
76
+ const nockReqHeaderOverwrite = get(opts, 'nockReqHeaderOverwrite', {});
76
77
  const fixtureFolder = resolve(get(opts, 'fixtureFolder', '$FILENAME__fixtures'));
77
78
  const envVarsFile = resolve(get(opts, 'envVarsFile', '$FILENAME.env.yml'));
78
79
  const envVars = get(opts, 'envVars', null);
@@ -178,6 +179,7 @@ const desc = (suiteName, optsOrTests, testsOrNull = null) => {
178
179
  requestRecorder = RequestRecorder({
179
180
  cassetteFolder: `${nockFolder}/`,
180
181
  stripHeaders: nockStripHeaders,
182
+ reqHeaderOverwrite: nockReqHeaderOverwrite,
181
183
  strict: true,
182
184
  heal: nockHeal,
183
185
  modifiers: nockModifiers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-tdd",
3
- "version": "3.0.6",
3
+ "version": "3.2.0",
4
4
  "description": "Drop in extension for mocha to abstract commonly used test setups",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -48,7 +48,7 @@
48
48
  "@blackflux/robo-config-plugin": "5.3.0",
49
49
  "aws-sdk": "2.981.0",
50
50
  "aws-sdk-wrap": "9.6.0",
51
- "axios": "0.21.1",
51
+ "axios": "0.24.0",
52
52
  "babel-eslint": "10.1.0",
53
53
  "babel-preset-latest-node": "5.5.1",
54
54
  "chai": "4.3.4",