From d9f073e12297d5d623c96fc9392fbc6c29627b5a Mon Sep 17 00:00:00 2001 From: Wenzhe Wang Date: Tue, 13 Feb 2024 22:56:28 +0800 Subject: [PATCH] feat(linter): detect jest file by default glob pattern (#2408) closes: #2397 --- crates/oxc_linter/src/rules/jest/no_export.rs | 42 +++++++++++---- crates/oxc_linter/src/utils/jest.rs | 51 +++++++++++++++---- 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/crates/oxc_linter/src/rules/jest/no_export.rs b/crates/oxc_linter/src/rules/jest/no_export.rs index abc339487..565b7b706 100644 --- a/crates/oxc_linter/src/rules/jest/no_export.rs +++ b/crates/oxc_linter/src/rules/jest/no_export.rs @@ -57,19 +57,30 @@ impl Rule for NoExport { #[test] fn test() { use crate::tester::Tester; + use std::path::PathBuf; let pass = vec![ - ("describe('a test', () => { expect(1).toBe(1); })", None), - ("window.location = 'valid'", None), - ("module.somethingElse = 'foo';", None), - ("export const myThing = 'valid'", None), - ("export default function () {}", None), - ("module.exports = function(){}", None), - ("module.exports.myThing = 'valid';", None), + ( + "describe('a test', () => { expect(1).toBe(1); })", + None, + None, + Some(PathBuf::from("foo.test.js")), + ), + ("window.location = 'valid'", None, None, None), + ("module.somethingElse = 'foo';", None, None, None), + ("export const myThing = 'valid'", None, None, None), + ("export default function () {}", None, None, None), + ("module.exports = function(){}", None, None, None), + ("module.exports.myThing = 'valid';", None, None, None), ]; let fail = vec![ - ("export const myThing = 'invalid'; test('a test', () => { expect(1).toBe(1);});", None), + ( + "export const myThing = 'invalid'; test('a test', () => { expect(1).toBe(1);});", + None, + None, + Some(PathBuf::from("foo.test.js")), + ), ( " export const myThing = 'invalid'; @@ -79,6 +90,8 @@ fn test() { }); ", None, + None, + Some(PathBuf::from("foo.test.js")), ), ( " @@ -89,6 +102,8 @@ fn test() { }); ", None, + None, + Some(PathBuf::from("foo.test.js")), ), ( " @@ -98,8 +113,15 @@ fn test() { }); ", None, + None, + Some(PathBuf::from("foo.test.js")), + ), + ( + "export default function() {}; test('a test', () => { expect(1).toBe(1);});", + None, + None, + Some(PathBuf::from("foo.test.js")), ), - ("export default function() {}; test('a test', () => { expect(1).toBe(1);});", None), ( " const foo = 1; @@ -109,6 +131,8 @@ fn test() { export {foo, bar}; ", None, + None, + Some(PathBuf::from("foo.test.js")), ), // TODO: support `module.exports` // ("module.exports['invalid'] = function() {}; test('a test', () => { expect(1).toBe(1);});", None), diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index a50885f56..258d46b04 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -74,19 +74,19 @@ pub enum JestGeneralFnKind { Jest, } +/// pub fn is_jest_file(ctx: &LintContext) -> bool { - if JEST_METHOD_NAMES - .iter() - .any(|name| ctx.scopes().root_unresolved_references().contains_key(*name)) - { + if ctx.file_path().components().any(|c| match c { + std::path::Component::Normal(p) => p == std::ffi::OsStr::new("__tests__"), + _ => false, + }) { return true; - }; + } - let import_entries = &ctx.semantic().module_record().import_entries; - - return import_entries.iter().any(|import_entry| { - matches!(import_entry.module_request.name().as_str(), "@jest/globals") - }); + let file_path = ctx.file_path().to_string_lossy(); + ["spec.js", "spec.jsx", "spec.ts", "spec.tsx", "test.js", "test.jsx", "test.ts", "test.tsx"] + .iter() + .any(|ext| file_path.ends_with(ext)) } pub fn is_type_of_jest_fn_call<'a>( @@ -286,3 +286,34 @@ pub fn get_node_name_vec<'a>(expr: &'a Expression<'a>) -> Vec> { fn is_pure_string(template_literal: &TemplateLiteral) -> bool { template_literal.expressions.is_empty() && template_literal.quasis.len() == 1 } +#[cfg(test)] +mod test { + use crate::LintContext; + use oxc_allocator::Allocator; + use oxc_parser::Parser; + use oxc_semantic::SemanticBuilder; + use oxc_span::SourceType; + use std::{path::Path, rc::Rc}; + + #[test] + fn test_is_jest_file() { + let allocator = Allocator::default(); + let source_type = SourceType::default(); + let parser_ret = Parser::new(&allocator, "", source_type).parse(); + let program = allocator.alloc(parser_ret.program); + let semantic_ret = SemanticBuilder::new("", source_type).build(program).semantic; + let semantic_ret = Rc::new(semantic_ret); + + let path = Path::new("foo.js"); + let ctx = LintContext::new(Box::from(path), &semantic_ret); + assert!(!super::is_jest_file(&ctx)); + + let path = Path::new("foo.test.js"); + let ctx = LintContext::new(Box::from(path), &semantic_ret); + assert!(super::is_jest_file(&ctx)); + + let path = Path::new("__tests__/foo/test.spec.js"); + let ctx = LintContext::new(Box::from(path), &semantic_ret); + assert!(super::is_jest_file(&ctx)); + } +}