Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(testing): Implement "assertSnapshot" #2039

Merged
merged 47 commits into from
Apr 20, 2022

Conversation

hyp3rflow
Copy link
Contributor

@hyp3rflow hyp3rflow commented Mar 17, 2022

@CLAassistant
Copy link

CLAassistant commented Mar 17, 2022

CLA assistant check
All committers have signed the CLA.

@hyp3rflow
Copy link
Contributor Author

Oh.. How can I convert this PR to draft? I created this just for showing the naive implementation of assertSnapshot on this PR denoland/deno#14007. but I accidentally clicked Create Pull Request instead of draft one :q

@kt3k kt3k marked this pull request as draft March 17, 2022 16:18
@kt3k
Copy link
Member

kt3k commented Mar 17, 2022

converted to draft

Copy link
Contributor

@bcheidemann bcheidemann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obviously this is just a starting point so hopefully I've not been overzealous with my comments! :D

testing/asserts.ts Outdated Show resolved Hide resolved
testing/asserts.ts Outdated Show resolved Hide resolved
const updatedSnapshotFile: Record<string, unknown> = {};
// const snapshotMap: Record<string, number> = {};

export async function assertSnapshot(context: Deno.TestContext, actual: unknown) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we also expose a function called assertSnapshotSync.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good! I think we can implement that after denoland/deno#14007 merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to do this with the way we now read the snapshot file. I think this can probably be implemented separately if it's needed

testing/asserts.ts Outdated Show resolved Hide resolved
}
const snapshot = snapshotFile?.[name];
if (!context.update) {
assertEquals(actual, snapshot);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation will error on:

const actual = {
  fn: () => { /* ... */ }
}

As snapshots need to be represented as strings anyway, I would argue it makes more sense to use the _format helper here to convert the snapshot into a string. We could store the snapshot like this:

{
  "Snapshot Test > babo > merong": [
    "{",
    "  b: 2,",
    "  c: 3,",
    "}"
  ]
}

This would enable us to catch a much wider range of errors in our snapshots. E.g. the following would error on assertSnapshot:

// First run
const actual = {
  testClass: class Test {}
}
// --snip--
assertSnapshot(test, actual)

// Second run
const actual = {
  testClass: class NotTest {}
}
// --snip--
assertSnapshot(test, actual)

But would not error (or worse, always error) if we only store the JSON representation.

Additionally, this representation enables us to use the diff and buildMessage to produce helpful output.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Deno.inspect() can be a good choice for serialization method. What do you think? @bcheidemann

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this would definitely do the job. I would suggest to use the _format helper which wraps Deno.inspect. This ensures the output will be consistent with other asserts 🙂

}
if (!isEqual) console.info("Snapshot updated", name);
}
updatedSnapshotFile[name] = actual;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean this implementation supports only 1 snapshot per test case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the test case mean one Deno.test?
This implementation supports 1 snapshot per test module (one .test.ts file)

for example, if I test a.test.ts with --update arg,

Deno.test("Snapshot Test A", async (t) => {
  await assertSnapshot(t, { a: 1, b: 2 });
  await t.step("yaho", async (t) => {
    await assertSnapshot(t, { b: 2, c: 3 });
    await t.step("haha", async (t) => {
      await assertSnapshot(t, { b: 2, c: 4 });
    });
  });
});

Deno.test("Snapshot Test A2", async (t) => {
  await assertSnapshot(t, { a: 1, b: 2 });
  await t.step("what", async (t) => {
    await assertSnapshot(t, { b: 2, c: 3 });
    await t.step("good", async (t) => {
      await assertSnapshot(t, { b: 2, c: 4 });
    });
  });
});

the output, a.test.snap will be like this

