From 273025e49d7809eabbf6ec438257c60f4d80c96b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 23 Jul 2024 12:26:35 -0500 Subject: [PATCH] feat(assert): Allow comparing json to jsonlines Fixes #348 --- crates/snapbox/src/assert/mod.rs | 4 +- crates/snapbox/src/data/filters.rs | 17 ++++- crates/snapbox/src/data/mod.rs | 94 ++++++++++++++++++++++++ crates/snapbox/tests/testsuite/assert.rs | 19 +++-- 4 files changed, 125 insertions(+), 9 deletions(-) diff --git a/crates/snapbox/src/assert/mod.rs b/crates/snapbox/src/assert/mod.rs index 889c71bb..3f647a35 100644 --- a/crates/snapbox/src/assert/mod.rs +++ b/crates/snapbox/src/assert/mod.rs @@ -118,8 +118,8 @@ impl Assert { } // On `expected` being an error, make a best guess - let format = expected.intended_format(); - actual = actual.coerce_to(format); + actual = actual.coerce_to(expected.against_format()); + actual = actual.coerce_to(expected.intended_format()); if self.normalize_paths && expected.filters.is_paths_set() { actual = FilterPaths.filter(actual); diff --git a/crates/snapbox/src/data/filters.rs b/crates/snapbox/src/data/filters.rs index 5b0103b3..d9c95587 100644 --- a/crates/snapbox/src/data/filters.rs +++ b/crates/snapbox/src/data/filters.rs @@ -1,6 +1,9 @@ +use crate::data::DataFormat; + #[derive(Copy, Clone, Default, Debug, PartialEq, Eq)] pub(crate) struct FilterSet { flags: usize, + against: Option, } impl FilterSet { @@ -9,7 +12,10 @@ impl FilterSet { } pub(crate) const fn empty() -> Self { - Self { flags: 0 } + Self { + flags: 0, + against: None, + } } pub(crate) fn redactions(mut self) -> Self { @@ -32,6 +38,11 @@ impl FilterSet { self } + pub(crate) fn against(mut self, format: DataFormat) -> Self { + self.against = Some(format); + self + } + pub(crate) const fn is_redaction_set(&self) -> bool { self.is_set(Self::REDACTIONS) } @@ -47,6 +58,10 @@ impl FilterSet { pub(crate) const fn is_unordered_set(&self) -> bool { self.is_set(Self::UNORDERED) } + + pub(crate) const fn get_against(&self) -> Option { + self.against + } } impl FilterSet { diff --git a/crates/snapbox/src/data/mod.rs b/crates/snapbox/src/data/mod.rs index ae7c7a22..4704466a 100644 --- a/crates/snapbox/src/data/mod.rs +++ b/crates/snapbox/src/data/mod.rs @@ -192,6 +192,67 @@ pub trait IntoData: Sized { self.is_termsvg() } + /// Override the type this snapshot will be compared against + /// + /// Normally, the `actual` data is coerced to [`IntoData::is`]. + /// This allows overriding that so you can store your snapshot in a more readable, diffable + /// format. + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "json")] { + /// use snapbox::prelude::*; + /// use snapbox::str; + /// + /// let expected = str![[r#"{"hello": "world"}"#]] + /// .against(snapbox::data::DataFormat::JsonLines); + /// # } + /// ``` + fn against(self, format: DataFormat) -> Data { + self.into_data().against(format) + } + + /// Initialize as json or [`Error`][DataFormat::Error] + /// + /// This is generally used for `expected` data + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "json")] { + /// use snapbox::prelude::*; + /// use snapbox::str; + /// + /// let expected = str![[r#"{"hello": "world"}"#]] + /// .is_json(); + /// # } + /// ``` + #[cfg(feature = "json")] + fn against_json(self) -> Data { + self.against(DataFormat::Json) + } + + /// Initialize as json lines or [`Error`][DataFormat::Error] + /// + /// This is generally used for `expected` data + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "json")] { + /// use snapbox::prelude::*; + /// use snapbox::str; + /// + /// let expected = str![[r#"{"hello": "world"}"#]] + /// .against_jsonlines(); + /// # } + /// ``` + #[cfg(feature = "json")] + fn against_jsonlines(self) -> Data { + self.against(DataFormat::JsonLines) + } + /// Convert to [`Data`], applying defaults fn into_data(self) -> Data; } @@ -577,6 +638,29 @@ impl Data { }) } + /// Override the type this snapshot will be compared against + /// + /// Normally, the `actual` data is coerced to [`Data::is`]. + /// This allows overriding that so you can store your snapshot in a more readable, diffable + /// format. + /// + /// # Examples + /// + /// ```rust + /// # #[cfg(feature = "json")] { + /// use snapbox::prelude::*; + /// use snapbox::str; + /// + /// let expected = str![[r#"{"hello": "world"}"#]] + /// .is(snapbox::data::DataFormat::Json) + /// .against(snapbox::data::DataFormat::JsonLines); + /// # } + /// ``` + fn against(mut self, format: DataFormat) -> Data { + self.filters = self.filters.against(format); + self + } + /// Convert `Self` to [`format`][DataFormat] if possible /// /// This is generally used on `actual` data to make it match `expected` @@ -592,6 +676,10 @@ impl Data { (DataInner::Json(inner), DataFormat::Json) => DataInner::Json(inner), #[cfg(feature = "json")] (DataInner::JsonLines(inner), DataFormat::JsonLines) => DataInner::JsonLines(inner), + #[cfg(feature = "json")] + (DataInner::JsonLines(inner), DataFormat::Json) => DataInner::Json(inner), + #[cfg(feature = "json")] + (DataInner::Json(inner), DataFormat::JsonLines) => DataInner::JsonLines(inner), #[cfg(feature = "term-svg")] (DataInner::TermSvg(inner), DataFormat::TermSvg) => DataInner::TermSvg(inner), (DataInner::Binary(inner), _) => { @@ -705,6 +793,12 @@ impl Data { } } + pub(crate) fn against_format(&self) -> DataFormat { + self.filters + .get_against() + .unwrap_or_else(|| self.intended_format()) + } + pub(crate) fn relevant(&self) -> Option<&str> { match &self.inner { DataInner::Error(_) => None, diff --git a/crates/snapbox/tests/testsuite/assert.rs b/crates/snapbox/tests/testsuite/assert.rs index bbf96ae9..d81a974b 100644 --- a/crates/snapbox/tests/testsuite/assert.rs +++ b/crates/snapbox/tests/testsuite/assert.rs @@ -38,11 +38,18 @@ fn actual_expected_formats_differ() { {"order": 3} "#, str![[r#" -{} -{"order":1} -{"order":2} -{"order":3} - -"#]].is_jsonlines(), +[ + {}, + { + "order": 1 + }, + { + "order": 2 + }, + { + "order": 3 + } +] +"#]].is_json().against_jsonlines(), ); }