Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added compact JSON format #288

Merged
merged 2 commits into from
Sep 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
26 changes: 13 additions & 13 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ jobs:
- name: Test
run: make test

build-old-stable:
name: Check on 1.51.0
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: 1.51.0
profile: minimal
override: true
- name: Check
run: cargo check --no-default-features
## our dependencies no longer support this
# build-old-stable:
# name: Check on 1.51.0
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v1
# - uses: actions-rs/toolchain@v1
# with:
# toolchain: 1.51.0
# profile: minimal
# override: true
# - name: Check
# run: cargo check --no-default-features
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ license = "Apache-2.0"
authors = ["Armin Ronacher <armin.ronacher@active-4.com>"]
description = "A snapshot testing library for Rust"
edition = "2018"
rust-version = "1.51.0"
rust-version = "1.56.0"
homepage = "https://insta.rs/"
repository = "https://github.com/mitsuhiko/insta"
keywords = ["snapshot", "testing", "jest", "approval"]
Expand Down
70 changes: 53 additions & 17 deletions src/content/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use std::fmt::{Display, Write};

use crate::content::Content;

/// The maximum number of characters to print in a single line
/// when [`to_string_pretty`] is used.
const COMPACT_MAX_CHARS: usize = 120;

pub fn format_float<T: Display>(value: T) -> String {
let mut rv = format!("{}", value);
if !rv.contains('.') {
Expand All @@ -10,10 +14,17 @@ pub fn format_float<T: Display>(value: T) -> String {
rv
}

#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Format {
Condensed,
SingleLine,
Pretty,
}

/// Serializes a serializable to JSON.
pub struct Serializer {
out: String,
pretty: bool,
format: Format,
indentation: usize,
}

Expand All @@ -22,7 +33,7 @@ impl Serializer {
pub fn new() -> Serializer {
Serializer {
out: String::new(),
pretty: false,
format: Format::Condensed,
indentation: 0,
}
}
Expand All @@ -32,7 +43,7 @@ impl Serializer {
}

fn write_indentation(&mut self) {
if self.pretty {
if self.format == Format::Pretty {
write!(self.out, "{: ^1$}", "", self.indentation * 2).unwrap();
}
}
Expand All @@ -44,31 +55,40 @@ impl Serializer {

fn end_container(&mut self, c: char, empty: bool) {
self.indentation -= 1;
if self.pretty && !empty {
if self.format == Format::Pretty && !empty {
self.write_char('\n');
self.write_indentation();
}
self.write_char(c);
}

fn write_comma(&mut self, first: bool) {
if self.pretty {
if first {
self.write_char('\n');
} else {
self.write_str(",\n");
match self.format {
Format::Pretty => {
if first {
self.write_char('\n');
} else {
self.write_str(",\n");
}
self.write_indentation();
}
Format::Condensed => {
if !first {
self.write_char(',');
}
}
Format::SingleLine => {
if !first {
self.write_str(", ");
}
}
self.write_indentation();
} else if !first {
self.write_char(',');
}
}

fn write_colon(&mut self) {
if self.pretty {
self.write_str(": ");
} else {
self.write_char(':');
match self.format {
Format::Pretty | Format::SingleLine => self.write_str(": "),
Format::Condensed => self.write_char(':'),
}
}

Expand Down Expand Up @@ -277,11 +297,27 @@ pub fn to_string(value: &Content) -> String {
ser.into_result()
}

/// Serializes a value to JSON in single-line format.
pub fn to_string_compact(value: &Content) -> String {
let mut ser = Serializer::new();
ser.format = Format::SingleLine;
ser.serialize(value);
let rv = ser.into_result();
// this is pretty wasteful as we just format twice
// but it's acceptable for the way this is used in
// insta.
if rv.chars().count() > COMPACT_MAX_CHARS {
to_string_pretty(value)
} else {
rv
}
}

/// Serializes a value to JSON pretty
#[allow(unused)]
pub fn to_string_pretty(value: &Content) -> String {
let mut ser = Serializer::new();
ser.pretty = true;
ser.format = Format::Pretty;
ser.serialize(value);
ser.into_result()
}
Expand Down
49 changes: 47 additions & 2 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ macro_rules! assert_toml_snapshot {

/// Asserts a `Serialize` snapshot in YAML format.
///
/// **Feature:** `yaml` (to be disabled by default)
/// **Feature:** `yaml`
///
/// The value needs to implement the `serde::Serialize` trait and the snapshot
/// will be serialized in YAML format. This does mean that unlike the debug
Expand Down Expand Up @@ -216,7 +216,7 @@ macro_rules! assert_ron_snapshot {

/// Asserts a `Serialize` snapshot in JSON format.
///
/// **Feature:** `json` (to be disabled by default)
/// **Feature:** `json`
///
/// This works exactly like [`assert_yaml_snapshot!`] but serializes in JSON format.
/// This is normally not recommended because it makes diffs less reliable, but it can
Expand Down Expand Up @@ -258,6 +258,51 @@ macro_rules! assert_json_snapshot {
}};
}

/// Asserts a `Serialize` snapshot in compact JSON format.
///
/// **Feature:** `json`
///
/// This works exactly like [`assert_json_snapshot!`] but serializes into a single
/// line for as long as the output is less than 120 characters. This can be useful
/// in cases where you are working with small result outputs but comes at the cost
/// of slightly worse diffing behavior.
///
/// Example:
///
/// ```no_run
/// # use insta::*;
/// assert_compact_json_snapshot!(vec![1, 2, 3]);
/// ```
///
/// The third argument to the macro can be an object expression for redaction.
/// It's in the form `{ selector => replacement }`. For more information
/// about redactions refer to the [redactions feature in the guide](https://insta.rs/docs/redactions/).
///
/// The snapshot name is optional but can be provided as first argument.
#[cfg(feature = "json")]
#[cfg_attr(docsrs, doc(cfg(feature = "json")))]
#[macro_export]
macro_rules! assert_compact_json_snapshot {
($value:expr, @$snapshot:literal) => {{
$crate::_assert_serialized_snapshot!($value, JsonCompact, @$snapshot);
}};
($value:expr, {$($k:expr => $v:expr),*$(,)?}, @$snapshot:literal) => {{
$crate::_assert_serialized_snapshot!($value, {$($k => $v),*}, JsonCompact, @$snapshot);
}};
($value:expr, {$($k:expr => $v:expr),*$(,)?}) => {{
$crate::_assert_serialized_snapshot!($crate::_macro_support::AutoName, $value, {$($k => $v),*}, JsonCompact);
}};
($name:expr, $value:expr) => {{
$crate::_assert_serialized_snapshot!(Some($name), $value, JsonCompact);
}};
($name:expr, $value:expr, {$($k:expr => $v:expr),*$(,)?}) => {{
$crate::_assert_serialized_snapshot!(Some($name), $value, {$($k => $v),*}, JsonCompact);
}};
($value:expr) => {{
$crate::_assert_serialized_snapshot!($crate::_macro_support::AutoName, $value, JsonCompact);
}};
}

#[doc(hidden)]
#[macro_export]
macro_rules! _assert_serialized_snapshot {
Expand Down
2 changes: 2 additions & 0 deletions src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub enum SerializationFormat {
Toml,
Yaml,
Json,
JsonCompact,
}

pub enum SnapshotLocation {
Expand Down Expand Up @@ -47,6 +48,7 @@ pub fn serialize_content(
}
}
SerializationFormat::Json => json::to_string_pretty(&content),
SerializationFormat::JsonCompact => json::to_string_compact(&content),
#[cfg(feature = "csv")]
SerializationFormat::Csv => {
let mut buf = Vec::with_capacity(128);
Expand Down
47 changes: 45 additions & 2 deletions tests/test_inline.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#[cfg(feature = "csv")]
use insta::assert_csv_snapshot;
#[cfg(feature = "json")]
use insta::assert_json_snapshot;
#[cfg(feature = "ron")]
use insta::assert_ron_snapshot;
#[cfg(feature = "toml")]
use insta::assert_toml_snapshot;
#[cfg(feature = "yaml")]
use insta::assert_yaml_snapshot;
#[cfg(feature = "json")]
use insta::{assert_compact_json_snapshot, assert_json_snapshot};

use insta::{assert_debug_snapshot, assert_snapshot};
use std::thread;
Expand Down Expand Up @@ -235,3 +235,46 @@ fn test_multiline_with_empty_lines() {
# alternative
"###);
}

#[cfg(feature = "json")]
#[test]
fn test_compact_json() {
assert_compact_json_snapshot!((1..30).collect::<Vec<_>>(), @"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]");
assert_compact_json_snapshot!((1..34).collect::<Vec<_>>(), @r###"
[
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33
]
"###);
}