Skip to content

Commit 792209f

Browse files
test: cover uncovered branches in remapping remap function
Add remap_no_upstream_mapping_no_name and remap_upstream_found_no_name tests to cover the unnamed mapping branches. Modify remap_single_level and remap_generated_only_passthrough to include a second source that triggers the loader's None branch.
1 parent a77b4fc commit 792209f

1 file changed

Lines changed: 229 additions & 3 deletions

File tree

crates/remapping/src/lib.rs

Lines changed: 229 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,12 @@ mod tests {
437437

438438
#[test]
439439
fn remap_single_level() {
440-
// outer: output.js → intermediate.js
440+
// outer: output.js → intermediate.js + other.js (second source has no upstream)
441+
// AAAA maps gen(0,0) → intermediate.js(0,0)
442+
// KCAA maps gen(0,5) → other.js(0,0) (source delta +1)
443+
// ;ADCA maps gen(1,0) → intermediate.js(1,0) (source delta -1, line delta +1)
441444
let outer = SourceMap::from_json(
442-
r#"{"version":3,"sources":["intermediate.js"],"names":[],"mappings":"AAAA;AACA"}"#,
445+
r#"{"version":3,"sources":["intermediate.js","other.js"],"names":[],"mappings":"AAAA,KCAA;ADCA"}"#,
443446
)
444447
.unwrap();
445448

@@ -457,7 +460,9 @@ mod tests {
457460
}
458461
});
459462

460-
assert_eq!(result.sources, vec!["original.js"]);
463+
assert!(result.sources.contains(&"original.js".to_string()));
464+
// other.js passes through since loader returns None
465+
assert!(result.sources.contains(&"other.js".to_string()));
461466

462467
// Line 0 col 0 in outer → line 0 col 0 in intermediate → line 1 col 0 in original
463468
let loc = result.original_position_for(0, 0).unwrap();
@@ -569,4 +574,225 @@ mod tests {
569574
}
570575

