Skip to content

Commit 6ad6e01

Browse files
cjihrigtargos
authored andcommitted
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. PR-URL: #53853 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
1 parent 698e44f commit 6ad6e01

File tree

5 files changed

+171
-143
lines changed

5 files changed

+171
-143
lines changed

lib/internal/test_runner/snapshot.js

+72-63
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;
@@ -164,12 +125,7 @@ class SnapshotManager {
164125
}
165126
}
166127

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

190206
createAssert() {
191207
const manager = this;
192208

193209
return function snapshotAssertion(actual, options = kEmptyObject) {
194210
emitExperimentalWarning(kExperimentalWarning);
195-
// Resolve the snapshot file here so that any resolution errors are
196-
// surfaced as early as possible.
197-
manager.resolveSnapshotFile();
198-
199-
const { fullName } = this;
200-
const id = manager.nextId(fullName);
201-
202211
validateObject(options, 'options');
203-
204212
const {
205213
serializers = serializerFns,
206214
} = options;
207-
208215
validateFunctionArray(serializers, 'options.serializers');
209-
216+
const { filePath, fullName } = this;
217+
const snapshotFile = manager.resolveSnapshotFile(filePath);
210218
const value = manager.serialize(actual, serializers);
219+
const id = snapshotFile.nextId(fullName);
211220

212221
if (manager.updateSnapshots) {
213-
manager.setSnapshot(id, value);
222+
snapshotFile.setSnapshot(id, value);
214223
} else {
215-
manager.readSnapshotFile();
216-
strictEqual(value, manager.getSnapshot(id));
224+
snapshotFile.readFile();
225+
strictEqual(value, snapshotFile.getSnapshot(id));
217226
}
218227
};
219228
}

lib/internal/test_runner/test.js

+2-2
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
}
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

+2
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,5 @@ test('`${foo}`', async (t) => {
2626
test('escapes in `\\${foo}`\n', async (t) => {
2727
t.assert.snapshot('`\\${foo}`\n');
2828
});
29+
30+
require('./imported-tests');

0 commit comments

Comments
 (0)