1 // ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ 2 // ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ 3 // ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ 4 // ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ 5 // ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ 6 // ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ 7 8 #[cfg(test)] 9 mod passing { 10 use reqwest::blocking::Client; 11 use reqwest::Url; 12 use std::collections::HashMap; 13 14 use crate::css; 15 use crate::opts::Options; 16 17 #[test] empty_input()18 fn empty_input() { 19 let cache = &mut HashMap::new(); 20 let client = Client::new(); 21 let document_url: Url = Url::parse("data:,").unwrap(); 22 let options = Options::default(); 23 24 assert_eq!( 25 css::embed_css(cache, &client, &document_url, "", &options, 0), 26 "" 27 ); 28 } 29 30 #[test] trim_if_empty()31 fn trim_if_empty() { 32 let cache = &mut HashMap::new(); 33 let client = Client::new(); 34 let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap(); 35 let options = Options::default(); 36 37 assert_eq!( 38 css::embed_css(cache, &client, &document_url, "\t \t ", &options, 0,), 39 "" 40 ); 41 } 42 43 #[test] style_exclude_unquoted_images()44 fn style_exclude_unquoted_images() { 45 let cache = &mut HashMap::new(); 46 let client = Client::new(); 47 let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap(); 48 let mut options = Options::default(); 49 options.no_images = true; 50 options.silent = true; 51 52 const STYLE: &str = "/* border: none;*/\ 53 background-image: url(https://somewhere.com/bg.png); \ 54 list-style: url(/assets/images/bullet.svg);\ 55 width:99.998%; \ 56 margin-top: -20px; \ 57 line-height: -1; \ 58 height: calc(100vh - 10pt)"; 59 60 assert_eq!( 61 css::embed_css(cache, &client, &document_url, &STYLE, &options, 0,), 62 format!( 63 "/* border: none;*/\ 64 background-image: url(\"{empty_image}\"); \ 65 list-style: url(\"{empty_image}\");\ 66 width:99.998%; \ 67 margin-top: -20px; \ 68 line-height: -1; \ 69 height: calc(100vh - 10pt)", 70 empty_image = empty_image!() 71 ) 72 ); 73 } 74 75 #[test] style_exclude_single_quoted_images()76 fn style_exclude_single_quoted_images() { 77 let cache = &mut HashMap::new(); 78 let client = Client::new(); 79 let document_url: Url = Url::parse("data:,").unwrap(); 80 let mut options = Options::default(); 81 options.no_images = true; 82 options.silent = true; 83 84 const STYLE: &str = "/* border: none;*/\ 85 background-image: url('https://somewhere.com/bg.png'); \ 86 list-style: url('/assets/images/bullet.svg');\ 87 width:99.998%; \ 88 margin-top: -20px; \ 89 line-height: -1; \ 90 height: calc(100vh - 10pt)"; 91 92 assert_eq!( 93 css::embed_css(cache, &client, &document_url, &STYLE, &options, 0), 94 format!( 95 "/* border: none;*/\ 96 background-image: url(\"{empty_image}\"); \ 97 list-style: url(\"{empty_image}\");\ 98 width:99.998%; \ 99 margin-top: -20px; \ 100 line-height: -1; \ 101 height: calc(100vh - 10pt)", 102 empty_image = empty_image!() 103 ) 104 ); 105 } 106 107 #[test] style_block()108 fn style_block() { 109 let cache = &mut HashMap::new(); 110 let client = Client::new(); 111 let document_url: Url = Url::parse("file:///").unwrap(); 112 let mut options = Options::default(); 113 options.silent = true; 114 115 const CSS: &str = "\ 116 #id.class-name:not(:nth-child(3n+0)) {\n \ 117 // border: none;\n \ 118 background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=\");\n\ 119 }\n\ 120 \n\ 121 html > body {}"; 122 123 assert_eq!( 124 css::embed_css(cache, &client, &document_url, &CSS, &options, 0), 125 CSS 126 ); 127 } 128 129 #[test] attribute_selectors()130 fn attribute_selectors() { 131 let cache = &mut HashMap::new(); 132 let client = Client::new(); 133 let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap(); 134 let mut options = Options::default(); 135 options.silent = true; 136 137 const CSS: &str = "\ 138 [data-value] { 139 /* Attribute exists */ 140 } 141 142 [data-value=\"foo\"] { 143 /* Attribute has this exact value */ 144 } 145 146 [data-value*=\"foo\"] { 147 /* Attribute value contains this value somewhere in it */ 148 } 149 150 [data-value~=\"foo\"] { 151 /* Attribute has this value in a space-separated list somewhere */ 152 } 153 154 [data-value^=\"foo\"] { 155 /* Attribute value starts with this */ 156 } 157 158 [data-value|=\"foo\"] { 159 /* Attribute value starts with this in a dash-separated list */ 160 } 161 162 [data-value$=\"foo\"] { 163 /* Attribute value ends with this */ 164 } 165 "; 166 167 assert_eq!( 168 css::embed_css(cache, &client, &document_url, &CSS, &options, 0), 169 CSS 170 ); 171 } 172 173 #[test] import_string()174 fn import_string() { 175 let cache = &mut HashMap::new(); 176 let client = Client::new(); 177 let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap(); 178 let mut options = Options::default(); 179 options.silent = true; 180 181 const CSS: &str = "\ 182 @charset 'UTF-8';\n\ 183 \n\ 184 @import 'data:text/css,html{background-color:%23000}';\n\ 185 \n\ 186 @import url('data:text/css,html{color:%23fff}')\n\ 187 "; 188 189 assert_eq!( 190 css::embed_css(cache, &client, &document_url, &CSS, &options, 0,), 191 "\ 192 @charset \"UTF-8\";\n\ 193 \n\ 194 @import \"data:text/css;base64,aHRtbHtiYWNrZ3JvdW5kLWNvbG9yOiMwMDB9\";\n\ 195 \n\ 196 @import url(\"data:text/css;base64,aHRtbHtjb2xvcjojZmZmfQ==\")\n\ 197 " 198 ); 199 } 200 201 #[test] hash_urls()202 fn hash_urls() { 203 let cache = &mut HashMap::new(); 204 let client = Client::new(); 205 let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap(); 206 let mut options = Options::default(); 207 options.silent = true; 208 209 const CSS: &str = "\ 210 body {\n \ 211 behavior: url(#default#something);\n\ 212 }\n\ 213 \n\ 214 .scissorHalf {\n \ 215 offset-path: url(#somePath);\n\ 216 }\n\ 217 "; 218 219 assert_eq!( 220 css::embed_css(cache, &client, &document_url, &CSS, &options, 0,), 221 CSS 222 ); 223 } 224 225 #[test] transform_percentages_and_degrees()226 fn transform_percentages_and_degrees() { 227 let cache = &mut HashMap::new(); 228 let client = Client::new(); 229 let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap(); 230 let mut options = Options::default(); 231 options.silent = true; 232 233 const CSS: &str = "\ 234 div {\n \ 235 transform: translate(-50%, -50%) rotate(-45deg);\n\ 236 transform: translate(50%, 50%) rotate(45deg);\n\ 237 transform: translate(+50%, +50%) rotate(+45deg);\n\ 238 }\n\ 239 "; 240 241 assert_eq!( 242 css::embed_css(cache, &client, &document_url, &CSS, &options, 0,), 243 CSS 244 ); 245 } 246 247 #[test] unusual_indents()248 fn unusual_indents() { 249 let cache = &mut HashMap::new(); 250 let client = Client::new(); 251 let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap(); 252 let mut options = Options::default(); 253 options.silent = true; 254 255 const CSS: &str = "\ 256 .is\\:good:hover {\n \ 257 color: green\n\ 258 }\n\ 259 \n\ 260 #\\~\\!\\@\\$\\%\\^\\&\\*\\(\\)\\+\\=\\,\\.\\/\\\\\\'\\\"\\;\\:\\?\\>\\<\\[\\]\\{\\}\\|\\`\\# {\n \ 261 color: black\n\ 262 }\n\ 263 "; 264 265 assert_eq!( 266 css::embed_css(cache, &client, &document_url, &CSS, &options, 0,), 267 CSS 268 ); 269 } 270 271 #[test] exclude_fonts()272 fn exclude_fonts() { 273 let cache = &mut HashMap::new(); 274 let client = Client::new(); 275 let document_url: Url = Url::parse("https://doesntmatter.local/").unwrap(); 276 let mut options = Options::default(); 277 options.no_fonts = true; 278 options.silent = true; 279 280 const CSS: &str = "\ 281 @font-face {\n \ 282 font-family: 'My Font';\n \ 283 src: url(my_font.woff);\n\ 284 }\n\ 285 \n\ 286 #identifier {\n \ 287 font-family: 'My Font' Arial\n\ 288 }\n\ 289 \n\ 290 @font-face {\n \ 291 font-family: 'My Font';\n \ 292 src: url(my_font.woff);\n\ 293 }\n\ 294 \n\ 295 div {\n \ 296 font-family: 'My Font' Verdana\n\ 297 }\n\ 298 "; 299 const CSS_OUT: &str = " \ 300 \n\ 301 \n\ 302 #identifier {\n \ 303 font-family: \"My Font\" Arial\n\ 304 }\n\ 305 \n \ 306 \n\ 307 \n\ 308 div {\n \ 309 font-family: \"My Font\" Verdana\n\ 310 }\n\ 311 "; 312 313 assert_eq!( 314 css::embed_css(cache, &client, &document_url, &CSS, &options, 0,), 315 CSS_OUT 316 ); 317 } 318 319 #[test] content()320 fn content() { 321 let cache = &mut HashMap::new(); 322 let client = Client::new(); 323 let document_url: Url = Url::parse("data:,").unwrap(); 324 let mut options = Options::default(); 325 options.silent = true; 326 327 const CSS: &str = "\ 328 #language a[href=\"#translations\"]:before {\n\ 329 content: url(data:,) \"\\A\";\n\ 330 white-space: pre }\n\ 331 "; 332 const CSS_OUT: &str = "\ 333 #language a[href=\"#translations\"]:before {\n\ 334 content: url(\"data:text/plain;base64,\") \"\\a \";\n\ 335 white-space: pre }\n\ 336 "; 337 338 assert_eq!( 339 css::embed_css(cache, &client, &document_url, &CSS, &options, 0,), 340 CSS_OUT 341 ); 342 } 343 344 #[test] ie_css_hack()345 fn ie_css_hack() { 346 let cache = &mut HashMap::new(); 347 let client = Client::new(); 348 let document_url: Url = Url::parse("data:,").unwrap(); 349 let mut options = Options::default(); 350 options.silent = true; 351 352 const CSS: &str = "\ 353 div#p>svg>foreignObject>section:not(\\9) {\n\ 354 width: 300px;\n\ 355 width: 500px\\9;\n\ 356 }\n\ 357 "; 358 const CSS_OUT: &str = "\ 359 div#p>svg>foreignObject>section:not(\\9) {\n\ 360 width: 300px;\n\ 361 width: 500px\t;\n\ 362 }\n\ 363 "; 364 365 assert_eq!( 366 css::embed_css(cache, &client, &document_url, &CSS, &options, 0,), 367 CSS_OUT 368 ); 369 } 370 } 371