ava 0.21.0 → 0.25.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.
@@ -48,13 +48,13 @@ if (opts.tty) {
48
48
  }
49
49
 
50
50
  if (debug.enabled) {
51
- // Forward the `time-require` `--sorted` flag.
51
+ // Forward the `@ladjs/time-require` `--sorted` flag.
52
52
  // Intended for internal optimization tests only.
53
53
  if (opts._sorted) {
54
54
  process.argv.push('--sorted');
55
55
  }
56
56
 
57
- require('time-require'); // eslint-disable-line import/no-unassigned-import
57
+ require('@ladjs/time-require'); // eslint-disable-line import/no-unassigned-import
58
58
  }
59
59
 
60
60
  const sourceMapCache = new Map();
@@ -15,7 +15,9 @@ exports.forError = error => {
15
15
  Visit the following URL for more details:
16
16
 
17
17
  ${chalk.blue.underline('https://github.com/avajs/ava#throwsfunctionpromise-error-message')}`;
18
- } else if (assertion === 'snapshot') {
18
+ }
19
+
20
+ if (assertion === 'snapshot') {
19
21
  const name = error.improperUsage.name;
20
22
  const snapPath = error.improperUsage.snapPath;
21
23
 
@@ -5,12 +5,12 @@ const lastLineTracker = require('last-line-stream/tracker');
5
5
  const plur = require('plur');
6
6
  const spinners = require('cli-spinners');
7
7
  const chalk = require('chalk');
8
+ const figures = require('figures');
8
9
  const cliTruncate = require('cli-truncate');
9
10
  const cross = require('figures').cross;
10
11
  const indentString = require('indent-string');
11
12
  const ansiEscapes = require('ansi-escapes');
12
13
  const trimOffNewlines = require('trim-off-newlines');
13
- const extractStack = require('../extract-stack');
14
14
  const codeExcerpt = require('../code-excerpt');
15
15
  const colors = require('../colors');
16
16
  const formatSerializedError = require('./format-serialized-error');
@@ -33,6 +33,7 @@ class MiniReporter {
33
33
  this.stream = process.stderr;
34
34
  this.stringDecoder = new StringDecoder();
35
35
  }
36
+
36
37
  start() {
37
38
  this.interval = setInterval(() => {
38
39
  this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;
@@ -41,6 +42,7 @@ class MiniReporter {
41
42
 
42
43
  return this.prefix('');
43
44
  }
45
+
44
46
  reset() {
45
47
  this.clearInterval();
46
48
  this.passCount = 0;
@@ -56,13 +58,16 @@ class MiniReporter {
56
58
  this.spinnerIndex = 0;
57
59
  this.lastLineTracker = lastLineTracker();
58
60
  }
61
+
59
62
  spinnerChar() {
60
63
  return this.spinnerFrames[this.spinnerIndex];
61
64
  }
65
+
62
66
  clearInterval() {
63
67
  clearInterval(this.interval);
64
68
  this.interval = null;
65
69
  }
70
+
66
71
  test(test) {
67
72
  if (test.todo) {
68
73
  this.todoCount++;
@@ -83,6 +88,7 @@ class MiniReporter {
83
88
 
84
89
  return this.prefix(this._test(test));
85
90
  }
91
+
86
92
  prefix(str) {
87
93
  str = str || this.currentTest;
88
94
  this.currentTest = str;
@@ -91,6 +97,7 @@ class MiniReporter {
91
97
  // TODO(jamestalmage): Figure out why it's needed and document it here
92
98
  return ` \n ${this.spinnerChar()} ${str}`;
93
99
  }
100
+
94
101
  _test(test) {
95
102
  const SPINNER_WIDTH = 3;
96
103
  const PADDING = 1;
@@ -102,6 +109,7 @@ class MiniReporter {
102
109
 
103
110
  return title + '\n' + this.reportCounts();
104
111
  }
112
+
105
113
  unhandledError(err) {
106
114
  if (err.type === 'exception') {
107
115
  this.exceptionCount++;
@@ -109,6 +117,7 @@ class MiniReporter {
109
117
  this.rejectionCount++;
110
118
  }
111
119
  }
120
+
112
121
  reportCounts(time) {
113
122
  const lines = [
114
123
  this.passCount > 0 ? '\n ' + colors.pass(this.passCount, 'passed') : '',
@@ -124,6 +133,7 @@ class MiniReporter {
124
133
 
125
134
  return lines.join('');
126
135
  }
136
+
127
137
  finish(runStatus) {
128
138
  this.clearInterval();
129
139
  let time;
@@ -163,6 +173,21 @@ class MiniReporter {
163
173
  }
164
174
 
165
175
  status += ' ' + colors.title(test.title) + '\n';
176
+
177
+ if (test.logs) {
178
+ test.logs.forEach(log => {
179
+ const logLines = indentString(colors.log(log), 6);
180
+ const logLinesWithLeadingFigure = logLines.replace(
181
+ /^ {6}/,
182
+ ` ${colors.information(figures.info)} `
183
+ );
184
+
185
+ status += logLinesWithLeadingFigure + '\n';
186
+ });
187
+
188
+ status += '\n';
189
+ }
190
+
166
191
  if (test.error.source) {
167
192
  status += ' ' + colors.errorSource(test.error.source.file + ':' + test.error.source.line) + '\n';
168
193
 
@@ -191,9 +216,9 @@ class MiniReporter {
191
216
  }
192
217
 
193
218
  if (test.error.stack) {
194
- const extracted = extractStack(test.error.stack);
195
- if (extracted.includes('\n')) {
196
- status += '\n' + indentString(colors.errorStack(extracted), 2) + '\n';
219
+ const stack = test.error.stack;
220
+ if (stack.includes('\n')) {
221
+ status += '\n' + indentString(colors.errorStack(stack), 2) + '\n';
197
222
  }
198
223
  }
199
224
 
@@ -212,14 +237,14 @@ class MiniReporter {
212
237
  status += ' ' + colors.error(cross + ' ' + err.message) + '\n\n';
213
238
  } else {
214
239
  const title = err.type === 'rejection' ? 'Unhandled Rejection' : 'Uncaught Exception';
215
- let description = err.stack ? err.stack.trimRight() : JSON.stringify(err);
216
- description = description.split('\n');
217
- const errorTitle = err.name ? description[0] : 'Threw non-error: ' + description[0];
218
- const errorStack = description.slice(1).join('\n');
219
-
220
240
  status += ' ' + colors.title(title) + '\n';
221
- status += ' ' + colors.stack(errorTitle) + '\n';
222
- status += colors.errorStack(errorStack) + '\n\n';
241
+
242
+ if (err.name) {
243
+ status += ' ' + colors.stack(err.summary) + '\n';
244
+ status += colors.errorStack(err.stack) + '\n\n';
245
+ } else {
246
+ status += ' Threw non-error: ' + err.summary + '\n';
247
+ }
223
248
  }
224
249
  });
225
250
  }
@@ -235,24 +260,30 @@ class MiniReporter {
235
260
 
236
261
  return '\n' + trimOffNewlines(status) + '\n';
237
262
  }
263
+
238
264
  section() {
239
265
  return '\n' + chalk.gray.dim('\u2500'.repeat(process.stdout.columns || 80));
240
266
  }
267
+
241
268
  clear() {
242
269
  return '';
243
270
  }
271
+
244
272
  write(str) {
245
273
  cliCursor.hide();
246
274
  this.currentStatus = str;
247
275
  this._update();
248
276
  this.statusLineCount = this.currentStatus.split('\n').length;
249
277
  }
278
+
250
279
  stdout(data) {
251
280
  this._update(data);
252
281
  }
282
+
253
283
  stderr(data) {
254
284
  this._update(data);
255
285
  }
286
+
256
287
  _update(data) {
257
288
  let str = '';
258
289
  let ct = this.statusLineCount;
@@ -1,14 +1,6 @@
1
1
  'use strict';
2
- const format = require('util').format;
3
- const indentString = require('indent-string');
2
+ const supertap = require('supertap');
4
3
  const stripAnsi = require('strip-ansi');
5
- const yaml = require('js-yaml');
6
- const extractStack = require('../extract-stack');
7
-
8
- // Parses stack trace and extracts original function name, file name and line
9
- function getSourceFromStack(stack) {
10
- return extractStack(stack).split('\n')[0];
11
- }
12
4
 
13
5
  function dumpError(error, includeMessage) {
14
6
  const obj = Object.assign({}, error.object);
@@ -35,82 +27,64 @@ function dumpError(error, includeMessage) {
35
27
  }
36
28
 
37
29
  if (error.stack) {
38
- obj.at = getSourceFromStack(error.stack);
30
+ obj.at = error.stack.split('\n')[0];
39
31
  }
40
32
 
41
- return ` ---\n${indentString(yaml.safeDump(obj).trim(), 4)}\n ...`;
33
+ return obj;
42
34
  }
43
35
 
44
36
  class TapReporter {
45
37
  constructor() {
46
38
  this.i = 0;
47
39
  }
40
+
48
41
  start() {
49
- return 'TAP version 13';
42
+ return supertap.start();
50
43
  }
51
- test(test) {
52
- let output;
53
-
54
- let directive = '';
55
- const passed = test.todo ? 'not ok' : 'ok';
56
44
 
57
- if (test.todo) {
58
- directive = '# TODO';
59
- } else if (test.skip) {
60
- directive = '# SKIP';
61
- }
62
-
63
- const title = stripAnsi(test.title);
64
-
65
- if (test.error) {
66
- output = [
67
- '# ' + title,
68
- format('not ok %d - %s', ++this.i, title),
69
- dumpError(test.error, true)
70
- ];
71
- } else {
72
- output = [
73
- `# ${title}`,
74
- format('%s %d - %s %s', passed, ++this.i, title, directive).trim()
75
- ];
76
- }
77
-
78
- return output.join('\n');
45
+ test(test) {
46
+ return supertap.test(test.title, {
47
+ passed: !test.error,
48
+ index: ++this.i,
49
+ todo: test.todo,
50
+ skip: test.skip,
51
+ comment: test.logs,
52
+ error: test.error ? dumpError(test.error, true) : null
53
+ });
79
54
  }
