Skip to content

Commit 04a98c3

Browse files
authoredFeb 13, 2025
Fix inline snapshot handling within allow_duplicates! block (#722)
#712 There are two problems: 1. single-line snapshot isn't deduplicated 2. the last assertion in the `allow_duplicates!` block is always chosen ```rust #[test] fn test_dup() { for _ in 0..2 { insta::allow_duplicates! { // shouldn't be updated twice insta::assert_snapshot!("foo", @""); } } } ``` ```rust #[test] fn test_bad_location() { for _ in 0..2 { insta::allow_duplicates! { // each snapshot should be updated accordingly insta::assert_snapshot!("1", @" 1a 1b "); insta::assert_snapshot!("2", @" 2a 2b "); } } } ```
1 parent 24f32aa commit 04a98c3

File tree

3 files changed

+201
-1
lines changed

3 files changed

+201
-1
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ All notable changes to insta and cargo-insta are documented here.
1111
## 1.42.2
1212

1313
- Stop `\t` and `\x1b` (ANSI color escape) from causing snapshots to be escaped. #715
14+
- Improved handling of inline snapshots within `allow_duplicates! { .. }`. #712
1415

1516
## 1.42.1
1617

‎cargo-insta/src/inline.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ impl FilePatcher {
7676
if self
7777
.inline_snapshots
7878
.last()
79-
.map_or(false, |x| x.end.0 > line)
79+
// x.end.0 is 0-origin whereas line is 1-origin
80+
.map_or(false, |x| x.end.0 >= line - 1)
8081
{
8182
return false;
8283
}
@@ -253,6 +254,18 @@ impl FilePatcher {
253254
return;
254255
}
255256

257+
// recurse into block-like macro such as allow_duplicates! { .. }
258+
if matches!(i.delimiter, syn::MacroDelimiter::Brace(_)) {
259+
if let Ok(stmts) = i.parse_body_with(syn::Block::parse_within) {
260+
for stmt in &stmts {
261+
self.visit_stmt(stmt);
262+
}
263+
return;
264+
}
265+
// TODO: perhaps, we can return here and remove fallback to
266+
// self.scan_nested_macros(&tokens)
267+
}
268+
256269
let indentation = indentation(span_start, self.2);
257270
if !self.try_extract_snapshot(&tokens, indentation) {
258271
// if we can't extract a snapshot here we want to scan for nested
@@ -357,4 +370,47 @@ fn test_function() {
357370
// Assert the indentation
358371
assert_debug_snapshot!(snapshot.indentation, @r#""\t""#);
359372
}
373+
374+
#[test]
375+
fn test_find_snapshot_macro_within_allow_duplicates() {
376+
let content = r######"
377+
fn test_function() {
378+
insta::allow_duplicates! {
379+
for x in 0..10 {
380+
insta::assert_snapshot!("foo", @"foo"); // 5
381+
insta::assert_snapshot!("bar", @"bar"); // 6
382+
}
383+
}
384+
}
385+
"######;
386+
387+
let file_patcher = FilePatcher {
388+
filename: PathBuf::new(),
389+
lines: content.lines().map(String::from).collect(),
390+
source: syn::parse_file(content).unwrap(),
391+
inline_snapshots: vec![],
392+
};
393+
394+
let snapshot5 = file_patcher.find_snapshot_macro(5).unwrap();
395+
let snapshot6 = file_patcher.find_snapshot_macro(6).unwrap();
396+
397+
// Extract the snapshot contents
398+
let snapshot_content5 = file_patcher.lines[snapshot5.start.0..=snapshot5.end.0].to_vec();
399+
let snapshot_content6 = file_patcher.lines[snapshot6.start.0..=snapshot6.end.0].to_vec();
400+
401+
assert_debug_snapshot!(snapshot_content5, @r#"
402+
[
403+
" insta::assert_snapshot!(\"foo\", @\"foo\"); // 5",
404+
]
405+
"#);
406+
assert_debug_snapshot!(snapshot_content6, @r#"
407+
[
408+
" insta::assert_snapshot!(\"bar\", @\"bar\"); // 6",
409+
]
410+
"#);
411+
412+
// Assert the indentation
413+
assert_debug_snapshot!(snapshot5.indentation, @r#"" ""#);
414+
assert_debug_snapshot!(snapshot6.indentation, @r#"" ""#);
415+
}
360416
}

‎cargo-insta/tests/functional/inline.rs

+143
Original file line numberDiff line numberDiff line change
@@ -337,3 +337,146 @@ fn test_hashtag_escape() {
337337
}
338338
"####);
339339
}
340+
341+
#[test]
342+
fn test_single_line_duplicates() {
343+
let test_project = TestFiles::new()
344+
.add_cargo_toml("test_single_line_duplicates")
345+
.add_file(
346+
"src/lib.rs",
347+
r#####"
348+
#[test]
349+
fn test_single_line_duplicates() {
350+
for _ in 0..2 {
351+
insta::allow_duplicates! {
352+
insta::assert_snapshot!("foo", @"");
353+
}
354+
}
355+
}
356+
"#####
357+
.to_string(),
358+
)
359+
.create_project();
360+
361+
let output = test_project
362+
.insta_cmd()
363+
.args(["test", "--accept"])
364+
.output()
365+
.unwrap();
366+
367+
assert!(&output.status.success());
368+
369+
// diff shouldn't be applied twice
370+
assert_snapshot!(test_project.diff("src/lib.rs"), @r#"
371+
--- Original: src/lib.rs
372+
+++ Updated: src/lib.rs
373+
@@ -3,7 +3,7 @@
374+
fn test_single_line_duplicates() {
375+
for _ in 0..2 {
376+
insta::allow_duplicates! {
377+
- insta::assert_snapshot!("foo", @"");
378+
+ insta::assert_snapshot!("foo", @"foo");
379+
}
380+
}
381+
}
382+
"#);
383+
}
384+
385+
#[test]
386+
fn test_single_line_assertions() {
387+
let test_project = TestFiles::new()
388+
.add_cargo_toml("test_single_line_assertions")
389+
.add_file(
390+
"src/lib.rs",
391+
r#####"
392+
#[test]
393+
fn test_single_line_assertions() {
394+
insta::assert_snapshot!("foo", @"");
395+
insta::assert_snapshot!("bar", @"");
396+
}
397+
"#####
398+
.to_string(),
399+
)
400+
.create_project();
401+
402+
let output = test_project
403+
.insta_cmd()
404+
.args(["test", "--accept"])
405+
.output()
406+
.unwrap();
407+
408+
assert!(&output.status.success());
409+
410+
// all adjacent lines should be updated
411+
assert_snapshot!(test_project.diff("src/lib.rs"), @r#"
412+
--- Original: src/lib.rs
413+
+++ Updated: src/lib.rs
414+
@@ -1,6 +1,6 @@
415+
416+
#[test]
417+
fn test_single_line_assertions() {
418+
- insta::assert_snapshot!("foo", @"");
419+
- insta::assert_snapshot!("bar", @"");
420+
+ insta::assert_snapshot!("foo", @"foo");
421+
+ insta::assert_snapshot!("bar", @"bar");
422+
}
423+
"#);
424+
}
425+
426+
#[test]
427+
fn test_multiple_assertions_within_allow_duplicates() {
428+
let test_project = TestFiles::new()
429+
.add_cargo_toml("test_multiple_assertions_within_allow_duplicates")
430+
.add_file(
431+
"src/lib.rs",
432+
r#####"
433+
#[test]
434+
fn test_multiple_assertions_within_allow_duplicates() {
435+
for _ in 0..2 {
436+
insta::allow_duplicates! {
437+
insta::assert_snapshot!("1", @"
438+
1a
439+
1b
440+
");
441+
insta::assert_snapshot!("2", @"
442+
2a
443+
2b
444+
");
445+
}
446+
}
447+
}
448+
"#####
449+
.to_string(),
450+
)
451+
.create_project();
452+
453+
let output = test_project
454+
.insta_cmd()
455+
.args(["test", "--accept"])
456+
.output()
457+
.unwrap();
458+
459+
assert!(&output.status.success());
460+
461+
assert_snapshot!(test_project.diff("src/lib.rs"), @r#"
462+
--- Original: src/lib.rs
463+
+++ Updated: src/lib.rs
464+
@@ -3,14 +3,8 @@
465+
fn test_multiple_assertions_within_allow_duplicates() {
466+
for _ in 0..2 {
467+
insta::allow_duplicates! {
468+
- insta::assert_snapshot!("1", @"
469+
- 1a
470+
- 1b
471+
- ");
472+
- insta::assert_snapshot!("2", @"
473+
- 2a
474+
- 2b
475+
- ");
476+
+ insta::assert_snapshot!("1", @"1");
477+
+ insta::assert_snapshot!("2", @"2");
478+
}
479+
}
480+
}
481+
"#);
482+
}

0 commit comments

Comments
 (0)