mitsuhiko / insta

A snapshot testing library for rust
https://insta.rs
Apache License 2.0
2.18k stars 98 forks source link

Suggestion: Templated snapshot file paths #647

Open Techassi opened 1 week ago

Techassi commented 1 week ago

This came up in #456 regarding #377.

I suggest supporting templated snapshot file paths. The user has access to various variables to construct the final path. The exact variables (and their names) will be discussed in this issue.

In #456 I suggested the following ones: crate, module, test_function_name, suffix, and input_file (for glob tests). To get the discussion going, I will lay out a bunch of details for the variables I initially suggested.

Additionally, we need to decide where and how to define the file name template. One obvious place is the Settings struct. Also, we need to decide the templating syntax, for example using { var } or {{ var }}.

I'm happy to hear feedback and I'm also happy to implement this once we nailed down all parts of this suggestion.

max-sixty commented 1 week ago

This sounds great!

Couple of small questions, no need to answer them all before adding some code. (If we're confident it's extensible, we could add something quite small to the repo and then gradually expand...)

Techassi commented 1 week ago

A bunch of answers/comments to/on your questions.

Do we start with allowing customizing the path only, or also the file name?

I envisioned templating the whole path and file name. For example {crate}_my/path/{test_function_name}.

Is it defined relative to the root of the crate? Or src/? Or the test module?

I would allow relative paths (no absolute ones) without any path traversals (..) relative to a well known location. I would suggest CARGO_MANIFEST_DIR, because it allows the most flexibility to store (input) and snapshot files related to the crate we are running the tests in. Users could then store snapshots in locations like mycrate/fixtures/snapshots, mycrate/src/snapshots or mycrate/tests. Basically which location fits their needs best.

The Settings struct is fine to start, but I think we probably want it in the settings file eventually, because it's likely something we want across a whole project

Yes, that makes sense. I wasn't sure what the exact status of the config file is, but I can see that we want to make it a first class citizen. I can imagine it might be quite hard to make it accessible in all assertions in an efficient way.

Would extension play in here? Or that's added onto the end separately?

Actually, good question. I would say we always add the extension (.snap and .snap.new) to the templated path outself (while removing any prior extension) to keep it consistent. This would help cargo-insta to discover files like it currently does.

If we want to enable customizing it, cargo-insta also needs to know it to discover the files correctly.

Happy to hear your opinions on it.

Because this would go into the core insta, we want to keep the dependencies light. So that would mean either a very simple templating like { var } which we process ourselves

Yes, makes sense. I would also argue that the simple syntax should be sufficient to start with.

max-sixty commented 1 week ago

That all sounds great! Very much agree with your points.

Re the root path — I do think we want to be able to write a template that gives the current behavior. The current behavior is ../snapshots/{...} relative to the path of the test file. So maybe that means we need something like "absolute path -> CARGO_MANIFEST_DIR" & "relative path -> test file path". Or some variable for the test file path.

FWIW we already have some support for custom extensions, and I've improved the support in 1.41. But ideally we can merge parts of the broader features without dealing with all possible issues, and extensions seem particularly minor.

Would you want to have a swing at an initial version of this, to start?

Techassi commented 1 week ago

I do think we want to be able to write a template that gives the current behavior. The current behavior is ../snapshots/{...} relative to the path of the test file.

True, I didn't think about that, so good catch.

So maybe that means we need something like "absolute path -> CARGO_MANIFEST_DIR" & "relative path -> test file path".

What exactly do you mean by that? Maybe a better solution might look like this:

FWIW we already have some support for custom extensions, and I've improved the support in 1.41. But ideally we can merge parts of the broader features without dealing with all possible issues, and extensions seem particularly minor.

Yes, we can delay this feature/discussion. It would however be nice if you could bring me up to speed on the current situation. I haven't looked into custom extensions yet.

Would you want to have a swing at an initial version of this, to start?

Sure thing, I can start cooking up some stuff. Once I have some code ready, I will open a PR in the next few hours/days.

max-sixty commented 6 days ago
  • Allow relative paths, to enable the current behaviour. Relative paths are "capped" to traverse only up to CARGO_MANIFEST_DIR to prevent placing files outside of the Rust workspace.

    • Allow absolute paths, which however need to start with CARGO_MANIFEST_DIR to again prevent unwanted path traversals. We could even provide a templating variable { cargo_manifest_dir } to ease construction of these absolute paths.

Yes this sort of thing sounds good! And we could have source_file as a variable so something like {source_file}/../snapshots/ is the default path

max-sixty commented 6 days ago

It would however be nice if you could bring me up to speed on the current situation. I haven't looked into custom extensions yet.

This is a field on Settings: https://github.com/mitsuhiko/insta/blob/ee829d825b1ff561a4696f42f7d3a7a0b9860b62/insta/src/settings.rs#L65, and can be passed as --extensions to cargo-insta: https://github.com/mitsuhiko/insta/blob/53dc3de744ab6d8a724071a85c65e282d20fddf9/cargo-insta/src/cli.rs#L93-L95