From 4990ecf7b0235afed23dcf45a72efd43fa196f56 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 15 May 2024 14:22:21 -0500 Subject: [PATCH] fix(filter): Move from `data` to `filter` Cherry-pick fe2fb1e606f81400cfb55a66515beee2d2f67602 (#288) Cherry-pick 79e31673db8ce35c7bd0e59f95b732c983b329a6 (#285) --- crates/snapbox/src/data/mod.rs | 4 +- crates/snapbox/src/data/normalize.rs | 188 +------------- crates/snapbox/src/data/tests.rs | 346 -------------------------- crates/snapbox/src/filter/mod.rs | 199 +++++++++++++++ crates/snapbox/src/filter/test.rs | 351 +++++++++++++++++++++++++++ crates/snapbox/src/lib.rs | 1 + 6 files changed, 561 insertions(+), 528 deletions(-) create mode 100644 crates/snapbox/src/filter/mod.rs create mode 100644 crates/snapbox/src/filter/test.rs diff --git a/crates/snapbox/src/data/mod.rs b/crates/snapbox/src/data/mod.rs index bb6080c4..56a73384 100644 --- a/crates/snapbox/src/data/mod.rs +++ b/crates/snapbox/src/data/mod.rs @@ -118,8 +118,8 @@ macro_rules! str { /// This provides conveniences for tracking the intended format (binary vs text). #[derive(Clone, Debug)] pub struct Data { - inner: DataInner, - source: Option, + pub(crate) inner: DataInner, + pub(crate) source: Option, } #[derive(Clone, Debug)] diff --git a/crates/snapbox/src/data/normalize.rs b/crates/snapbox/src/data/normalize.rs index fc8339c7..dd7f4769 100644 --- a/crates/snapbox/src/data/normalize.rs +++ b/crates/snapbox/src/data/normalize.rs @@ -1,196 +1,24 @@ -//! Normalize `actual` or `expected` [`Data`] -//! -//! This can be done for -//! - Making snapshots consistent across platforms or conditional compilation -//! - Focusing snapshots on the characteristics of the data being tested +#![allow(deprecated)] use super::Data; -use super::DataInner; -pub trait Normalize { - fn normalize(&self, data: Data) -> Data; -} +pub use crate::filter::Normalize; +#[deprecated(since = "0.5.11", note = "Replaced with `filter::NormalizeNewlines")] pub struct NormalizeNewlines; impl Normalize for NormalizeNewlines { fn normalize(&self, data: Data) -> Data { - let source = data.source; - let inner = match data.inner { - DataInner::Error(err) => DataInner::Error(err), - DataInner::Binary(bin) => DataInner::Binary(bin), - DataInner::Text(text) => { - let lines = crate::utils::normalize_lines(&text); - DataInner::Text(lines) - } - #[cfg(feature = "json")] - DataInner::Json(value) => { - let mut value = value; - normalize_value(&mut value, crate::utils::normalize_lines); - DataInner::Json(value) - } - #[cfg(feature = "term-svg")] - DataInner::TermSvg(text) => { - let lines = crate::utils::normalize_lines(&text); - DataInner::TermSvg(lines) - } - }; - Data { inner, source } + crate::filter::NormalizeNewlines.normalize(data) } } +#[deprecated(since = "0.5.11", note = "Replaced with `filter::NormalizePaths")] pub struct NormalizePaths; impl Normalize for NormalizePaths { fn normalize(&self, data: Data) -> Data { - let source = data.source; - let inner = match data.inner { - DataInner::Error(err) => DataInner::Error(err), - DataInner::Binary(bin) => DataInner::Binary(bin), - DataInner::Text(text) => { - let lines = crate::utils::normalize_paths(&text); - DataInner::Text(lines) - } - #[cfg(feature = "json")] - DataInner::Json(value) => { - let mut value = value; - normalize_value(&mut value, crate::utils::normalize_paths); - DataInner::Json(value) - } - #[cfg(feature = "term-svg")] - DataInner::TermSvg(text) => { - let lines = crate::utils::normalize_paths(&text); - DataInner::TermSvg(lines) - } - }; - Data { inner, source } - } -} - -pub struct NormalizeMatches<'a> { - substitutions: &'a crate::Substitutions, - pattern: &'a Data, -} - -impl<'a> NormalizeMatches<'a> { - pub fn new(substitutions: &'a crate::Substitutions, pattern: &'a Data) -> Self { - NormalizeMatches { - substitutions, - pattern, - } - } -} - -impl Normalize for NormalizeMatches<'_> { - fn normalize(&self, data: Data) -> Data { - let source = data.source; - let inner = match data.inner { - DataInner::Error(err) => DataInner::Error(err), - DataInner::Binary(bin) => DataInner::Binary(bin), - DataInner::Text(text) => { - if let Some(pattern) = self.pattern.render() { - let lines = self.substitutions.normalize(&text, &pattern); - DataInner::Text(lines) - } else { - DataInner::Text(text) - } - } - #[cfg(feature = "json")] - DataInner::Json(value) => { - let mut value = value; - if let DataInner::Json(exp) = &self.pattern.inner { - normalize_value_matches(&mut value, exp, self.substitutions); - } - DataInner::Json(value) - } - #[cfg(feature = "term-svg")] - DataInner::TermSvg(text) => { - if let Some(pattern) = self.pattern.render() { - let lines = self.substitutions.normalize(&text, &pattern); - DataInner::TermSvg(lines) - } else { - DataInner::TermSvg(text) - } - } - }; - Data { inner, source } + crate::filter::NormalizePaths.normalize(data) } } -#[cfg(feature = "structured-data")] -fn normalize_value(value: &mut serde_json::Value, op: fn(&str) -> String) { - match value { - serde_json::Value::String(str) => { - *str = op(str); - } - serde_json::Value::Array(arr) => { - for value in arr.iter_mut() { - normalize_value(value, op) - } - } - serde_json::Value::Object(obj) => { - for (_, value) in obj.iter_mut() { - normalize_value(value, op) - } - } - _ => {} - } -} - -#[cfg(feature = "structured-data")] -fn normalize_value_matches( - actual: &mut serde_json::Value, - expected: &serde_json::Value, - substitutions: &crate::Substitutions, -) { - use serde_json::Value::*; - - const VALUE_WILDCARD: &str = "{...}"; - - match (actual, expected) { - (act, String(exp)) if exp == VALUE_WILDCARD => { - *act = serde_json::json!(VALUE_WILDCARD); - } - (String(act), String(exp)) => { - *act = substitutions.normalize(act, exp); - } - (Array(act), Array(exp)) => { - let mut sections = exp.split(|e| e == VALUE_WILDCARD).peekable(); - let mut processed = 0; - while let Some(expected_subset) = sections.next() { - // Process all values in the current section - if !expected_subset.is_empty() { - let actual_subset = &mut act[processed..processed + expected_subset.len()]; - for (a, e) in actual_subset.iter_mut().zip(expected_subset) { - normalize_value_matches(a, e, substitutions); - } - processed += expected_subset.len(); - } - - if let Some(next_section) = sections.peek() { - // If the next section has nothing in it, replace from processed to end with - // a single "{...}" - if next_section.is_empty() { - act.splice(processed.., vec![String(VALUE_WILDCARD.to_owned())]); - processed += 1; - } else { - let first = next_section.first().unwrap(); - // Replace everything up until the value we are looking for with - // a single "{...}". - if let Some(index) = act.iter().position(|v| v == first) { - act.splice(processed..index, vec![String(VALUE_WILDCARD.to_owned())]); - processed += 1; - } else { - // If we cannot find the value we are looking for return early - break; - } - } - } - } - } - (Object(act), Object(exp)) => { - for (a, e) in act.iter_mut().zip(exp).filter(|(a, e)| a.0 == e.0) { - normalize_value_matches(a.1, e.1, substitutions) - } - } - (_, _) => {} - } -} +#[deprecated(since = "0.5.11", note = "Replaced with `filter::NormalizeMatches")] +pub type NormalizeMatches<'a> = crate::filter::NormalizeMatches<'a>; diff --git a/crates/snapbox/src/data/tests.rs b/crates/snapbox/src/data/tests.rs index 552ed012..79139bf3 100644 --- a/crates/snapbox/src/data/tests.rs +++ b/crates/snapbox/src/data/tests.rs @@ -215,352 +215,6 @@ fn json_to_text_coerce_equals_render() { assert_eq!(Data::text(d.render().unwrap()), text); } -// Tests for normalization on json -#[test] -#[cfg(feature = "json")] -fn json_normalize_paths_and_lines() { - let json = json!({"name": "John\\Doe\r\n"}); - let data = Data::json(json); - let data = NormalizePaths.normalize(data); - assert_eq!(Data::json(json!({"name": "John/Doe\r\n"})), data); - let data = NormalizeNewlines.normalize(data); - assert_eq!(Data::json(json!({"name": "John/Doe\n"})), data); -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_obj_paths_and_lines() { - let json = json!({ - "person": { - "name": "John\\Doe\r\n", - "nickname": "Jo\\hn\r\n", - } - }); - let data = Data::json(json); - let data = NormalizePaths.normalize(data); - let assert = json!({ - "person": { - "name": "John/Doe\r\n", - "nickname": "Jo/hn\r\n", - } - }); - assert_eq!(Data::json(assert), data); - let data = NormalizeNewlines.normalize(data); - let assert = json!({ - "person": { - "name": "John/Doe\n", - "nickname": "Jo/hn\n", - } - }); - assert_eq!(Data::json(assert), data); -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_array_paths_and_lines() { - let json = json!({"people": ["John\\Doe\r\n", "Jo\\hn\r\n"]}); - let data = Data::json(json); - let data = NormalizePaths.normalize(data); - let paths = json!({"people": ["John/Doe\r\n", "Jo/hn\r\n"]}); - assert_eq!(Data::json(paths), data); - let data = NormalizeNewlines.normalize(data); - let new_lines = json!({"people": ["John/Doe\n", "Jo/hn\n"]}); - assert_eq!(Data::json(new_lines), data); -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_array_obj_paths_and_lines() { - let json = json!({ - "people": [ - { - "name": "John\\Doe\r\n", - "nickname": "Jo\\hn\r\n", - } - ] - }); - let data = Data::json(json); - let data = NormalizePaths.normalize(data); - let paths = json!({ - "people": [ - { - "name": "John/Doe\r\n", - "nickname": "Jo/hn\r\n", - } - ] - }); - assert_eq!(Data::json(paths), data); - let data = NormalizeNewlines.normalize(data); - let new_lines = json!({ - "people": [ - { - "name": "John/Doe\n", - "nickname": "Jo/hn\n", - } - ] - }); - assert_eq!(Data::json(new_lines), data); -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_matches_string() { - let exp = json!({"name": "{...}"}); - let expected = Data::json(exp); - let actual = json!({"name": "JohnDoe"}); - let actual = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); - if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { - assert_eq!(exp, act); - } -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_matches_array() { - let exp = json!({"people": "{...}"}); - let expected = Data::json(exp); - let actual = json!({ - "people": [ - { - "name": "JohnDoe", - "nickname": "John", - } - ] - }); - let actual = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); - if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { - assert_eq!(exp, act); - } -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_matches_obj() { - let exp = json!({"people": "{...}"}); - let expected = Data::json(exp); - let actual = json!({ - "people": { - "name": "JohnDoe", - "nickname": "John", - } - }); - let actual = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); - if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { - assert_eq!(exp, act); - } -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_matches_diff_order_array() { - let exp = json!({ - "people": ["John", "Jane"] - }); - let expected = Data::json(exp); - let actual = json!({ - "people": ["Jane", "John"] - }); - let actual = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); - if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { - assert_ne!(exp, act); - } -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_wildcard_object_first() { - let exp = json!({ - "people": [ - "{...}", - { - "name": "three", - "nickname": "3", - } - ] - }); - let expected = Data::json(exp); - let actual = json!({ - "people": [ - { - "name": "one", - "nickname": "1", - }, - { - "name": "two", - "nickname": "2", - }, - { - "name": "three", - "nickname": "3", - } - ] - }); - let actual = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); - if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { - assert_eq!(exp, act); - } -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_wildcard_array_first() { - let exp = json!([ - "{...}", - { - "name": "three", - "nickname": "3", - } - ]); - let expected = Data::json(exp); - let actual = json!([ - { - "name": "one", - "nickname": "1", - }, - { - "name": "two", - "nickname": "2", - }, - { - "name": "three", - "nickname": "3", - } - ]); - let actual = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); - if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { - assert_eq!(exp, act); - } -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_wildcard_array_first_last() { - let exp = json!([ - "{...}", - { - "name": "two", - "nickname": "2", - }, - "{...}" - ]); - let expected = Data::json(exp); - let actual = json!([ - { - "name": "one", - "nickname": "1", - }, - { - "name": "two", - "nickname": "2", - }, - { - "name": "three", - "nickname": "3", - }, - { - "name": "four", - "nickname": "4", - } - ]); - let actual = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); - if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { - assert_eq!(exp, act); - } -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_wildcard_array_middle_last() { - let exp = json!([ - { - "name": "one", - "nickname": "1", - }, - "{...}", - { - "name": "three", - "nickname": "3", - }, - "{...}" - ]); - let expected = Data::json(exp); - let actual = json!([ - { - "name": "one", - "nickname": "1", - }, - { - "name": "two", - "nickname": "2", - }, - { - "name": "three", - "nickname": "3", - }, - { - "name": "four", - "nickname": "4", - }, - { - "name": "five", - "nickname": "5", - } - ]); - let actual = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); - if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { - assert_eq!(exp, act); - } -} - -#[test] -#[cfg(feature = "json")] -fn json_normalize_wildcard_array_middle_last_early_return() { - let exp = json!([ - { - "name": "one", - "nickname": "1", - }, - "{...}", - { - "name": "three", - "nickname": "3", - }, - "{...}" - ]); - let expected = Data::json(exp); - let actual = json!([ - { - "name": "one", - "nickname": "1", - }, - { - "name": "two", - "nickname": "2", - }, - { - "name": "four", - "nickname": "4", - }, - { - "name": "five", - "nickname": "5", - } - ]); - let actual_normalized = - NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual.clone())); - if let DataInner::Json(act) = actual_normalized.inner { - assert_eq!(act, actual); - } -} - #[cfg(feature = "term-svg")] mod text_elem { use super::super::*; diff --git a/crates/snapbox/src/filter/mod.rs b/crates/snapbox/src/filter/mod.rs new file mode 100644 index 00000000..7b46ede9 --- /dev/null +++ b/crates/snapbox/src/filter/mod.rs @@ -0,0 +1,199 @@ +//! Normalize `actual` or `expected` [`Data`] +//! +//! This can be done for +//! - Making snapshots consistent across platforms or conditional compilation +//! - Focusing snapshots on the characteristics of the data being tested + +#[cfg(test)] +mod test; + +use crate::data::DataInner; +use crate::Data; + +pub trait Normalize { + fn normalize(&self, data: Data) -> Data; +} + +pub struct NormalizeNewlines; +impl Normalize for NormalizeNewlines { + fn normalize(&self, data: Data) -> Data { + let source = data.source; + let inner = match data.inner { + DataInner::Error(err) => DataInner::Error(err), + DataInner::Binary(bin) => DataInner::Binary(bin), + DataInner::Text(text) => { + let lines = crate::utils::normalize_lines(&text); + DataInner::Text(lines) + } + #[cfg(feature = "json")] + DataInner::Json(value) => { + let mut value = value; + normalize_value(&mut value, crate::utils::normalize_lines); + DataInner::Json(value) + } + #[cfg(feature = "term-svg")] + DataInner::TermSvg(text) => { + let lines = crate::utils::normalize_lines(&text); + DataInner::TermSvg(lines) + } + }; + Data { inner, source } + } +} + +pub struct NormalizePaths; +impl Normalize for NormalizePaths { + fn normalize(&self, data: Data) -> Data { + let source = data.source; + let inner = match data.inner { + DataInner::Error(err) => DataInner::Error(err), + DataInner::Binary(bin) => DataInner::Binary(bin), + DataInner::Text(text) => { + let lines = crate::utils::normalize_paths(&text); + DataInner::Text(lines) + } + #[cfg(feature = "json")] + DataInner::Json(value) => { + let mut value = value; + normalize_value(&mut value, crate::utils::normalize_paths); + DataInner::Json(value) + } + #[cfg(feature = "term-svg")] + DataInner::TermSvg(text) => { + let lines = crate::utils::normalize_paths(&text); + DataInner::TermSvg(lines) + } + }; + Data { inner, source } + } +} + +pub struct NormalizeMatches<'a> { + substitutions: &'a crate::Substitutions, + pattern: &'a Data, +} + +impl<'a> NormalizeMatches<'a> { + pub fn new(substitutions: &'a crate::Substitutions, pattern: &'a Data) -> Self { + NormalizeMatches { + substitutions, + pattern, + } + } +} + +impl Normalize for NormalizeMatches<'_> { + fn normalize(&self, data: Data) -> Data { + let source = data.source; + let inner = match data.inner { + DataInner::Error(err) => DataInner::Error(err), + DataInner::Binary(bin) => DataInner::Binary(bin), + DataInner::Text(text) => { + if let Some(pattern) = self.pattern.render() { + let lines = self.substitutions.normalize(&text, &pattern); + DataInner::Text(lines) + } else { + DataInner::Text(text) + } + } + #[cfg(feature = "json")] + DataInner::Json(value) => { + let mut value = value; + if let DataInner::Json(exp) = &self.pattern.inner { + normalize_value_matches(&mut value, exp, self.substitutions); + } + DataInner::Json(value) + } + #[cfg(feature = "term-svg")] + DataInner::TermSvg(text) => { + if let Some(pattern) = self.pattern.render() { + let lines = self.substitutions.normalize(&text, &pattern); + DataInner::TermSvg(lines) + } else { + DataInner::TermSvg(text) + } + } + }; + Data { inner, source } + } +} + +#[cfg(feature = "structured-data")] +fn normalize_value(value: &mut serde_json::Value, op: fn(&str) -> String) { + match value { + serde_json::Value::String(str) => { + *str = op(str); + } + serde_json::Value::Array(arr) => { + for value in arr.iter_mut() { + normalize_value(value, op) + } + } + serde_json::Value::Object(obj) => { + for (_, value) in obj.iter_mut() { + normalize_value(value, op) + } + } + _ => {} + } +} + +#[cfg(feature = "structured-data")] +fn normalize_value_matches( + actual: &mut serde_json::Value, + expected: &serde_json::Value, + substitutions: &crate::Substitutions, +) { + use serde_json::Value::*; + + const VALUE_WILDCARD: &str = "{...}"; + + match (actual, expected) { + (act, String(exp)) if exp == VALUE_WILDCARD => { + *act = serde_json::json!(VALUE_WILDCARD); + } + (String(act), String(exp)) => { + *act = substitutions.normalize(act, exp); + } + (Array(act), Array(exp)) => { + let mut sections = exp.split(|e| e == VALUE_WILDCARD).peekable(); + let mut processed = 0; + while let Some(expected_subset) = sections.next() { + // Process all values in the current section + if !expected_subset.is_empty() { + let actual_subset = &mut act[processed..processed + expected_subset.len()]; + for (a, e) in actual_subset.iter_mut().zip(expected_subset) { + normalize_value_matches(a, e, substitutions); + } + processed += expected_subset.len(); + } + + if let Some(next_section) = sections.peek() { + // If the next section has nothing in it, replace from processed to end with + // a single "{...}" + if next_section.is_empty() { + act.splice(processed.., vec![String(VALUE_WILDCARD.to_owned())]); + processed += 1; + } else { + let first = next_section.first().unwrap(); + // Replace everything up until the value we are looking for with + // a single "{...}". + if let Some(index) = act.iter().position(|v| v == first) { + act.splice(processed..index, vec![String(VALUE_WILDCARD.to_owned())]); + processed += 1; + } else { + // If we cannot find the value we are looking for return early + break; + } + } + } + } + } + (Object(act), Object(exp)) => { + for (a, e) in act.iter_mut().zip(exp).filter(|(a, e)| a.0 == e.0) { + normalize_value_matches(a.1, e.1, substitutions) + } + } + (_, _) => {} + } +} diff --git a/crates/snapbox/src/filter/test.rs b/crates/snapbox/src/filter/test.rs new file mode 100644 index 00000000..7f3e474c --- /dev/null +++ b/crates/snapbox/src/filter/test.rs @@ -0,0 +1,351 @@ +#[cfg(feature = "json")] +use serde_json::json; + +#[cfg(feature = "json")] +use super::*; + +// Tests for normalization on json +#[test] +#[cfg(feature = "json")] +fn json_normalize_paths_and_lines() { + let json = json!({"name": "John\\Doe\r\n"}); + let data = Data::json(json); + let data = NormalizePaths.normalize(data); + assert_eq!(Data::json(json!({"name": "John/Doe\r\n"})), data); + let data = NormalizeNewlines.normalize(data); + assert_eq!(Data::json(json!({"name": "John/Doe\n"})), data); +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_obj_paths_and_lines() { + let json = json!({ + "person": { + "name": "John\\Doe\r\n", + "nickname": "Jo\\hn\r\n", + } + }); + let data = Data::json(json); + let data = NormalizePaths.normalize(data); + let assert = json!({ + "person": { + "name": "John/Doe\r\n", + "nickname": "Jo/hn\r\n", + } + }); + assert_eq!(Data::json(assert), data); + let data = NormalizeNewlines.normalize(data); + let assert = json!({ + "person": { + "name": "John/Doe\n", + "nickname": "Jo/hn\n", + } + }); + assert_eq!(Data::json(assert), data); +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_array_paths_and_lines() { + let json = json!({"people": ["John\\Doe\r\n", "Jo\\hn\r\n"]}); + let data = Data::json(json); + let data = NormalizePaths.normalize(data); + let paths = json!({"people": ["John/Doe\r\n", "Jo/hn\r\n"]}); + assert_eq!(Data::json(paths), data); + let data = NormalizeNewlines.normalize(data); + let new_lines = json!({"people": ["John/Doe\n", "Jo/hn\n"]}); + assert_eq!(Data::json(new_lines), data); +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_array_obj_paths_and_lines() { + let json = json!({ + "people": [ + { + "name": "John\\Doe\r\n", + "nickname": "Jo\\hn\r\n", + } + ] + }); + let data = Data::json(json); + let data = NormalizePaths.normalize(data); + let paths = json!({ + "people": [ + { + "name": "John/Doe\r\n", + "nickname": "Jo/hn\r\n", + } + ] + }); + assert_eq!(Data::json(paths), data); + let data = NormalizeNewlines.normalize(data); + let new_lines = json!({ + "people": [ + { + "name": "John/Doe\n", + "nickname": "Jo/hn\n", + } + ] + }); + assert_eq!(Data::json(new_lines), data); +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_matches_string() { + let exp = json!({"name": "{...}"}); + let expected = Data::json(exp); + let actual = json!({"name": "JohnDoe"}); + let actual = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); + if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { + assert_eq!(exp, act); + } +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_matches_array() { + let exp = json!({"people": "{...}"}); + let expected = Data::json(exp); + let actual = json!({ + "people": [ + { + "name": "JohnDoe", + "nickname": "John", + } + ] + }); + let actual = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); + if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { + assert_eq!(exp, act); + } +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_matches_obj() { + let exp = json!({"people": "{...}"}); + let expected = Data::json(exp); + let actual = json!({ + "people": { + "name": "JohnDoe", + "nickname": "John", + } + }); + let actual = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); + if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { + assert_eq!(exp, act); + } +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_matches_diff_order_array() { + let exp = json!({ + "people": ["John", "Jane"] + }); + let expected = Data::json(exp); + let actual = json!({ + "people": ["Jane", "John"] + }); + let actual = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); + if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { + assert_ne!(exp, act); + } +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_wildcard_object_first() { + let exp = json!({ + "people": [ + "{...}", + { + "name": "three", + "nickname": "3", + } + ] + }); + let expected = Data::json(exp); + let actual = json!({ + "people": [ + { + "name": "one", + "nickname": "1", + }, + { + "name": "two", + "nickname": "2", + }, + { + "name": "three", + "nickname": "3", + } + ] + }); + let actual = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); + if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { + assert_eq!(exp, act); + } +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_wildcard_array_first() { + let exp = json!([ + "{...}", + { + "name": "three", + "nickname": "3", + } + ]); + let expected = Data::json(exp); + let actual = json!([ + { + "name": "one", + "nickname": "1", + }, + { + "name": "two", + "nickname": "2", + }, + { + "name": "three", + "nickname": "3", + } + ]); + let actual = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); + if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { + assert_eq!(exp, act); + } +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_wildcard_array_first_last() { + let exp = json!([ + "{...}", + { + "name": "two", + "nickname": "2", + }, + "{...}" + ]); + let expected = Data::json(exp); + let actual = json!([ + { + "name": "one", + "nickname": "1", + }, + { + "name": "two", + "nickname": "2", + }, + { + "name": "three", + "nickname": "3", + }, + { + "name": "four", + "nickname": "4", + } + ]); + let actual = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); + if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { + assert_eq!(exp, act); + } +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_wildcard_array_middle_last() { + let exp = json!([ + { + "name": "one", + "nickname": "1", + }, + "{...}", + { + "name": "three", + "nickname": "3", + }, + "{...}" + ]); + let expected = Data::json(exp); + let actual = json!([ + { + "name": "one", + "nickname": "1", + }, + { + "name": "two", + "nickname": "2", + }, + { + "name": "three", + "nickname": "3", + }, + { + "name": "four", + "nickname": "4", + }, + { + "name": "five", + "nickname": "5", + } + ]); + let actual = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual)); + if let (DataInner::Json(exp), DataInner::Json(act)) = (expected.inner, actual.inner) { + assert_eq!(exp, act); + } +} + +#[test] +#[cfg(feature = "json")] +fn json_normalize_wildcard_array_middle_last_early_return() { + let exp = json!([ + { + "name": "one", + "nickname": "1", + }, + "{...}", + { + "name": "three", + "nickname": "3", + }, + "{...}" + ]); + let expected = Data::json(exp); + let actual = json!([ + { + "name": "one", + "nickname": "1", + }, + { + "name": "two", + "nickname": "2", + }, + { + "name": "four", + "nickname": "4", + }, + { + "name": "five", + "nickname": "5", + } + ]); + let actual_normalized = + NormalizeMatches::new(&Default::default(), &expected).normalize(Data::json(actual.clone())); + if let DataInner::Json(act) = actual_normalized.inner { + assert_eq!(act, actual); + } +} diff --git a/crates/snapbox/src/lib.rs b/crates/snapbox/src/lib.rs index ce9fb75b..87066a09 100644 --- a/crates/snapbox/src/lib.rs +++ b/crates/snapbox/src/lib.rs @@ -103,6 +103,7 @@ mod substitutions; pub mod cmd; pub mod data; +pub mod filter; pub mod path; pub mod report; pub mod utils;