Skip to main content

muffy_validation/
lib.rs

1//! Document validation.
2
3extern crate alloc;
4
5mod error;
6
7pub use self::error::*;
8use muffy_document::html::Element;
9use muffy_validation_macro::html;
10
11html! {}
12
13#[cfg(test)]
14mod tests {
15    use super::*;
16    use alloc::sync::Arc;
17    use muffy_document::html::Node;
18    use regex::Regex;
19
20    fn create_element(
21        name: &str,
22        attributes: Vec<(&str, &str)>,
23        children: Vec<Element>,
24    ) -> Element {
25        Element::new(
26            name.to_owned(),
27            attributes
28                .into_iter()
29                .map(|(k, v)| (k.to_owned(), v.to_owned()))
30                .collect(),
31            children
32                .into_iter()
33                .map(|e| Arc::new(Node::Element(e)))
34                .collect(),
35        )
36    }
37
38    #[test]
39    fn validate_invalid_element_name() {
40        let element = create_element("invalid", vec![], vec![]);
41
42        assert_eq!(
43            validate_html_element(&element, &[], &[]),
44            Err(MarkupError::UnknownTag("invalid".to_owned()))
45        );
46    }
47
48    mod div {
49        use super::*;
50
51        #[test]
52        fn validate_valid_element() {
53            let element = create_element("div", vec![], vec![]);
54
55            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
56        }
57
58        #[test]
59        fn validate_valid_attributes() {
60            let element = create_element("div", vec![("id", "foo"), ("class", "bar")], vec![]);
61
62            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
63        }
64
65        #[test]
66        fn validate_invalid_attribute() {
67            let element = create_element("div", vec![("invalid", "foo")], vec![]);
68
69            assert_eq!(
70                validate_html_element(&element, &[], &[]),
71                Err(MarkupError::InvalidElement {
72                    attributes: [("invalid".into(), [AttributeError::NotAllowed].into())].into(),
73                    children: Default::default(),
74                })
75            );
76        }
77
78        #[test]
79        fn validate_multiple_invalid_attributes() {
80            let element = create_element(
81                "div",
82                vec![("invalid-one", "foo"), ("invalid-two", "bar")],
83                vec![],
84            );
85
86            assert_eq!(
87                validate_html_element(&element, &[], &[]),
88                Err(MarkupError::InvalidElement {
89                    attributes: [
90                        ("invalid-one".into(), [AttributeError::NotAllowed].into()),
91                        ("invalid-two".into(), [AttributeError::NotAllowed].into()),
92                    ]
93                    .into(),
94                    children: Default::default(),
95                })
96            );
97        }
98
99        #[test]
100        fn validate_ignored_attribute_regex() {
101            let element = create_element("div", vec![("data-foo", "bar")], vec![]);
102
103            assert_eq!(
104                validate_html_element(&element, &[Regex::new("^data-.*$").unwrap()], &[]),
105                Ok(())
106            );
107        }
108
109        #[test]
110        fn validate_ignored_element_regex() {
111            let element = create_element(
112                "div",
113                vec![],
114                vec![create_element("custom-element-123", vec![], vec![])],
115            );
116
117            assert_eq!(
118                validate_html_element(&element, &[], &[Regex::new("^custom-element-.*$").unwrap()]),
119                Ok(())
120            );
121        }
122
123        #[test]
124        fn validate_ignored_unknown_tag_regex() {
125            let element = create_element("custom-element-456", vec![], vec![]);
126
127            assert_eq!(
128                validate_html_element(&element, &[], &[Regex::new("^custom-element-.*$").unwrap()]),
129                Ok(())
130            );
131        }
132
133        #[test]
134        fn validate_ignored_known_tag_regex() {
135            let element = create_element("div", vec![("invalid", "foo")], vec![]);
136
137            assert_eq!(
138                validate_html_element(&element, &[], &[Regex::new("^div$").unwrap()]),
139                Ok(())
140            );
141        }
142
143        #[test]
144        fn validate_non_ignored_known_tag_regex() {
145            let element = create_element("div", vec![("invalid", "foo")], vec![]);
146
147            assert_eq!(
148                validate_html_element(&element, &[], &[Regex::new("^span$").unwrap()]),
149                Err(MarkupError::InvalidElement {
150                    attributes: [("invalid".into(), [AttributeError::NotAllowed].into())].into(),
151                    children: Default::default(),
152                })
153            );
154        }
155
156        #[test]
157        fn validate_valid_child() {
158            let element = create_element("div", vec![], vec![create_element("p", vec![], vec![])]);
159
160            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
161        }
162    }
163
164    mod p {
165        use super::*;
166
167        #[test]
168        fn validate_valid_element() {
169            let element = create_element("p", vec![], vec![]);
170
171            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
172        }
173
174        #[test]
175        fn validate_invalid_child() {
176            let element = create_element("p", vec![], vec![create_element("div", vec![], vec![])]);
177
178            assert_eq!(
179                validate_html_element(&element, &[], &[]),
180                Err(MarkupError::InvalidElement {
181                    attributes: Default::default(),
182                    children: [("div".into(), [ChildError::NotAllowed].into())].into(),
183                })
184            );
185        }
186
187        #[test]
188        fn validate_multiple_invalid_children() {
189            let element = create_element(
190                "p",
191                vec![],
192                vec![
193                    create_element("div", vec![], vec![]),
194                    create_element("table", vec![], vec![]),
195                ],
196            );
197
198            assert_eq!(
199                validate_html_element(&element, &[], &[]),
200                Err(MarkupError::InvalidElement {
201                    attributes: Default::default(),
202                    children: [
203                        ("div".into(), [ChildError::NotAllowed].into()),
204                        ("table".into(), [ChildError::NotAllowed].into()),
205                    ]
206                    .into(),
207                })
208            );
209        }
210
211        #[test]
212        fn validate_ignored_known_tag_regex() {
213            let element = create_element("p", vec![], vec![create_element("div", vec![], vec![])]);
214
215            assert_eq!(
216                validate_html_element(&element, &[], &[Regex::new("^p$").unwrap()]),
217                Ok(())
218            );
219        }
220    }
221
222    mod mark {
223        use super::*;
224
225        #[test]
226        fn validate_valid_element() {
227            let element = create_element("mark", vec![], vec![]);
228
229            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
230        }
231
232        #[test]
233        fn validate_valid_child() {
234            let element =
235                create_element("mark", vec![], vec![create_element("span", vec![], vec![])]);
236
237            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
238        }
239
240        #[test]
241        fn validate_invalid_child() {
242            let element =
243                create_element("mark", vec![], vec![create_element("div", vec![], vec![])]);
244
245            assert_eq!(
246                validate_html_element(&element, &[], &[]),
247                Err(MarkupError::InvalidElement {
248                    attributes: Default::default(),
249                    children: [("div".into(), [ChildError::NotAllowed].into())].into(),
250                })
251            );
252        }
253    }
254
255    mod html {
256        use super::*;
257
258        #[test]
259        fn validate_valid_element() {
260            let element = create_element("html", vec![], vec![]);
261
262            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
263        }
264
265        #[test]
266        fn validate_valid_children() {
267            let element = create_element(
268                "html",
269                vec![],
270                vec![
271                    create_element("head", vec![], vec![]),
272                    create_element("body", vec![], vec![]),
273                ],
274            );
275
276            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
277        }
278    }
279
280    mod head {
281        use super::*;
282
283        #[test]
284        fn validate_valid_child() {
285            let element = create_element(
286                "head",
287                vec![],
288                vec![create_element("title", vec![], vec![])],
289            );
290
291            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
292        }
293
294        #[test]
295        fn validate_invalid_child() {
296            let element = create_element("head", vec![], vec![create_element("p", vec![], vec![])]);
297
298            assert_eq!(
299                validate_html_element(&element, &[], &[]),
300                Err(MarkupError::InvalidElement {
301                    attributes: Default::default(),
302                    children: [("p".into(), [ChildError::NotAllowed].into())].into(),
303                })
304            );
305        }
306    }
307
308    mod title {
309        use super::*;
310
311        #[test]
312        fn validate_invalid_child() {
313            let element =
314                create_element("title", vec![], vec![create_element("div", vec![], vec![])]);
315
316            assert_eq!(
317                validate_html_element(&element, &[], &[]),
318                Err(MarkupError::InvalidElement {
319                    attributes: Default::default(),
320                    children: [("div".into(), [ChildError::NotAllowed].into())].into(),
321                })
322            );
323        }
324    }
325
326    mod ul {
327        use super::*;
328
329        #[test]
330        fn validate_valid_child() {
331            let element = create_element("ul", vec![], vec![create_element("li", vec![], vec![])]);
332
333            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
334        }
335
336        #[test]
337        fn validate_invalid_child() {
338            let element = create_element("ul", vec![], vec![create_element("p", vec![], vec![])]);
339
340            assert_eq!(
341                validate_html_element(&element, &[], &[]),
342                Err(MarkupError::InvalidElement {
343                    attributes: Default::default(),
344                    children: [("p".into(), [ChildError::NotAllowed].into())].into(),
345                })
346            );
347        }
348    }
349
350    mod table {
351        use super::*;
352
353        #[test]
354        fn validate_valid_child() {
355            let element =
356                create_element("table", vec![], vec![create_element("tr", vec![], vec![])]);
357
358            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
359        }
360
361        #[test]
362        fn validate_invalid_child() {
363            let element =
364                create_element("table", vec![], vec![create_element("p", vec![], vec![])]);
365
366            assert_eq!(
367                validate_html_element(&element, &[], &[]),
368                Err(MarkupError::InvalidElement {
369                    attributes: Default::default(),
370                    children: [("p".into(), [ChildError::NotAllowed].into())].into(),
371                })
372            );
373        }
374    }
375
376    mod tr {
377        use super::*;
378
379        #[test]
380        fn validate_valid_children() {
381            let element = create_element(
382                "tr",
383                vec![],
384                vec![
385                    create_element("th", vec![], vec![]),
386                    create_element("td", vec![], vec![]),
387                ],
388            );
389
390            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
391        }
392    }
393
394    mod form {
395        use super::*;
396
397        #[test]
398        fn validate_valid_attributes() {
399            let element = create_element("form", vec![("action", "/"), ("method", "post")], vec![]);
400
401            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
402        }
403
404        #[test]
405        fn validate_valid_child() {
406            let element = create_element(
407                "form",
408                vec![],
409                vec![create_element("input", vec![], vec![])],
410            );
411
412            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
413        }
414    }
415
416    mod img {
417        use super::*;
418
419        #[test]
420        fn validate_valid_attributes() {
421            let element = create_element(
422                "img",
423                vec![("src", "img.png"), ("alt", "description")],
424                vec![],
425            );
426
427            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
428        }
429    }
430
431    mod picture {
432        use super::*;
433
434        #[test]
435        fn validate_valid_element() {
436            let element = create_element("picture", vec![], vec![]);
437
438            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
439        }
440
441        #[test]
442        fn validate_valid_children() {
443            let element = create_element(
444                "picture",
445                vec![],
446                vec![
447                    create_element("source", vec![], vec![]),
448                    create_element("img", vec![], vec![]),
449                ],
450            );
451
452            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
453        }
454
455        #[test]
456        fn validate_invalid_child() {
457            let element =
458                create_element("picture", vec![], vec![create_element("p", vec![], vec![])]);
459
460            assert_eq!(
461                validate_html_element(&element, &[], &[]),
462                Err(MarkupError::InvalidElement {
463                    attributes: Default::default(),
464                    children: [("p".into(), [ChildError::NotAllowed].into())].into(),
465                })
466            );
467        }
468    }
469
470    mod video {
471        use super::*;
472
473        #[test]
474        fn validate_valid_attributes() {
475            let element =
476                create_element("video", vec![("src", "vid.mp4"), ("controls", "")], vec![]);
477
478            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
479        }
480
481        #[test]
482        fn validate_valid_child() {
483            let element = create_element(
484                "video",
485                vec![],
486                vec![create_element("track", vec![], vec![])],
487            );
488
489            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
490        }
491    }
492
493    mod meta {
494        use super::*;
495
496        #[test]
497        fn validate_valid_name_content() {
498            let element = create_element(
499                "meta",
500                vec![("name", "description"), ("content", "stuff")],
501                vec![],
502            );
503
504            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
505        }
506
507        #[test]
508        fn validate_valid_charset() {
509            let element = create_element("meta", vec![("charset", "utf-8")], vec![]);
510
511            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
512        }
513
514        #[test]
515        fn validate_valid_property() {
516            let element = create_element("meta", vec![("property", "og:image")], vec![]);
517
518            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
519        }
520    }
521
522    mod link {
523        use super::*;
524
525        #[test]
526        fn validate_valid_attributes() {
527            let element = create_element(
528                "link",
529                vec![("rel", "stylesheet"), ("href", "style.css")],
530                vec![],
531            );
532
533            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
534        }
535    }
536
537    mod noscript {
538        use super::*;
539
540        #[test]
541        fn validate_valid_element() {
542            let element = create_element("noscript", vec![], vec![]);
543
544            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
545        }
546
547        #[test]
548        fn validate_valid_flow_child() {
549            let element = create_element(
550                "noscript",
551                vec![],
552                vec![create_element("div", vec![], vec![])],
553            );
554
555            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
556        }
557
558        #[test]
559        fn validate_valid_head_child() {
560            let element = create_element(
561                "noscript",
562                vec![],
563                vec![create_element("link", vec![], vec![])],
564            );
565
566            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
567        }
568
569        #[test]
570        fn validate_invalid_child() {
571            let element = create_element(
572                "noscript",
573                vec![],
574                vec![create_element("title", vec![], vec![])],
575            );
576
577            assert_eq!(
578                validate_html_element(&element, &[], &[]),
579                Err(MarkupError::InvalidElement {
580                    attributes: Default::default(),
581                    children: [("title".into(), [ChildError::NotAllowed].into())].into(),
582                })
583            );
584        }
585    }
586}