55
+
80
56
  unhandledError(err) {
81
- const output = [
82
- `# ${err.message}`,
83
- format('not ok %d - %s', ++this.i, err.message)
84
- ];
57
+ let error;
58
+
85
59
  // AvaErrors don't have stack traces
86
60
  if (err.type !== 'exception' || err.name !== 'AvaError') {
87
- output.push(dumpError(err, false));
61
+ error = dumpError(err, false);
88
62
  }
89
63
 
90
- return output.join('\n');
64
+ return supertap.test(err.message, {
65
+ passed: false,
66
+ index: ++this.i,
67
+ error
68
+ });
91
69
  }
92
- finish(runStatus) {
93
- const output = [
94
- '',
95
- '1..' + (runStatus.passCount + runStatus.failCount + runStatus.skipCount),
96
- '# tests ' + (runStatus.passCount + runStatus.failCount + runStatus.skipCount),
97
- '# pass ' + runStatus.passCount
98
- ];
99
-
100
- if (runStatus.skipCount > 0) {
101
- output.push(`# skip ${runStatus.skipCount}`);
102
- }
103
70
 
104
- output.push('# fail ' + (runStatus.failCount + runStatus.rejectionCount + runStatus.exceptionCount), '');
105
-
106
- return output.join('\n');
71
+ finish(runStatus) {
72
+ return supertap.finish({
73
+ passed: runStatus.passCount,
74
+ failed: runStatus.failCount,
75
+ skipped: runStatus.skipCount,
76
+ crashed: runStatus.rejectionCount + runStatus.exceptionCount
77
+ });
107
78
  }
79
+
108
80
  write(str) {
109
81
  console.log(str);
110
82
  }
83
+
111
84
  stdout(data) {
112
85
  process.stderr.write(data);
113
86
  }
87
+
114
88
  stderr(data) {
115
89
  this.stdout(data);
116
90
  }
@@ -5,7 +5,6 @@ const figures = require('figures');
5
5
  const chalk = require('chalk');
6
6
  const plur = require('plur');
7
7
  const trimOffNewlines = require('trim-off-newlines');
8
- const extractStack = require('../extract-stack');
9
8
  const codeExcerpt = require('../code-excerpt');
10
9
  const colors = require('../colors');
11
10
  const formatSerializedError = require('./format-serialized-error');
@@ -20,34 +19,46 @@ class VerboseReporter {
20
19
  colors[key].enabled = this.options.color;
21
20
  }
22
21
  }
