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