1extern 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 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}