Skip to content

Commit 42b738f

Browse files
authored
test: add separate snapshot update command and rely on object comparison rather than strings (#1007)
In the previous commit I tried to stabilise the produced snapshots so that they match the output from the discogs function, however it led to some hidden problems with oxfmt. The only reason I tried to do it is to keep the `pnpm test:discogs -u` command so that vitest handles snapshot files, however fixing the hidden issue mentioned above together with already a complicated setup I think we shouldn't rely on snapshot comparison as strings at all. It's much easier to compare them as objects and just create a separate command to regenerate snapshots, and this is what the current PR is doing.
1 parent 4b9e3a5 commit 42b738f

4 files changed

Lines changed: 34 additions & 40 deletions

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
"lint-fix": "pnpm run eslint -- --fix",
3232
"oxlint": "oxlint --type-aware",
3333
"eslint": "eslint .",
34+
"check": "pnpm run format:check && pnpm run type-check && pnpm run lint",
3435
"test": "vitest run",
3536
"test:discogs": "vitest run tests/discogs",
37+
"test:discogs:update-snapshots": "node --strip-types tests/discogs/update-snapshots.ts && oxfmt tests/discogs/snapshots",
3638
"test:discogs:update-fixtures": "node --strip-types tests/discogs/fetch-fixtures.ts && pnpm run format tests/discogs/fixtures",
3739
"test:watch": "vitest",
3840
"generate-readme": "node --strip-types ./tools/generate-readme.ts > README.md",

tests/discogs/README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ A **fixture** is the JSON returned by the Discogs API for a release (e.g. `fixtu
2020

2121
A **snapshot** is the JSON produced by running `parseDiscogsRelease` on that fixture (e.g. `snapshots/1996829.json`). It is the expected MusicBrainz import data: artist credits, labels, discs, tracks, durations, and so on.
2222

23-
Snapshot files are regenerated by the tests with stable key ordering when `pnpm test:discogs -u` is used.
23+
Snapshot files are formatted by oxfmt. Regeneration is a separate command from the tests because the tests compare parsed JSON rather than serialized text.
2424

2525
When you change the parser, tests re-parse the fixtures and compare the result to the snapshots.
2626

@@ -48,8 +48,9 @@ CI runs `pnpm test:discogs` on every pull request. No Discogs API calls are made
4848
Use the API URL (`https://api.discogs.com/releases/<id>`), not the website URL.
4949

5050
3. Fetch the fixture.
51-
4. Run all tests. Missing snapshot will be automatically created.
52-
5. Commit the new fixture, snapshot, and config change.
51+
4. Regenerate the snapshots.
52+
5. Run all tests.
53+
6. Commit the new fixture, snapshot, and config change.
5354

5455
Pick releases that cover distinct edge cases (multi-disc tracklists, unusual side numbering, nested sub-tracks, etc.) rather than many similar ones.
5556

@@ -70,9 +71,9 @@ pnpm test:discogs:update-fixtures
7071
Regenerate snapshots after an intentional parser change (no network request):
7172

7273
```bash
73-
pnpm test:discogs -u
74+
pnpm test:discogs:update-snapshots
7475
```
7576

76-
After adding an entry to `config.ts`, run `pnpm test:discogs:update-fixtures` once to create its fixture, then `pnpm test:discogs` to initialize its snapshot and confirm everything passes.
77+
After adding an entry to `config.ts`, run `pnpm test:discogs:update-fixtures` once to create its fixture, then regenerate the snapshots and run the tests.
7778

78-
If a test fails after a parser change you intended, review the diff, then run `pnpm test:discogs -u` to approve the new snapshots and commit the updated files.
79+
If a test fails after a parser change you intended, review the failure, regenerate the snapshots, inspect the file diff, and commit the updated files.

tests/discogs/discogs.test.ts

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,47 +13,16 @@ const SNAPSHOTS_DIR = path.join(DIR, 'snapshots');
1313

1414
const parseDiscogsRelease = loadParseDiscogsRelease();
1515

16-
function sortJsonValue(value: unknown): unknown {
17-
if (Array.isArray(value)) {
18-
return value.map(sortJsonValue);
19-
}
20-
21-
if (value !== null && typeof value === 'object') {
22-
return Object.fromEntries(
23-
Object.entries(value)
24-
.sort(([left], [right]) => left.localeCompare(right))
25-
.map(([key, child]) => [key, sortJsonValue(child)]),
26-
);
27-
}
28-
29-
return value;
30-
}
31-
32-
function stableStringify(value: unknown): string {
33-
return `${JSON.stringify(sortJsonValue(JSON.parse(JSON.stringify(value))), null, 4)}\n`;
34-
}
35-
36-
// These tests compare parsed JSON structurally, so mirror Vitest's snapshot update flags explicitly.
37-
function shouldUpdateSnapshots(): boolean {
38-
return process.argv.some(arg => arg === '-u' || arg === '--update' || arg === '--updateSnapshot');
39-
}
40-
4116
const releases = RELEASES.map(({ url, description }) => ({
4217
id: path.basename(new URL(url).pathname),
4318
description,
4419
}));
4520

4621
test.each(releases)('$id: $description', ({ id }) => {
4722
const fixture = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, `${id}.json`), 'utf8')) as Record<string, unknown>;
48-
const snapshotPath = path.join(SNAPSHOTS_DIR, `${id}.json`);
49-
const result = parseDiscogsRelease(fixture);
50-
const serializedResult = stableStringify(result);
23+
const snapshot = JSON.parse(fs.readFileSync(path.join(SNAPSHOTS_DIR, `${id}.json`), 'utf8')) as Record<string, unknown>;
5124

52-
if (shouldUpdateSnapshots() || !fs.existsSync(snapshotPath)) {
53-
fs.writeFileSync(snapshotPath, serializedResult, 'utf8');
54-
return;
55-
}
25+
const result = JSON.parse(JSON.stringify(parseDiscogsRelease(fixture))) as Record<string, unknown>;
5626

57-
const snapshot = JSON.parse(fs.readFileSync(snapshotPath, 'utf8')) as Record<string, unknown>;
58-
expect(JSON.parse(serializedResult)).toEqual(snapshot);
27+
expect(result).toEqual(snapshot);
5928
});

tests/discogs/update-snapshots.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as fs from 'node:fs';
2+
import * as path from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
5+
import { RELEASES } from './config.ts';
6+
import { loadParseDiscogsRelease } from './load-parser.ts';
7+
8+
const DIR = path.dirname(fileURLToPath(import.meta.url));
9+
const FIXTURES_DIR = path.join(DIR, 'fixtures');
10+
const SNAPSHOTS_DIR = path.join(DIR, 'snapshots');
11+
12+
const parseDiscogsRelease = loadParseDiscogsRelease();
13+
14+
fs.mkdirSync(SNAPSHOTS_DIR, { recursive: true });
15+
16+
for (const { url } of RELEASES) {
17+
const id = path.basename(new URL(url).pathname);
18+
const fixture = JSON.parse(fs.readFileSync(path.join(FIXTURES_DIR, `${id}.json`), 'utf8')) as Record<string, unknown>;
19+
const result = parseDiscogsRelease(fixture);
20+
21+
fs.writeFileSync(path.join(SNAPSHOTS_DIR, `${id}.json`), `${JSON.stringify(result, null, 4)}\n`);
22+
}

0 commit comments

Comments
 (0)