22
+
23
23
  start() {
24
24
  return '';
25
25
  }
26
+
26
27
  test(test, runStatus) {
28
+ const lines = [];
27
29
  if (test.error) {
28
- return ' ' + colors.error(figures.cross) + ' ' + test.title + ' ' + colors.error(test.error.message);
29
- }
30
-
31
- if (test.todo) {
32
- return ' ' + colors.todo('- ' + test.title);
30
+ lines.push(' ' + colors.error(figures.cross) + ' ' + test.title + ' ' + colors.error(test.error.message));
31
+ } else if (test.todo) {
32
+ lines.push(' ' + colors.todo('- ' + test.title));
33
33
  } else if (test.skip) {
34
- return ' ' + colors.skip('- ' + test.title);
35
- }
34
+ lines.push(' ' + colors.skip('- ' + test.title));
35
+ } else if (test.failing) {
36
+ lines.push(' ' + colors.error(figures.tick) + ' ' + colors.error(test.title));
37
+ } else if (runStatus.fileCount === 1 && runStatus.testCount === 1 && test.title === '[anonymous]') {
38
+ // No output
39
+ } else {
40
+ // Display duration only over a threshold
41
+ const threshold = 100;
42
+ const duration = test.duration > threshold ? colors.duration(' (' + prettyMs(test.duration) + ')') : '';
36
43
 
37
- if (test.failing) {
38
- return ' ' + colors.error(figures.tick) + ' ' + colors.error(test.title);
44
+ lines.push(' ' + colors.pass(figures.tick) + ' ' + test.title + duration);
39
45
  }
40
46
 
41
- if (runStatus.fileCount === 1 && runStatus.testCount === 1 && test.title === '[anonymous]') {
42
- return undefined;
43
- }
47
+ if (test.logs) {
48
+ test.logs.forEach(log => {
49
+ const logLines = indentString(colors.log(log), 6);
50
+ const logLinesWithLeadingFigure = logLines.replace(
51
+ /^ {6}/,
52
+ ` ${colors.information(figures.info)} `
53
+ );
44
54
 
45
- // Display duration only over a threshold
46
- const threshold = 100;
47
- const duration = test.duration > threshold ? colors.duration(' (' + prettyMs(test.duration) + ')') : '';
55
+ lines.push(logLinesWithLeadingFigure);
56
+ });
57
+ }
48
58
 
49
- return ' ' + colors.pass(figures.tick) + ' ' + test.title + duration;
59
+ return lines.length > 0 ? lines.join('\n') : undefined;
50
60
  }
