Skip to content

Commit 14ec281

Browse files
Introducing AVA snapshots v3
The v3 format is encoded using CBOR. It contains all information necessary to regenerate the report file. This allows for snapshots to be updated even if tests or assertions are skipped. This is not backwards compatible with the v2 format used in AVA 3. Co-authored-by: Mark Wubben <[email protected]>
1 parent cbec4cb commit 14ec281

File tree

164 files changed

+2241
-729
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

164 files changed

+2241
-729
lines changed

docs/01-writing-tests.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ You can use the `.only` modifier with all tests. It cannot be used with hooks or
120120

121121
*Note:* The `.only` modifier applies to the test file it's defined in, so if you run multiple test files, tests in other files will still run. If you want to only run the `test.only` test, provide just that test file to AVA.
122122

123-
You cannot update snapshots when using `.only()`.
123+
In AVA 3, you cannot update snapshots when using `.only()`.
124124

125125
## Skipping tests
126126

@@ -134,7 +134,9 @@ test.skip('will not be run', t => {
134134

135135
You must specify the implementation function. You can use the `.skip` modifier with all tests and hooks, but not with `.todo()`. You can not apply further modifiers to `.skip`.
136136

137-
You cannot update snapshots when using `.skip()`. If the test is likely to be failing for a while, use `.failing()` instead.
137+
If the test is likely to be failing for a while, use `.failing()` instead.
138+
139+
In AVA 3, you cannot update snapshots when using `.skip()`.
138140

139141
## Test placeholders ("todo")
140142

docs/03-assertions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ Compares the `expected` value with a previously recorded snapshot. Snapshots are
337337

338338
AVA 3 supports an `options` object that lets you select a specific snapshot, for instance `{id: 'my snapshot'}`. This is buggy and will be removed in AVA 4.
339339

340-
Snapshot assertions cannot be skipped when snapshots are being updated.
340+
In AVA 3, you cannot update snapshots while using `t.snapshot.skip()`.
341341

342342
### `.try(title?, implementation | macro | macro[], ...args?)`
343343

docs/04-snapshot-testing.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ You can then check your code. If the change was intentional you can use the `--u
4444
$ ava --update-snapshots
4545
```
4646

47+
In AVA 4, if you need to update snapshots for only a particular test, you can use `--update-snapshots` together with e.g. `--match` or `.only()` to select the test.
48+
4749
You can specify a fixed location for storing the snapshot files in AVA's [`package.json` configuration](./06-configuration.md):
4850

4951
**`package.json`:**

lib/cli.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,6 @@ exports.run = async () => { // eslint-disable-line complexity
210210
const chalkOptions = {level: combined.color === false ? 0 : require('chalk').level};
211211
const chalk = require('./chalk').set(chalkOptions);
212212

213-
if (combined.updateSnapshots && combined.match) {
214-
exit('Snapshots cannot be updated when matching specific tests.');
215-
}
216-
217213
if (confError) {
218214
if (confError.parent) {
219215
exit(`${confError.message}\n\n${chalk.gray((confError.parent && confError.parent.stack) || confError.parent)}`);
@@ -390,9 +386,6 @@ exports.run = async () => { // eslint-disable-line complexity
390386
pattern: normalizePattern(path.relative(projectDir, path.resolve(process.cwd(), pattern))),
391387
...rest
392388
}));
393-
if (combined.updateSnapshots && filter.some(condition => condition.lineNumbers !== null)) {
394-
exit('Snapshots cannot be updated when selecting specific tests by their line number.');
395-
}
396389

397390
const api = new Api({
398391
cacheEnabled: combined.cache !== false,

lib/reporters/default.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,6 @@ class Reporter {
198198
this.sharedWorkerErrors = [];
199199
this.uncaughtExceptions = [];
200200
this.unhandledRejections = [];
201-
this.unsavedSnapshots = [];
202201

203202
this.previousFailures = 0;
204203

@@ -354,10 +353,6 @@ class Reporter {
354353
break;
355354
}
356355

357-
case 'snapshot-error':
358-
this.unsavedSnapshots.push(event);
359-
break;
360-
361356
case 'uncaught-exception': {
362357
this.uncaughtExceptions.push(event);
363358

@@ -825,16 +820,6 @@ class Reporter {
825820
}
826821
}
827822

828-
if (this.unsavedSnapshots.length > 0) {
829-
this.lineWriter.writeLine(colors.title('Could not update snapshots for the following test files:'));
830-
this.lineWriter.writeLine();
831-
for (const event of this.unsavedSnapshots) {
832-
this.lineWriter.writeLine(`${figures.warning} ${this.relativeFile(event.testFile)}`);
833-
}
834-
835-
this.lineWriter.writeLine();
836-
}
837-
838823
if (this.failFastEnabled && (this.stats.remainingTests > 0 || this.stats.files > this.stats.finishedWorkers)) {
839824
let remaining = '';
840825
if (this.stats.remainingTests > 0) {

lib/reporters/tap.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,6 @@ class TapReporter {
165165
this.writeTest(evt, {passed: false, todo: true, skip: false});
166166
}
167167

168-
break;
169-
case 'snapshot-error':
170-
this.writeComment(evt, {title: 'Could not update snapshots'});
171168
break;
172169
case 'stats':
173170
this.stats = evt.stats;

lib/runner.js

Lines changed: 35 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,24 @@ class Runner extends Emittery {
2323
this.recordNewSnapshots = options.recordNewSnapshots === true;
2424
this.runOnlyExclusive = options.runOnlyExclusive === true;
2525
this.serial = options.serial === true;
26-
this.skippingTests = false;
2726
this.snapshotDir = options.snapshotDir;
2827
this.updateSnapshots = options.updateSnapshots;
2928

3029
this.activeRunnables = new Set();
3130
this.boundCompareTestSnapshot = this.compareTestSnapshot.bind(this);
32-
this.skippedSnapshots = false;
3331
this.boundSkipSnapshot = this.skipSnapshot.bind(this);
3432
this.interrupted = false;
35-
this.snapshots = null;
33+
this.snapshots = snapshotManager.load({
34+
file: this.file,
35+
fixedLocation: this.snapshotDir,
36+
projectDir: this.projectDir,
37+
recordNewSnapshots: this.recordNewSnapshots,
38+
updating: this.updateSnapshots
39+
});
40+
if (this.snapshots.snapPath !== undefined) {
41+
this.emit('dependency', this.snapshots.snapPath);
42+
}
43+
3644
this.nextTaskIndex = 0;
3745
this.tasks = {
3846
after: [],
@@ -152,15 +160,14 @@ class Runner extends Emittery {
152160
task.metadata.exclusive = matcher([title], this.match).length === 1;
153161
}
154162

155-
if (task.metadata.skipped) {
156-
this.skippingTests = true;
157-
}
158-
159163
if (task.metadata.exclusive) {
160164
this.runOnlyExclusive = true;
161165
}
162166

163167
this.tasks[metadata.serial ? 'serial' : 'concurrent'].push(task);
168+
169+
this.snapshots.touch(title, metadata.taskIndex);
170+
164171
this.emit('stateChange', {
165172
type: 'declared-test',
166173
title,
@@ -185,49 +192,15 @@ class Runner extends Emittery {
185192
}
186193

187194
compareTestSnapshot(options) {
188-
if (!this.snapshots) {
189-
this.snapshots = snapshotManager.load({
190-
file: this.file,
191-
fixedLocation: this.snapshotDir,
192-
projectDir: this.projectDir,
193-
recordNewSnapshots: this.recordNewSnapshots,
194-
updating: this.updateSnapshots && !this.runOnlyExclusive && !this.skippingTests
195-
});
196-
this.emit('dependency', this.snapshots.snapPath);
197-
}
198-
199195
return this.snapshots.compare(options);
200196
}
201197

202-
skipSnapshot() {
203-
this.skippedSnapshots = true;
198+
skipSnapshot(options) {
199+
return this.snapshots.skipSnapshot(options);
204200
}
205201

206202
saveSnapshotState() {
207-
if (
208-
this.updateSnapshots &&
209-
(
210-
this.runOnlyExclusive ||
211-
this.skippingTests ||
212-
this.skippedSnapshots
213-
)
214-
) {
215-
return {cannotSave: true};
216-
}
217-
218-
if (this.snapshots) {
219-
return {touchedFiles: this.snapshots.save()};
220-
}
221-
222-
if (this.updateSnapshots) {
223-
return {touchedFiles: snapshotManager.cleanSnapshots({
224-
file: this.file,
225-
fixedLocation: this.snapshotDir,
226-
projectDir: this.projectDir
227-
})};
228-
}
229-
230-
return {};
203+
return {touchedFiles: this.snapshots.save()};
231204
}
232205

233206
onRun(runnable) {
@@ -301,7 +274,7 @@ class Runner extends Emittery {
301274
return result;
302275
}
303276

304-
async runHooks(tasks, contextRef, {titleSuffix, testPassed, associatedTaskIndex} = {}) {
277+
async runHooks(tasks, contextRef, {titleSuffix, testPassed} = {}) {
305278
const hooks = tasks.map(task => new Runnable({
306279
contextRef,
307280
experiments: this.experiments,
@@ -312,7 +285,7 @@ class Runner extends Emittery {
312285
compareTestSnapshot: this.boundCompareTestSnapshot,
313286
skipSnapshot: this.boundSkipSnapshot,
314287
updateSnapshots: this.updateSnapshots,
315-
metadata: {...task.metadata, associatedTaskIndex},
288+
metadata: task.metadata,
316289
powerAssert: this.powerAssert,
317290
title: `${task.title}${titleSuffix || ''}`,
318291
isHook: true,
@@ -347,8 +320,7 @@ class Runner extends Emittery {
347320
this.tasks.beforeEach,
348321
contextRef,
349322
{
350-
titleSuffix: hookSuffix,
351-
associatedTaskIndex: task.metadata.taskIndex
323+
titleSuffix: hookSuffix
352324
}
353325
);
354326

@@ -388,8 +360,7 @@ class Runner extends Emittery {
388360
contextRef,
389361
{
390362
titleSuffix: hookSuffix,
391-
testPassed: testOk,
392-
associatedTaskIndex: task.metadata.taskIndex
363+
testPassed: testOk
393364
});
394365
} else {
395366
this.emit('stateChange', {
@@ -409,8 +380,7 @@ class Runner extends Emittery {
409380
contextRef,
410381
{
411382
titleSuffix: hookSuffix,
412-
testPassed: testOk,
413-
associatedTaskIndex: task.metadata.taskIndex
383+
testPassed: testOk
414384
});
415385
return alwaysOk && hooksOk && testOk;
416386
}
@@ -420,10 +390,12 @@ class Runner extends Emittery {
420390
const serialTests = [];
421391
for (const task of this.tasks.serial) {
422392
if (this.runOnlyExclusive && !task.metadata.exclusive) {
393+
this.snapshots.skipBlock(task.title, task.metadata.taskIndex);
423394
continue;
424395
}
425396

426397
if (this.checkSelectedByLineNumbers && !task.metadata.selected) {
398+
this.snapshots.skipBlock(task.title, task.metadata.taskIndex);
427399
continue;
428400
}
429401

@@ -435,17 +407,21 @@ class Runner extends Emittery {
435407
todo: false
436408
});
437409

438-
if (!task.metadata.skipped) {
410+
if (task.metadata.skipped) {
411+
this.snapshots.skipBlock(task.title, task.metadata.taskIndex);
412+
} else {
439413
serialTests.push(task);
440414
}
441415
}
442416

443417
for (const task of this.tasks.concurrent) {
444418
if (this.runOnlyExclusive && !task.metadata.exclusive) {
419+
this.snapshots.skipBlock(task.title, task.metadata.taskIndex);
445420
continue;
446421
}
447422

448423
if (this.checkSelectedByLineNumbers && !task.metadata.selected) {
424+
this.snapshots.skipBlock(task.title, task.metadata.taskIndex);
449425
continue;
450426
}
451427

@@ -457,12 +433,12 @@ class Runner extends Emittery {
457433
todo: false
458434
});
459435

460-
if (!task.metadata.skipped) {
461-
if (this.serial) {
462-
serialTests.push(task);
463-
} else {
464-
concurrentTests.push(task);
465-
}
436+
if (task.metadata.skipped) {
437+
this.snapshots.skipBlock(task.title, task.metadata.taskIndex);
438+
} else if (this.serial) {
439+
serialTests.push(task);
440+
} else {
441+
concurrentTests.push(task);
466442
}
467443
}
468444

0 commit comments

Comments
 (0)