mod utils;
use utils::parse;
#[test]
fn test_attribute_standard() {
assert_eq!(
parse(r#"<div class="foo"></div>"#),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_attribute_css_custom_property_name() {
assert_eq!(
parse(r#"<div --rail-color="rgb(0, 0, 0)"></div>"#),
r#"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value)))) (end_tag name: (tag_name))))"#
);
}
#[test]
fn test_multiline_attribute_css_custom_property_name() {
assert_eq!(
parse("<div\n\tid=\"x\"\n\t--rail-color=\"rgb(0, 0, 0)\"\n/>"),
r#"(document (element (self_closing_tag name: (tag_name) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value))) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value))))))"#
);
}
#[test]
fn test_multiline_attributes_after_blank_line_whitespace_matrix() {
let cases = [
("lf_spaces", "\n \n"),
("lf_tabs", "\n\t\t\n"),
("lf_mixed", "\n \t \t\n"),
("lf_multiple_blank_lines", "\n \n\t\t\n \n"),
("crlf_spaces", "\r\n \r\n"),
("crlf_tabs", "\r\n\t\t\r\n"),
("cr_only_spaces", "\r \r"),
("space_before_lf_spaces", " \n \n"),
("form_feed", "\n\x0c\n"),
("vertical_tab", "\n\x0b\n"),
("nbsp_around_lf", "\u{00A0}\n\u{00A0}\n"),
("unicode_line_separator_tabs", "\u{2028}\t\t\u{2028}"),
("unicode_paragraph_separator_tabs", "\u{2029}\t\t\u{2029}"),
("unicode_next_line_tabs", "\u{0085}\t\t\u{0085}"),
("zero_width_space_around_lf", "\u{200B}\n\u{200B}\n"),
("bom_around_lf", "\u{FEFF}\n\u{FEFF}\n"),
];
let expected = r#"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value))) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value)))) (end_tag name: (tag_name))))"#;
for (name, gap) in cases {
let source =
format!("<button{gap} type=\"button\"\n class=\"x\"\n ></button>");
assert_eq!(parse(&source), expected, "{name}");
}
}
#[test]
fn test_attribute_expression_value() {
assert_eq!(
parse("<input value={text} />"),
"(document (element (self_closing_tag name: (tag_name) (attribute name: (attribute_name) value: (expression content: (js))))))"
);
}
#[test]
fn test_tag_line_comment_between_attributes() {
assert_eq!(
parse("<div // comment\n class=\"x\" />"),
"(document (element (self_closing_tag name: (tag_name) (tag_comment kind: (line_comment)) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value))))))"
);
}
#[test]
fn test_tag_block_comment_between_attributes() {
assert_eq!(
parse("<span /* inline */ data-one=\"1\"></span>"),
"(document (element (start_tag name: (tag_name) (tag_comment kind: (block_comment)) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_tag_comment_in_namespaced_tag() {
assert_eq!(
parse("<svelte:head // note\n data-x=\"1\"></svelte:head>"),
"(document (element (start_tag name: (tag_name namespace: (tag_namespace) name: (tag_local_name)) (tag_comment kind: (line_comment)) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value)))) (end_tag name: (tag_name namespace: (tag_namespace) name: (tag_local_name)))))"
);
}
#[test]
fn test_tag_comment_in_member_tag() {
assert_eq!(
parse("<UI.Button /* note */ data-x=\"1\" />"),
"(document (element (self_closing_tag name: (tag_name object: (tag_member) property: (tag_member)) (tag_comment kind: (block_comment)) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value))))))"
);
}
#[test]
fn test_tag_line_comment_stops_before_tag_close() {
assert_eq!(
parse("<div // note></div>"),
"(document (element (start_tag name: (tag_name) (tag_comment kind: (line_comment))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_shorthand_attribute() {
assert_eq!(
parse("<div {hidden}></div>"),
"(document (element (start_tag name: (tag_name) (attribute (shorthand_attribute content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_shorthand_attribute_multiple() {
assert_eq!(
parse("<div {id} {hidden}></div>"),
"(document (element (start_tag name: (tag_name) (attribute (shorthand_attribute content: (js))) (attribute (shorthand_attribute content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_multiline_shorthand_attribute() {
assert_eq!(
parse("<div\n {hidden}\n></div>"),
"(document (element (start_tag name: (tag_name) (attribute (shorthand_attribute content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_spread_attribute() {
assert_eq!(
parse("<Component {...props} />"),
"(document (element (self_closing_tag name: (tag_name) (attribute (shorthand_attribute content: (js))))))"
);
}
#[test]
fn test_spread_attribute_with_others() {
assert_eq!(
parse(r#"<Component id="main" {...props} />"#),
"(document (element (self_closing_tag name: (tag_name) (attribute name: (attribute_name) value: (quoted_attribute_value (attribute_value))) (attribute (shorthand_attribute content: (js))))))"
);
}
#[test]
fn test_spread_attribute_nested_braces() {
assert_eq!(
parse("<input {...({})} onfocus={() => console.log('x')} />"),
"(document (element (self_closing_tag name: (tag_name) (attribute (shorthand_attribute content: (js))) (attribute name: (attribute_name) value: (expression content: (js))))))"
);
}
#[test]
fn test_spread_attribute_complex_expression() {
assert_eq!(
parse("<div {...{a: 1, b: 2}} />"),
"(document (element (self_closing_tag name: (tag_name) (attribute (shorthand_attribute content: (js))))))"
);
}
#[test]
fn test_spread_and_shorthand_together() {
assert_eq!(
parse("<input {...rest} {value} />"),
"(document (element (self_closing_tag name: (tag_name) (attribute (shorthand_attribute content: (js))) (attribute (shorthand_attribute content: (js))))))"
);
}
#[test]
fn test_directive_bind() {
assert_eq!(
parse("<input bind:value={name} />"),
"(document (element (self_closing_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)) value: (expression content: (js))))))"
);
}
#[test]
fn test_directive_on() {
assert_eq!(
parse("<button on:click={handleClick}></button>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)) value: (expression content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_with_modifiers() {
assert_eq!(
parse("<button on:click|preventDefault|stopPropagation={handler}></button>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier) (attribute_modifiers (attribute_modifier) (attribute_modifier))) value: (expression content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_class() {
assert_eq!(
parse("<div class:active={isActive}></div>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)) value: (expression content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_class_with_colon_in_name() {
assert_eq!(
parse("<div class:foo:bar={enabled}></div>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)) value: (expression content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_style() {
assert_eq!(
parse("<div style:color={textColor}></div>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)) value: (expression content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_style_custom_property() {
assert_eq!(
parse("<div style:--color={textColor}></div>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)) value: (expression content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_style_unquoted_mixed_value() {
assert_eq!(
parse("<div style:attr=string{mixed}></div>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)) value: (unquoted_attribute_value (attribute_value) (expression content: (js))))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_use() {
assert_eq!(
parse("<div use:tooltip></div>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_use_store_member_action() {
assert_eq!(
parse("<div use:$store.action={text}></div>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)) value: (expression content: (js)))) (end_tag name: (tag_name))))"
);
}
#[test]
fn test_directive_transition() {
assert_eq!(
parse("<div transition:fade></div>"),
"(document (element (start_tag name: (tag_name) (attribute name: (attribute_name (attribute_directive) (attribute_identifier)))) (end_tag name: (tag_name))))"
);
}