571576
// ── Clone needed for SourceMap in tests ──────────────────────
577+
578+
#[test]
579+
fn concat_updates_source_content_on_duplicate() {
580+
// First map has no sourcesContent, second has it for same source
581+
let a = SourceMap::from_json(
582+
r#"{"version":3,"sources":["shared.js"],"names":[],"mappings":"AAAA"}"#,
583+
)
584+
.unwrap();
585+
let b = SourceMap::from_json(
586+
r#"{"version":3,"sources":["shared.js"],"sourcesContent":["var x = 1;"],"names":[],"mappings":"AAAA"}"#,
587+
)
588+
.unwrap();
589+
590+
let mut builder = ConcatBuilder::new(None);
591+
builder.add_map(&a, 0);
592+
builder.add_map(&b, 1);
593+
594+
let result = builder.build();
595+
assert_eq!(result.sources.len(), 1);
596+
assert_eq!(
597+
result.sources_content,
598+
vec![Some("var x = 1;".to_string())]
599+
);
600+
}
601+
602+
#[test]
603+
fn concat_deduplicates_names() {
604+
let a = SourceMap::from_json(
605+
r#"{"version":3,"sources":["a.js"],"names":["sharedName"],"mappings":"AAAAA"}"#,
606+
)
607+
.unwrap();
608+
let b = SourceMap::from_json(
609+
r#"{"version":3,"sources":["b.js"],"names":["sharedName"],"mappings":"AAAAA"}"#,
610+
)
611+
.unwrap();
612+
613+
let mut builder = ConcatBuilder::new(None);
614+
builder.add_map(&a, 0);
615+
builder.add_map(&b, 1);
616+
617+
let result = builder.build();
618+
// Names should be deduplicated
619+
assert_eq!(result.names.len(), 1);
620+
assert_eq!(result.names[0], "sharedName");
621+
}
622+
623+
#[test]
624+
fn concat_with_ignore_list() {
625+
let a = SourceMap::from_json(
626+
r#"{"version":3,"sources":["vendor.js"],"names":[],"mappings":"AAAA","ignoreList":[0]}"#,
627+
)
628+
.unwrap();
629+
630+
let mut builder = ConcatBuilder::new(None);
631+
builder.add_map(&a, 0);
632+
633+
let result = builder.build();
634+
assert_eq!(result.ignore_list, vec![0]);
635+
}
636+
637+
#[test]
638+
fn concat_with_generated_only_mappings() {
639+
// Map with a generated-only segment (1-field segment, no source info)
640+
let a = SourceMap::from_json(
641+
r#"{"version":3,"sources":["a.js"],"names":[],"mappings":"A,AAAA"}"#,
642+
)
643+
.unwrap();
644+
645+
let mut builder = ConcatBuilder::new(None);
646+
builder.add_map(&a, 0);
647+
648+
let result = builder.build();
649+
// Should have both mappings, including the generated-only one
650+
assert!(result.mapping_count() >= 1);
651+
}
652+
653+
#[test]
654+
fn remap_generated_only_passthrough() {
655+
// Outer map with a generated-only segment and two sources (second has no upstream)
656+
// A = generated-only segment at col 0
657+
// ,AAAA = gen(0,4)→a.js(0,0)
658+
// ,KCAA = gen(0,9)→other.js(0,0) (source delta +1)
659+
let outer = SourceMap::from_json(
660+
r#"{"version":3,"sources":["a.js","other.js"],"names":[],"mappings":"A,AAAA,KCAA"}"#,
661+
)
662+
.unwrap();
663+
664+
let inner = SourceMap::from_json(
665+
r#"{"version":3,"sources":["original.js"],"names":[],"mappings":"AAAA"}"#,
666+
)
667+
.unwrap();
668+
669+
let result = remap(&outer, |source| {
670+
if source == "a.js" {
671+
Some(inner.clone())
672+
} else {
673+
None
674+
}
675+
});
676+
677+
// Result should have mappings for the generated-only, remapped, and passthrough
678+
assert!(result.mapping_count() >= 2);
679+
assert!(result.sources.contains(&"original.js".to_string()));
680+
assert!(result.sources.contains(&"other.js".to_string()));
681+
}
682+
683+
#[test]
684+
fn remap_no_upstream_mapping_with_name() {
685+
// Outer has named mapping but upstream lookup finds no match at that position
686+
let outer = SourceMap::from_json(
687+
r#"{"version":3,"sources":["compiled.js"],"names":["myFunc"],"mappings":"AAAAA"}"#,
688+
)
689+
.unwrap();
690+
691+
// Inner map maps different position (line 5, not line 0)
692+
let inner = SourceMap::from_json(
693+
r#"{"version":3,"sources":["original.ts"],"names":[],"mappings":";;;;AAAA"}"#,
694+
)
695+
.unwrap();
696+
697+
let result = remap(&outer, |_| Some(inner.clone()));
698+
699+
// The outer mapping at (0,0) maps to (0,0) in compiled.js
700+
// Inner doesn't have a mapping at (0,0), so it falls through
701+
// The name from outer should be preserved
702+
let loc = result.original_position_for(0, 0).unwrap();
703+
assert!(loc.name.is_some());
704+
assert_eq!(result.name(loc.name.unwrap()), "myFunc");
705+
}
706+
707+
#[test]
708+
fn remap_no_upstream_with_sources_content_and_name() {
709+
let outer = SourceMap::from_json(
710+
r#"{"version":3,"sources":["a.js"],"sourcesContent":["var a;"],"names":["fn1"],"mappings":"AAAAA"}"#,
711+
)
712+
.unwrap();
713+
714+
// No upstream — everything passes through
715+
let result = remap(&outer, |_| None);
716+
717+
assert_eq!(result.sources, vec!["a.js"]);
718+
assert_eq!(
719+
result.sources_content,
720+
vec![Some("var a;".to_string())]
721+
);
722+
let loc = result.original_position_for(0, 0).unwrap();
723+
assert!(loc.name.is_some());
724+
assert_eq!(result.name(loc.name.unwrap()), "fn1");
725+
}
726+
727+
#[test]
728+
fn remap_no_upstream_no_name() {
729+
let outer = SourceMap::from_json(
730+
r#"{"version":3,"sources":["a.js"],"sourcesContent":["var a;"],"names":[],"mappings":"AAAA"}"#,
731+
)
732+
.unwrap();
733+
734+
let result = remap(&outer, |_| None);
735+
let loc = result.original_position_for(0, 0).unwrap();
736+
assert!(loc.name.is_none());
737+
}
738+
739+
#[test]
740+
fn remap_no_upstream_mapping_no_name() {
741+
// Outer has a mapping with NO name pointing to compiled.js
742+
// AAAA = gen(0,0) → compiled.js(0,0), no name (4-field segment)
743+
let outer = SourceMap::from_json(
744+
r#"{"version":3,"sources":["compiled.js"],"names":[],"mappings":"AAAA"}"#,
745+
)
746+
.unwrap();
747+
748+
// Inner map only has mappings at line 5, not at line 0
749+
// So original_position_for(0, 0) returns None → takes the None branch
750+
// Since the outer mapping has no name, this hits the else at lines 268-272
751+
let inner = SourceMap::from_json(
752+
r#"{"version":3,"sources":["original.ts"],"names":[],"mappings":";;;;AAAA"}"#,
753+
)
754+
.unwrap();
755+
756+
let result = remap(&outer, |_| Some(inner.clone()));
757+
758+
// Falls through to the None branch (no upstream match at position)
759+
// Since outer has no name, the mapping is added without a name
760+
let loc = result.original_position_for(0, 0).unwrap();
761+
assert_eq!(result.source(loc.source), "compiled.js");
762+
assert_eq!(loc.line, 0);
763+
assert_eq!(loc.column, 0);
764+
assert!(loc.name.is_none());
765+
}
766+
767+
#[test]
768+
fn remap_upstream_found_no_name() {
769+
// Outer has a named mapping, but upstream has NO name
770+
// The upstream mapping is found but has no name_index
771+
// Since upstream has no name, the name resolution falls to the outer name
772+
// This is already covered by remap_preserves_names
773+
//
774+
// What we need instead: outer has NO name AND upstream has NO name
775+
// → name_idx is None → hits the add_mapping branch (line 246-252)
776+
let outer = SourceMap::from_json(
777+
r#"{"version":3,"sources":["intermediate.js"],"names":[],"mappings":"AAAA"}"#,
778+
)
779+
.unwrap();
780+
781+
// Inner maps intermediate.js(0,0) → original.js(0,0) with NO name
782+
let inner = SourceMap::from_json(
783+
r#"{"version":3,"sources":["original.js"],"names":[],"mappings":"AAAA"}"#,
784+
)
785+
.unwrap();
786+
787+
let result = remap(&outer, |_| Some(inner.clone()));
788+
789+
assert_eq!(result.sources, vec!["original.js"]);
790+
let loc = result.original_position_for(0, 0).unwrap();
791+
assert_eq!(result.source(loc.source), "original.js");
792+
assert_eq!(loc.line, 0);
793+
assert_eq!(loc.column, 0);
794+
// Neither outer nor upstream has a name, so result has no name
795+
assert!(loc.name.is_none());
796+
assert!(result.names.is_empty());
797+
}
572798
}

0 commit comments

Comments
 (0)