cypress 12.1.0 → 12.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.
@@ -3,67 +3,58 @@
3
3
  const {
4
4
  errors
5
5
  } = require('../errors');
6
+
6
7
  /**
7
8
  * Throws an error with "details" property from
8
9
  * "errors" object.
9
10
  * @param {Object} details - Error details
10
11
  */
11
-
12
-
13
12
  const throwInvalidOptionError = details => {
14
13
  if (!details) {
15
14
  details = errors.unknownError;
16
- } // throw this error synchronously, it will be caught later on and
17
- // the details will be propagated to the promise chain
18
-
15
+ }
19
16
 
17
+ // throw this error synchronously, it will be caught later on and
18
+ // the details will be propagated to the promise chain
20
19
  const err = new Error();
21
20
  err.details = details;
22
21
  throw err;
23
22
  };
23
+
24
24
  /**
25
25
  * Selects exec args based on the configured `testingType`
26
26
  * @param {string} testingType The type of tests being executed
27
27
  * @returns {string[]} The array of new exec arguments
28
28
  */
29
-
30
-
31
29
  const processTestingType = options => {
32
30
  if (options.e2e && options.component) {
33
31
  return throwInvalidOptionError(errors.incompatibleTestTypeFlags);
34
32
  }
35
-
36
33
  if (options.testingType && (options.component || options.e2e)) {
37
34
  return throwInvalidOptionError(errors.incompatibleTestTypeFlags);
38
35
  }
39
-
40
36
  if (options.testingType === 'component' || options.component || options.ct) {
41
37
  return ['--testing-type', 'component'];
42
38
  }
43
-
44
39
  if (options.testingType === 'e2e' || options.e2e) {
45
40
  return ['--testing-type', 'e2e'];
46
41
  }
47
-
48
42
  if (options.testingType) {
49
43
  return throwInvalidOptionError(errors.invalidTestingType);
50
44
  }
51
-
52
45
  return [];
53
46
  };
47
+
54
48
  /**
55
49
  * Throws an error if configFile is string 'false' or boolean false
56
50
  * @param {*} options
57
51
  */
58
-
59
-
60
52
  const checkConfigFile = options => {
61
53
  // CLI will parse as string, module API can pass in boolean
62
54
  if (options.configFile === 'false' || options.configFile === false) {
63
55
  throwInvalidOptionError(errors.invalidConfigFile);
64
56
  }
65
57
  };
