"), ["
"]);
+ assert_eq!(tokens(""), ["
"]);
+ // A `>` inside a quoted value does not end the tag.
+ assert_eq!(tokens(r#"t"#), ["b>", "T:t", ""]);
+}
+
+#[test]
+fn attribute_values_are_decoded_on_demand() {
+ let mut t = Lexer::new(r#""#);
+ let Some(Token::Start(tag)) = t.next() else { panic!("no tag") };
+ assert_eq!(tag.attr("href").as_deref(), Some("?a=1&b=2©=3"));
+ assert_eq!(tag.raw_attr("href"), Some("?a=1&b=2©=3"));
+ assert_eq!(tag.attr("missing"), None);
+ assert!(tag.has("checked") && !tag.has("check"));
+ assert_eq!(tag.attributes().map(|(n, _)| n).collect:: x"), [" ", "T:x"]);
+ assert_eq!(tokens("x"), ["T:x"]);
+ assert_eq!(tokens("abc"), ["T:a", "T:b", "T:c"]);
+ assert_eq!(tokens("shown"), ["T:shown"]);
+ assert_eq!(tokens("shown"), ["T:shown"]);
+ // An unfinished comment swallows the rest.
+ assert_eq!(tokens("a
", "
", "", "
", ""] {
+ assert!(closes(yes), "{yes}");
+ }
+ // A `/` that ends an unquoted value, or stands anywhere but before the `>`, is not it.
+ for no in ["
", "
", "", "", "
d"), ["T:a < b and c > d"]);
+ assert_eq!(tokens("1<2<3"), ["T:1<2<3"]);
+ assert_eq!(tokens("<"), ["T:<"]);
+ assert_eq!(tokens("x<"), ["T:x<"]);
+ assert_eq!(tokens("<3 <- x"), ["T:<3 <- x"]);
+ assert_eq!(tokens("a>b"), ["T:a", "T:b"]);
+}
+
+#[test]
+fn comments_and_declarations_vanish() {
+ assert_eq!(tokens("ab"), ["T:a", "T:b"]);
+ assert_eq!(tokens("