node-tdd 3.3.2 → 3.3.3

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.
@@ -1,54 +1,43 @@
1
- "use strict";
2
-
3
- const assert = require('assert');
4
-
5
- const http = require('http');
6
-
7
- const https = require('http');
8
-
9
- const path = require('path');
10
-
11
- const fs = require('smart-fs');
12
-
13
- const Joi = require('joi-strict');
14
-
15
- const nock = require('nock');
16
-
17
- const cloneDeep = require('lodash.clonedeep');
18
-
19
- const compareUrls = require('compare-urls');
20
-
21
- const nockCommon = require('nock/lib/common');
22
-
23
- const nockListener = require('./request-recorder/nock-listener');
24
-
25
- const nockMock = require('./request-recorder/nock-mock');
26
-
27
- const healSqsSendMessageBatch = require('./request-recorder/heal-sqs-send-message-batch');
28
-
29
- const applyModifiers = require('./request-recorder/apply-modifiers');
30
-
31
- const {
1
+ import assert from 'assert';
2
+ import http from 'http';
3
+ import path from 'path';
4
+ import fs from 'smart-fs';
5
+ import Joi from 'joi-strict';
6
+ import nock from 'nock';
7
+ import cloneDeep from 'lodash.clonedeep';
8
+ import compareUrls from 'compare-urls';
9
+ import nockCommon from 'nock/lib/common.js';
10
+ import nockListener from './request-recorder/nock-listener.js';
11
+ import nockMock from './request-recorder/nock-mock.js';
12
+ import healSqsSendMessageBatch from './request-recorder/heal-sqs-send-message-batch.js';
13
+ import applyModifiers from './request-recorder/apply-modifiers.js';
14
+ import requestInjector from './request-recorder/request-injector.js';
15
+
16
+ import {
32
17
  buildKey,
33
18
  tryParseJson,
34
19
  nullAsString,
35
20
  convertHeaders,
36
21
  rewriteHeaders
37
- } = require('./request-recorder/util');
38
-
39
- const requestInjector = require('./request-recorder/request-injector');
22
+ } from './request-recorder/util.js';
40
23
 
41
24
  const nockBack = nock.back;
42
25
  const nockRecorder = nock.recorder;
43
26
 
