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
196    mod p {
197        use super::*;
198
199        #[test]
200        fn validate_valid_element() {
201            let element = create_element("p", vec![], vec![]);
202
203            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
204        }
205
206        #[test]
207        fn validate_valid_text() {
208            let element = Element::new(
209                "p".into(),
210                vec![],
211                vec![Arc::new(Node::Text("hello".into()))],
212            );
213
214            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
215        }
216
217        #[test]
218        fn validate_invalid_child() {
219            let element = create_element("p", vec![], vec![create_element("div", vec![], vec![])]);
220
221            assert_eq!(
222                validate_html_element(&element, &[], &[]),
223                Err(MarkupError::InvalidElement {
224                    invalid_attributes: Default::default(),
225                    invalid_children: [("div".into(), [ChildError::NotAllowed].into())].into(),
226                    missing_attributes: Default::default(),
227                    missing_children: Default::default(),
228                })
229            );
230        }
231
232        #[test]
233        fn validate_multiple_invalid_children() {
234            let element = create_element(
235                "p",
236                vec![],
237                vec![
238                    create_element("div", vec![], vec![]),
239                    create_element("table", vec![], vec![]),
240                ],
241            );
242
243            assert_eq!(
244                validate_html_element(&element, &[], &[]),
245                Err(MarkupError::InvalidElement {
246                    invalid_attributes: Default::default(),
247                    invalid_children: [
248                        ("div".into(), [ChildError::NotAllowed].into()),
249                        ("table".into(), [ChildError::NotAllowed].into()),
250                    ]
251                    .into(),
252                    missing_attributes: Default::default(),
253                    missing_children: Default::default(),
254                })
255            );
256        }
257
258        #[test]
259        fn validate_ignored_known_tag_regex() {
260            let element = create_element("p", vec![], vec![create_element("div", vec![], vec![])]);
261
262            assert_eq!(
263                validate_html_element(&element, &[], &[Regex::new("^p$").unwrap()]),
264                Ok(())
265            );
266        }
267    }
268
269    mod a {
270        use super::*;
271
272        #[test]
273        fn validate_valid_link() {
274            let element = create_element("a", vec![("href", "/")], vec![]);
275
276            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
277        }
278
279        #[test]
280        fn validate_valid_prefixed_link() {
281            let element = create_element("a", vec![("xlink:href", "/")], vec![]);
282
283            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
284        }
285
286        #[test]
287        fn validate_valid_placeholder_link() {
288            let element = create_element("a", vec![], vec![]);
289
290            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
291        }
292
293        #[test]
294        fn validate_missing_href() {
295            let element = create_element("a", vec![("download", "")], vec![]);
296
297            assert_eq!(
298                validate_html_element(&element, &[], &[]),
299                Err(MarkupError::InvalidElement {
300                    invalid_attributes: Default::default(),
301                    invalid_children: Default::default(),
302                    missing_attributes: ["href".into()].into(),
303                    missing_children: Default::default(),
304                })
305            );
306        }
307    }
308
309    mod mark {
310        use super::*;
311
312        #[test]
313        fn validate_valid_element() {
314            let element = create_element("mark", vec![], vec![]);
315
316            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
317        }
318
319        #[test]
320        fn validate_valid_child() {
321            let element =
322                create_element("mark", vec![], vec![create_element("span", vec![], vec![])]);
323
324            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
325        }
326
327        #[test]
328        fn validate_invalid_child() {
329            let element =
330                create_element("mark", vec![], vec![create_element("div", vec![], vec![])]);
331
332            assert_eq!(
333                validate_html_element(&element, &[], &[]),
334                Err(MarkupError::InvalidElement {
335                    invalid_attributes: Default::default(),
336                    invalid_children: [("div".into(), [ChildError::NotAllowed].into())].into(),
337                    missing_attributes: Default::default(),
338                    missing_children: Default::default(),
339                })
340            );
341        }
342    }
343
344    mod html {
345        use super::*;
346
347        #[test]
348        fn validate_missing_children() {
349            let element = create_element("html", vec![], vec![]);
350
351            assert_eq!(
352                validate_html_element(&element, &[], &[]),
353                Err(MarkupError::InvalidElement {
354                    invalid_attributes: Default::default(),
355                    invalid_children: Default::default(),
356                    missing_attributes: Default::default(),
357                    missing_children: ["head".into()].into(),
358                })
359            );
360        }
361
362        #[test]
363        fn validate_valid_children() {
364            let element = create_element(
365                "html",
366                vec![],
367                vec![
368                    create_element("head", vec![], vec![]),
369                    create_element("body", vec![], vec![]),
370                ],
371            );
372
373            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
374        }
375
376        #[test]
377        fn validate_misplaced_children() {
378            let element = create_element(
379                "html",
380                vec![],
381                vec![
382                    create_element("body", vec![], vec![]),
383                    create_element("head", vec![], vec![]),
384                ],
385            );
386
387            assert_eq!(
388                validate_html_element(&element, &[], &[]),
389                Err(MarkupError::InvalidElement {
390                    invalid_attributes: Default::default(),
391                    invalid_children: [("body".into(), [ChildError::Misplaced].into())].into(),
392                    missing_attributes: Default::default(),
393                    missing_children: Default::default(),
394                })
395            );
396        }
397    }
398
399    mod head {
400        use super::*;
401
402        #[test]
403        fn validate_valid_child() {
404            let element = create_element(
405                "head",
406                vec![],
407                vec![create_element("title", vec![], vec![])],
408            );
409
410            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
411        }
412
413        #[test]
414        fn validate_missing_title() {
415            let element = create_element("head", vec![], vec![]);
416
417            assert_eq!(
418                validate_html_element(&element, &[], &[]),
419                Err(MarkupError::InvalidElement {
420                    invalid_attributes: Default::default(),
421                    invalid_children: Default::default(),
422                    missing_attributes: Default::default(),
423                    missing_children: ["title".into()].into(),
424                })
425            );
426        }
427
428        #[test]
429        fn validate_invalid_child() {
430            let element = create_element("head", vec![], vec![create_element("p", vec![], vec![])]);
431
432            assert_eq!(
433                validate_html_element(&element, &[], &[]),
434                Err(MarkupError::InvalidElement {
435                    invalid_attributes: Default::default(),
436                    invalid_children: [("p".into(), [ChildError::NotAllowed].into())].into(),
437                    missing_attributes: Default::default(),
438                    missing_children: ["title".into()].into(),
439                })
440            );
441        }
442    }
443
444    mod title {
445        use super::*;
446
447        #[test]
448        fn validate_invalid_child() {
449            let element =
450                create_element("title", vec![], vec![create_element("div", vec![], vec![])]);
451
452            assert_eq!(
453                validate_html_element(&element, &[], &[]),
454                Err(MarkupError::InvalidElement {
455                    invalid_attributes: Default::default(),
456                    invalid_children: [("div".into(), [ChildError::NotAllowed].into())].into(),
457                    missing_attributes: Default::default(),
458                    missing_children: Default::default(),
459                })
460            );
461        }
462    }
463
464    mod ul {
465        use super::*;
466
467        #[test]
468        fn validate_valid_child() {
469            let element = create_element("ul", vec![], vec![create_element("li", vec![], vec![])]);
470
471            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
472        }
473
474        #[test]
475        fn validate_whitespace_text() {
476            let element = Element::new(
477                "ul".into(),
478                vec![],
479                vec![
480                    Arc::new(Node::Text("\n    ".into())),
481                    Arc::new(Node::Element(create_element("li", vec![], vec![]))),
482                ],
483            );
484
485            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
486        }
487
488        #[test]
489        fn validate_invalid_text() {
490            let element = Element::new(
491                "ul".into(),
492                vec![],
493                vec![
494                    Arc::new(Node::Text("orphan".into())),
495                    Arc::new(Node::Element(create_element("li", vec![], vec![]))),
496                ],
497            );
498
499            assert_eq!(
500                validate_html_element(&element, &[], &[]),
501                Err(MarkupError::InvalidElement {
502                    invalid_attributes: Default::default(),
503                    invalid_children: [("#text".into(), [ChildError::NotAllowed].into())].into(),
504                    missing_attributes: Default::default(),
505                    missing_children: Default::default(),
506                })
507            );
508        }
509
510        #[test]
511        fn validate_invalid_child() {
512            let element = create_element("ul", vec![], vec![create_element("p", vec![], vec![])]);
513
514            assert_eq!(
515                validate_html_element(&element, &[], &[]),
516                Err(MarkupError::InvalidElement {
517                    invalid_attributes: Default::default(),
518                    invalid_children: [("p".into(), [ChildError::NotAllowed].into())].into(),
519                    missing_attributes: Default::default(),
520                    missing_children: Default::default(),
521                })
522            );
523        }
524    }
525
526    mod table {
527        use super::*;
528
529        #[test]
530        fn validate_valid_child() {
531            let element =
532                create_element("table", vec![], vec![create_element("tr", vec![], vec![])]);
533
534            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
535        }
536
537        #[test]
538        fn validate_valid_ordered_children() {
539            let element = create_element(
540                "table",
541                vec![],
542                vec![
543                    create_element("caption", vec![], vec![]),
544                    create_element("thead", vec![], vec![]),
545                    create_element("tbody", vec![], vec![]),
546                    create_element("tfoot", vec![], vec![]),
547                ],
548            );
549
550            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
551        }
552
553        #[test]
554        fn validate_misplaced_child() {
555            let element = create_element(
556                "table",
557                vec![],
558                vec![
559                    create_element("tfoot", vec![], vec![]),
560                    create_element("thead", vec![], vec![]),
561                ],
562            );
563
564            assert_eq!(
565                validate_html_element(&element, &[], &[]),
566                Err(MarkupError::InvalidElement {
567                    invalid_attributes: Default::default(),
568                    invalid_children: [("thead".into(), [ChildError::Misplaced].into())].into(),
569                    missing_attributes: Default::default(),
570                    missing_children: Default::default(),
571                })
572            );
573        }
574
575        #[test]
576        fn validate_invalid_child() {
577            let element =
578                create_element("table", vec![], vec![create_element("p", vec![], vec![])]);
579
580            assert_eq!(
581                validate_html_element(&element, &[], &[]),
582                Err(MarkupError::InvalidElement {
583                    invalid_attributes: Default::default(),
584                    invalid_children: [("p".into(), [ChildError::NotAllowed].into())].into(),
585                    missing_attributes: Default::default(),
586                    missing_children: Default::default(),
587                })
588            );
589        }
590    }
591
592    mod tr {
593        use super::*;
594
595        #[test]
596        fn validate_valid_children() {
597            let element = create_element(
598                "tr",
599                vec![],
600                vec![
601                    create_element("th", vec![], vec![]),
602                    create_element("td", vec![], vec![]),
603                ],
604            );
605
606            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
607        }
608    }
609
610    mod form {
611        use super::*;
612
613        #[test]
614        fn validate_valid_attributes() {
615            let element = create_element("form", vec![("action", "/"), ("method", "post")], vec![]);
616
617            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
618        }
619
620        #[test]
621        fn validate_valid_child() {
622            let element = create_element(
623                "form",
624                vec![],
625                vec![create_element("input", vec![], vec![])],
626            );
627
628            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
629        }
630    }
631
632    mod img {
633        use super::*;
634
635        #[test]
636        fn validate_valid_attributes() {
637            let element = create_element(
638                "img",
639                vec![("src", "img.png"), ("alt", "description")],
640                vec![],
641            );
642
643            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
644        }
645    }
646
647    mod picture {
648        use super::*;
649
650        #[test]
651        fn validate_missing_child() {
652            let element = create_element("picture", vec![], vec![]);
653
654            assert_eq!(
655                validate_html_element(&element, &[], &[]),
656                Err(MarkupError::InvalidElement {
657                    invalid_attributes: Default::default(),
658                    invalid_children: Default::default(),
659                    missing_attributes: Default::default(),
660                    missing_children: ["img".into()].into(),
661                })
662            );
663        }
664
665        #[test]
666        fn validate_valid_children() {
667            let element = create_element(
668                "picture",
669                vec![],
670                vec![
671                    create_element("source", vec![], vec![]),
672                    create_element("img", vec![], vec![]),
673                ],
674            );
675
676            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
677        }
678
679        #[test]
680        fn validate_misplaced_child() {
681            let element = create_element(
682                "picture",
683                vec![],
684                vec![
685                    create_element("img", vec![], vec![]),
686                    create_element("source", vec![], vec![]),
687                ],
688            );
689
690            assert_eq!(
691                validate_html_element(&element, &[], &[]),
692                Err(MarkupError::InvalidElement {
693                    invalid_attributes: Default::default(),
694                    invalid_children: [("source".into(), [ChildError::Misplaced].into())].into(),
695                    missing_attributes: Default::default(),
696                    missing_children: Default::default(),
697                })
698            );
699        }
700
701        #[test]
702        fn validate_invalid_child() {
703            let element =
704                create_element("picture", vec![], vec![create_element("p", vec![], vec![])]);
705
706            assert_eq!(
707                validate_html_element(&element, &[], &[]),
708                Err(MarkupError::InvalidElement {
709                    invalid_attributes: Default::default(),
710                    invalid_children: [("p".into(), [ChildError::NotAllowed].into())].into(),
711                    missing_attributes: Default::default(),
712                    missing_children: ["img".into()].into(),
713                })
714            );
715        }
716    }
717
718    mod video {
719        use super::*;
720
721        #[test]
722        fn validate_valid_attributes() {
723            let element =
724                create_element("video", vec![("src", "vid.mp4"), ("controls", "")], vec![]);
725
726            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
727        }
728
729        #[test]
730        fn validate_valid_child() {
731            let element = create_element(
732                "video",
733                vec![],
734                vec![create_element("track", vec![], vec![])],
735            );
736
737            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
738        }
739    }
740
741    mod meta {
742        use super::*;
743
744        #[test]
745        fn validate_valid_name_content() {
746            let element = create_element(
747                "meta",
748                vec![("name", "description"), ("content", "stuff")],
749                vec![],
750            );
751
752            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
753        }
754
755        #[test]
756        fn validate_valid_charset() {
757            let element = create_element("meta", vec![("charset", "utf-8")], vec![]);
758
759            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
760        }
761
762        #[test]
763        fn validate_valid_property() {
764            let element = create_element(
765                "meta",
766                vec![("property", "og:image"), ("content", "image.png")],
767                vec![],
768            );
769
770            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
771        }
772
773        #[test]
774        fn validate_missing_content() {
775            let element = create_element("meta", vec![("name", "description")], vec![]);
776
777            assert_eq!(
778                validate_html_element(&element, &[], &[]),
779                Err(MarkupError::InvalidElement {
780                    invalid_attributes: Default::default(),
781                    invalid_children: Default::default(),
782                    missing_attributes: ["content".into()].into(),
783                    missing_children: Default::default(),
784                })
785            );
786        }
787
788        #[test]
789        fn validate_conflicting_charset() {
790            let element = create_element(
791                "meta",
792                vec![
793                    ("charset", "utf-8"),
794                    ("name", "description"),
795                    ("content", "stuff"),
796                ],
797                vec![],
798            );
799
800            assert_eq!(
801                validate_html_element(&element, &[], &[]),
802                Err(MarkupError::InvalidElement {
803                    invalid_attributes: [("charset".into(), [AttributeError::Conflict].into())]
804                        .into(),
805                    invalid_children: Default::default(),
806                    missing_attributes: Default::default(),
807                    missing_children: Default::default(),
808                })
809            );
810        }
811    }
812
813    mod link {
814        use super::*;
815
816        #[test]
817        fn validate_valid_attributes() {
818            let element = create_element(
819                "link",
820                vec![("rel", "stylesheet"), ("href", "style.css")],
821                vec![],
822            );
823
824            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
825        }
826
827        #[test]
828        fn validate_valid_rel_without_href() {
829            let element = create_element("link", vec![("rel", "preload")], vec![]);
830
831            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
832        }
833
834        #[test]
835        fn validate_missing_rel() {
836            let element = create_element("link", vec![("href", "style.css")], vec![]);
837
838            // The schema alternatively requires either the `rel` attribute or
839            // the `itemprop` attribute, and one minimal diagnosis is reported.
840            assert_eq!(
841                validate_html_element(&element, &[], &[]),
842                Err(MarkupError::InvalidElement {
843                    invalid_attributes: Default::default(),
844                    invalid_children: Default::default(),
845                    missing_attributes: ["itemprop".into()].into(),
846                    missing_children: Default::default(),
847                })
848            );
849        }
850    }
851
852    mod svg {
853        use super::*;
854
855        #[test]
856        fn validate_valid_element() {
857            let element = create_element("svg", vec![], vec![]);
858
859            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
860        }
861
862        #[test]
863        fn validate_valid_attributes() {
864            let element = create_element(
865                "svg",
866                vec![
867                    ("version", "1.1"),
868                    ("viewBox", "0 0 10 10"),
869                    ("width", "10"),
870                    ("height", "10"),
871                ],
872                vec![],
873            );
874
875            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
876        }
877
878        #[test]
879        fn validate_valid_aria_attributes() {
880            let element = create_element(
881                "svg",
882                vec![("role", "img"), ("aria-label", "description")],
883                vec![],
884            );
885
886            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
887        }
888
889        #[test]
890        fn validate_valid_child() {
891            let element = create_element(
892                "svg",
893                vec![],
894                vec![create_element("circle", vec![("r", "1")], vec![])],
895            );
896
897            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
898        }
899
900        #[test]
901        fn validate_valid_text_child() {
902            let element = Element::new(
903                "text".into(),
904                vec![],
905                vec![Arc::new(Node::Text("hello".into()))],
906            );
907
908            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
909        }
910
911        #[test]
912        fn validate_valid_camel_case_element() {
913            let element = create_element(
914                "linearGradient",
915                vec![("id", "gradient")],
916                vec![create_element("stop", vec![("offset", "0")], vec![])],
917            );
918
919            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
920        }
921
922        #[test]
923        fn validate_valid_link() {
924            let element = create_element(
925                "a",
926                vec![("href", "/")],
927                vec![create_element("rect", vec![], vec![])],
928            );
929
930            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
931        }
932
933        #[test]
934        fn validate_valid_image_link() {
935            let element = create_element("image", vec![("href", "/foo.png")], vec![]);
936
937            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
938        }
939
940        #[test]
941        fn validate_valid_prefixed_image_link() {
942            let element = create_element("image", vec![("xlink:href", "/foo.png")], vec![]);
943
944            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
945        }
946
947        #[test]
948        fn validate_valid_foreign_object() {
949            let element = create_element(
950                "foreignObject",
951                vec![],
952                vec![create_element("div", vec![], vec![])],
953            );
954
955            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
956        }
957
958        #[test]
959        fn validate_invalid_foreign_object_child() {
960            let element = create_element(
961                "foreignObject",
962                vec![],
963                vec![create_element("title", vec![], vec![])],
964            );
965
966            assert_eq!(
967                validate_html_element(&element, &[], &[]),
968                Err(MarkupError::InvalidElement {
969                    invalid_attributes: Default::default(),
970                    invalid_children: [("title".into(), [ChildError::NotAllowed].into())].into(),
971                    missing_attributes: Default::default(),
972                    missing_children: Default::default(),
973                })
974            );
975        }
976
977        #[test]
978        fn validate_invalid_element_name() {
979            let element = create_element("invalid", vec![], vec![]);
980
981            assert_eq!(
982                validate_html_element(&element, &[], &[]),
983                Err(MarkupError::UnknownTag("invalid".to_owned()))
984            );
985        }
986
987        #[test]
988        fn validate_invalid_attribute() {
989            let element = create_element("circle", vec![("invalid", "foo")], vec![]);
990
991            assert_eq!(
992                validate_html_element(&element, &[], &[]),
993                Err(MarkupError::InvalidElement {
994                    invalid_attributes: [("invalid".into(), [AttributeError::NotAllowed].into())]
995                        .into(),
996                    invalid_children: Default::default(),
997                    missing_attributes: Default::default(),
998                    missing_children: Default::default(),
999                })
1000            );
1001        }
1002
1003        #[test]
1004        fn validate_invalid_child() {
1005            let element =
1006                create_element("svg", vec![], vec![create_element("html", vec![], vec![])]);
1007
1008            assert_eq!(
1009                validate_html_element(&element, &[], &[]),
1010                Err(MarkupError::InvalidElement {
1011                    invalid_attributes: Default::default(),
1012                    invalid_children: [("html".into(), [ChildError::NotAllowed].into())].into(),
1013                    missing_attributes: Default::default(),
1014                    missing_children: Default::default(),
1015                })
1016            );
1017        }
1018
1019        #[test]
1020        fn validate_missing_attribute() {
1021            let element = create_element("animate", vec![], vec![]);
1022
1023            assert_eq!(
1024                validate_html_element(&element, &[], &[]),
1025                Err(MarkupError::InvalidElement {
1026                    invalid_attributes: Default::default(),
1027                    invalid_children: Default::default(),
1028                    missing_attributes: ["attributeName".into()].into(),
1029                    missing_children: Default::default(),
1030                })
1031            );
1032        }
1033
1034        #[test]
1035        fn validate_ignored_attribute_regex() {
1036            let element = create_element("circle", vec![("data-foo", "bar")], vec![]);
1037
1038            assert_eq!(
1039                validate_html_element(&element, &[Regex::new("^data-.*$").unwrap()], &[]),
1040                Ok(())
1041            );
1042        }
1043
1044        #[test]
1045        fn validate_ignored_element_regex() {
1046            let element = create_element("foreignObject", vec![], vec![]);
1047
1048            assert_eq!(
1049                validate_html_element(&element, &[], &[Regex::new("^foreignObject$").unwrap()]),
1050                Ok(())
1051            );
1052        }
1053    }
1054
1055    mod noscript {
1056        use super::*;
1057
1058        #[test]
1059        fn validate_valid_element() {
1060            let element = create_element("noscript", vec![], vec![]);
1061
1062            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1063        }
1064
1065        #[test]
1066        fn validate_valid_flow_child() {
1067            let element = create_element(
1068                "noscript",
1069                vec![],
1070                vec![create_element("div", vec![], vec![])],
1071            );
1072
1073            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1074        }
1075
1076        #[test]
1077        fn validate_valid_head_child() {
1078            let element = create_element(
1079                "noscript",
1080                vec![],
1081                vec![create_element("link", vec![], vec![])],
1082            );
1083
1084            assert_eq!(validate_html_element(&element, &[], &[]), Ok(()));
1085        }
1086
1087        #[test]
1088        fn validate_invalid_child() {
1089            let element = create_element(
1090                "noscript",
1091                vec![],
1092                vec![create_element("title", vec![], vec![])],
1093            );
1094
1095            assert_eq!(
1096                validate_html_element(&element, &[], &[]),
1097                Err(MarkupError::InvalidElement {
1098                    invalid_attributes: Default::default(),
1099                    invalid_children: [("title".into(), [ChildError::NotAllowed].into())].into(),
1100                    missing_attributes: Default::default(),
1101                    missing_children: Default::default(),
1102                })
1103            );
1104        }
1105    }
1106}