snapshot_testing/
lib.rs

1#![doc = include_str!("../README.md")]
2
3/// Assert that `value` equals the snapshot at `snapshot_path`. If there is a
4/// mismatch the function will panic with a helpful diff that shows what
5/// changed.
6///
7/// Set the env var `UPDATE_SNAPSHOTS=yes` to write `value` to `snapshot_file`
8/// instead of asserting equality.
9#[track_caller]
10pub fn assert_eq_or_update(value: impl AsRef<str>, snapshot_path: impl AsRef<std::path::Path>) {
11    let snapshot_path = snapshot_path.as_ref();
12
13    if update_snapshot() {
14        ensure_parent_dir_exists(snapshot_path);
15
16        std::fs::write(snapshot_path, value.as_ref())
17            .unwrap_or_else(|e| panic!("Error writing {snapshot_path:?}: {e}"));
18    } else {
19        let snapshot = std::fs::read_to_string(snapshot_path)
20            .unwrap_or_else(|e| panic!("Error reading {snapshot_path:?}: {e}"));
21
22        maybe_enable_colors();
23
24        similar_asserts::assert_eq!(
25            value.as_ref(),
26            snapshot,
27            "\n\n{}: run with env var `UPDATE_SNAPSHOTS=yes` to update snapshots\n",
28            console::style("help").cyan()
29        );
30    }
31}
32
33/// Don't introduce new environment variables for users. Do our best based on
34/// existing environment. Even better would be if `console` didn't have global
35/// state, but for now we accept that that is what `similar_asserts` is using.
36///
37/// Also see:
38/// * [`CLICOLOR_FORCE`](https://github.com/console-rs/console/blob/a51fcead7cda/src/utils.rs#L18)
39/// * [`NO_COLOR`](https://github.com/console-rs/console/blob/a51fcead7cda/src/unix_term.rs#L29)
40/// * [`TERM`](https://github.com/console-rs/console/blob/a51fcead7cda/src/unix_term.rs#L33)
41fn maybe_enable_colors() {
42    // Only care about `CARGO_TERM_COLOR=always`. Otherwise the `console`
43    // defaults are fine.
44    if let Ok(color) = std::env::var("CARGO_TERM_COLOR") {
45        if color == "always" {
46            console::set_colors_enabled(true);
47        }
48    }
49}
50
51fn ensure_parent_dir_exists(snapshot_path: &std::path::Path) {
52    if !snapshot_path.exists() {
53        std::fs::create_dir_all(snapshot_path.parent().unwrap())
54            .unwrap_or_else(|e| panic!("Error creating directory for {snapshot_path:?}: {e}"));
55    }
56}
57
58fn update_snapshot() -> bool {
59    std::env::var("UPDATE_SNAPSHOTS")
60        .map(|s| s.to_lowercase())
61        .is_ok_and(|s| s == "1" || s == "yes" || s == "true")
62}