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