44
- module.exports = opts => {
27
+ export default (opts) => {
45
28
  Joi.assert(opts, Joi.object().keys({
46
29
  cassetteFolder: Joi.string(),
47
30
  stripHeaders: Joi.boolean(),
48
- reqHeaderOverwrite: Joi.object().pattern(Joi.string().case('lower'), Joi.alternatives(Joi.string(), Joi.function())),
31
+ reqHeaderOverwrite: Joi.object().pattern(
32
+ Joi.string().case('lower'),
33
+ Joi.alternatives(Joi.string(), Joi.function())
34
+ ),
49
35
  strict: Joi.boolean(),
50
36
  heal: Joi.alternatives(Joi.boolean(), Joi.string()),
51
- modifiers: Joi.object().pattern(Joi.string(), Joi.alternatives(Joi.function(), Joi.link('#modifiers')))
37
+ modifiers: Joi.object().pattern(
38
+ Joi.string(),
39
+ Joi.alternatives(Joi.function(), Joi.link('#modifiers'))
40
+ )
52
41
  }), 'Invalid Options Provided');
53
42
  let nockDone = null;
54
43
  let cassetteFilePath = null;
@@ -58,47 +47,44 @@ module.exports = opts => {
58
47
  const expectedCassette = [];
59
48
  const pendingMocks = [];
60
49
 
61
- const anyFlagPresent = flags => {
50
+ const anyFlagPresent = (flags) => {
62
51
  assert(Array.isArray(flags) && flags.length !== 0);
63
-
64
52
  if (typeof opts.heal !== 'string') {
65
53
  return false;
66
54
  }
67
-
68
55
  const needleFlags = opts.heal.split(',');
69
- return flags.some(flag => needleFlags.includes(flag));
56
+ return flags.some((flag) => needleFlags.includes(flag));
70
57
  };
71
58
 
72
59
  const overwriteHeaders = (key, value, headers) => {
73
60
  if (key in opts.reqHeaderOverwrite) {
74
- return typeof opts.reqHeaderOverwrite[key] === 'function' ? opts.reqHeaderOverwrite[key]({
75
- key,
76
- value,
77
- headers
78
- }) : opts.reqHeaderOverwrite[key];
61
+ return typeof opts.reqHeaderOverwrite[key] === 'function'
62
+ ? opts.reqHeaderOverwrite[key]({ key, value, headers })
63
+ : opts.reqHeaderOverwrite[key];
79
64
  }
80
-
81
65
  return value;
82
66
  };
83
67
 
84
- return {
85
- inject: async cassetteFile => {
68
+ return ({
69
+ inject: async (cassetteFile) => {
86
70
  assert(nockDone === null);
87
71
  knownCassetteNames.push(cassetteFile);
88
72
  records.length = 0;
89
73
  outOfOrderErrors.length = 0;
90
74
  expectedCassette.length = 0;
91
75
  pendingMocks.length = 0;
76
+
92
77
  cassetteFilePath = path.join(opts.cassetteFolder, cassetteFile);
93
78
  const hasCassette = fs.existsSync(cassetteFilePath);
94
-
95
79
  if (hasCassette) {
96
80
  const cassetteContent = fs.smartRead(cassetteFilePath);
97
- pendingMocks.push(...nock.define(cassetteContent).map((e, idx) => ({
98
- idx,
99
- key: buildKey(e.interceptors[0]),
100
- record: cassetteContent[idx]
101
- })));
81
+ pendingMocks.push(...nock
82
+ .define(cassetteContent)
83
+ .map((e, idx) => ({
84
+ idx,
85
+ key: buildKey(e.interceptors[0]),
86
+ record: cassetteContent[idx]
87
+ })));
102
88
  }
103
89
 
104
90
  nockBack.setMode(hasCassette ? 'lockdown' : 'record');
@@ -106,12 +92,7 @@ module.exports = opts => {
106
92
  nockMock.patch();
107
93
  nockListener.subscribe('no match', () => {
108
94
  assert(hasCassette === true);
109
- const {
110
- protocol,
111
- options,
112
- body
113
- } = requestInjector.getLast();
114
-
95
+ const { protocol, options, body } = requestInjector.getLast();
115
96
  if (anyFlagPresent(['record'])) {
116
97
  expectedCassette.push(async () => {
117
98
  nockRecorder.rec({
@@ -119,25 +100,20 @@ module.exports = opts => {
119
100
  dont_print: true,
120
101
  enable_reqheaders_recording: true
121
102
  });
122
- await new Promise(resolve => {
103
+ await new Promise((resolve) => {
123
104
  options.protocol = `${protocol}:`;
124
- const r = {
125
- http,
126
- https
127
- }[protocol].request(options, response => {
105
+ const r = { http, https: http }[protocol].request(options, (response) => {
128
106
  response.on('data', () => {});
129
107
  response.on('end', resolve);
130
108
  });
131
-
132
109
  if (body !== undefined) {
133
110
  r.write(body);
134
111
  }
135
-
136
112
  r.end();
137
113
  });
138
114
  const recorded = nockRecorder.play();
139
115
  nockRecorder.clear();
140
- return recorded.map(record => Object.assign(record, {
116
+ return recorded.map((record) => Object.assign(record, {
141
117
  headers: opts.stripHeaders === true ? undefined : convertHeaders(record.rawHeaders),
142
118
  rawHeaders: undefined,
143
119
  reqheaders: rewriteHeaders(record.reqheaders, overwriteHeaders)
@@ -156,86 +132,93 @@ module.exports = opts => {
156
132
  });
157
133
  }
158
134
  });
159
- nockDone = await new Promise(resolve => nockBack(cassetteFile, {
160
- before: (scope, scopeIdx) => {
161
- records.push(cloneDeep(scope));
162
- applyModifiers(scope, opts.modifiers); // eslint-disable-next-line no-param-reassign
163
-
164
- scope.reqheaders = rewriteHeaders(scope.reqheaders, (k, valueRecording) => valueRequest => {
165
- const match = nockCommon.matchStringOrRegexp(valueRequest, /^\^.*\$$/.test(valueRecording) ? new RegExp(valueRecording) : valueRecording);
166
-
167
- if (!match && anyFlagPresent(['magic', 'headers'])) {
168
- const idx = pendingMocks.findIndex(m => m.idx === scopeIdx); // overwrite existing headers
169
-
170
- pendingMocks[idx].record.reqheaders[k] = valueRequest;
171
- return true;
172
- }
173
-
174
- return match;
175
- }); // eslint-disable-next-line no-param-reassign
176
-
177
- scope.filteringRequestBody = body => {
178
- if (anyFlagPresent(['magic', 'body'])) {
179
- const idx = pendingMocks.findIndex(m => m.idx === scopeIdx);
180
- const requestBody = tryParseJson(body);
181
- pendingMocks[idx].record.body = nullAsString(requestBody);
182
- return scope.body;
183
- }
184
-
185
- return body;
186
- }; // eslint-disable-next-line no-param-reassign
187
-
188
-
189
- scope.filteringPath = requestPath => {
190
- if (anyFlagPresent(['magic', 'path'])) {
191
- const idx = pendingMocks.findIndex(m => m.idx === scopeIdx);
192
-
193
- if (!compareUrls(pendingMocks[idx].record.path, requestPath)) {
194
- pendingMocks[idx].record.path = requestPath;
135
+ nockDone = await new Promise((resolve) => {
136
+ nockBack(cassetteFile, {
137
+ before: (scope, scopeIdx) => {
138
+ records.push(cloneDeep(scope));
139
+ applyModifiers(scope, opts.modifiers);
140
+ // eslint-disable-next-line no-param-reassign
141
+ scope.reqheaders = rewriteHeaders(
142
+ scope.reqheaders,
143
+ (k, valueRecording) => (valueRequest) => {
144
+ const match = nockCommon.matchStringOrRegexp(
145
+ valueRequest,
146
+ /^\^.*\$$/.test(valueRecording) ? new RegExp(valueRecording) : valueRecording
147
+ );
148
+
149
+ if (!match && anyFlagPresent(['magic', 'headers'])) {
150
+ const idx = pendingMocks.findIndex((m) => m.idx === scopeIdx);
151
+ // overwrite existing headers
152
+ pendingMocks[idx].record.reqheaders[k] = valueRequest;
153
+ return true;
154
+ }
155
+
156
+ return match;
157
+ }
158
+ );
159
+ // eslint-disable-next-line no-param-reassign
160
+ scope.filteringRequestBody = (body) => {
161
+ if (anyFlagPresent(['magic', 'body'])) {
162
+ const idx = pendingMocks.findIndex((m) => m.idx === scopeIdx);
163
+ const requestBody = tryParseJson(body);
164
+ pendingMocks[idx].record.body = nullAsString(requestBody);
165
+ return scope.body;
166
+ }
167
+ return body;
168
+ };
169
+ // eslint-disable-next-line no-param-reassign
170
+ scope.filteringPath = (requestPath) => {
171
+ if (anyFlagPresent(['magic', 'path'])) {
172
+ const idx = pendingMocks.findIndex((m) => m.idx === scopeIdx);
173
+ if (!compareUrls(pendingMocks[idx].record.path, requestPath)) {
174
+ pendingMocks[idx].record.path = requestPath;
175
+ }
176
+ return scope.path;
177
+ }
178
+ return requestPath;
179
+ };
180
+ return scope;
181
+ },
182
+ after: (scope, scopeIdx) => {
183
+ scope.on('request', (req, interceptor, requestBodyString) => {
184
+ const idx = pendingMocks.findIndex((e) => e.idx === scopeIdx);
185
+
186
+ if (anyFlagPresent(['magic', 'headers'])) {
187
+ // add new headers
188
+ const reqheaders = {
189
+ ...rewriteHeaders(req.headers),
190
+ ...rewriteHeaders(pendingMocks[idx].record.reqheaders)
191
+ };
192
+ pendingMocks[idx].record.reqheaders = rewriteHeaders(reqheaders, overwriteHeaders);
195
193
  }
196
194
 
197
- return scope.path;
198
- }
199
-
200
- return requestPath;
201
- };
202
-
203
- return scope;
204
- },
205
- after: (scope, scopeIdx) => {
206
- scope.on('request', (req, interceptor, requestBodyString) => {
207
- const idx = pendingMocks.findIndex(e => e.idx === scopeIdx);
208
-
209
- if (anyFlagPresent(['magic', 'headers'])) {
210
- // add new headers
211
- const reqheaders = { ...rewriteHeaders(req.headers),
212
- ...rewriteHeaders(pendingMocks[idx].record.reqheaders)
213
- };
214
- pendingMocks[idx].record.reqheaders = rewriteHeaders(reqheaders, overwriteHeaders);
215
- }
216
-
217
- if (anyFlagPresent(['magic', 'response'])) {
218
- const responseBody = tryParseJson([healSqsSendMessageBatch].reduce((respBody, fn) => fn(requestBodyString, respBody, scope), interceptor.body)); // eslint-disable-next-line no-param-reassign
219
-
220
- interceptor.body = responseBody;
221
- pendingMocks[idx].record.response = responseBody;
222
- }
223
-
224
- expectedCassette.push(pendingMocks[idx].record);
225
-
226
- if (idx !== 0) {
227
- outOfOrderErrors.push(pendingMocks[idx].key);
228
- }
195
+ if (anyFlagPresent(['magic', 'response'])) {
196
+ const responseBody = tryParseJson([
197
+ healSqsSendMessageBatch
198
+ ].reduce(
199
+ (respBody, fn) => fn(requestBodyString, respBody, scope),
200
+ interceptor.body
201
+ ));
202
+ // eslint-disable-next-line no-param-reassign
203
+ interceptor.body = responseBody;
204
+ pendingMocks[idx].record.response = responseBody;
205
+ }
229
206
 
230
- pendingMocks.splice(idx, 1);
231
- });
232
- },
233
- afterRecord: recordings => JSON.stringify(recordings.map(r => ({ ...r,
234
- body: tryParseJson(r.body),
235
- rawHeaders: opts.stripHeaders === true ? undefined : r.rawHeaders,
236
- reqheaders: rewriteHeaders(r.reqheaders, overwriteHeaders)
237
- })), null, 2)
238
- }, resolve));
207
+ expectedCassette.push(pendingMocks[idx].record);
208
+ if (idx !== 0) {
209
+ outOfOrderErrors.push(pendingMocks[idx].key);
210
+ }
211
+ pendingMocks.splice(idx, 1);
212
+ });
213
+ },
214
+ afterRecord: (recordings) => JSON.stringify(recordings.map((r) => ({
215
+ ...r,
216
+ body: tryParseJson(r.body),
217
+ rawHeaders: opts.stripHeaders === true ? undefined : r.rawHeaders,
218
+ reqheaders: rewriteHeaders(r.reqheaders, overwriteHeaders)
219
+ })), null, 2)
220
+ }, resolve);
221
+ });
239
222
  requestInjector.inject();
240
223
  },
241
224
  release: async () => {
@@ -245,7 +228,7 @@ module.exports = opts => {
245
228
  for (let idx = 0; idx < expectedCassette.length; idx += 1) {
246
229
  if (typeof expectedCassette[idx] === 'function') {
247
230
  // eslint-disable-next-line no-await-in-loop
248
- expectedCassette.splice(idx, 1, ...(await expectedCassette[idx]()));
231
+ expectedCassette.splice(idx, 1, ...await expectedCassette[idx]());
249
232
  idx -= 1;
250
233
  }
251
234
  }
@@ -256,26 +239,25 @@ module.exports = opts => {
256
239
  nockMock.unpatch();
257
240
 
258
241
  if (opts.heal !== false) {
259
- fs.smartWrite(cassetteFilePath, anyFlagPresent(['prune']) ? expectedCassette : [...expectedCassette, ...pendingMocks.map(({
260
- record
261
- }) => record)], {
262
- keepOrder: outOfOrderErrors.length === 0 && pendingMocks.length === 0
263
- });
242
+ fs.smartWrite(
243
+ cassetteFilePath,
244
+ anyFlagPresent(['prune'])
245
+ ? expectedCassette
246
+ : [...expectedCassette, ...pendingMocks.map(({ record }) => record)],
247
+ { keepOrder: outOfOrderErrors.length === 0 && pendingMocks.length === 0 }
248
+ );
264
249
  }
265
-
266
250
  if (opts.strict !== false) {
267
251
  if (outOfOrderErrors.length !== 0) {
268
252
  throw new Error(`Out of Order Recordings: ${outOfOrderErrors.join(', ')}`);
269
253
  }
270
-
271
254
  if (pendingMocks.length !== 0) {
272
- throw new Error(`Unmatched Recordings: ${pendingMocks.map(e => e.key).join(', ')}`);
255
+ throw new Error(`Unmatched Recordings: ${pendingMocks.map((e) => e.key).join(', ')}`);
273
256
  }
274
257
  }
275
258
  },
276
259
  shutdown: () => {
277
- const unexpectedFiles = fs.walkDir(opts.cassetteFolder).filter(f => !knownCassetteNames.includes(f));
278
-
260
+ const unexpectedFiles = fs.walkDir(opts.cassetteFolder).filter((f) => !knownCassetteNames.includes(f));
279
261
  if (unexpectedFiles.length !== 0) {
280
262
  throw new Error(`Unexpected file(s) in cassette folder: ${unexpectedFiles.join(', ')}`);
281
263
  }
@@ -283,9 +265,9 @@ module.exports = opts => {
283
265
  get: () => ({
284
266
  records: records.slice(),
285
267
  outOfOrderErrors: outOfOrderErrors.slice(),
286
- unmatchedRecordings: pendingMocks.map(e => e.key).slice(),
268
+ unmatchedRecordings: pendingMocks.map((e) => e.key).slice(),
287
269
  expectedCassette: expectedCassette.slice(),
288
270
  cassetteFilePath
289
271
  })
290
- };
291
- };
272
+ });
273
+ };
@@ -1,19 +1,22 @@
1
- "use strict";
1
+ import assert from 'assert';
2
+ import Joi from 'joi-strict';
3
+ import tk from 'timekeeper';
2
4
 
3
- const assert = require('assert');
4
-
5
- const Joi = require('joi-strict');
6
-
7
- const tk = require('timekeeper');
8
-
9
- module.exports = opts => {
5
+ export default (opts) => {
10
6
  Joi.assert(opts, Joi.object().keys({
11
- timestamp: Joi.alternatives(Joi.number().integer().min(0), Joi.date().iso())
7
+ timestamp: Joi.alternatives(
8
+ Joi.number().integer().min(0),
9
+ Joi.date().iso()
10
+ )
12
11
  }), 'Invalid Options Provided');
13
12
  return {
14
13
  inject: () => {
15
14
  assert(tk.isKeepingTime() === false);
16
- tk.freeze(Number.isInteger(opts.timestamp) ? new Date(opts.timestamp * 1000) : new Date(opts.timestamp));
15
+ tk.freeze(
16
+ Number.isInteger(opts.timestamp)
17
+ ? new Date(opts.timestamp * 1000)
18
+ : new Date(opts.timestamp)
19
+ );
17
20
  },
18
21
  release: () => {
19
22
  assert(tk.isKeepingTime());
@@ -21,4 +24,4 @@ module.exports = opts => {
21
24
  },
22
25
  isInjected: () => tk.isKeepingTime()
23
26
  };
24
- };
27
+ };