Repository navigation
Conversation
1241c1a to
dfac52e
Compare
dfac52e to
15e35db
Compare
| ) => Promise<void>, | ||
| ) => Promise<void> | ||
|
|
||
| abstract class SnapshotReviewer { |
There was a problem hiding this comment.
This class is totally unnecessary now, but I think it could be used as a base for having this feature in UI mode by creating a custom UISnapshotReviewer (like NodeSnapshotReviewer) with a custom renderer.
There was a problem hiding this comment.
I think we should avoid speculative abstraction for now.
| try { | ||
| await this.collectReviews(this.#review) | ||
| await this.#updateSnapshots() | ||
| // @todo what should happen when there's no approved updates? |
There was a problem hiding this comment.
Should we rerun all tests if no updates were approved?
| export async function saveSnapshotUpdates( | ||
| snapshots: Map<SnapshotEnvironment, readonly SnapshotUpdate[]>, | ||
| ): Promise<void> { |
There was a problem hiding this comment.
Not sure if this is the best way to handle the updates or maybe change SnapshotState, but it seems to be working fine with all kinds of snapshots.
It should probably be marked as @internal tho 🤔
There was a problem hiding this comment.
I think ideally we'd want get the failure/update information from SnapshotState since that's more like a ground truth source. I didn't think through what it would actually take for that though.
Code wise, the approach with the error meta and chai.ts stack fixup does look fairly clean, so it's not a big deal, but I'd like to see how the SnapshotState alternative approach looks like realistically.
There was a problem hiding this comment.
I thought that bug was just a weird interaction between soft, resolves/rejects, and the snapshot matchers. I should have checked 😅
|
|
||
| export interface RawSnapshot extends RawSnapshotInfo { | ||
| snapshot: string | ||
| file: string |
There was a problem hiding this comment.
This was a redundant property since it's set in RawSnapshotInfo already
| } | ||
| } | ||
|
|
||
| await Promise.all(fsEvents) |
There was a problem hiding this comment.
Not sure how I feel about waiting for these events to fire, since they might not be that reliable, but so far it seems it's working fine 👀
There was a problem hiding this comment.
The watcher workaround looks quite a magic. For example, it looks like saveInlineSnapshots/saveSnapshotFile/saveRawSnapshots aren't awaited but is it also part of it?
There was a problem hiding this comment.
From the code perspective, it looks more natural for watcher workaround to live on snapshot-review.ts than saveSnapshotUpdates. Also I wonder if it would be enough to just add some delay of setTimeout and pray watcher is settled for practice cases. (If it's CI issue, then we may find different surgical workaround for that specifically).
There was a problem hiding this comment.
Yes, it tracks file updates (with a timeout) and it doesn't await the updates themselves.
I agree with you, it would make more sense for it to live in the review process rather than here, I'm going to do that 👍🏼
Also I wonder if it would be enough to just add some delay of setTimeout and pray watcher is settled for practice cases.
I tried but fs events seem to be quite "random" in timing, so it's easy to get a re-run after enabling the watcher unless using a high timeout.
There was a problem hiding this comment.
Now the update waits for the saves and then the reviewer waits for the events only after running the tests, just in case some are still pending being dispatched.
| review: { | ||
| shorthand: 'i', | ||
| // @todo implement | ||
| description: | ||
| 'Run tests with soft snapshot assertions to collect all proposed changes, then review each one interactively to accept, reject, or skip it', | ||
| }, |
There was a problem hiding this comment.
The idea here would be to (as the description says) run snapshot assertions as soft assertions. This way we can collect all the failures and have a single review phase, otherwise users would have to run → review → [run → review → [...]].
If we decide to go with it, it should probably be in a follow-up task tho.
| const DIFF_COL_WIDTH = 5 | ||
| const HUNK_RE = /^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/ | ||
|
|
||
| function formatDiff(diff: string): string { |
There was a problem hiding this comment.
This might be unnecessary, but I like that it keeps the same layout throughout a review:
interactive-review.webm
d3a7a1c to
fe628d9
Compare
fe628d9 to
92d45e5
Compare
✅ Deploy Preview for vitest-dev ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
| message: () => errorMessage || 'Snapshot properties mismatched', | ||
| actual: received, | ||
| expected: properties, | ||
| meta: createMetaObject({ |
There was a problem hiding this comment.
This Snapshot properties mismatched case's meta isn't from snapshot failure but only matcher mismatch so it's not usable for review and seems to break the review update. Perhaps, we can remove meta thus exclude from review?
agent repro 🤖
import { appendFileSync, readFileSync } from 'node:fs'
import { expect, test } from 'vitest'
import { runInlineTests } from '../../../test-utils'
test('property matcher failure', async () => {
const { vitest, ctx, fs } = await runInlineTests(
{
'basic.test.ts': `
import { expect, test } from 'vitest'
test('props', () => {
expect({ id: 123, name: 'x' }).toMatchSnapshot({ id: expect.any(String) })
})
`,
'__snapshots__/basic.test.ts.snap': `// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[\`props 1\`] = \`
{
"id": Any<String>,
"name": "x",
}
\`;
`,
},
{ update: 'none', watch: true },
)
const before = ctx!.vite.watcher.listenerCount('change')
vitest.write('i')
await vitest.waitForStdout('Reviewing [1/1]')
appendFileSync('/tmp/probe-out.txt', String(['--- REVIEW SCREEN ---\n' + vitest.stdout].map(x => typeof x === 'string' ? x : JSON.stringify(x)).join(' ')) + '\n')
vitest.write('a')
await vitest.waitForStdout('Watching for file changes')
await new Promise(r => setTimeout(r, 500))
const after = ctx!.vite.watcher.listenerCount('change')
appendFileSync('/tmp/probe-out.txt', String(['--- SNAP FILE ---\n' + readFileSync(fs.resolveFile('__snapshots__/basic.test.ts.snap'), 'utf-8')].map(x => typeof x === 'string' ? x : JSON.stringify(x)).join(' ')) + '\n')
appendFileSync('/tmp/probe-out.txt', String(['--- change listeners ---', { before, after }].map(x => typeof x === 'string' ? x : JSON.stringify(x)).join(' ')) + '\n')
})There was a problem hiding this comment.
Does #11790 cover this bit too?
Yes, it's been removed 😄
| ) => Promise<void>, | ||
| ) => Promise<void> | ||
|
|
||
| abstract class SnapshotReviewer { |
There was a problem hiding this comment.
I think we should avoid speculative abstraction for now.
| } | ||
| } | ||
|
|
||
| await Promise.all(fsEvents) |
There was a problem hiding this comment.
From the code perspective, it looks more natural for watcher workaround to live on snapshot-review.ts than saveSnapshotUpdates. Also I wonder if it would be enough to just add some delay of setTimeout and pray watcher is settled for practice cases. (If it's CI issue, then we may find different surgical workaround for that specifically).
Description
Resolves #2229
Closes #9297
UX largely inspired by Insta's
reviewcommand.Demo on a project with different types of snapshots:
interactive-review.webm
Possible follow-ups:
Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
pnpm-lock.yamlunless you introduce a new test example.Tests
pnpm test:ci.Documentation
pnpm run docscommand.Changesets
feat:,fix:,perf:,docs:, orchore:.