diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 2ba898bc3ada..968ea1e4abb5 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -204,6 +204,7 @@ mod unicorn { mod jsx_a11y { pub mod alt_text; pub mod anchor_has_content; + pub mod anchor_is_valid; pub mod html_has_lang; } @@ -384,5 +385,6 @@ oxc_macros::declare_all_lint_rules! { import::no_amd, jsx_a11y::alt_text, jsx_a11y::anchor_has_content, + jsx_a11y::anchor_is_valid, jsx_a11y::html_has_lang } diff --git a/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs b/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs new file mode 100644 index 000000000000..7e6e6ca01a72 --- /dev/null +++ b/crates/oxc_linter/src/rules/jsx_a11y/anchor_is_valid.rs @@ -0,0 +1,668 @@ +use oxc_ast::{ + ast::{Expression, JSXAttributeItem, JSXAttributeValue, JSXElementName, JSXExpression}, + AstKind, +}; +use oxc_diagnostics::{ + miette::{self, Diagnostic}, + thiserror::Error, +}; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, utils::has_jsx_prop_lowercase, AstNode}; + +#[derive(Debug, Error, Diagnostic)] +enum AnchorIsValidDiagnostic { + #[error( + "eslint-plugin-jsx-a11y(anchor-is-valid): Missing `href` attribute for the `a` element." + )] + #[diagnostic(severity(warning), help("Provide an href for the `a` element."))] + MissingHrefAttribute(#[label] Span), + + #[error("eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element.")] + #[diagnostic(severity(warning), help("Provide a correct href for the `a` element."))] + IncorrectHref(#[label] Span), + + #[error("eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`.")] + #[diagnostic(severity(warning), help("Use a `button` element instead of an `a` element."))] + CantBeAnchor(#[label] Span), +} +#[derive(Debug, Default, Clone)] +pub struct AnchorIsValid; + +declare_oxc_lint!( + /// ### What it does + /// The HTML element, with a valid href attribute, is formally defined as representing a **hyperlink**. + /// That is, a link between one HTML document and another, or between one location inside an HTML document and another location inside the same document. + /// + /// While before it was possible to attach logic to an anchor element, with the advent of JSX libraries, + /// it's now easier to attach logic to any HTML element, anchors included. + /// + /// This rule is designed to prevent users to attach logic at the click of anchors, and also makes + /// sure that the `href` provided to the anchor element is valid. If the anchor has logic attached to it, + /// the rules suggests to turn it to a `button`, because that's likely what the user wants. + /// + /// Anchor `` elements should be used for navigation, while `` should be + /// used for user interaction. + /// + /// Consider the following: + /// + /// ```javascript + /// Perform action + /// Perform action + /// Perform action + /// ```` + /// + /// All these anchor implementations indicate that the element is only used to execute JavaScript code. All the above should be replaced with: + /// + /// ```javascript + /// + /// ``` + /// ` + /// ### Why is this bad? + /// There are **many reasons** why an anchor should not have a logic and have a correct `href` attribute: + /// - it can disrupt the correct flow of the user navigation e.g. a user that wants to open the link + /// in another tab, but the default "click" behaviour is prevented + /// - it can source of invalid links, and crawlers can't navigate the website, risking to penalise SEO ranking + /// + /// ### Example + /// + /// #### Valid + /// + /// ```javascript + /// navigate here + /// ``` + /// + /// ```javascript + /// navigate here + /// ``` + /// + /// ```javascript + /// navigate here + /// ``` + /// + /// #### Invalid + /// + /// ```javascript + /// navigate here + /// ``` + /// ```javascript + /// navigate here + /// ``` + /// ```javascript + /// navigate here + /// ``` + /// ```javascript + /// navigate here + /// ``` + /// ```javascript + /// navigate here + /// ``` + /// + /// ### Reference + /// + /// - [WCAG 2.1.1](https://www.w3.org/WAI/WCAG21/Understanding/keyboard) + AnchorIsValid, + correctness +); + +fn check_value_is_empty(value: &JSXAttributeValue) -> bool { + match value { + JSXAttributeValue::Element(_) => false, + JSXAttributeValue::StringLiteral(str_lit) => { + str_lit.value.is_empty() + || str_lit.value == "#" + || str_lit.value == "javascript:void(0)" + } + JSXAttributeValue::ExpressionContainer(exp) => { + if let JSXExpression::Expression(jsexp) = &exp.expression { + if let Expression::Identifier(ident) = jsexp { + if ident.name == "undefined" { + return true; + } + } else if let Expression::NullLiteral(_) = jsexp { + return true; + } else if let Expression::StringLiteral(str_lit) = jsexp { + return str_lit.value.is_empty() + || str_lit.value == "#" + || str_lit.value == "javascript:void(0)"; + } + }; + false + } + JSXAttributeValue::Fragment(_) => true, + } +} + +impl Rule for AnchorIsValid { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + if let AstKind::JSXElement(jsx_el) = node.kind() { + let JSXElementName::Identifier(ident) = &jsx_el.opening_element.name else { return }; + let name = ident.name.as_str(); + if name == "a" { + if let Option::Some(herf_attr) = + has_jsx_prop_lowercase(&jsx_el.opening_element, "href") + { + // Check if the 'a' element has a correct href attribute + match herf_attr { + JSXAttributeItem::Attribute(attr) => match &attr.value { + Some(value) => { + let is_empty = check_value_is_empty(value); + if is_empty { + if has_jsx_prop_lowercase(&jsx_el.opening_element, "onclick") + .is_some() + { + ctx.diagnostic(AnchorIsValidDiagnostic::CantBeAnchor( + ident.span, + )); + return; + } + ctx.diagnostic(AnchorIsValidDiagnostic::IncorrectHref( + ident.span, + )); + return; + } + } + None => { + ctx.diagnostic(AnchorIsValidDiagnostic::IncorrectHref(ident.span)); + return; + } + }, + + JSXAttributeItem::SpreadAttribute(_) => { + // pass + return; + } + } + return; + } + // Exclude '' case + let has_spreed_attr = + jsx_el.opening_element.attributes.iter().any(|attr| match attr { + JSXAttributeItem::SpreadAttribute(_) => true, + JSXAttributeItem::Attribute(_) => false, + }); + + if has_spreed_attr { + return; + } + + ctx.diagnostic(AnchorIsValidDiagnostic::MissingHrefAttribute(ident.span)); + } + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + // let components = vec![1]; + // let specialLink = vec![1]; + // let componentsAndSpecialLink = vec![1]; + // let invalidHrefAspect = vec![1]; + // let preferButtonAspect = vec![1]; + // let preferButtonInvalidHrefAspect = vec![1]; + // let noHrefAspect = vec![1]; + // let noHrefPreferButtonAspect = vec![1]; + // let componentsAndSpecialLinkAndInvalidHrefAspect = vec![1]; + // let noHrefInvalidHrefAspect = vec![1]; + // let componentsAndSpecialLinkAndNoHrefAspect = vec![1]; + + // https://raw.githubusercontent.com/jsx-eslint/eslint-plugin-jsx-a11y/main/__tests__/src/rules/anchor-is-valid-test.js + let pass = vec![ + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"
", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"test", None), + (r"", None), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#"
"#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#"
"#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + (r"", None), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#"
"#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#"test"#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#"
"#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#"test"#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // (r#"
"#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#"test"#, Some(serde_json::json!(componentsAndSpecialLink))), + (r" void 0} />", None), + (r" void 0} />", None), + (r" void 0} />", None), + (r" void 0} />", None), + (r" void 0} />", None), + (r"
void 0} />", None), + (r" void 0} />", None), + (r" void 0} />", None), + (r" void 0} />", None), + (r" void 0} />", None), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(components)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(components)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(components)), + // ), + // (r#"
void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // ( + // r#"
void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // ( + // r#"
void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // (r#""#, Some(serde_json::json!(invalidHrefAspect))), + // (r#""#, Some(serde_json::json!(invalidHrefAspect))), + // (r#""#, Some(serde_json::json!(invalidHrefAspect))), + // (r#""#, Some(serde_json::json!(preferButtonAspect))), + // (r#""#, Some(serde_json::json!(preferButtonAspect))), + // (r#""#, Some(serde_json::json!(preferButtonAspect))), + // (r#""#, Some(serde_json::json!(preferButtonInvalidHrefAspect))), + // (r#""#, Some(serde_json::json!(preferButtonInvalidHrefAspect))), + // (r#""#, Some(serde_json::json!(preferButtonInvalidHrefAspect))), + // (r#""#, Some(serde_json::json!(preferButtonAspect))), + // (r#""#, Some(serde_json::json!(preferButtonAspect))), + // (r#""#, Some(serde_json::json!(preferButtonAspect))), + // (r#""#, Some(serde_json::json!(preferButtonAspect))), + // (r#""#, Some(serde_json::json!(noHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefPreferButtonAspect))), + // (r#""#, Some(serde_json::json!(noHrefPreferButtonAspect))), + // (r#""#, Some(serde_json::json!(noHrefPreferButtonAspect))), + // ( + // r#""#, + // Some(serde_json::json!(noHrefPreferButtonAspect)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(invalidHrefAspect))), + // (r#" void 0} />"#, Some(serde_json::json!(noHrefAspect))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(noHrefAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(noHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndInvalidHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndInvalidHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndInvalidHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndInvalidHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndInvalidHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndInvalidHrefAspect)), + // ), + ]; + + let fail = vec![ + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r"", None), + (r" void 0} />", None), + (r" void 0} />", None), + (r" void 0} />", None), + (r" void 0} />", None), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#""#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(components)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(components)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // (r#" void 0} />"#, Some(serde_json::json!(components))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(components)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(components)), + // ), + // (r#" void 0} />"#, None), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#""#, Some(serde_json::json!(specialLink))), + // (r#" void 0} />"#, Some(serde_json::json!(specialLink))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(specialLink)), + // ), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // (r#""#, Some(serde_json::json!(componentsAndSpecialLink))), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(componentsAndSpecialLink)), + // ), + // (r#""#, Some(serde_json::json!(noHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefPreferButtonAspect))), + // (r#""#, Some(serde_json::json!(noHrefInvalidHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefPreferButtonAspect))), + // (r#""#, Some(serde_json::json!(noHrefInvalidHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefAspect))), + // (r#""#, Some(serde_json::json!(noHrefPreferButtonAspect))), + // (r#""#, Some(serde_json::json!(noHrefInvalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(noHrefInvalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(invalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(noHrefInvalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(preferButtonInvalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(invalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(noHrefInvalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(preferButtonInvalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(invalidHrefAspect))), + // (r#";"#, Some(serde_json::json!(noHrefInvalidHrefAspect))), + // ( + // r#";"#, + // Some(serde_json::json!(preferButtonInvalidHrefAspect)), + // ), + // (r#";"#, Some(serde_json::json!(invalidHrefAspect))), + // ( + // r#";"#, + // Some(serde_json::json!(noHrefInvalidHrefAspect)), + // ), + // ( + // r#";"#, + // Some(serde_json::json!(preferButtonInvalidHrefAspect)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(preferButtonAspect))), + // (r#" void 0} />"#, Some(serde_json::json!(preferButtonInvalidHrefAspect))), + // (r#" void 0} />"#, Some(serde_json::json!(noHrefPreferButtonAspect))), + // (r#" void 0} />"#, Some(serde_json::json!(noHrefAspect))), + // (r#" void 0} />"#, Some(serde_json::json!(noHrefInvalidHrefAspect))), + // (r#" void 0} />"#, Some(serde_json::json!(preferButtonAspect))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(noHrefPreferButtonAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(preferButtonInvalidHrefAspect)), + // ), + // (r#" void 0} />"#, Some(serde_json::json!(invalidHrefAspect))), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(noHrefInvalidHrefAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(preferButtonAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(noHrefPreferButtonAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(preferButtonInvalidHrefAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(invalidHrefAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(noHrefInvalidHrefAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(preferButtonAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(noHrefPreferButtonAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(preferButtonInvalidHrefAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(invalidHrefAspect)), + // ), + // ( + // r#" void 0} />"#, + // Some(serde_json::json!(noHrefInvalidHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndNoHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndNoHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndNoHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndNoHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndNoHrefAspect)), + // ), + // ( + // r#""#, + // Some(serde_json::json!(componentsAndSpecialLinkAndNoHrefAspect)), + // ), + ]; + + Tester::new(AnchorIsValid::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/anchor_is_valid.snap b/crates/oxc_linter/src/snapshots/anchor_is_valid.snap new file mode 100644 index 000000000000..ebd30a7537b6 --- /dev/null +++ b/crates/oxc_linter/src/snapshots/anchor_is_valid.snap @@ -0,0 +1,89 @@ +--- +source: crates/oxc_linter/src/tester.rs +assertion_line: 119 +expression: anchor_is_valid +--- + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): Missing `href` attribute for the `a` element. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ + · ─ + ╰──── + help: Provide an href for the `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ + · ─ + ╰──── + help: Provide a correct href for the `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ + · ─ + ╰──── + help: Provide a correct href for the `a` element. + + × Unterminated string + ╭─[anchor_is_valid.tsx:1:1] + 1 │ + · ─ + ╰──── + help: Provide a correct href for the `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ + · ─ + ╰──── + help: Provide a correct href for the `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ + · ─ + ╰──── + help: Provide a correct href for the `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): Use an incorrect href for the 'a' element. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ + · ─ + ╰──── + help: Provide a correct href for the `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): Missing `href` attribute for the `a` element. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ void 0} /> + · ─ + ╰──── + help: Provide an href for the `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ void 0} /> + · ─ + ╰──── + help: Use a `button` element instead of an `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ void 0} /> + · ─ + ╰──── + help: Use a `button` element instead of an `a` element. + + ⚠ eslint-plugin-jsx-a11y(anchor-is-valid): The a element has `href` and `onClick`. + ╭─[anchor_is_valid.tsx:1:1] + 1 │ void 0} /> + · ─ + ╰──── + help: Use a `button` element instead of an `a` element. + +