66
-
67
58
  module.exports = {
68
59
  throwInvalidOptionError,
69
60
  processTestingType,
package/lib/exec/spawn.js CHANGED
@@ -1,129 +1,109 @@
1
1
  "use strict";
2
2
 
3
3
  const _ = require('lodash');
4
-
5
4
  const os = require('os');
6
-
7
5
  const cp = require('child_process');
8
-
9
6
  const path = require('path');
10
-
11
7
  const Promise = require('bluebird');
12
-
13
8
  const debug = require('debug')('cypress:cli');
14
-
15
9
  const debugElectron = require('debug')('cypress:electron');
16
-
17
10
  const util = require('../util');
18
-
19
11
  const state = require('../tasks/state');
20
-
21
12
  const xvfb = require('./xvfb');
22
-
23
13
  const verify = require('../tasks/verify');
24
-
25
14
  const errors = require('../errors');
26
-
27
15
  const isXlibOrLibudevRe = /^(?:Xlib|libudev)/;
28
16
  const isHighSierraWarningRe = /\*\*\* WARNING/;
29
- const isRenderWorkerRe = /\.RenderWorker-/; // Chromium (which Electron uses) always makes several attempts to connect to the system dbus.
17
+ const isRenderWorkerRe = /\.RenderWorker-/;
18
+
19
+ // Chromium (which Electron uses) always makes several attempts to connect to the system dbus.
30
20
  // This works fine in most desktop environments, but in a docker container, there is no dbus service
31
21
  // and Chromium emits several error lines, similar to these:
22
+
32
23
  // [1957:0406/160550.146820:ERROR:bus.cc(392)] Failed to connect to the bus: Failed to connect to socket /var/run/dbus/system_bus_socket: No such file or directory
33
24
  // [1957:0406/160550.147994:ERROR:bus.cc(392)] Failed to connect to the bus: Address does not contain a colon
25
+
34
26
  // These warnings are absolutely harmless. Failure to connect to dbus means that electron won't be able to access the user's
35
27
  // credential wallet (none exists in a docker container) and won't show up in the system tray (again, none exists).
36
28
  // Failure to connect is expected and normal here, but users frequently misidentify these errors as the cause of their problems.
29
+
37
30
  // https://github.com/cypress-io/cypress/issues/19299
31
+ const isDbusWarning = /Failed to connect to the bus:/;
38
32
 
39
- const isDbusWarning = /Failed to connect to the bus:/; // Electron began logging these on self-signed certs with 17.0.0-alpha.4.
33
+ // Electron began logging these on self-signed certs with 17.0.0-alpha.4.
40
34
  // Once this is fixed upstream this regex can be removed: https://github.com/electron/electron/issues/34583
41
35
  // Sample:
42
36
  // [3801:0606/152837.383892:ERROR:cert_verify_proc_builtin.cc(681)] CertVerifyProcBuiltin for www.googletagmanager.com failed:
43
37
  // ----- Certificate i=0 (OU=Cypress Proxy Server Certificate,O=Cypress Proxy CA,L=Internet,ST=Internet,C=Internet,CN=www.googletagmanager.com) -----
44
38
  // ERROR: No matching issuer found
39
+ const isCertVerifyProcBuiltin = /(^\[.*ERROR:cert_verify_proc_builtin\.cc|^----- Certificate i=0 \(OU=Cypress Proxy|^ERROR: No matching issuer found$)/;
45
40
 
46
- const isCertVerifyProcBuiltin = /(^\[.*ERROR:cert_verify_proc_builtin\.cc|^----- Certificate i=0 \(OU=Cypress Proxy|^ERROR: No matching issuer found$)/; // Electron logs a benign warning about WebSwapCGLLayer on MacOS v12 and Electron v18 due to a naming collision in shared libraries.
41
+ // Electron logs a benign warning about WebSwapCGLLayer on MacOS v12 and Electron v18 due to a naming collision in shared libraries.
47
42
  // Once this is fixed upstream this regex can be removed: https://github.com/electron/electron/issues/33685
48
43
  // Sample:
49
44
  // objc[60540]: Class WebSwapCGLLayer is implemented in both /System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks/WebCore.framework/Versions/A/Frameworks/libANGLE-shared.dylib (0x7ffa5a006318) and /{path/to/app}/node_modules/electron/dist/Electron.app/Contents/Frameworks/Electron Framework.framework/Versions/A/Libraries/libGLESv2.dylib (0x10f8a89c8). One of the two will be used. Which one is undefined.
50
-
51
45
  const isMacOSElectronWebSwapCGLLayerWarning = /^objc\[\d+\]: Class WebSwapCGLLayer is implemented in both.*Which one is undefined\./;
52
46
  const GARBAGE_WARNINGS = [isXlibOrLibudevRe, isHighSierraWarningRe, isRenderWorkerRe, isDbusWarning, isCertVerifyProcBuiltin, isMacOSElectronWebSwapCGLLayerWarning];
53
-
54
47
  const isGarbageLineWarning = str => {
55
48
  return _.some(GARBAGE_WARNINGS, re => {
56
49
  return re.test(str);
57
50
  });
58
51
  };
59
-
60
52
  function isPlatform(platform) {
61
53
  return os.platform() === platform;
62
54
  }
63
-
64
55
  function needsStderrPiped(needsXvfb) {
65
56
  return _.some([isPlatform('darwin'), needsXvfb && isPlatform('linux'), util.isPossibleLinuxWithIncorrectDisplay()]);
66
57
  }
67
-
68
58
  function needsEverythingPipedDirectly() {
69
59
  return isPlatform('win32');
70
60
  }
71
-
72
61
  function getStdio(needsXvfb) {
73
62
  if (needsEverythingPipedDirectly()) {
74
63
  return 'pipe';
75
- } // https://github.com/cypress-io/cypress/issues/921
64
+ }
65
+
66
+ // https://github.com/cypress-io/cypress/issues/921
76
67
  // https://github.com/cypress-io/cypress/issues/1143
77
68
  // https://github.com/cypress-io/cypress/issues/1745
78
-
79
-
80
69
  if (needsStderrPiped(needsXvfb)) {
81
70
  // returning pipe here so we can massage stderr
82
71
  // and remove garbage from Xlib and libuv
83
72
  // due to starting the Xvfb process on linux
84
73
  return ['inherit', 'inherit', 'pipe'];
85
74
  }
86
-
87
75
  return 'inherit';
88
76
  }
89
-
90
77
  module.exports = {
91
78
  isGarbageLineWarning,
92
-
93
79
  start(args, options = {}) {
94
80
  const needsXvfb = xvfb.isNeeded();
95
81
  let executable = state.getPathToExecutable(state.getBinaryDir());
96
-
97
82
  if (util.getEnv('CYPRESS_RUN_BINARY')) {
98
83
  executable = path.resolve(util.getEnv('CYPRESS_RUN_BINARY'));
99
84
  }
85
+ debug('needs to start own Xvfb?', needsXvfb);
100
86
 
101
- debug('needs to start own Xvfb?', needsXvfb); // Always push cwd into the args
87
+ // Always push cwd into the args
102
88
  // which additionally acts as a signal to the
103
89
  // binary that it was invoked through the NPM module
104
-
105
90
  args = args || [];
106
-
107
91
  if (typeof args === 'string') {
108
92
  args = [args];
109
93
  }
110
-
111
94
  args = [...args, '--cwd', process.cwd(), '--userNodePath', process.execPath, '--userNodeVersion', process.versions.node];
112
-
113
95
  _.defaults(options, {
114
96
  dev: false,
115
97
  env: process.env,
116
98
  detached: false,
117
99
  stdio: getStdio(needsXvfb)
118
100
  });
119
-
120
101
  const spawn = (overrides = {}) => {
121
102
  return new Promise((resolve, reject) => {
122
103
  _.defaults(overrides, {
123
104
  onStderrData: false,
124
105
  electronLogging: false
125
106
  });
126
-
127
107
  const {
128
108
  onStderrData,
129
109
  electronLogging
@@ -132,46 +112,39 @@ module.exports = {
132
112
  const electronArgs = [];
133
113
  const node11WindowsFix = isPlatform('win32');
134
114
  let startScriptPath;
135
-
136
115
  if (options.dev) {
137
- executable = 'node'; // if we're in dev then reset
116
+ executable = 'node';
117
+ // if we're in dev then reset
138
118
  // the launch cmd to be 'npm run dev'
139
-
140
119
  startScriptPath = path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'), debug('in dev mode the args became %o', args);
141
120
  }
142
-
143
121
  if (!options.dev && verify.needsSandbox()) {
144
122
  electronArgs.push('--no-sandbox');
145
- } // strip dev out of child process options
123
+ }
146
124
 
125
+ // strip dev out of child process options
147
126
  /**
148
127
  * @type {import('child_process').ForkOptions}
149
128
  */
129
+ let stdioOptions = _.pick(options, 'env', 'detached', 'stdio');
150
130
 
151
-
152
- let stdioOptions = _.pick(options, 'env', 'detached', 'stdio'); // figure out if we're going to be force enabling or disabling colors.
131
+ // figure out if we're going to be force enabling or disabling colors.
153
132
  // also figure out whether we should force stdout and stderr into thinking
154
133
  // it is a tty as opposed to a pipe.
155
-
156
-
157
134
  stdioOptions.env = _.extend({}, stdioOptions.env, envOverrides);
158
-
159
135
  if (node11WindowsFix) {
160
136
  stdioOptions = _.extend({}, stdioOptions, {
161
137
  windowsHide: false
162
138
  });
163
139
  }
164
-
165
140
  if (electronLogging) {
166
141
  stdioOptions.env.ELECTRON_ENABLE_LOGGING = true;
167
142
  }
168
-
169
143
  if (util.isPossibleLinuxWithIncorrectDisplay()) {
170
144
  // make sure we use the latest DISPLAY variable if any
171
145
  debug('passing DISPLAY', process.env.DISPLAY);
172
146
  stdioOptions.env.DISPLAY = process.env.DISPLAY;
173
147
  }
174
-
175
148
  if (stdioOptions.env.ELECTRON_RUN_AS_NODE) {
176
149
  // Since we are running electron as node, we need to add an entry point file.
177
150
  startScriptPath = path.join(state.getBinaryPkgPath(path.dirname(executable)), '..', 'index.js');
@@ -182,19 +155,15 @@ module.exports = {
182
155
  // https://github.com/cypress-io/cypress/issues/5466
183
156
  args = [...electronArgs, '--', ...args];
184
157
  }
185
-
186
158
  if (startScriptPath) {
187
159
  args.unshift(startScriptPath);
188
160
  }
189
-
190
161
  if (process.env.CYPRESS_INTERNAL_DEV_DEBUG) {
191
162
  args.unshift(process.env.CYPRESS_INTERNAL_DEV_DEBUG);
192
163
  }
193
-
194
164
  debug('spawn args %o %o', args, _.omit(stdioOptions, 'env'));
195
165
  debug('spawning Cypress with executable: %s', executable);
196
166
  const child = cp.spawn(executable, args, stdioOptions);
197
-
198
167
  function resolveOn(event) {
199
168
  return function (code, signal) {
200
169
  debug('child event fired %o', {
@@ -202,131 +171,119 @@ module.exports = {
202
171
  code,
203
172
  signal
204
173
  });
205
-
206
174
  if (code === null) {
207
175
  const errorObject = errors.errors.childProcessKilled(event, signal);
208
176
  return errors.getError(errorObject).then(reject);
209
177
  }
210
-
211
178
  resolve(code);
212
179
  };
213
180
  }
214
-
215
181
  child.on('close', resolveOn('close'));
216
182
  child.on('exit', resolveOn('exit'));
217
- child.on('error', reject); // if stdio options is set to 'pipe', then
183
+ child.on('error', reject);
184
+
185
+ // if stdio options is set to 'pipe', then
218
186
  // we should set up pipes:
219
187
  // process STDIN (read stream) => child STDIN (writeable)
220
188
  // child STDOUT => process STDOUT
221
189
  // child STDERR => process STDERR with additional filtering
222
-
223
190
  if (child.stdin) {
224
191
  debug('piping process STDIN into child STDIN');
225
192
  process.stdin.pipe(child.stdin);
226
193
  }
227
-
228
194
  if (child.stdout) {
229
195
  debug('piping child STDOUT to process STDOUT');
230
196
  child.stdout.pipe(process.stdout);
231
- } // if this is defined then we are manually piping for linux
232
- // to filter out the garbage
233
-
197
+ }
234
198
 
199
+ // if this is defined then we are manually piping for linux
200
+ // to filter out the garbage
235
201
  if (child.stderr) {
236
202
  debug('piping child STDERR to process STDERR');
237
203
  child.stderr.on('data', data => {
238
- const str = data.toString(); // bail if this is warning line garbage
204
+ const str = data.toString();
239
205
 
206
+ // bail if this is warning line garbage
240
207
  if (isGarbageLineWarning(str)) {
241
208
  return;
242
- } // if we have a callback and this explictly returns
243
- // false then bail
244
-
209
+ }
245
210
 
211
+ // if we have a callback and this explictly returns
212
+ // false then bail
246
213
  if (onStderrData && onStderrData(str) === false) {
247
214
  return;
248
- } // else pass it along!
249
-
215
+ }
250
216
 
217
+ // else pass it along!
251
218
  process.stderr.write(data);
252
219
  });
253
- } // https://github.com/cypress-io/cypress/issues/1841
220
+ }
221
+
222
+ // https://github.com/cypress-io/cypress/issues/1841
254
223
  // https://github.com/cypress-io/cypress/issues/5241
255
224
  // In some versions of node, it will throw on windows
256
225
  // when you close the parent process after piping
257
226
  // into the child process. unpiping does not seem
258
227
  // to have any effect. so we're just catching the
259
228
  // error here and not doing anything.
260
-
261
-
262
229
  process.stdin.on('error', err => {
263
230
  if (['EPIPE', 'ENOTCONN'].includes(err.code)) {
264
231
  return;
265
232
  }
266
-
267
233
  throw err;
268
234
  });
269
-
270
235
  if (stdioOptions.detached) {
271
236
  child.unref();
272
237
  }
273
238
  });
274
239
  };
275
-
276
240
  const spawnInXvfb = () => {
277
241
  return xvfb.start().then(userFriendlySpawn).finally(xvfb.stop);
278
242
  };
279
-
280
243
  const userFriendlySpawn = linuxWithDisplayEnv => {
281
244
  debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv));
282
245
  let brokenGtkDisplay;
283
246
  const overrides = {};
284
-
285
247
  if (linuxWithDisplayEnv) {
286
248
  _.extend(overrides, {
287
249
  electronLogging: true,
288
-
289
250
  onStderrData(str) {
290
251
  // if we receive a broken pipe anywhere
291
252
  // then we know that's why cypress exited early
292
253
  if (util.isBrokenGtkDisplay(str)) {
293
254
  brokenGtkDisplay = true;
294
- } // we should attempt to always slurp up
255
+ }
256
+
257
+ // we should attempt to always slurp up
295
258
  // the stderr logs unless we've explicitly
296
259
  // enabled the electron debug logging
297
-
298
-
299
260
  if (!debugElectron.enabled) {
300
261
  return false;
301
262
  }
302
263
  }
303
-
304
264
  });
305
265
  }
306
-
307
266
  return spawn(overrides).then(code => {
308
267
  if (code !== 0 && brokenGtkDisplay) {
309
268
  util.logBrokenGtkDisplayWarning();
310
269
  return spawnInXvfb();
311
270
  }
312
-
313
271
  return code;
314
- }) // we can format and handle an error message from the code above
272
+ })
273
+ // we can format and handle an error message from the code above
315
274
  // prevent wrapping error again by using "known: undefined" filter
316
275
  .catch({
317
276
  known: undefined
318
277
  }, errors.throwFormErrorText(errors.errors.unexpected));
319
278
  };
