Skip to main content

muffy_validation/
lib.rs

1//! Document validation.
2
3extern crate alloc;
4
5mod attribute_set;
6mod content;
7mod error;
8mod rule;
9mod validation;
10mod variant;
11
12pub use self::error::*;
13use self::{
14    attribute_set::AttributeSet, content::Content, rule::Rule, validation::validate_rule,
15    variant::Variant,
16};
17use muffy_document::document::Element;
18use muffy_validation_macro::html;
19
20html! {}
21
22#[cfg(test)]
23mod tests {
24    use super::*;
25    use alloc::sync::Arc;
26    use muffy_document::document::Node;
27    use regex::Regex;
28
29    fn create_element(
30        name: &str,
31        attributes: Vec<(&str, &str)>,
32        children: Vec<Element>,
33    ) -> Element {
34        Element::new(
35            name.to_owned(),
36            attributes
37                .into_iter()
38                .map(|(k, v)| (k.to_owned(), v.to_owned()))
39                .collect(),
40            children
41                .into_iter()
42                .map(|e| Arc::new(Node::Element(e)))
43                .collect(),
44        )
45    }
46
47    #[test]
48    fn validate_invalid_element_name() {
49        let element = create_element("invalid", vec![], vec![]);
50
51        assert_eq!(
52            validate_html_element(&element, &[], &[]),
53            Err(MarkupError::UnknownTag("invalid".to_owned()))
54        );
55    }
56
57    mod div {
58        use super::*;
59
60        #[test]
61        fn validate_valid_attribute_name_prefix() {
62            let element = create_element("div", vec![("lang", "en"), ("xml:lang", "en")], vec![]);
63
64            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
65        }
66
67        #[test]
68        fn validate_valid_element() {
69            let element = create_element("div", vec![], vec![]);
70
71            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
72        }
73
74        #[test]
75        fn validate_valid_attributes() {
76            let element = create_element("div", vec![("id", "foo"), ("class", "bar")], vec![]);
77
78            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
79        }
80
81        #[test]
82        fn validate_invalid_attribute() {
83            let element = create_element("div", vec![("invalid", "foo")], vec![]);
84
85            assert_eq!(
86                validate_html_element(&element, &[], &[]),
87                Err(MarkupError::InvalidElement {
88                    invalid_attributes: [("invalid".into(), [AttributeError::NotAllowed].into())]
89                        .into(),
90                    invalid_children: Default::default(),
91                    missing_attributes: Default::default(),
92                    missing_children: Default::default(),
93                })
94            );
95        }
96
97        #[test]
98        fn validate_multiple_invalid_attributes() {
99            let element = create_element(
100                "div",
101                vec![("invalid-one", "foo"), ("invalid-two", "bar")],
102                vec![],
103            );
104
105            assert_eq!(
106                validate_html_element(&element, &[], &[]),
107                Err(MarkupError::InvalidElement {
108                    invalid_attributes: [
109                        ("invalid-one".into(), [AttributeError::NotAllowed].into()),
110                        ("invalid-two".into(), [AttributeError::NotAllowed].into()),
111                    ]
112                    .into(),
113                    invalid_children: Default::default(),
114                    missing_attributes: Default::default(),
115                    missing_children: Default::default(),
116                })
117            );
118        }
119
120        #[test]
121        fn validate_ignored_attribute_regex() {
122            let element = create_element("div", vec![("data-foo", "bar")], vec![]);
123
124            assert_eq!(
125                validate_html_element(&element, &[Regex::new("^data-.*$").unwrap()], &[]),
126                Ok(())
127            );
128        }
129
130        #[test]
131        fn validate_ignored_element_regex() {
132            let element = create_element(
133                "div",
134                vec![],
135                vec![create_element("custom-element-123", vec![], vec![])],
136            );
137
138            assert_eq!(
139                validate_html_element(&element, &[], &[Regex::new("^custom-element-.*$").unwrap()]),
140                Ok(())
141            );
142        }
143
144        #[test]
145        fn validate_ignored_unknown_tag_regex() {
146            let element = create_element("custom-element-456", vec![], vec![]);
147
148            assert_eq!(
149                validate_html_element(&element, &[], &[Regex::new("^custom-element-.*$").unwrap()]),
150                Ok(())
151            );
152        }
153
154        #[test]
155        fn validate_ignored_known_tag_regex() {
156            let element = create_element("div", vec![("invalid", "foo")], vec![]);
157
158            assert_eq!(
159                validate_html_element(&element, &[], &[Regex::new("^div$").unwrap()]),
160                Ok(())
161            );
162        }
163
164        #[test]
165        fn validate_non_ignored_known_tag_regex() {
166            let element = create_element("div", vec![("invalid", "foo")], vec![]);
167
168            assert_eq!(
169                validate_html_element(&element, &[], &[Regex::new("^span$").unwrap()]),
170                Err(MarkupError::InvalidElement {
171                    invalid_attributes: [("invalid".into(), [AttributeError::NotAllowed].into())]
172                        .into(),
173                    invalid_children: Default::default(),
174                    missing_attributes: Default::default(),
175                    missing_children: Default::default(),
176                })
177            );
178        }
179
180        #[test]
181        fn validate_valid_child() {
182            let element = create_element("div", vec![], vec![create_element("p", vec![], vec![])]);
183
184            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
185        }
186
187        #[test]
188        fn validate_valid_svg_child() {
189            let element =
190                create_element("div", vec![], vec![create_element("svg", vec![], vec![])]);
191
192            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
193        }
194
195        #[test]
196        fn validate_valid_math_child() {
197            let element =
198                create_element("div", vec![], vec![create_element("math", vec![], vec![])]);
199
200            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
201        }
202    }
203
204    mod p {
205        use super::*;
206
207        #[test]
208        fn validate_valid_element() {
209            let element = create_element("p", vec![], vec![]);
210
211            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
212        }
213
214        #[test]
215        fn validate_valid_text() {
216            let element = Element::new(
217                "p".into(),
218                vec![],
219                vec![Arc::new(Node::Text("hello".into()))],
220            );
221
222            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
223        }
224
225        #[test]
226        fn validate_invalid_child() {
227            let element = create_element("p", vec![], vec![create_element("div", vec![], vec![])]);
228
229            assert_eq!(
230                validate_html_element(&element, &[], &[]),
231                Err(MarkupError::InvalidElement {
232                    invalid_attributes: Default::default(),
233                    invalid_children: [("div".into(), [ChildError::NotAllowed].into())].into(),
234                    missing_attributes: Default::default(),
235                    missing_children: Default::default(),
236                })
237            );
238        }
239
240        #[test]
241        fn validate_multiple_invalid_children() {
242            let element = create_element(
243                "p",
244                vec![],
245                vec![
246                    create_element("div", vec![], vec![]),
247                    create_element("table", vec![], vec![]),
248                ],
249            );
250
251            assert_eq!(
252                validate_html_element(&element, &[], &[]),
253                Err(MarkupError::InvalidElement {
254                    invalid_attributes: Default::default(),
255                    invalid_children: [
256                        ("div".into(), [ChildError::NotAllowed].into()),
257                        ("table".into(), [ChildError::NotAllowed].into()),
258                    ]
259                    .into(),
260                    missing_attributes: Default::default(),
261                    missing_children: Default::default(),
262                })
263            );
264        }
265
266        #[test]
267        fn validate_ignored_known_tag_regex() {
268            let element = create_element("p", vec![], vec![create_element("div", vec![], vec![])]);
269
270            assert_eq!(
271                validate_html_element(&element, &[], &[Regex::new("^p$").unwrap()]),
272                Ok(())
273            );
274        }
275    }
276
277    mod a {
278        use super::*;
279
280        #[test]
281        fn validate_valid_link() {
282            let element = create_element("a", vec![("href", "/")], vec![]);
283
284            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
285        }
286
287        #[test]
288        fn validate_valid_prefixed_link() {
289            let element = create_element("a", vec![("xlink:href", "/")], vec![]);
290
291            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
292        }
293
294        #[test]
295        fn validate_valid_placeholder_link() {
296            let element = create_element("a", vec![], vec![]);
297
298            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
299        }
300
301        #[test]
302        fn validate_missing_href() {
303            let element = create_element("a", vec![("download", "")], vec![]);
304
305            assert_eq!(
306                validate_html_element(&element, &[], &[]),
307                Err(MarkupError::InvalidElement {
308                    invalid_attributes: Default::default(),
309                    invalid_children: Default::default(),
310                    missing_attributes: ["href".into()].into(),
311                    missing_children: Default::default(),
312                })
313            );
314        }
315    }
316
317    mod mark {
318        use super::*;
319
320        #[test]
321        fn validate_valid_element() {
322            let element = create_element("mark", vec![], vec![]);
323
324            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
325        }
326
327        #[test]
328        fn validate_valid_child() {
329            let element =
330                create_element("mark", vec![], vec![create_element("span", vec![], vec![])]);
331
332            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
333        }
334
335        #[test]
336        fn validate_invalid_child() {
337            let element =
338                create_element("mark", vec![], vec![create_element("div", vec![], vec![])]);
339
340            assert_eq!(
341                validate_html_element(&element, &[], &[]),
342                Err(MarkupError::InvalidElement {
343                    invalid_attributes: Default::default(),
344                    invalid_children: [("div".into(), [ChildError::NotAllowed].into())].into(),
345                    missing_attributes: Default::default(),
346                    missing_children: Default::default(),
347                })
348            );
349        }
350    }
351
352    mod html {
353        use super::*;
354
355        #[test]
356        fn validate_missing_children() {
357            let element = create_element("html", vec![], vec![]);
358
359            assert_eq!(
360                validate_html_element(&element, &[], &[]),
361                Err(MarkupError::InvalidElement {
362                    invalid_attributes: Default::default(),
363                    invalid_children: Default::default(),
364                    missing_attributes: Default::default(),
365                    missing_children: ["head".into()].into(),
366                })
367            );
368        }
369
370        #[test]
371        fn validate_valid_children() {
372            let element = create_element(
373                "html",
374                vec![],
375                vec![
376                    create_element("head", vec![], vec![]),
377                    create_element("body", vec![], vec![]),
378                ],
379            );
380
381            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
382        }
383
384        #[test]
385        fn validate_misplaced_children() {
386            let element = create_element(
387                "html",
388                vec![],
389                vec![
390                    create_element("body", vec![], vec![]),
391                    create_element("head", vec![], vec![]),
392                ],
393            );
394
395            assert_eq!(
396                validate_html_element(&element, &[], &[]),
397                Err(MarkupError::InvalidElement {
398                    invalid_attributes: Default::default(),
399                    invalid_children: [("body".into(), [ChildError::Misplaced].into())].into(),
400                    missing_attributes: Default::default(),
401                    missing_children: Default::default(),
402                })
403            );
404        }
405    }
406
407    mod head {
408        use super::*;
409
410        #[test]
411        fn validate_valid_child() {
412            let element = create_element(
413                "head",
414                vec![],
415                vec![create_element("title", vec![], vec![])],
416            );
417
418            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
419        }
420
421        #[test]
422        fn validate_missing_title() {
423            let element = create_element("head", vec![], vec![]);
424
425            assert_eq!(
426                validate_html_element(&element, &[], &[]),
427                Err(MarkupError::InvalidElement {
428                    invalid_attributes: Default::default(),
429                    invalid_children: Default::default(),
430                    missing_attributes: Default::default(),
431                    missing_children: ["title".into()].into(),
432                })
433            );
434        }
435
436        #[test]
437        fn validate_invalid_child() {
438            let element = create_element("head", vec![], vec![create_element("p", vec![], vec![])]);
439
440            assert_eq!(
441                validate_html_element(&element, &[], &[]),
442                Err(MarkupError::InvalidElement {
443                    invalid_attributes: Default::default(),
444                    invalid_children: [("p".into(), [ChildError::NotAllowed].into())].into(),
445                    missing_attributes: Default::default(),
446                    missing_children: ["title".into()].into(),
447                })
448            );
449        }
450    }
451
452    mod title {
453        use super::*;
454
455        #[test]
456        fn validate_invalid_child() {
457            let element =
458                create_element("title", vec![], vec![create_element("div", vec![], vec![])]);
459
460            assert_eq!(
461                validate_html_element(&element, &[], &[]),
462                Err(MarkupError::InvalidElement {
463                    invalid_attributes: Default::default(),
464                    invalid_children: [("div".into(), [ChildError::NotAllowed].into())].into(),
465                    missing_attributes: Default::default(),
466                    missing_children: Default::default(),
467                })
468            );
469        }
470    }
471
472    mod ul {
473        use super::*;
474
475        #[test]
476        fn validate_valid_child() {
477            let element = create_element("ul", vec![], vec![create_element("li", vec![], vec![])]);
478
479            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
480        }
481
482        #[test]
483        fn validate_whitespace_text() {
484            let element = Element::new(
485                "ul".into(),
486                vec![],
487                vec![
488                    Arc::new(Node::Text("\n    ".into())),
489                    Arc::new(Node::Element(create_element("li", vec![], vec![]))),
490                ],
491            );
492
493            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
494        }
495
496        #[test]
497        fn validate_invalid_text() {
498            let element = Element::new(
499                "ul".into(),
500                vec![],
501                vec![
502                    Arc::new(Node::Text("orphan".into())),
503                    Arc::new(Node::Element(create_element("li", vec![], vec![]))),
504                ],
505            );
506
507            assert_eq!(
508                validate_html_element(&element, &[], &[]),
509                Err(MarkupError::InvalidElement {
510                    invalid_attributes: Default::default(),
511                    invalid_children: [("#text".into(), [ChildError::NotAllowed].into())].into(),
512                    missing_attributes: Default::default(),
513                    missing_children: Default::default(),
514                })
515            );
516        }
517
518        #[test]
519        fn validate_invalid_child() {
520            let element = create_element("ul", vec![], vec![create_element("p", vec![], vec![])]);
521
522            assert_eq!(
523                validate_html_element(&element, &[], &[]),
524                Err(MarkupError::InvalidElement {
525                    invalid_attributes: Default::default(),
526                    invalid_children: [("p".into(), [ChildError::NotAllowed].into())].into(),
527                    missing_attributes: Default::default(),
528                    missing_children: Default::default(),
529                })
530            );
531        }
532    }
533
534    mod table {
535        use super::*;
536
537        #[test]
538        fn validate_valid_child() {
539            let element =
540                create_element("table", vec![], vec![create_element("tr", vec![], vec![])]);
541
542            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
543        }
544
545        #[test]
546        fn validate_valid_ordered_children() {
547            let element = create_element(
548                "table",
549                vec![],
550                vec![
551                    create_element("caption", vec![], vec![]),
552                    create_element("thead", vec![], vec![]),
553                    create_element("tbody", vec![], vec![]),
554                    create_element("tfoot", vec![], vec![]),
555                ],
556            );
557
558            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
559        }
560
561        #[test]
562        fn validate_misplaced_child() {
563            let element = create_element(
564                "table",
565                vec![],
566                vec![
567                    create_element("tfoot", vec![], vec![]),
568                    create_element("thead", vec![], vec![]),
569                ],
570            );
571
572            assert_eq!(
573                validate_html_element(&element, &[], &[]),
574                Err(MarkupError::InvalidElement {
575                    invalid_attributes: Default::default(),
576                    invalid_children: [("thead".into(), [ChildError::Misplaced].into())].into(),
577                    missing_attributes: Default::default(),
578                    missing_children: Default::default(),
579                })
580            );
581        }
582
583        #[test]
584        fn validate_invalid_child() {
585            let element =
586                create_element("table", vec![], vec![create_element("p", vec![], vec![])]);
587
588            assert_eq!(
589                validate_html_element(&element, &[], &[]),
590                Err(MarkupError::InvalidElement {
591                    invalid_attributes: Default::default(),
592                    invalid_children: [("p".into(), [ChildError::NotAllowed].into())].into(),
593                    missing_attributes: Default::default(),
594                    missing_children: Default::default(),
595                })
596            );
597        }
598    }
599
600    mod tr {
601        use super::*;
602
603        #[test]
604        fn validate_valid_children() {
605            let element = create_element(
606                "tr",
607                vec![],
608                vec![
609                    create_element("th", vec![], vec![]),
610                    create_element("td", vec![], vec![]),
611                ],
612            );
613
614            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
615        }
616    }
617
618    mod form {
619        use super::*;
620
621        #[test]
622        fn validate_valid_attributes() {
623            let element = create_element("form", vec![("action", "/"), ("method", "post")], vec![]);
624
625            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
626        }
627
628        #[test]
629        fn validate_valid_child() {
630            let element = create_element(
631                "form",
632                vec![],
633                vec![create_element("input", vec![], vec![])],
634            );
635
636            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
637        }
638    }
639
640    mod img {
641        use super::*;
642
643        #[test]
644        fn validate_valid_attributes() {
645            let element = create_element(
646                "img",
647                vec![("src", "img.png"), ("alt", "description")],
648                vec![],
649            );
650
651            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
652        }
653    }
654
655    mod picture {
656        use super::*;
657
658        #[test]
659        fn validate_missing_child() {
660            let element = create_element("picture", vec![], vec![]);
661
662            assert_eq!(
663                validate_html_element(&element, &[], &[]),
664                Err(MarkupError::InvalidElement {
665                    invalid_attributes: Default::default(),
666                    invalid_children: Default::default(),
667                    missing_attributes: Default::default(),
668                    missing_children: ["img".into()].into(),
669                })
670            );
671        }
672
673        #[test]
674        fn validate_valid_children() {
675            let element = create_element(
676                "picture",
677                vec![],
678                vec![
679                    create_element("source", vec![], vec![]),
680                    create_element("img", vec![], vec![]),
681                ],
682            );
683
684            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
685        }
686
687        #[test]
688        fn validate_misplaced_child() {
689            let element = create_element(
690                "picture",
691                vec![],
692                vec![
693                    create_element("img", vec![], vec![]),
694                    create_element("source", vec![], vec![]),
695                ],
696            );
697
698            assert_eq!(
699                validate_html_element(&element, &[], &[]),
700                Err(MarkupError::InvalidElement {
701                    invalid_attributes: Default::default(),
702                    invalid_children: [("source".into(), [ChildError::Misplaced].into())].into(),
703                    missing_attributes: Default::default(),
704                    missing_children: Default::default(),
705                })
706            );
707        }
708
709        #[test]
710        fn validate_invalid_child() {
711            let element =
712                create_element("picture", vec![], vec![create_element("p", vec![], vec![])]);
713
714            assert_eq!(
715                validate_html_element(&element, &[], &[]),
716                Err(MarkupError::InvalidElement {
717                    invalid_attributes: Default::default(),
718                    invalid_children: [("p".into(), [ChildError::NotAllowed].into())].into(),
719                    missing_attributes: Default::default(),
720                    missing_children: ["img".into()].into(),
721                })
722            );
723        }
724    }
725
726    mod video {
727        use super::*;
728
729        #[test]
730        fn validate_valid_attributes() {
731            let element =
732                create_element("video", vec![("src", "vid.mp4"), ("controls", "")], vec![]);
733
734            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
735        }
736
737        #[test]
738        fn validate_valid_child() {
739            let element = create_element(
740                "video",
741                vec![],
742                vec![create_element("track", vec![], vec![])],
743            );
744
745            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
746        }
747    }
748
749    mod meta {
750        use super::*;
751
752        #[test]
753        fn validate_valid_name_content() {
754            let element = create_element(
755                "meta",
756                vec![("name", "description"), ("content", "stuff")],
757                vec![],
758            );
759
760            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
761        }
762
763        #[test]
764        fn validate_valid_charset() {
765            let element = create_element("meta", vec![("charset", "utf-8")], vec![]);
766
767            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
768        }
769
770        #[test]
771        fn validate_valid_property() {
772            let element = create_element(
773                "meta",
774                vec![("property", "og:image"), ("content", "image.png")],
775                vec![],
776            );
777
778            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
779        }
780
781        #[test]
782        fn validate_missing_content() {
783            let element = create_element("meta", vec![("name", "description")], vec![]);
784
785            assert_eq!(
786                validate_html_element(&element, &[], &[]),
787                Err(MarkupError::InvalidElement {
788                    invalid_attributes: Default::default(),
789                    invalid_children: Default::default(),
790                    missing_attributes: ["content".into()].into(),
791                    missing_children: Default::default(),
792                })
793            );
794        }
795
796        #[test]
797        fn validate_conflicting_charset() {
798            let element = create_element(
799                "meta",
800                vec![
801                    ("charset", "utf-8"),
802                    ("name", "description"),
803                    ("content", "stuff"),
804                ],
805                vec![],
806            );
807
808            assert_eq!(
809                validate_html_element(&element, &[], &[]),
810                Err(MarkupError::InvalidElement {
811                    invalid_attributes: [("charset".into(), [AttributeError::Conflict].into())]
812                        .into(),
813                    invalid_children: Default::default(),
814                    missing_attributes: Default::default(),
815                    missing_children: Default::default(),
816                })
817            );
818        }
819    }
820
821    mod link {
822        use super::*;
823
824        #[test]
825        fn validate_valid_attributes() {
826            let element = create_element(
827                "link",
828                vec![("rel", "stylesheet"), ("href", "style.css")],
829                vec![],
830            );
831
832            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
833        }
834
835        #[test]
836        fn validate_valid_rel_without_href() {
837            let element = create_element("link", vec![("rel", "preload")], vec![]);
838
839            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
840        }
841
842        #[test]
843        fn validate_missing_rel() {
844            let element = create_element("link", vec![("href", "style.css")], vec![]);
845
846            // The schema alternatively requires either the `rel` attribute or
847            // the `itemprop` attribute, and one minimal diagnosis is reported.
848            assert_eq!(
849                validate_html_element(&element, &[], &[]),
850                Err(MarkupError::InvalidElement {
851                    invalid_attributes: Default::default(),
852                    invalid_children: Default::default(),
853                    missing_attributes: ["itemprop".into()].into(),
854                    missing_children: Default::default(),
855                })
856            );
857        }
858    }
859
860    mod svg {
861        use super::*;
862
863        #[test]
864        fn validate_valid_element() {
865            let element = create_element("svg", vec![], vec![]);
866
867            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
868        }
869
870        #[test]
871        fn validate_valid_attributes() {
872            let element = create_element(
873                "svg",
874                vec![
875                    ("version", "1.1"),
876                    ("viewBox", "0 0 10 10"),
877                    ("width", "10"),
878                    ("height", "10"),
879                ],
880                vec![],
881            );
882
883            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
884        }
885
886        #[test]
887        fn validate_valid_aria_attributes() {
888            let element = create_element(
889                "svg",
890                vec![("role", "img"), ("aria-label", "description")],
891                vec![],
892            );
893
894            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
895        }
896
897        #[test]
898        fn validate_valid_child() {
899            let element = create_element(
900                "svg",
901                vec![],
902                vec![create_element("circle", vec![("r", "1")], vec![])],
903            );
904
905            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
906        }
907
908        #[test]
909        fn validate_valid_text_child() {
910            let element = Element::new(
911                "text".into(),
912                vec![],
913                vec![Arc::new(Node::Text("hello".into()))],
914            );
915
916            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
917        }
918
919        #[test]
920        fn validate_valid_camel_case_element() {
921            let element = create_element(
922                "linearGradient",
923                vec![("id", "gradient")],
924                vec![create_element("stop", vec![("offset", "0")], vec![])],
925            );
926
927            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
928        }
929
930        #[test]
931        fn validate_valid_link() {
932            let element = create_element(
933                "a",
934                vec![("href", "/")],
935                vec![create_element("rect", vec![], vec![])],
936            );
937
938            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
939        }
940
941        #[test]
942        fn validate_valid_image_link() {
943            let element = create_element("image", vec![("href", "/foo.png")], vec![]);
944
945            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
946        }
947
948        #[test]
949        fn validate_valid_prefixed_image_link() {
950            let element = create_element("image", vec![("xlink:href", "/foo.png")], vec![]);
951
952            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
953        }
954
955        #[test]
956        fn validate_valid_foreign_object() {
957            let element = create_element(
958                "foreignObject",
959                vec![],
960                vec![create_element("div", vec![], vec![])],
961            );
962
963            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
964        }
965
966        #[test]
967        fn validate_invalid_foreign_object_child() {
968            let element = create_element(
969                "foreignObject",
970                vec![],
971                vec![create_element("title", vec![], vec![])],
972            );
973
974            assert_eq!(
975                validate_html_element(&element, &[], &[]),
976                Err(MarkupError::InvalidElement {
977                    invalid_attributes: Default::default(),
978                    invalid_children: [("title".into(), [ChildError::NotAllowed].into())].into(),
979                    missing_attributes: Default::default(),
980                    missing_children: Default::default(),
981                })
982            );
983        }
984
985        #[test]
986        fn validate_invalid_element_name() {
987            let element = create_element("invalid", vec![], vec![]);
988
989            assert_eq!(
990                validate_html_element(&element, &[], &[]),
991                Err(MarkupError::UnknownTag("invalid".to_owned()))
992            );
993        }
994
995        #[test]
996        fn validate_invalid_attribute() {
997            let element = create_element("circle", vec![("invalid", "foo")], vec![]);
998
999            assert_eq!(
1000                validate_html_element(&element, &[], &[]),
1001                Err(MarkupError::InvalidElement {
1002                    invalid_attributes: [("invalid".into(), [AttributeError::NotAllowed].into())]
1003                        .into(),
1004                    invalid_children: Default::default(),
1005                    missing_attributes: Default::default(),
1006                    missing_children: Default::default(),
1007                })
1008            );
1009        }
1010
1011        #[test]
1012        fn validate_invalid_child() {
1013            let element =
1014                create_element("svg", vec![], vec![create_element("html", vec![], vec![])]);
1015
1016            assert_eq!(
1017                validate_html_element(&element, &[], &[]),
1018                Err(MarkupError::InvalidElement {
1019                    invalid_attributes: Default::default(),
1020                    invalid_children: [("html".into(), [ChildError::NotAllowed].into())].into(),
1021                    missing_attributes: Default::default(),
1022                    missing_children: Default::default(),
1023                })
1024            );
1025        }
1026
1027        #[test]
1028        fn validate_missing_attribute() {
1029            let element = create_element("animate", vec![], vec![]);
1030
1031            assert_eq!(
1032                validate_html_element(&element, &[], &[]),
1033                Err(MarkupError::InvalidElement {
1034                    invalid_attributes: Default::default(),
1035                    invalid_children: Default::default(),
1036                    missing_attributes: ["attributeName".into()].into(),
1037                    missing_children: Default::default(),
1038                })
1039            );
1040        }
1041
1042        #[test]
1043        fn validate_ignored_attribute_regex() {
1044            let element = create_element("circle", vec![("data-foo", "bar")], vec![]);
1045
1046            assert_eq!(
1047                validate_html_element(&element, &[Regex::new("^data-.*$").unwrap()], &[]),
1048                Ok(())
1049            );
1050        }
1051
1052        #[test]
1053        fn validate_ignored_element_regex() {
1054            let element = create_element("foreignObject", vec![], vec![]);
1055
1056            assert_eq!(
1057                validate_html_element(&element, &[], &[Regex::new("^foreignObject$").unwrap()]),
1058                Ok(())
1059            );
1060        }
1061    }
1062
1063    mod math {
1064        use super::*;
1065
1066        #[test]
1067        fn validate_valid_element() {
1068            let element = create_element("math", vec![], vec![]);
1069
1070            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1071        }
1072
1073        #[test]
1074        fn validate_valid_attributes() {
1075            let element =
1076                create_element("math", vec![("display", "block"), ("alttext", "x")], vec![]);
1077
1078            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1079        }
1080
1081        #[test]
1082        fn validate_valid_aria_attributes() {
1083            let element = create_element(
1084                "math",
1085                vec![("role", "math"), ("aria-label", "description")],
1086                vec![],
1087            );
1088
1089            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1090        }
1091
1092        #[test]
1093        fn validate_valid_child() {
1094            let element =
1095                create_element("math", vec![], vec![create_element("mi", vec![], vec![])]);
1096
1097            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1098        }
1099
1100        #[test]
1101        fn validate_valid_text_child() {
1102            let element = Element::new("mi".into(), vec![], vec![Arc::new(Node::Text("x".into()))]);
1103
1104            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1105        }
1106
1107        #[test]
1108        fn validate_valid_fraction_children() {
1109            let element = create_element(
1110                "mfrac",
1111                vec![],
1112                vec![
1113                    create_element("mi", vec![], vec![]),
1114                    create_element("mn", vec![], vec![]),
1115                ],
1116            );
1117
1118            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1119        }
1120
1121        #[test]
1122        fn validate_valid_annotated_children() {
1123            let element = create_element(
1124                "semantics",
1125                vec![],
1126                vec![
1127                    create_element("mi", vec![], vec![]),
1128                    create_element(
1129                        "annotation",
1130                        vec![("encoding", "application/x-tex")],
1131                        vec![],
1132                    ),
1133                ],
1134            );
1135
1136            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1137        }
1138
1139        #[test]
1140        fn validate_valid_html_child_of_token_element() {
1141            let element = create_element(
1142                "mtext",
1143                vec![],
1144                vec![create_element("span", vec![], vec![])],
1145            );
1146
1147            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1148        }
1149
1150        #[test]
1151        fn validate_invalid_attribute() {
1152            let element = create_element("math", vec![("invalid", "foo")], vec![]);
1153
1154            assert_eq!(
1155                validate_html_element(&element, &[], &[]),
1156                Err(MarkupError::InvalidElement {
1157                    invalid_attributes: [("invalid".into(), [AttributeError::NotAllowed].into())]
1158                        .into(),
1159                    invalid_children: Default::default(),
1160                    missing_attributes: Default::default(),
1161                    missing_children: Default::default(),
1162                })
1163            );
1164        }
1165
1166        #[test]
1167        fn validate_invalid_child() {
1168            let element =
1169                create_element("math", vec![], vec![create_element("div", vec![], vec![])]);
1170
1171            assert_eq!(
1172                validate_html_element(&element, &[], &[]),
1173                Err(MarkupError::InvalidElement {
1174                    invalid_attributes: Default::default(),
1175                    invalid_children: [("div".into(), [ChildError::NotAllowed].into())].into(),
1176                    missing_attributes: Default::default(),
1177                    missing_children: Default::default(),
1178                })
1179            );
1180        }
1181    }
1182
1183    mod noscript {
1184        use super::*;
1185
1186        #[test]
1187        fn validate_valid_element() {
1188            let element = create_element("noscript", vec![], vec![]);
1189
1190            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1191        }
1192
1193        #[test]
1194        fn validate_valid_flow_child() {
1195            let element = create_element(
1196                "noscript",
1197                vec![],
1198                vec![create_element("div", vec![], vec![])],
1199            );
1200
1201            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1202        }
1203
1204        #[test]
1205        fn validate_valid_head_child() {
1206            let element = create_element(
1207                "noscript",
1208                vec![],
1209                vec![create_element("link", vec![], vec![])],
1210            );
1211
1212            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1213        }
1214
1215        #[test]
1216        fn validate_invalid_child() {
1217            let element = create_element(
1218                "noscript",
1219                vec![],
1220                vec![create_element("title", vec![], vec![])],
1221            );
1222
1223            assert_eq!(
1224                validate_html_element(&element, &[], &[]),
1225                Err(MarkupError::InvalidElement {
1226                    invalid_attributes: Default::default(),
1227                    invalid_children: [("title".into(), [ChildError::NotAllowed].into())].into(),
1228                    missing_attributes: Default::default(),
1229                    missing_children: Default::default(),
1230                })
1231            );
1232        }
1233    }
1234}