snapshot_testing/
lib.rs

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