diff --git a/Cargo.lock b/Cargo.lock index def6ffd4185ef..a6756661b6ce8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2219,6 +2219,7 @@ version = "0.15.8" dependencies = [ "bpaf", "ignore", + "insta", "jemallocator", "mimalloc", "oxc-miette", diff --git a/apps/oxlint/Cargo.toml b/apps/oxlint/Cargo.toml index e4c81eb50f326..1c1bec5e2bd37 100644 --- a/apps/oxlint/Cargo.toml +++ b/apps/oxlint/Cargo.toml @@ -37,6 +37,7 @@ oxc_span = { workspace = true } bpaf = { workspace = true, features = ["autocomplete", "bright-color", "derive"] } ignore = { workspace = true, features = ["simd-accel"] } +insta = { workspace = true } miette = { workspace = true } rayon = { workspace = true } rustc-hash = { workspace = true } diff --git a/apps/oxlint/src/lib.rs b/apps/oxlint/src/lib.rs index d8d5293f2060f..91d18c97a190b 100644 --- a/apps/oxlint/src/lib.rs +++ b/apps/oxlint/src/lib.rs @@ -3,6 +3,7 @@ mod lint; mod output_formatter; mod result; mod runner; +mod tester; mod walk; pub mod cli { diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index d68385b779ed2..9cfc4d69ccccd 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -126,7 +126,14 @@ impl Runner for LintRunner { if let Some(basic_config_file) = oxlintrc_for_print { let config_file = config_builder.resolve_final_config_file(basic_config_file); if misc_options.print_config { - return CliRunResult::PrintConfigResult { config_file }; + stdout + .write_all(config_file.as_bytes()) + .or_else(Self::check_for_writer_error) + .unwrap(); + stdout.write_all(b"\n").or_else(Self::check_for_writer_error).unwrap(); + stdout.flush().unwrap(); + + return CliRunResult::PrintConfigResult; } else if basic_options.init { let schema_relative_path = "node_modules/oxlint/configuration_schema.json"; let configuration = if self.cwd.join(schema_relative_path).is_file() { @@ -323,65 +330,18 @@ impl LintRunner { #[cfg(test)] mod test { - use std::{env, fs, path::MAIN_SEPARATOR_STR}; + use std::fs; use super::LintRunner; - use crate::cli::{lint_command, CliRunResult, LintResult, Runner}; - - fn test(args: &[&str]) -> LintResult { - let mut new_args = vec!["--silent"]; - new_args.extend(args); - let options = lint_command().run_inner(new_args.as_slice()).unwrap(); - let mut output = Vec::new(); - - match LintRunner::new(options).run(&mut output) { - CliRunResult::LintResult(lint_result) => lint_result, - other => panic!("{other:?}"), - } - } - - fn test_with_cwd(cwd: &str, args: &[&str]) -> LintResult { - let mut new_args = vec!["--silent"]; - new_args.extend(args); - - let options = lint_command().run_inner(new_args.as_slice()).unwrap(); - - let mut current_cwd = env::current_dir().unwrap(); - - let part_cwd = if MAIN_SEPARATOR_STR == "/" { - cwd.into() - } else { - #[expect(clippy::disallowed_methods)] - cwd.replace('/', MAIN_SEPARATOR_STR) - }; - - current_cwd.push(part_cwd); - let mut output = Vec::new(); - - match LintRunner::new(options).with_cwd(current_cwd).run(&mut output) { - CliRunResult::LintResult(lint_result) => lint_result, - other => panic!("{other:?}"), - } - } - - fn test_invalid_options(args: &[&str]) -> String { - let mut new_args = vec!["--quiet"]; - new_args.extend(args); - let options = lint_command().run_inner(new_args.as_slice()).unwrap(); - let mut output = Vec::new(); - - match LintRunner::new(options).run(&mut output) { - CliRunResult::InvalidOptions { message } => message, - other => { - panic!("Expected InvalidOptions, got {other:?}"); - } - } - } + use crate::{ + cli::{lint_command, CliRunResult, Runner}, + tester::Tester, + }; #[test] fn no_arg() { let args = &[]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert!(result.number_of_warnings > 0); assert_eq!(result.number_of_errors, 0); } @@ -389,7 +349,7 @@ mod test { #[test] fn dir() { let args = &["fixtures/linter"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 3); assert_eq!(result.number_of_warnings, 3); assert_eq!(result.number_of_errors, 0); @@ -398,7 +358,7 @@ mod test { #[test] fn cwd() { let args = &["debugger.js"]; - let result = test_with_cwd("fixtures/linter", args); + let result = Tester::new().with_cwd("fixtures/linter".into()).get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); assert_eq!(result.number_of_errors, 0); @@ -407,7 +367,7 @@ mod test { #[test] fn file() { let args = &["fixtures/linter/debugger.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); assert_eq!(result.number_of_errors, 0); @@ -416,7 +376,7 @@ mod test { #[test] fn multi_files() { let args = &["fixtures/linter/debugger.js", "fixtures/linter/nan.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 2); assert_eq!(result.number_of_warnings, 2); assert_eq!(result.number_of_errors, 0); @@ -425,7 +385,7 @@ mod test { #[test] fn wrong_extension() { let args = &["foo.asdf"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 0); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -435,7 +395,7 @@ mod test { fn ignore_pattern() { let args = &["--ignore-pattern", "**/*.js", "--ignore-pattern", "**/*.vue", "fixtures/linter"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 0); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -447,7 +407,7 @@ mod test { #[test] fn ignore_file_overrides_explicit_args() { let args = &["--ignore-path", "fixtures/linter/.customignore", "fixtures/linter/nan.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 0); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -461,7 +421,7 @@ mod test { "--no-ignore", "fixtures/linter/nan.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); assert_eq!(result.number_of_errors, 0); @@ -470,7 +430,7 @@ mod test { #[test] fn ignore_flow() { let args = &["--import-plugin", "fixtures/flow/index.mjs"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -480,7 +440,7 @@ mod test { // https://github.com/oxc-project/oxc/issues/7406 fn ignore_flow_import_plugin_directory() { let args = &["--import-plugin", "-A all", "-D no-cycle", "fixtures/flow/"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 2); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -489,7 +449,7 @@ mod test { #[test] fn filter_allow_all() { let args = &["-A", "all", "fixtures/linter"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert!(result.number_of_files > 0); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -498,7 +458,7 @@ mod test { #[test] fn filter_allow_one() { let args = &["-W", "correctness", "-A", "no-debugger", "fixtures/linter/debugger.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -507,7 +467,7 @@ mod test { #[test] fn filter_error() { let args = &["-D", "correctness", "fixtures/linter/debugger.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); @@ -516,7 +476,7 @@ mod test { #[test] fn eslintrc_error() { let args = &["-c", "fixtures/linter/eslintrc.json", "fixtures/linter/debugger.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); @@ -525,7 +485,7 @@ mod test { #[test] fn eslintrc_off() { let args = &["-c", "fixtures/eslintrc_off/eslintrc.json", "fixtures/eslintrc_off/test.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); // triggered by no_empty_file assert_eq!(result.number_of_errors, 0); @@ -534,7 +494,9 @@ mod test { #[test] fn oxlint_config_auto_detection() { let args = &["debugger.js"]; - let result = test_with_cwd("fixtures/auto_config_detection", args); + let result = + Tester::new().with_cwd("fixtures/auto_config_detection".into()).get_lint_result(args); + assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); @@ -549,7 +511,7 @@ mod test { "fixtures/no_undef/eslintrc.json", "fixtures/no_undef/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); assert_eq!(result.number_of_errors, 0); @@ -564,7 +526,7 @@ mod test { "fixtures/eslintrc_env/eslintrc_no_env.json", "fixtures/eslintrc_env/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); assert_eq!(result.number_of_errors, 0); @@ -577,7 +539,7 @@ mod test { "fixtures/eslintrc_env/eslintrc_env_browser.json", "fixtures/eslintrc_env/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -592,7 +554,7 @@ mod test { "no-empty", "fixtures/no_empty_allow_empty_catch/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -607,7 +569,7 @@ mod test { "no-empty", "fixtures/no_empty_disallow_empty_catch/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); assert_eq!(result.number_of_errors, 0); @@ -617,7 +579,7 @@ mod test { fn no_console_off() { let args = &["-c", "fixtures/no_console_off/eslintrc.json", "fixtures/no_console_off/test.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -630,7 +592,7 @@ mod test { "fixtures/typescript_eslint/eslintrc.json", "fixtures/typescript_eslint/test.ts", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 3); assert_eq!(result.number_of_errors, 0); @@ -644,13 +606,16 @@ mod test { "--disable-typescript-plugin", "fixtures/typescript_eslint/test.ts", ]; - test(args); + let result = Tester::new().get_lint_result(args); + assert_eq!(result.number_of_files, 1); + assert_eq!(result.number_of_warnings, 2); + assert_eq!(result.number_of_errors, 0); } #[test] fn lint_vue_file() { let args = &["fixtures/vue/debugger.vue"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 2); assert_eq!(result.number_of_errors, 0); @@ -659,7 +624,7 @@ mod test { #[test] fn lint_empty_vue_file() { let args = &["fixtures/vue/empty.vue"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -668,7 +633,7 @@ mod test { #[test] fn lint_astro_file() { let args = &["fixtures/astro/debugger.astro"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 4); assert_eq!(result.number_of_errors, 0); @@ -677,7 +642,7 @@ mod test { #[test] fn lint_svelte_file() { let args = &["fixtures/svelte/debugger.svelte"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); assert_eq!(result.number_of_errors, 0); @@ -686,10 +651,11 @@ mod test { #[test] fn test_tsconfig_option() { // passed - test(&["--tsconfig", "fixtures/tsconfig/tsconfig.json"]); + Tester::new().get_lint_result(&["--tsconfig", "fixtures/tsconfig/tsconfig.json"]); // failed - assert!(test_invalid_options(&["--tsconfig", "oxc/tsconfig.json"]) + assert!(Tester::new() + .get_invalid_option_result(&["--tsconfig", "oxc/tsconfig.json"]) .contains("oxc/tsconfig.json\" does not exist, Please provide a valid tsconfig file.")); } @@ -700,7 +666,10 @@ mod test { "fixtures/eslintrc_vitest_replace/eslintrc.json", "fixtures/eslintrc_vitest_replace/foo.test.js", ]; - test(args); + let result = Tester::new().get_lint_result(args); + assert_eq!(result.number_of_files, 1); + assert_eq!(result.number_of_warnings, 0); + assert_eq!(result.number_of_errors, 0); } #[test] @@ -711,7 +680,7 @@ mod test { "fixtures/eslintrc_vitest_replace/eslintrc.json", "fixtures/eslintrc_vitest_replace/foo.test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_errors, 1); } @@ -719,7 +688,7 @@ mod test { #[test] fn test_import_plugin_enabled_in_config() { let args = &["-c", "fixtures/import/.oxlintrc.json", "fixtures/import/test.js"]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); @@ -736,14 +705,15 @@ mod test { assert_eq!(&content, "debugger\n"); // Apply fix to the file. - let _ = test(args); + let _ = Tester::new().get_lint_result(args); #[expect(clippy::disallowed_methods)] let new_content = fs::read_to_string(file).unwrap().replace("\r\n", "\n"); assert_eq!(new_content, "\n"); // File should not be modified if no fix is applied. - let modified_before = fs::metadata(file).unwrap().modified().unwrap(); - let _ = test(args); + let modified_before: std::time::SystemTime = + fs::metadata(file).unwrap().modified().unwrap(); + let _ = Tester::new().get_lint_result(args); let modified_after = fs::metadata(file).unwrap().modified().unwrap(); assert_eq!(modified_before, modified_after); @@ -754,18 +724,7 @@ mod test { #[test] fn test_print_config_ban_all_rules() { let args = &["-A", "all", "--print-config"]; - let options = lint_command().run_inner(args).unwrap(); - let mut output = Vec::new(); - let ret = LintRunner::new(options).run(&mut output); - let CliRunResult::PrintConfigResult { config_file: config } = ret else { - panic!("Expected PrintConfigResult, got {ret:?}") - }; - - #[expect(clippy::disallowed_methods)] - let expect_json = std::fs::read_to_string("fixtures/print_config/normal/expect.json") - .unwrap() - .replace("\r\n", "\n"); - assert_eq!(config, expect_json.trim()); + Tester::new().test_and_snapshot(args); } #[test] @@ -779,19 +738,7 @@ mod test { "eqeqeq", "--print-config", ]; - let options = lint_command().run_inner(args).unwrap(); - let mut output = Vec::new(); - let ret = LintRunner::new(options).run(&mut output); - let CliRunResult::PrintConfigResult { config_file: config } = ret else { - panic!("Expected PrintConfigResult, got {ret:?}") - }; - - #[expect(clippy::disallowed_methods)] - let expect_json = std::fs::read_to_string("fixtures/print_config/ban_rules/expect.json") - .unwrap() - .replace("\r\n", "\n"); - - assert_eq!(config, expect_json.trim()); + Tester::new().test_and_snapshot(args); } #[test] @@ -809,20 +756,23 @@ mod test { #[test] fn test_overrides() { - let result = - test(&["-c", "fixtures/overrides/.oxlintrc.json", "fixtures/overrides/test.js"]); + let args = &["-c", "fixtures/overrides/.oxlintrc.json", "fixtures/overrides/test.js"]; + let result = Tester::new().get_lint_result(args); + assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); - let result = - test(&["-c", "fixtures/overrides/.oxlintrc.json", "fixtures/overrides/test.ts"]); + let args = &["-c", "fixtures/overrides/.oxlintrc.json", "fixtures/overrides/test.ts"]; + let result = Tester::new().get_lint_result(args); + assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 1); assert_eq!(result.number_of_errors, 1); - let result = - test(&["-c", "fixtures/overrides/.oxlintrc.json", "fixtures/overrides/other.jsx"]); + let args = &["-c", "fixtures/overrides/.oxlintrc.json", "fixtures/overrides/other.jsx"]; + let result = Tester::new().get_lint_result(args); + assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); @@ -830,8 +780,9 @@ mod test { #[test] fn test_overrides_directories() { - let result = - test(&["-c", "fixtures/overrides/directories-config.json", "fixtures/overrides"]); + let args = &["-c", "fixtures/overrides/directories-config.json", "fixtures/overrides"]; + let result = Tester::new().get_lint_result(args); + assert_eq!(result.number_of_files, 7); assert_eq!(result.number_of_warnings, 2); assert_eq!(result.number_of_errors, 2); @@ -839,20 +790,22 @@ mod test { #[test] fn test_config_ignore_patterns_extension() { - let result = test(&[ + let args = &[ "-c", "fixtures/config_ignore_patterns/ignore_extension/eslintrc.json", "fixtures/config_ignore_patterns/ignore_extension", - ]); + ]; + let result = Tester::new().get_lint_result(args); + assert_eq!(result.number_of_files, 1); } #[test] fn test_config_ignore_patterns_directory() { - let result = test_with_cwd( - "fixtures/config_ignore_patterns/ignore_directory", - &["-c", "eslintrc.json"], - ); + let result = Tester::new() + .with_cwd("fixtures/config_ignore_patterns/ignore_directory".into()) + .get_lint_result(&["-c", "eslintrc.json"]); + assert_eq!(result.number_of_files, 1); } @@ -865,7 +818,7 @@ mod test { "fixtures/issue_7566/tests/main.js", "fixtures/issue_7566/tests/function/main.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 0); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 0); @@ -878,7 +831,7 @@ mod test { "fixtures/jest_and_vitest_alias_rules/oxlint-jest.json", "fixtures/jest_and_vitest_alias_rules/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); @@ -888,7 +841,7 @@ mod test { "fixtures/jest_and_vitest_alias_rules/oxlint-vitest.json", "fixtures/jest_and_vitest_alias_rules/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); @@ -901,7 +854,7 @@ mod test { "fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json", "fixtures/eslint_and_typescript_alias_rules/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); @@ -911,9 +864,15 @@ mod test { "fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json", "fixtures/eslint_and_typescript_alias_rules/test.js", ]; - let result = test(args); + let result = Tester::new().get_lint_result(args); assert_eq!(result.number_of_files, 1); assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); } + + #[test] + fn test_print_config() { + let args = &["--print-config"]; + Tester::new().test_and_snapshot(args); + } } diff --git a/apps/oxlint/src/result.rs b/apps/oxlint/src/result.rs index caeeb2050b45d..2854bdef1f83f 100644 --- a/apps/oxlint/src/result.rs +++ b/apps/oxlint/src/result.rs @@ -9,7 +9,7 @@ pub enum CliRunResult { InvalidOptions { message: String }, PathNotFound { paths: Vec }, LintResult(LintResult), - PrintConfigResult { config_file: String }, + PrintConfigResult, ConfigFileInitResult { message: String }, } @@ -30,7 +30,7 @@ impl Termination for CliRunResult { #[allow(clippy::print_stdout, clippy::print_stderr)] fn report(self) -> ExitCode { match self { - Self::None => ExitCode::from(0), + Self::None | Self::PrintConfigResult => ExitCode::from(0), Self::InvalidOptions { message } => { println!("Invalid Options: {message}"); ExitCode::from(1) @@ -45,10 +45,6 @@ impl Termination for CliRunResult { number_of_errors: _, exit_code, }) => exit_code, - Self::PrintConfigResult { config_file } => { - println!("{config_file}"); - ExitCode::from(0) - } Self::ConfigFileInitResult { message } => { println!("{message}"); ExitCode::from(0) diff --git a/apps/oxlint/src/snapshots/--print-config@oxlint.snap b/apps/oxlint/src/snapshots/--print-config@oxlint.snap new file mode 100644 index 0000000000000..1552a078f166d --- /dev/null +++ b/apps/oxlint/src/snapshots/--print-config@oxlint.snap @@ -0,0 +1,142 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +--print-config +---------- +{ + "plugins": [ + "react", + "unicorn", + "typescript", + "oxc" + ], + "categories": {}, + "rules": { + "oxc/only-used-in-recursion": "warn", + "oxc/bad-object-literal-comparison": "warn", + "react/jsx-key": "warn", + "no-control-regex": "warn", + "no-with": "warn", + "typescript/no-non-null-asserted-optional-chain": "warn", + "typescript/no-this-alias": "warn", + "oxc/double-comparisons": "warn", + "no-import-assign": "warn", + "for-direction": "warn", + "oxc/missing-throw": "warn", + "no-unsafe-negation": "warn", + "no-dupe-keys": "warn", + "no-irregular-whitespace": "warn", + "react/no-children-prop": "warn", + "typescript/no-wrapper-object-types": "warn", + "no-unused-labels": "warn", + "no-empty-character-class": "warn", + "require-yield": "warn", + "no-loss-of-precision": "warn", + "react/no-direct-mutation-state": "warn", + "unicorn/no-single-promise-in-promise-methods": "warn", + "no-cond-assign": "warn", + "no-useless-catch": "warn", + "no-empty-static-block": "warn", + "react/no-render-return-value": "warn", + "no-sparse-arrays": "warn", + "no-constant-binary-expression": "warn", + "no-useless-escape": "warn", + "use-isnan": "warn", + "oxc/number-arg-out-of-range": "warn", + "oxc/bad-min-max-func": "warn", + "no-this-before-super": "warn", + "react/jsx-no-duplicate-props": "warn", + "no-extra-boolean-cast": "warn", + "no-obj-calls": "warn", + "oxc/const-comparisons": "warn", + "unicorn/no-useless-length-check": "warn", + "no-delete-var": "warn", + "react/jsx-no-undef": "warn", + "no-global-assign": "warn", + "typescript/no-unsafe-declaration-merging": "warn", + "no-async-promise-executor": "warn", + "no-unsafe-finally": "warn", + "no-dupe-else-if": "warn", + "typescript/no-duplicate-enum-values": "warn", + "no-invalid-regexp": "warn", + "unicorn/prefer-string-starts-ends-with": "warn", + "no-caller": "warn", + "no-self-assign": "warn", + "no-compare-neg-zero": "warn", + "no-unused-vars": "warn", + "no-empty-pattern": "warn", + "react/no-is-mounted": "warn", + "oxc/bad-array-method-on-arguments": "warn", + "unicorn/no-await-in-promise-methods": "warn", + "no-shadow-restricted-names": "warn", + "no-const-assign": "warn", + "unicorn/no-thenable": "warn", + "typescript/triple-slash-reference": "warn", + "react/no-string-refs": "warn", + "no-new-native-nonconstructor": "warn", + "oxc/bad-comparison-sequence": "warn", + "oxc/uninvoked-array-callback": "warn", + "unicorn/no-empty-file": "warn", + "unicorn/no-unnecessary-await": "warn", + "no-nonoctal-decimal-escape": "warn", + "oxc/bad-replace-all-arg": "warn", + "unicorn/no-invalid-remove-event-listener": "warn", + "no-debugger": "warn", + "react/jsx-no-target-blank": "warn", + "no-func-assign": "warn", + "unicorn/no-useless-fallback-in-spread": "warn", + "oxc/erasing-op": "warn", + "unicorn/prefer-set-size": "warn", + "unicorn/no-useless-spread": "warn", + "no-dupe-class-members": "warn", + "react/jsx-props-no-spread-multi": "warn", + "typescript/no-useless-empty-export": "warn", + "react/void-dom-elements-no-children": "warn", + "unicorn/no-new-array": "warn", + "no-unsafe-optional-chaining": "warn", + "no-duplicate-case": "warn", + "react/no-danger-with-children": "warn", + "typescript/prefer-as-const": "warn", + "no-class-assign": "warn", + "no-unused-private-class-members": "warn", + "typescript/no-extra-non-null-assertion": "warn", + "react/no-find-dom-node": "warn", + "no-setter-return": "warn", + "typescript/no-misused-new": "warn", + "unicorn/no-document-cookie": "warn", + "oxc/bad-char-at-comparison": "warn", + "no-constant-condition": "warn", + "no-useless-rename": "warn", + "no-ex-assign": "warn", + "valid-typeof": "warn" + }, + "settings": { + "jsx-a11y": { + "polymorphicPropName": null, + "components": {} + }, + "next": { + "rootDir": [] + }, + "react": { + "formComponents": [], + "linkComponents": [] + }, + "jsdoc": { + "ignorePrivate": false, + "ignoreInternal": false, + "ignoreReplacesDocs": true, + "overrideReplacesDocs": true, + "augmentsExtendsReplacesDocs": false, + "implementsReplacesDocs": false, + "exemptDestructuredRootsFromChecks": false, + "tagNamePreference": {} + } + }, + "env": { + "builtin": true + }, + "globals": {}, + "ignorePatterns": [] +} diff --git a/apps/oxlint/fixtures/print_config/normal/expect.json b/apps/oxlint/src/snapshots/-A all --print-config@oxlint.snap similarity index 89% rename from apps/oxlint/fixtures/print_config/normal/expect.json rename to apps/oxlint/src/snapshots/-A all --print-config@oxlint.snap index bd944f9338081..b3e4703c1e9eb 100644 --- a/apps/oxlint/fixtures/print_config/normal/expect.json +++ b/apps/oxlint/src/snapshots/-A all --print-config@oxlint.snap @@ -1,3 +1,9 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +-A all --print-config +---------- { "plugins": [ "react", diff --git a/apps/oxlint/fixtures/print_config/ban_rules/expect.json b/apps/oxlint/src/snapshots/-c fixtures__print_config__ban_rules__eslintrc.json -A all -D eqeqeq --print-config@oxlint.snap similarity index 84% rename from apps/oxlint/fixtures/print_config/ban_rules/expect.json rename to apps/oxlint/src/snapshots/-c fixtures__print_config__ban_rules__eslintrc.json -A all -D eqeqeq --print-config@oxlint.snap index ee4c1a2e3c59b..603c71ac07cb1 100644 --- a/apps/oxlint/fixtures/print_config/ban_rules/expect.json +++ b/apps/oxlint/src/snapshots/-c fixtures__print_config__ban_rules__eslintrc.json -A all -D eqeqeq --print-config@oxlint.snap @@ -1,3 +1,9 @@ +--- +source: apps/oxlint/src/tester.rs +--- +########## +-c fixtures/print_config/ban_rules/eslintrc.json -A all -D eqeqeq --print-config +---------- { "plugins": [ "react", diff --git a/apps/oxlint/src/tester.rs b/apps/oxlint/src/tester.rs new file mode 100644 index 0000000000000..848437737f8c8 --- /dev/null +++ b/apps/oxlint/src/tester.rs @@ -0,0 +1,71 @@ +#[cfg(test)] +use crate::cli::{lint_command, CliRunResult, LintResult, LintRunner}; +#[cfg(test)] +use crate::runner::Runner; +#[cfg(test)] +use std::{env, path::PathBuf}; + +#[cfg(test)] +pub struct Tester { + cwd: PathBuf, +} + +#[cfg(test)] +impl Tester { + pub fn new() -> Self { + let cwd = env::current_dir().unwrap(); + + Self { cwd } + } + + pub fn with_cwd(mut self, cwd: PathBuf) -> Self { + self.cwd.push(cwd); + self + } + + pub fn get_lint_result(&self, args: &[&str]) -> LintResult { + let mut new_args = vec!["--silent"]; + new_args.extend(args); + + let options = lint_command().run_inner(new_args.as_slice()).unwrap(); + let mut output = Vec::new(); + match LintRunner::new(options).with_cwd(self.cwd.clone()).run(&mut output) { + CliRunResult::LintResult(lint_result) => lint_result, + other => panic!("{other:?}"), + } + } + + pub fn get_invalid_option_result(&self, args: &[&str]) -> String { + let mut new_args = vec!["--silent"]; + new_args.extend(args); + + let options = lint_command().run_inner(new_args.as_slice()).unwrap(); + let mut output = Vec::new(); + match LintRunner::new(options).with_cwd(self.cwd.clone()).run(&mut output) { + CliRunResult::InvalidOptions { message } => message, + other => { + panic!("Expected InvalidOptions, got {other:?}"); + } + } + } + + pub fn test_and_snapshot(&self, args: &[&str]) { + let mut settings = insta::Settings::clone_current(); + + let options = lint_command().run_inner(args).unwrap(); + let mut output: Vec = Vec::new(); + let args_string = args.join(" "); + + output.extend_from_slice(format!("########## \n{args_string}\n----------\n").as_bytes()); + let _ = LintRunner::new(options).with_cwd(self.cwd.clone()).run(&mut output); + output.push(b'\n'); + + settings.set_prepend_module_to_snapshot(false); + settings.set_omit_expression(true); + settings.set_snapshot_suffix("oxlint"); + + settings.bind(|| { + insta::assert_snapshot!(format!("{}", args_string), String::from_utf8(output).unwrap()); + }); + } +}