320
-
321
279
  if (needsXvfb) {
322
280
  return spawnInXvfb();
323
- } // if we are on linux and there's already a DISPLAY
281
+ }
282
+
283
+ // if we are on linux and there's already a DISPLAY
324
284
  // set, then we may need to rerun cypress after
325
285
  // spawning our own Xvfb server
326
-
327
-
328
286
  const linuxWithDisplayEnv = util.isPossibleLinuxWithIncorrectDisplay();
329
287
  return userFriendlySpawn(linuxWithDisplayEnv);
330
288
  }
331
-
332
289
  };
@@ -1,20 +1,14 @@
1
1
  "use strict";
2
2
 
3
3
  const Promise = require('bluebird');
4
-
5
4
  const debug = require('debug')('cypress:cli');
6
-
7
5
  const path = require('path');
8
-
9
6
  const util = require('../util');
10
-
11
7
  const state = require('../tasks/state');
12
-
13
8
  const {
14
9
  throwFormErrorText,
15
10
  errors
16
11
  } = require('../errors');
17
-
18
12
  const getVersions = () => {
19
13
  return Promise.try(() => {
20
14
  if (util.getEnv('CYPRESS_RUN_BINARY')) {
@@ -23,7 +17,6 @@ const getVersions = () => {
23
17
  if (!envBinaryDir) {
24
18
  return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))();
25
19
  }
26
-
27
20
  debug('CYPRESS_RUN_BINARY has binaryDir:', envBinaryDir);
28
21
  return envBinaryDir;
29
22
  }).catch({
@@ -32,7 +25,6 @@ const getVersions = () => {
32
25
  return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(err.message);
33
26
  });
34
27
  }
35
-
36
28
  return state.getBinaryDir();
37
29
  }).then(state.getBinaryPkgAsync).then(pkg => {
38
30
  const versions = {
@@ -56,7 +48,6 @@ const getVersions = () => {
56
48
  return versions;
57
49
  });
58
50
  };
59
-
60
51
  module.exports = {
61
52
  getVersions
62
53
  };
package/lib/exec/xvfb.js CHANGED
@@ -1,24 +1,17 @@
1
1
  "use strict";
2
2
 
3
3
  const os = require('os');
4
-
5
4
  const Promise = require('bluebird');
6
-
7
5
  const Xvfb = require('@cypress/xvfb');
8
-
9
6
  const {
10
7
  stripIndent
11
8
  } = require('common-tags');
12
-
13
9
  const Debug = require('debug');
14
-
15
10
  const {
16
11
  throwFormErrorText,
17
12
  errors
18
13
  } = require('../errors');
19
-
20
14
  const util = require('../util');
21
-
22
15
  const debug = Debug('cypress:cli');
23
16
  const debugXvfb = Debug('cypress:xvfb');
24
17
  debug.Debug = debugXvfb.Debug = Debug;
@@ -29,23 +22,23 @@ const xvfbOptions = {
29
22
  // need to explicitly define screen otherwise electron will crash
30
23
  // https://github.com/cypress-io/cypress/issues/6184
31
24
  xvfb_args: ['-screen', '0', '1280x1024x24'],
32
-
33
25
  onStderrData(data) {
34
26
  if (debugXvfb.enabled) {
35
27
  debugXvfb(data.toString());
36
28
  }
37
29
  }
38
-
39
30
  };
40
31
  const xvfb = Promise.promisifyAll(new Xvfb(xvfbOptions));
41
32
  module.exports = {
42
33
  _debugXvfb: debugXvfb,
43
34
  // expose for testing
35
+
44
36
  _xvfb: xvfb,
45
37
  // expose for testing
46
- _xvfbOptions: xvfbOptions,
47
38
 
39
+ _xvfbOptions: xvfbOptions,
48
40
  // expose for testing
41
+
49
42
  start() {
50
43
  debug('Starting Xvfb');
51
44
  return xvfb.startAsync().return(null).catch({
@@ -54,17 +47,15 @@ module.exports = {
54
47
  if (err.known) {
55
48
  throw err;
56
49
  }
57
-
58
50
  return throwFormErrorText(errors.missingXvfb)(err);
59
51
  });
60
52
  },
61
-
62
53
  stop() {
63
54
  debug('Stopping Xvfb');
64
- return xvfb.stopAsync().return(null).catch(() => {// noop
55
+ return xvfb.stopAsync().return(null).catch(() => {
56
+ // noop
65
57
  });
66
58
  },
67
-
68
59
  isNeeded() {
69
60
  if (process.env.ELECTRON_RUN_AS_NODE) {
70
61
  debug('Environment variable ELECTRON_RUN_AS_NODE detected, xvfb is not needed');
@@ -74,7 +65,6 @@ module.exports = {
74
65
  if (os.platform() !== 'linux') {
75
66
  return false;
76
67
  }
77
-
78
68
  if (process.env.DISPLAY) {
79
69
  const issueUrl = util.getGitHubIssueUrl(4034);
80
70
  const message = stripIndent`
@@ -90,12 +80,10 @@ module.exports = {
90
80
  debug(message);
91
81
  return false;
92
82
  }
93
-
94
83
  debug('undefined DISPLAY environment variable');
95
84
  debug('Cypress will spawn its own Xvfb');
96
85
  return true;
97
86
  },
98
-
99
87
  // async method, resolved with Boolean
100
88
  verify() {
101
89
  return xvfb.startAsync().return(true).catch(err => {
@@ -103,5 +91,4 @@ module.exports = {
103
91
  return false;
104
92
  }).finally(xvfb.stopAsync);
105
93
  }
106
-
107
94
  };
package/lib/fs.js CHANGED
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
2
 
3
3
  const Promise = require('bluebird');
4
-
5
4
  module.exports = Promise.promisifyAll(require('fs-extra'));
package/lib/logger.js CHANGED
@@ -1,13 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  const chalk = require('chalk');
4
-
5
4
  let logs = [];
6
-
7
5
  const logLevel = () => {
8
6
  return process.env.npm_config_loglevel || 'notice';
9
7
  };
10
-
11
8
  const error = (...messages) => {
12
9
  logs.push(messages.join(' '));
13
10
  console.log(chalk.red(...messages)); // eslint-disable-line no-console
@@ -28,26 +25,22 @@ const log = (...messages) => {
28
25
  const always = (...messages) => {
29
26
  logs.push(messages.join(' '));
30
27
  console.log(...messages); // eslint-disable-line no-console
31
- }; // splits long text into lines and calls log()
32
- // on each one to allow easy unit testing for specific message
33
-
28
+ };
34
29
 
30
+ // splits long text into lines and calls log()
31
+ // on each one to allow easy unit testing for specific message
35
32
  const logLines = text => {
36
33
  const lines = text.split('\n');
37
-
38
34
  for (const line of lines) {
39
35
  log(line);
40
36
  }
41
37
  };
42
-
43
38
  const print = () => {
44
39
  return logs.join('\n');
45
40
  };
46
-
47
41
  const reset = () => {
48
42
  logs = [];
49
43
  };
50
-
51
44
  module.exports = {
52
45
  log,
53
46
  warn,