Skip to content

Commit 8349967

Browse files
committed
test_runner: refactor snapshots to get file from context
This commit refactors the internals of snapshot tests to get the name of the test file from the test context instead of passing it to the SnapshotManager constructor. This is prep work for supporting running test files in the test runner process.
1 parent 9a932c0 commit 8349967

File tree

5 files changed

+171
-143
lines changed

5 files changed

+171
-143
lines changed

lib/internal/test_runner/snapshot.js

Lines changed: 72 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -58,50 +58,12 @@ function setDefaultSnapshotSerializers(serializers) {
5858
serializerFns = ArrayPrototypeSlice(serializers);
5959
}
6060

61-
class SnapshotManager {
62-
constructor(entryFile, updateSnapshots) {
63-
this.entryFile = entryFile;
64-
this.snapshotFile = undefined;
61+
class SnapshotFile {
62+
constructor(snapshotFile) {
63+
this.snapshotFile = snapshotFile;
6564
this.snapshots = { __proto__: null };
6665
this.nameCounts = new SafeMap();
67-
// A manager instance will only read or write snapshot files based on the
68-
// updateSnapshots argument.
69-
this.loaded = updateSnapshots;
70-
this.updateSnapshots = updateSnapshots;
71-
}
72-
73-
resolveSnapshotFile() {
74-
if (this.snapshotFile === undefined) {
75-
const resolved = resolveSnapshotPathFn(this.entryFile);
76-
77-
if (typeof resolved !== 'string') {
78-
const err = new ERR_INVALID_STATE('Invalid snapshot filename.');
79-
err.filename = resolved;
80-
throw err;
81-
}
82-
83-
this.snapshotFile = resolved;
84-
}
85-
}
86-
87-
serialize(input, serializers = serializerFns) {
88-
try {
89-
let value = input;
90-
91-
for (let i = 0; i < serializers.length; ++i) {
92-
const fn = serializers[i];
93-
value = fn(value);
94-
}
95-
96-
return `\n${templateEscape(value)}\n`;
97-
} catch (err) {
98-
const error = new ERR_INVALID_STATE(
99-
'The provided serializers did not generate a string.',
100-
);
101-
error.input = input;
102-
error.cause = err;
103-
throw error;
104-
}
66+
this.loaded = false;
10567
}
10668

10769
getSnapshot(id) {
@@ -122,12 +84,11 @@ class SnapshotManager {
12284

12385
nextId(name) {
12486
const count = this.nameCounts.get(name) ?? 1;
125-
12687
this.nameCounts.set(name, count + 1);
12788
return `${name} ${count}`;
12889
}
12990

130-
readSnapshotFile() {
91+
readFile() {
13192
if (this.loaded) {
13293
debug('skipping read of snapshot file');
13394
return;
@@ -162,12 +123,7 @@ class SnapshotManager {
162123
}
163124
}
164125

165-
writeSnapshotFile() {
166-
if (!this.updateSnapshots) {
167-
debug('skipping write of snapshot file');
168-
return;
169-
}
170-
126+
writeFile() {
171127
try {
172128
const keys = ArrayPrototypeSort(ObjectKeys(this.snapshots));
173129
const snapshotStrings = ArrayPrototypeMap(keys, (key) => {
@@ -184,34 +140,87 @@ class SnapshotManager {
184140
throw error;
185141
}
186142
}
143+
}
144+
145+
class SnapshotManager {
146+
constructor(updateSnapshots) {
147+
// A manager instance will only read or write snapshot files based on the
148+
// updateSnapshots argument.
149+
this.updateSnapshots = updateSnapshots;
150+
this.cache = new SafeMap();
151+
}
152+
153+
resolveSnapshotFile(entryFile) {
154+
let snapshotFile = this.cache.get(entryFile);
155+
156+
if (snapshotFile === undefined) {
157+
const resolved = resolveSnapshotPathFn(entryFile);
158+
159+
if (typeof resolved !== 'string') {
160+
const err = new ERR_INVALID_STATE('Invalid snapshot filename.');
161+
err.filename = resolved;
162+
throw err;
163+
}
164+
165+
snapshotFile = new SnapshotFile(resolved);
166+
snapshotFile.loaded = this.updateSnapshots;
167+
this.cache.set(entryFile, snapshotFile);
168+
}
169+
170+
return snapshotFile;
171+
}
172+
173+
serialize(input, serializers = serializerFns) {
174+
try {
175+
let value = input;
176+
177+
for (let i = 0; i < serializers.length; ++i) {
178+
const fn = serializers[i];
179+
value = fn(value);
180+
}
181+
182+
return `\n${templateEscape(value)}\n`;
183+
} catch (err) {
184+
const error = new ERR_INVALID_STATE(
185+
'The provided serializers did not generate a string.',
186+
);
187+
error.input = input;
188+
error.cause = err;
189+
throw error;
190+
}
191+
}
192+
193+
writeSnapshotFiles() {
194+
if (!this.updateSnapshots) {
195+
debug('skipping write of snapshot files');
196+
return;
197+
}
198+
199+
this.cache.forEach((snapshotFile) => {
200+
snapshotFile.writeFile();
201+
});
202+
}
187203

188204
createAssert() {
189205
const manager = this;
190206

191207
return function snapshotAssertion(actual, options = kEmptyObject) {
192208
emitExperimentalWarning(kExperimentalWarning);
193-
// Resolve the snapshot file here so that any resolution errors are
194-
// surfaced as early as possible.
195-
manager.resolveSnapshotFile();
196-
197-
const { fullName } = this;
198-
const id = manager.nextId(fullName);
199-
200209
validateObject(options, 'options');
201-
202210
const {
203211
serializers = serializerFns,
204212
} = options;
205-
206213
validateFunctionArray(serializers, 'options.serializers');
207-
214+
const { filePath, fullName } = this;
215+
const snapshotFile = manager.resolveSnapshotFile(filePath);
208216
const value = manager.serialize(actual, serializers);
217+
const id = snapshotFile.nextId(fullName);
209218

210219
if (manager.updateSnapshots) {
211-
manager.setSnapshot(id, value);
220+
snapshotFile.setSnapshot(id, value);
212221
} else {
213-
manager.readSnapshotFile();
214-
strictEqual(value, manager.getSnapshot(id));
222+
snapshotFile.readFile();
223+
strictEqual(value, snapshotFile.getSnapshot(id));
215224
}
216225
};
217226
}

lib/internal/test_runner/test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ function lazyAssertObject(harness) {
133133
const { getOptionValue } = require('internal/options');
134134
if (getOptionValue('--experimental-test-snapshots')) {
135135
const { SnapshotManager } = require('internal/test_runner/snapshot');
136-
harness.snapshotManager = new SnapshotManager(kFilename, updateSnapshots);
136+
harness.snapshotManager = new SnapshotManager(updateSnapshots);
137137
assertObj.set('snapshot', harness.snapshotManager.createAssert());
138138
}
139139
}
@@ -977,7 +977,7 @@ class Test extends AsyncResource {
977977

978978
// Call this harness.coverage() before collecting diagnostics, since failure to collect coverage is a diagnostic.
979979
const coverage = harness.coverage();
980-
harness.snapshotManager?.writeSnapshotFile();
980+
harness.snapshotManager?.writeSnapshotFiles();
981981
for (let i = 0; i < diagnostics.length; i++) {
982982
reporter.diagnostic(nesting, loc, diagnostics[i]);
983983
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
'use strict';
2+
const { suite, test } = require('node:test');
3+
4+
suite('imported suite', () => {
5+
test('imported test', (t) => {
6+
t.assert.snapshot({ foo: 1, bar: 2 });
7+
});
8+
});

test/fixtures/test-runner/snapshots/unit.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ test('`${foo}`', async (t) => {
2222
const options = { serializers: [() => { return '***'; }]};
2323
t.assert.snapshot('snapshotted string', options);
2424
});
25+
26+
require('./imported-tests');

0 commit comments

Comments
 (0)