@@ -775,6 +775,49 @@ mod tests {
775775 rewrite_creative_html, rewrite_srcset, rewrite_style_urls, sanitize_creative_html, to_abs,
776776 } ;
777777
778+ fn rewrite_srcset_attr ( attr_name : & str , attr_value : & str ) -> String {
779+ let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
780+ let html = format ! ( r#"<img {}="{}">"# , attr_name, attr_value) ;
781+ rewrite_creative_html ( & settings, & html)
782+ }
783+
784+ fn assert_rewritten_srcset_attr_case (
785+ case_name : & str ,
786+ attr_name : & str ,
787+ attr_value : & str ,
788+ expected_relative : & str ,
789+ expected_descriptors : & [ & str ] ,
790+ ) {
791+ let out = rewrite_srcset_attr ( attr_name, attr_value) ;
792+
793+ assert ! (
794+ out. matches( "/first-party/proxy?tsurl=" ) . count( ) >= 2 ,
795+ "case `{}` expected two rewritten {} candidates: {}" ,
796+ case_name,
797+ attr_name,
798+ out
799+ ) ;
800+ assert ! (
801+ out. contains( expected_relative) ,
802+ "case `{}` expected relative {} candidate `{}` to be preserved in {}" ,
803+ case_name,
804+ attr_name,
805+ expected_relative,
806+ out
807+ ) ;
808+
809+ for descriptor in expected_descriptors {
810+ assert ! (
811+ out. contains( descriptor) ,
812+ "case `{}` expected {} descriptor `{}` in {}" ,
813+ case_name,
814+ attr_name,
815+ descriptor,
816+ out
817+ ) ;
818+ }
819+ }
820+
778821 #[ test]
779822 fn rewrites_width_height_attrs ( ) {
780823 use crate :: http_util:: encode_url;
@@ -941,26 +984,50 @@ mod tests {
941984 }
942985
943986 #[ test]
944- fn rewrites_srcset_absolute_candidates_and_preserves_descriptors ( ) {
945- // Absolute + protocol-relative get rewritten to /first-party/proxy?tsurl=..., relative remains
946- let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
947- let html = r#"<img srcset="https://cdn.example/img-1x.png 1x, //cdn.example/img-2x.png 2x, /local/img.png 1x">"# ;
948- let out = rewrite_creative_html ( & settings, html) ;
949-
950- // Should have at least two proxied candidates
951- let cnt = out. matches ( "/first-party/proxy?tsurl=" ) . count ( ) ;
952- assert ! (
953- cnt >= 2 ,
954- "expected at least two rewritten candidates: {}" ,
955- out
956- ) ;
957-
958- // Descriptors preserved
959- assert ! ( out. contains( " 1x" ) ) ;
960- assert ! ( out. contains( " 2x" ) ) ;
987+ fn rewrites_srcset_attribute_cases ( ) {
988+ struct SrcsetCase < ' a > {
989+ name : & ' a str ,
990+ attr_value : & ' a str ,
991+ expected_relative : & ' a str ,
992+ expected_descriptors : & ' a [ & ' a str ] ,
993+ }
961994
962- // Relative left as-is
963- assert ! ( out. contains( "/local/img.png 1x" ) ) ;
995+ let cases = [
996+ SrcsetCase {
997+ name : "absolute and protocol-relative candidates" ,
998+ attr_value : "https://cdn.example/img-1x.png 1x, //cdn.example/img-2x.png 2x, /local/img.png 1x" ,
999+ expected_relative : "/local/img.png 1x" ,
1000+ expected_descriptors : & [ " 1x" , " 2x" ] ,
1001+ } ,
1002+ SrcsetCase {
1003+ name : "no-space commas with fractional density" ,
1004+ attr_value : "https://cdn.example/img-1x.png 1x,//cdn.example/img-1_5x.png 1.5x,/local/img.png 2x" ,
1005+ expected_relative : "/local/img.png 2x" ,
1006+ expected_descriptors : & [ " 1x" , " 1.5x" ] ,
1007+ } ,
1008+ SrcsetCase {
1009+ name : "relative middle candidate without leading slash" ,
1010+ attr_value : "https://cdn.example/a.png 1x,local/b.png 2x,//cdn.example/c.png 3x" ,
1011+ expected_relative : "local/b.png 2x" ,
1012+ expected_descriptors : & [ " 1x" , " 2x" , " 3x" ] ,
1013+ } ,
1014+ SrcsetCase {
1015+ name : "extra spaces normalize but preserve semantics" ,
1016+ attr_value : " https://cdn.example/a.png 1x , //cdn.example/b.png 2x , /local/c.png 1x " ,
1017+ expected_relative : "/local/c.png 1x" ,
1018+ expected_descriptors : & [ " 1x" , " 2x" ] ,
1019+ } ,
1020+ ] ;
1021+
1022+ for case in cases {
1023+ assert_rewritten_srcset_attr_case (
1024+ case. name ,
1025+ "srcset" ,
1026+ case. attr_value ,
1027+ case. expected_relative ,
1028+ case. expected_descriptors ,
1029+ ) ;
1030+ }
9641031 }
9651032
9661033 #[ test]
@@ -986,59 +1053,6 @@ mod tests {
9861053 assert ! ( out. contains( "<img src=\" /fallback.jpg\" " ) ) ;
9871054 }
9881055
989- #[ test]
990- fn rewrites_srcset_no_space_commas_preserves_descriptors ( ) {
991- let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
992- let html = r#"<img srcset="https://cdn.example/img-1x.png 1x,//cdn.example/img-1_5x.png 1.5x,/local/img.png 2x">"# ;
993- let out = rewrite_creative_html ( & settings, html) ;
994- // Absolute and protocol-relative candidates rewritten
995- assert ! (
996- out. matches( "/first-party/proxy?tsurl=" ) . count( ) >= 2 ,
997- "{}" ,
998- out
999- ) ;
1000- // Descriptors preserved (including fractional)
1001- assert ! ( out. contains( " 1x" ) ) ;
1002- assert ! ( out. contains( " 1.5x" ) ) ;
1003- // Relative left unchanged
1004- assert ! ( out. contains( "/local/img.png 2x" ) ) ;
1005- }
1006-
1007- #[ test]
1008- fn rewrites_srcset_relative_no_space_middle ( ) {
1009- let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
1010- // Relative candidate (no leading slash) in the middle, no space after commas
1011- let html =
1012- r#"<img srcset="https://cdn.example/a.png 1x,local/b.png 2x,//cdn.example/c.png 3x">"# ;
1013- let out = rewrite_creative_html ( & settings, html) ;
1014- // Two absolute/protocol-relative rewritten
1015- assert ! (
1016- out. matches( "/first-party/proxy?tsurl=" ) . count( ) >= 2 ,
1017- "{}" ,
1018- out
1019- ) ;
1020- // Relative preserved as-is
1021- assert ! ( out. contains( "local/b.png 2x" ) ) ;
1022- }
1023-
1024- #[ test]
1025- fn rewrites_srcset_with_extra_spaces ( ) {
1026- let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
1027- let html = r#"<img srcset=" https://cdn.example/a.png 1x , //cdn.example/b.png 2x , /local/c.png 1x ">"# ;
1028- let out = rewrite_creative_html ( & settings, html) ;
1029- // Two absolute/protocol-relative rewritten
1030- assert ! (
1031- out. matches( "/first-party/proxy?tsurl=" ) . count( ) >= 2 ,
1032- "{}" ,
1033- out
1034- ) ;
1035- // Relative preserved
1036- assert ! ( out. contains( "/local/c.png 1x" ) ) ;
1037- // Normalized spacing: single space before descriptor is acceptable
1038- assert ! ( out. contains( " 1x" ) ) ;
1039- assert ! ( out. contains( " 2x" ) ) ;
1040- }
1041-
10421056 #[ test]
10431057 fn rewrites_script_src_and_leaves_relative ( ) {
10441058 let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
@@ -1077,66 +1091,50 @@ mod tests {
10771091 }
10781092
10791093 #[ test]
1080- fn rewrites_imagesrcset_absolute_candidates_and_preserves_descriptors ( ) {
1081- let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
1082- // Use a valid quoted attribute; the previous string had malformed escapes
1083- let html = r#"<div imagesrcset="https://cdn.example/img-1x.png 1x, //cdn.example/img-2x.png 2x, /local/img.png 1x"></div>"# ;
1084- let out = rewrite_creative_html ( & settings, html) ;
1085- let cnt = out. matches ( "/first-party/proxy?tsurl=" ) . count ( ) ;
1086- assert ! (
1087- cnt >= 1 ,
1088- "expected at least one rewritten imagesrcset candidate: {}" ,
1089- out
1090- ) ;
1091- assert ! ( out. contains( "/local/img.png 1x" ) ) ;
1092- }
1093-
1094- #[ test]
1095- fn rewrites_imagesrcset_no_space_commas ( ) {
1096- let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
1097- let html = r#"<div imagesrcset="https://cdn.example/a.png 1x,//cdn.example/b.png 2x,/local/c.png 1x"></div>"# ;
1098- let out = rewrite_creative_html ( & settings, html) ;
1099- // At least two rewritten
1100- assert ! (
1101- out. matches( "/first-party/proxy?tsurl=" ) . count( ) >= 2 ,
1102- "{}" ,
1103- out
1104- ) ;
1105- // Relative preserved
1106- assert ! ( out. contains( "/local/c.png 1x" ) ) ;
1107- }
1108-
1109- #[ test]
1110- fn rewrites_imagesrcset_relative_no_space_middle ( ) {
1111- let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
1112- let html = r#"<div imagesrcset="https://cdn.example/a.png 1x,local/b.png 2x,//cdn.example/c.png 3x"></div>"# ;
1113- let out = rewrite_creative_html ( & settings, html) ;
1114- // Two absolute/protocol-relative rewritten
1115- assert ! (
1116- out. matches( "/first-party/proxy?tsurl=" ) . count( ) >= 2 ,
1117- "{}" ,
1118- out
1119- ) ;
1120- // Relative preserved
1121- assert ! ( out. contains( "local/b.png 2x" ) ) ;
1122- }
1094+ fn rewrites_imagesrcset_attribute_cases ( ) {
1095+ struct SrcsetCase < ' a > {
1096+ name : & ' a str ,
1097+ attr_value : & ' a str ,
1098+ expected_relative : & ' a str ,
1099+ expected_descriptors : & ' a [ & ' a str ] ,
1100+ }
11231101
1124- #[ test]
1125- fn rewrites_imagesrcset_with_extra_spaces ( ) {
1126- let settings = crate :: test_support:: tests:: create_test_settings ( ) ;
1127- let html = r#"<div imagesrcset=" https://cdn.example/a.png 1x , //cdn.example/b.png 2x , /local/c.png 1x "></div>"# ;
1128- let out = rewrite_creative_html ( & settings, html) ;
1129- // Two absolute/protocol-relative rewritten
1130- assert ! (
1131- out. matches( "/first-party/proxy?tsurl=" ) . count( ) >= 2 ,
1132- "{}" ,
1133- out
1134- ) ;
1135- // Relative preserved
1136- assert ! ( out. contains( "/local/c.png 1x" ) ) ;
1137- // Normalized spacing present
1138- assert ! ( out. contains( " 1x" ) ) ;
1139- assert ! ( out. contains( " 2x" ) ) ;
1102+ let cases = [
1103+ SrcsetCase {
1104+ name : "absolute and protocol-relative candidates" ,
1105+ attr_value : "https://cdn.example/img-1x.png 1x, //cdn.example/img-2x.png 2x, /local/img.png 1x" ,
1106+ expected_relative : "/local/img.png 1x" ,
1107+ expected_descriptors : & [ " 1x" , " 2x" ] ,
1108+ } ,
1109+ SrcsetCase {
1110+ name : "no-space commas" ,
1111+ attr_value : "https://cdn.example/a.png 1x,//cdn.example/b.png 2x,/local/c.png 1x" ,
1112+ expected_relative : "/local/c.png 1x" ,
1113+ expected_descriptors : & [ " 1x" , " 2x" ] ,
1114+ } ,
1115+ SrcsetCase {
1116+ name : "relative middle candidate without leading slash" ,
1117+ attr_value : "https://cdn.example/a.png 1x,local/b.png 2x,//cdn.example/c.png 3x" ,
1118+ expected_relative : "local/b.png 2x" ,
1119+ expected_descriptors : & [ " 1x" , " 2x" , " 3x" ] ,
1120+ } ,
1121+ SrcsetCase {
1122+ name : "extra spaces normalize but preserve semantics" ,
1123+ attr_value : " https://cdn.example/a.png 1x , //cdn.example/b.png 2x , /local/c.png 1x " ,
1124+ expected_relative : "/local/c.png 1x" ,
1125+ expected_descriptors : & [ " 1x" , " 2x" ] ,
1126+ } ,
1127+ ] ;
1128+
1129+ for case in cases {
1130+ assert_rewritten_srcset_attr_case (
1131+ case. name ,
1132+ "imagesrcset" ,
1133+ case. attr_value ,
1134+ case. expected_relative ,
1135+ case. expected_descriptors ,
1136+ ) ;
1137+ }
11401138 }
11411139
11421140 #[ test]
0 commit comments