{
  "Snapshot Test A": {
    "a": 1,
    "b": 2
  },
  "Snapshot Test A > yaho": {
    "b": 2,
    "c": 3
  },
  "Snapshot Test A > yaho > haha": {
    "b": 2,
    "c": 4
  },
  "Snapshot Test A2": {
    "a": 1,
    "b": 2
  },
  "Snapshot Test A2 > what": {
    "b": 2,
    "c": 3
  },
  "Snapshot Test A2 > what > good": {
    "b": 2,
    "c": 4
  }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the below case? I think the second assertSnapshot call overwrites the snapshot from the first call

Deno.test("Snapshot Test A", async (t) => {
  await assertSnapshot(t, { a: 1, b: 2 });
  await assertSnapshot(t, { c: 3, d: 4 });
});

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha! Sorry, now I know what you mean. That case will be handled with commented count. (Currently not implemented perfectly so I commented) @kt3k

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok. I see. getCount does that 👍

@@ -882,7 +882,7 @@ export async function assertSnapshot(context: Deno.TestContext, actual: unknown)
snapshotFile = file ? JSON.parse(file) : {};
}
const snapshot = snapshotFile?.[name];
if (!context.update) {
if (!Deno.args.includes('--update')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!Deno.args.includes('--update')) {
if (!Deno.args.some(arg => arg === '--update' || arg === '-u')) {

Could do something like this to support -u and --update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will handle this after denoland/deno#14007 merged!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW to pass -u option to Deno.args, you need to invoke test command like deno test -- -u because the flags before -- are consumed by Deno CLI itself. I'm ok with that for the first pass implementation, but it might be non ideal solution, I guess

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I also noticed that the flag for updating snapshot can affect other codes that wanted to test.. but I think that using Deno.args is better than making an option on Deno test cli.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo this is the best solution for first pass. But if/when snapshot testing is a more established then we should look to implement this on the Deno CLI instead; as @hyp3rflow mentioned, this can affect test execution.

@hyp3rflow
Copy link
Contributor Author

I changed the snapshot style to jest's snapshot module.

@bartlomieju
Copy link
Member

@hyp3rflow I just landed PR in Deno repo that adds necessary fields to Deno.TestContext

Copy link
Contributor

@bcheidemann bcheidemann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good! Just a couple comments but nothing major 🙂 Could you also format the file?

return context.name;
}
function getCount() {
const count = snapshotMap?.[name] ? snapshotMap[name] : 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const count = snapshotMap?.[name] ? snapshotMap[name] : 1;
const count = snapshotMap[name] ? snapshotMap[name] : 1;

snapshotMap will never be undefined so no need for ?.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 916 to 919
const buf = ['export const snapshot = {};\n'];
for (const [key, value] of Object.entries(updatedSnapshotFile)) {
buf.push(`\nsnapshot[\`${key}\`] = \`\n${_format(value)}\n\`;\n`);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice because you could easily automate conversion of Jest snapshots to Deno snapshots.

However, we will need to escape backticks (`) because otherwise the following code would break:

await assertSnapshot(context, "This snapshot will break because I added a ` ");

The same will be true of the snapshot name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

export async function assertSnapshot(context: Deno.TestContext, actual: unknown) {
const name = getName(context);
const count = getCount();
const testName = `${name} #${count}`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const testName = `${name} #${count}`;
const testName = `${name} ${count}`;

This makes it consistent with Jest snapshots, which don't have a #.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
if (!isEqual) console.info("Snapshot updated", testName);
updatedSnapshotFile[testName] = actual;
globalThis.onunload = writeSnapshotFileSync;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this function will be run once per assertSnapshot call. Can we refactor this so it only runs once?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if (isUpdate) {
let isEqual = true;
try {
assertEquals(_format(actual), snapshot.slice(1, -1));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given these values are used in both the if and else blocks, can we extract them out above the if statement for better readability/maintainability e.g.

// --snip--
const _actual = _format(actual);
const _expected = snapshot.slice(1, -1);
if (isUpdate) {
 // --snip--

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

buf.push(`\nsnapshot[\`${key}\`] = \`\n${_format(value)}\n\`;\n`);
}
Deno.writeTextFileSync(snapshotPath, buf.join(""));
console.log('Snapshot updated!');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
console.log('Snapshot updated!');
console.log(green(bold(` > ${n} snapshots updated.`)));

Maybe we could mimic Jest here and log how many snapshots were updated?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

} catch {
isEqual = false;
}
if (!isEqual) console.info("Snapshot updated", testName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!isEqual) console.info("Snapshot updated", testName);

Do we need this message?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed this for now

ensureFileSync(snapshotPath);
const buf = ['export const snapshot = {};\n'];
for (const [key, value] of Object.entries(updatedSnapshotFile)) {
buf.push(`\nsnapshot[\`${key}\`] = \`\n${_format(value)}\n\`;\n`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be done now, but Jest only adds the extra \ns when the formatted result contains a new line.

e.g.

// test file (jest)
expect("single line").toMatchSnapshot();

// snapshot file
exports[...] = `"single line"`;

// test file (jest)
expect("two\nlines").toMatchSnapshot();

// snapshot file
exports[...] = `
"two
lines"
`;

It would be nice if we could mimic this behaviour to make migration of tests easier. Again though, this can be a future improvement 😊

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@bartlomieju
Copy link
Member

@hyp3rflow any chance we could finish this PR for next week's release?

@disjukr
Copy link
Contributor

disjukr commented Apr 14, 2022

@bartlomieju He is absence now due to military issue (until 4/28 kst). I can fix the code on his branch, but it might be hard to finish for the next week's release.

@bcheidemann
Copy link
Contributor

bcheidemann commented Apr 14, 2022

@bartlomieju When is next weeks release? I have a 4 day weekend so I can pick this up and should have time to finish it. I would need to create a new PR though as I don't have write access to this branch.

@bartlomieju
Copy link
Member

@bartlomieju He is absence now due to military issue (until 4/28 kst). I can fix the code on his branch, but it might be hard to finish for the next week's release.

@disjukr thanks for letting me know.

@bartlomieju When is next weeks release? I have a 4 day weekend so I can pick this up and should have time to finish it. I would need to create a new PR though as I don't have write access to this branch.

The release is on Wednesday, April 20th. Feel free to open up a new branch. If we miss the date, that's not a big deal though, we'll just land it the following week.

@bcheidemann
Copy link
Contributor

@bartlomieju He is absence now due to military issue (until 4/28 kst). I can fix the code on his branch, but it might be hard to finish for the next week's release.

@disjukr thanks for letting me know.

@bartlomieju When is next weeks release? I have a 4 day weekend so I can pick this up and should have time to finish it. I would need to create a new PR though as I don't have write access to this branch.

The release is on Wednesday, April 20th. Feel free to open up a new branch. If we miss the date, that's not a big deal though, we'll just land it the following week.

Thanks, I'll open a new branch tomorrow 👍

@bartlomieju
Copy link
Member

The examples from testing/README.md don't work unfortunately:

// snap_test.ts
import { assertSnapshot } from "https://deno.land/std@$STD_VERSION/testing/snapshot.ts";

Deno.test("isSnapshotMatch", function (t): void {
  const a = {
    hello: "world!",
    example: 123,
  };
  assertSnapshot(test, a);
});
../deno/target/debug/deno test ./snap_test.ts --allow-read -- --update
running 1 test from ./snap_test.ts
isSnapshotMatch ... FAILED (7ms)

failures:

./snap_test.ts > isSnapshotMatch
Test case is leaking async ops.

-

To get more details where ops were leaked, run again with --trace-ops flag.

failures:

	./snap_test.ts
	isSnapshotMatch

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (24ms)

error: Test failed
../deno/target/debug/deno test ./snap_test.ts --allow-read -- --update
running 1 test from ./snap_test.ts
isSnapshotMatch ...
test result: FAILED. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (26ms)

error: Uncaught (in promise) PermissionDenied: Requires write access to "file:///Users/ib/dev/deno_std/__snapshots__", run again with the --allow-write flag
      await Deno.mkdir(dir, { recursive: true });
      ^
    at async Object.mkdir (deno:runtime/js/30_fs.js:109:5)
    at async ensureDir (file:///Users/ib/dev/deno_std/fs/ensure_dir.ts:22:7)
    at async ensureFile (file:///Users/ib/dev/deno_std/fs/ensure_file.ts:27:7)
    at async readSnapshotFile (file:///Users/ib/dev/deno_std/testing/snapshot.ts:187:7)
    at async assertSnapshot (file:///Users/ib/dev/deno_std/testing/snapshot.ts:105:27)

I think this should be caught be some integration tests. @bcheidemann let me know if you need any help with that.

This is very close to landing 🚀

@bcheidemann
Copy link
Contributor

bcheidemann commented Apr 19, 2022

Updating behavior is not covered by the unit tests. Can we somehow test it? (For example by creating tmp directory and run deno test command with -u option with Deno.run() API.)

@kt3k Good idea! 😊 I have added a test which does exactly this

@bcheidemann
Copy link
Contributor

bcheidemann commented Apr 19, 2022

The examples from testing/README.md don't work unfortunately:

// snap_test.ts
import { assertSnapshot } from "https://deno.land/std@$STD_VERSION/testing/snapshot.ts";

Deno.test("isSnapshotMatch", function (t): void {
  const a = {
    hello: "world!",
    example: 123,
  };
  assertSnapshot(test, a);
});
../deno/target/debug/deno test ./snap_test.ts --allow-read -- --update
running 1 test from ./snap_test.ts
isSnapshotMatch ... FAILED (7ms)

failures:

./snap_test.ts > isSnapshotMatch
Test case is leaking async ops.

-

To get more details where ops were leaked, run again with --trace-ops flag.

failures:

	./snap_test.ts
	isSnapshotMatch

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out (24ms)

error: Test failed
../deno/target/debug/deno test ./snap_test.ts --allow-read -- --update
running 1 test from ./snap_test.ts
isSnapshotMatch ...
test result: FAILED. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (26ms)

error: Uncaught (in promise) PermissionDenied: Requires write access to "file:///Users/ib/dev/deno_std/__snapshots__", run again with the --allow-write flag
      await Deno.mkdir(dir, { recursive: true });
      ^
    at async Object.mkdir (deno:runtime/js/30_fs.js:109:5)
    at async ensureDir (file:///Users/ib/dev/deno_std/fs/ensure_dir.ts:22:7)
    at async ensureFile (file:///Users/ib/dev/deno_std/fs/ensure_file.ts:27:7)
    at async readSnapshotFile (file:///Users/ib/dev/deno_std/testing/snapshot.ts:187:7)
    at async assertSnapshot (file:///Users/ib/dev/deno_std/testing/snapshot.ts:105:27)

I think this should be caught be some integration tests. @bcheidemann let me know if you need any help with that.

This is very close to landing rocket

@bartlomieju I have updated the examples in testing/README.md and these should now be correct 🙂

What do you suggest for integration tests?


EDIT: I wasn't able to reproduce the second error case (Test case is leaking async ops.) but the second is because the --allow-write flag must be passed when passing the --update flag. Do you think we should offer a more helpful error in this case? Happy to do that in this PR 👍

@kt3k
Copy link
Member

kt3k commented Apr 20, 2022

I wasn't able to reproduce the second error case (Test case is leaking async ops.)

This was probably caused by the absense of await before assertSnapshot in the previous version of readme example (I experienced a similar error).

Now I was able to run the README example on my end (with and without -u option) and snapshot files were created in documented places. Nice work 👍

false,
);

await assertSnapshot(t, formatOutput(result1.output).split("\n"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, nice usage of assertSnapshot 👍

Copy link
Member

@kt3k kt3k left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you for your contributions! @bcheidemann @hyp3rflow

const testDir = dirname(fromFileUrl(import.meta.url));
const tempDir = join(testDir, TEMP_DIR);
const tempTestFileName = "test.ts";
const tempTestFilePath = join(tempDir, tempTestFileName);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine for first pass, but ideally we like to use Deno.makeTempDir for making temp directory. Filed an issue for this #2129

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My initial implementation did use this but I refactored as it resulted in a random output path which I would have had to obfuscate in the snapshot.

What's the advantage of using this by the way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deno.makeTempDir creates a directory under the system's default temp directory and they are automatically removed by the OS some time later. So we don't need care much about those files even if the test code failed to remove it. Currently we need to remove testing/.tmp manually when the test case failed to remove it for some reason, and that's a little inconvenient

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation! 🙂

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thank you so much to @hyp3rflow and @bcheidemann, this is very useful feature and hopefully many users will find it helpful.

@bartlomieju bartlomieju changed the title proposal(testing/assertSnapshot): Implement assertSnapshot feat(testing): Implement "assertSnapshot" Apr 20, 2022
@bartlomieju bartlomieju merged commit 7b44ec3 into denoland:main Apr 20, 2022
@bcheidemann
Copy link
Contributor

LGTM! Thank you so much to @hyp3rflow and @bcheidemann, this is very useful feature and hopefully many users will find it helpful.

Thanks @bartlomieju @kt3k for all your support with this feature!

@jaydenseric
Copy link
Contributor

Just noticing this now; sad to see opinionated magic around where snapshots are saved (a __snapshots__ dir) instead of the assertSnapshot function just accepting 2 arguments; the actual value being asserted, and a file URL / string path to the snapshot file. That way just by reading argument intellisense it's predictable (and customisable) where the snapshots end up, and the snapshot can be held in the correct type of file for the data (.json, .html, .css, etc.) for syntax highlighting and such when directly viewing the snapshot file in an editor.

The simpler design also means a simpler implementation to write and maintain.

Here is an example of the (IMO) ideal assertSnapshot API in action on npm:

Note the optional third argument assertion that accepts an assertion function for comparing the actual text with the snapshot file text, in case you want to use your own custom one for a special kind of diff or something:

https://github.com/jaydenseric/snapshot-assertion/blob/5b7f7425a231081e8f97b58765be6a789cf72696/assertSnapshot.mjs#L16-L19

@kt3k
Copy link
Member

kt3k commented Apr 21, 2022

Explicit snapshot path looks nice to me. But implicit snapshot paths are also convenient for the users who don't care about snapshot paths / snapshot file syntax. Can we somehow support both patterns by overloading assertSnapshot?

@bcheidemann
Copy link
Contributor

Just noticing this now; sad to see opinionated magic around where snapshots are saved (a __snapshots__ dir) instead of the assertSnapshot function just accepting 2 arguments; the actual value being asserted, and a file URL / string path to the snapshot file. That way just by reading argument intellisense it's predictable (and customisable) where the snapshots end up, and the snapshot can be held in the correct type of file for the data (.json, .html, .css, etc.) for syntax highlighting and such when directly viewing the snapshot file in an editor.

The simpler design also means a simpler implementation to write and maintain.

Here is an example of the (IMO) ideal assertSnapshot API in action on npm:

Note the optional third argument assertion that accepts an assertion function for comparing the actual text with the snapshot file text, in case you want to use your own custom one for a special kind of diff or something:

https://github.com/jaydenseric/snapshot-assertion/blob/5b7f7425a231081e8f97b58765be6a789cf72696/assertSnapshot.mjs#L16-L19

I agree with a lot of what you're proposing but not necessarily the API. I also don't think it's too late to have a discussion around this - it's only the first pass of this feature. Why don't you raise an issue and we can continue the conversation there? 🤔

@bcheidemann
Copy link
Contributor

Explicit snapshot path looks nice to me. But implicit snapshot paths are also convenient for the users who don't care about snapshot paths / snapshot file syntax. Can we somehow support both patterns by overloading assertSnapshot?

I don't see why not, and I have a couple of ideas how it could be done without breaking changes to the API. An issue seems like the more appropriate place to discuss this though.

@hyp3rflow
Copy link
Contributor Author

I just came back earlier due to the pandemic. Thanks, @bcheidemann for your awesome works 😊
I'm also excited that I can use this feature in release!

@hyp3rflow hyp3rflow deleted the feat/assertSnapshot branch April 26, 2022 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

snapshot testing
7 participants