61
+
51
62
  unhandledError(err) {
52
63
  if (err.type === 'exception' && err.name === 'AvaError') {
53
64
  return colors.error(' ' + figures.cross + ' ' + err.message);
@@ -61,6 +72,7 @@ class VerboseReporter {
61
72
  let output = colors.error(types[err.type] + ':', err.file) + '\n';
62
73
 
63
74
  if (err.stack) {
75
+ output += ' ' + colors.stack(err.title || err.summary) + '\n';
64
76
  output += ' ' + colors.stack(err.stack) + '\n';
65
77
  } else {
66
78
  output += ' ' + colors.stack(JSON.stringify(err)) + '\n';
@@ -70,6 +82,7 @@ class VerboseReporter {
70
82
 
71
83
  return output;
72
84
  }
85
+
73
86
  finish(runStatus) {
74
87
  let output = '';
75
88
 
@@ -86,7 +99,9 @@ class VerboseReporter {
86
99
  ].filter(Boolean);
87
100
 
88
101
  if (lines.length > 0) {
89
- lines[0] += ' ' + chalk.gray.dim('[' + new Date().toLocaleTimeString('en-US', {hour12: false}) + ']');
102
+ if (this.options.watching) {
103
+ lines[0] += ' ' + chalk.gray.dim('[' + new Date().toLocaleTimeString('en-US', {hour12: false}) + ']');
104
+ }
90
105
  output += lines.join('\n') + '\n';
91
106
  }
92
107
 
@@ -104,6 +119,21 @@ class VerboseReporter {
104
119
  }
105
120
 
106
121
  output += ' ' + colors.title(test.title) + '\n';
122
+
123
+ if (test.logs) {
124
+ test.logs.forEach(log => {
125
+ const logLines = indentString(colors.log(log), 6);
126
+ const logLinesWithLeadingFigure = logLines.replace(
127
+ /^ {6}/,
128
+ ` ${colors.information(figures.info)} `
129
+ );
130
+
131
+ output += logLinesWithLeadingFigure + '\n';
132
+ });
133
+
134
+ output += '\n';
135
+ }
136
+
107
137
  if (test.error.source) {
108
138
  output += ' ' + colors.errorSource(test.error.source.file + ':' + test.error.source.line) + '\n';
109
139
 
@@ -132,9 +162,9 @@ class VerboseReporter {
132
162
  }
133
163
 
134
164
  if (test.error.stack) {
135
- const extracted = extractStack(test.error.stack);
136
- if (extracted.includes('\n')) {
137
- output += '\n' + indentString(colors.errorStack(extracted), 2) + '\n';
165
+ const stack = test.error.stack;
166
+ if (stack.includes('\n')) {
167
+ output += '\n' + indentString(colors.errorStack(stack), 2) + '\n';
138
168
  }
139
169
  }
140
170
 
@@ -153,15 +183,19 @@ class VerboseReporter {
153
183
 
154
184
  return '\n' + trimOffNewlines(output) + '\n';
155
185
  }
186
+
156
187
  section() {
157
188
  return chalk.gray.dim('\u2500'.repeat(process.stdout.columns || 80));
158
189
  }
190
+
159
191
  write(str) {
160
192
  console.error(str);
161
193
  }
194
+
162
195
  stdout(data) {
163
196
  process.stderr.write(data);
164
197
  }
198
+
165
199
  stderr(data) {
166
200
  process.stderr.write(data);
167
201
  }
package/lib/run-status.js CHANGED
@@ -44,6 +44,7 @@ class RunStatus extends EventEmitter {
44
44
 
45
45
  autoBind(this);
46
46
  }
47
+
47
48
  observeFork(emitter) {
48
49
  emitter
49
50
  .on('teardown', this.handleTeardown)
@@ -54,6 +55,7 @@ class RunStatus extends EventEmitter {
54
55
  .on('stdout', this.handleOutput.bind(this, 'stdout'))
55
56
  .on('stderr', this.handleOutput.bind(this, 'stderr'));
56
57
  }
58
+
57
59
  handleRejections(data) {
58
60
  this.rejectionCount += data.rejections.length;
59
61
 
@@ -64,6 +66,7 @@ class RunStatus extends EventEmitter {
64
66
  this.errors.push(err);
65
67
  });
66
68
  }
69
+
67
70
  handleExceptions(data) {
68
71
  this.exceptionCount++;
69
72
  const err = data.exception;
@@ -72,10 +75,12 @@ class RunStatus extends EventEmitter {
72
75
  this.emit('error', err, this);
73
76
  this.errors.push(err);
74
77
  }
78
+
75
79
  handleTeardown(data) {
76
80
  this.emit('dependencies', data.file, data.dependencies, this);
77
81
  this.emit('touchedFiles', data.touchedFiles);
78
82
  }
83
+
79
84
  handleStats(stats) {
80
85
  this.emit('stats', stats, this);
81
86
 
@@ -85,6 +90,7 @@ class RunStatus extends EventEmitter {
85
90
 
86
91
  this.testCount += stats.testCount;
87
92
  }
93
+
88
94
  handleTest(test) {
89
95
  test.title = this.prefixTitle(test.file) + test.title;
90
96
 
@@ -98,6 +104,7 @@ class RunStatus extends EventEmitter {
98
104
 
99
105
  this.emit('test', test, this);
100
106
  }
107
+
101
108
  prefixTitle(file) {
102
109
  if (!this.prefixTitles) {
103
110
  return '';
@@ -107,9 +114,11 @@ class RunStatus extends EventEmitter {
107
114
 
108
115
  return prefixTitle(file, this.base, separator);
109
116
  }
117
+
110
118
  handleOutput(channel, data) {
111
119
  this.emit(channel, data, this);
112
120
  }
121
+
113
122
  processResults(results) {
114
123
  // Assemble stats from all tests
115
124
  this.stats = results.map(result => result.stats);
package/lib/runner.js CHANGED
@@ -52,6 +52,7 @@ class Runner extends EventEmitter {
52
52
  this.projectDir = options.projectDir;
53
53
  this.serial = options.serial;
54
54
  this.updateSnapshots = options.updateSnapshots;
55
+ this.snapshotDir = options.snapshotDir;
55
56
 
56
57
  this.hasStarted = false;
57
58
  this.results = [];
@@ -112,7 +113,7 @@ class Runner extends EventEmitter {
112
113
  }
113
114
 
114
115
  if (metadata.type === 'test' && this.match.length > 0) {
115
- metadata.exclusive = title !== null && matcher([title], this.match).length === 1;
116
+ metadata.exclusive = matcher([title || ''], this.match).length === 1;
116
117
  }
117
118
 
118
119
  const validationError = validateTest(title, fn, metadata);
@@ -130,6 +131,7 @@ class Runner extends EventEmitter {
130
131
  addTestResult(result) {
131
132
  const test = result.result;
132
133
  const props = {
134
+ logs: test.logs,
133
135
  duration: test.duration,
134
136
  title: test.title,
135
137
  error: result.reason,
@@ -183,6 +185,8 @@ class Runner extends EventEmitter {
183
185
  compareTestSnapshot(options) {
184
186
  if (!this.snapshots) {
185
187
  this.snapshots = snapshotManager.load({
188
+ file: this.file,
189
+ fixedLocation: this.snapshotDir,
186
190
  name: path.basename(this.file),
187
191
  projectDir: this.projectDir,
188
192
  relFile: path.relative(this.projectDir, this.file),
@@ -219,6 +223,7 @@ class Runner extends EventEmitter {
219
223
  });
220
224
  return Bluebird.try(() => this.tests.build().run());
221
225
  }
226
+
222
227
  attributeLeakedError(err) {
223
228
  return this.tests.attributeLeakedError(err);
224
229
  }
@@ -4,7 +4,6 @@ const cleanYamlObject = require('clean-yaml-object');
4
4
  const StackUtils = require('stack-utils');
5
5
  const assert = require('./assert');
6
6
  const beautifyStack = require('./beautify-stack');
7
- const extractStack = require('./extract-stack');
8
7
 
9
8
  function isAvaAssertionError(source) {
10
9
  return source instanceof assert.AssertionError;
@@ -20,7 +19,7 @@ function extractSource(stack) {
20
19
  return null;
21
20
  }
22
21
 
23
- const firstStackLine = extractStack(stack).split('\n')[0];
22
+ const firstStackLine = stack.split('\n')[0];
24
23
  return stackUtils.parseLine(firstStackLine);
25
24
  }
26
25
  function buildSource(source) {
@@ -90,5 +89,11 @@ module.exports = error => {
90
89
  }
91
90
  }
92
91
 
92
+ if (typeof error.stack === 'string') {
93
+ retval.summary = error.stack.split('\n')[0];
94
+ } else {
95
+ retval.summary = JSON.stringify(error);
96
+ }
97
+
93
98
  return retval;
94
99
  };
@@ -11,6 +11,7 @@ const indentString = require('indent-string');
11
11
  const makeDir = require('make-dir');
12
12
  const md5Hex = require('md5-hex');
13
13
  const Buffer = require('safe-buffer').Buffer;
14
+ const convertSourceMap = require('convert-source-map');
14
15
 
15
16
  const concordanceOptions = require('./concordance-options').snapshotManager;
16
17
 
@@ -355,18 +356,39 @@ class Manager {
355
356
  }
356
357
  }
357
358
 
358
- function determineSnapshotDir(projectDir, testDir) {
359
- const parts = new Set(path.relative(projectDir, testDir).split(path.sep));
359
+ function determineSnapshotDir(options) {
360
+ const testDir = determineSourceMappedDir(options);
361
+ if (options.fixedLocation) {
362
+ const relativeTestLocation = path.relative(options.projectDir, testDir);
363
+ return path.join(options.fixedLocation, relativeTestLocation);
364
+ }
365
+
366
+ const parts = new Set(path.relative(options.projectDir, testDir).split(path.sep));
360
367
  if (parts.has('__tests__')) {
361
368
  return path.join(testDir, '__snapshots__');
362
- } else if (parts.has('test') || parts.has('tests')) { // Accept tests, even though it's not in the default test patterns
369
+ }
370
+ if (parts.has('test') || parts.has('tests')) { // Accept tests, even though it's not in the default test patterns
363
371
  return path.join(testDir, 'snapshots');
364
372
  }
373
+
365
374
  return testDir;
366
375
  }
367
376
 
377
+ function determineSourceMappedDir(options) {
378
+ const source = tryRead(options.file).toString();
379
+ const converter = convertSourceMap.fromSource(source) || convertSourceMap.fromMapFileSource(source, options.testDir);
380
+ if (converter) {
381
+ const map = converter.toObject();
382
+ const firstSource = `${map.sourceRoot || ''}${map.sources[0]}`;
383
+ const sourceFile = path.resolve(options.testDir, firstSource);
384
+ return path.dirname(sourceFile);
385
+ }
386
+
387
+ return options.testDir;
388
+ }
389
+
368
390
  function load(options) {
369
- const dir = determineSnapshotDir(options.projectDir, options.testDir);
391
+ const dir = determineSnapshotDir(options);
370
392
  const reportFile = `${options.name}.md`;
371
393
  const snapFile = `${options.name}.snap`;
372
394
  const snapPath = path.join(dir, snapFile);