mirror of
https://github.com/danbulant/oxc
synced 2026-05-20 04:38:54 +00:00
feat(linter): eslint-plugin-jest/require-hook (#3110)
part of https://github.com/oxc-project/oxc/issues/492 Rule Detail: [link](https://github.com/jest-community/eslint-plugin-jest/blob/main/src/rules/require-hook.ts)
This commit is contained in:
parent
388ee5135a
commit
bef8a71b9d
3 changed files with 792 additions and 0 deletions
|
|
@ -180,6 +180,7 @@ mod jest {
|
||||||
pub mod prefer_to_contain;
|
pub mod prefer_to_contain;
|
||||||
pub mod prefer_to_have_length;
|
pub mod prefer_to_have_length;
|
||||||
pub mod prefer_todo;
|
pub mod prefer_todo;
|
||||||
|
pub mod require_hook;
|
||||||
pub mod require_to_throw_message;
|
pub mod require_to_throw_message;
|
||||||
pub mod valid_describe_callback;
|
pub mod valid_describe_callback;
|
||||||
pub mod valid_expect;
|
pub mod valid_expect;
|
||||||
|
|
@ -525,6 +526,7 @@ oxc_macros::declare_all_lint_rules! {
|
||||||
jest::prefer_to_contain,
|
jest::prefer_to_contain,
|
||||||
jest::prefer_to_have_length,
|
jest::prefer_to_have_length,
|
||||||
jest::prefer_todo,
|
jest::prefer_todo,
|
||||||
|
jest::require_hook,
|
||||||
jest::require_to_throw_message,
|
jest::require_to_throw_message,
|
||||||
jest::valid_describe_callback,
|
jest::valid_describe_callback,
|
||||||
jest::valid_expect,
|
jest::valid_expect,
|
||||||
|
|
|
||||||
632
crates/oxc_linter/src/rules/jest/require_hook.rs
Normal file
632
crates/oxc_linter/src/rules/jest/require_hook.rs
Normal file
|
|
@ -0,0 +1,632 @@
|
||||||
|
use oxc_allocator::Vec as OxcVec;
|
||||||
|
use oxc_ast::{
|
||||||
|
ast::{Argument, Declaration, Expression, Statement, VariableDeclarationKind},
|
||||||
|
AstKind,
|
||||||
|
};
|
||||||
|
use oxc_diagnostics::{
|
||||||
|
miette::{self, Diagnostic},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
use oxc_macros::declare_oxc_lint;
|
||||||
|
use oxc_semantic::AstNode;
|
||||||
|
use oxc_span::Span;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
context::LintContext,
|
||||||
|
rule::Rule,
|
||||||
|
utils::{
|
||||||
|
get_node_name, is_type_of_jest_fn_call, parse_jest_fn_call, JestFnKind, JestGeneralFnKind,
|
||||||
|
PossibleJestNode,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Error, Diagnostic)]
|
||||||
|
#[error("eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.")]
|
||||||
|
#[diagnostic(severity(warning), help("This should be done within a hook"))]
|
||||||
|
struct UseHook(#[label] pub Span);
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct RequireHookConfig {
|
||||||
|
allowed_function_calls: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct RequireHook(Box<RequireHookConfig>);
|
||||||
|
|
||||||
|
impl std::ops::Deref for RequireHook {
|
||||||
|
type Target = RequireHookConfig;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_oxc_lint!(
|
||||||
|
/// ### What it does
|
||||||
|
/// This rule flags any expression that is either at the toplevel of a test file or
|
||||||
|
/// directly within the body of a `describe`, _except_ for the following:
|
||||||
|
///
|
||||||
|
/// - `import` statements
|
||||||
|
/// - `const` variables
|
||||||
|
/// - `let` _declarations_, and initializations to `null` or `undefined`
|
||||||
|
/// - Classes
|
||||||
|
/// - Types
|
||||||
|
/// - Calls to the standard Jest globals
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```javascript
|
||||||
|
/// // invalid
|
||||||
|
/// import { database, isCity } from '../database';
|
||||||
|
/// import { Logger } from '../../../src/Logger';
|
||||||
|
/// import { loadCities } from '../api';
|
||||||
|
///
|
||||||
|
/// jest.mock('../api');
|
||||||
|
///
|
||||||
|
/// const initializeCityDatabase = () => {
|
||||||
|
/// database.addCity('Vienna');
|
||||||
|
/// database.addCity('San Juan');
|
||||||
|
/// database.addCity('Wellington');
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// const clearCityDatabase = () => {
|
||||||
|
/// database.clear();
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// initializeCityDatabase();
|
||||||
|
///
|
||||||
|
/// test('that persists cities', () => {
|
||||||
|
/// expect(database.cities.length).toHaveLength(3);
|
||||||
|
/// });
|
||||||
|
/// test('city database has Vienna', () => {
|
||||||
|
/// expect(isCity('Vienna')).toBeTruthy();
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// test('city database has San Juan', () => {
|
||||||
|
/// expect(isCity('San Juan')).toBeTruthy();
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// describe('when loading cities from the api', () => {
|
||||||
|
/// let consoleWarnSpy = jest.spyOn(console, 'warn');
|
||||||
|
/// loadCities.mockResolvedValue(['Wellington', 'London']);
|
||||||
|
///
|
||||||
|
/// it('does not duplicate cities', async () => {
|
||||||
|
/// await database.loadCities();
|
||||||
|
/// expect(database.cities).toHaveLength(4);
|
||||||
|
/// });
|
||||||
|
/// });
|
||||||
|
/// clearCityDatabase();
|
||||||
|
///
|
||||||
|
/// // valid
|
||||||
|
/// import { database, isCity } from '../database';
|
||||||
|
/// import { Logger } from '../../../src/Logger';
|
||||||
|
/// import { loadCities } from '../api';
|
||||||
|
///
|
||||||
|
/// jest.mock('../api');
|
||||||
|
/// const initializeCityDatabase = () => {
|
||||||
|
/// database.addCity('Vienna');
|
||||||
|
/// database.addCity('San Juan');
|
||||||
|
/// database.addCity('Wellington');
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// const clearCityDatabase = () => {
|
||||||
|
/// database.clear();
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// beforeEach(() => {
|
||||||
|
/// initializeCityDatabase();
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// test('that persists cities', () => {
|
||||||
|
/// expect(database.cities.length).toHaveLength(3);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// test('city database has Vienna', () => {
|
||||||
|
/// expect(isCity('Vienna')).toBeTruthy();
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// test('city database has San Juan', () => {
|
||||||
|
/// expect(isCity('San Juan')).toBeTruthy();
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// describe('when loading cities from the api', () => {
|
||||||
|
/// let consoleWarnSpy;
|
||||||
|
/// beforeEach(() => {
|
||||||
|
/// consoleWarnSpy = jest.spyOn(console, 'warn');
|
||||||
|
/// loadCities.mockResolvedValue(['Wellington', 'London']);
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// it('does not duplicate cities', async () => {
|
||||||
|
/// await database.loadCities();
|
||||||
|
/// expect(database.cities).toHaveLength(4);
|
||||||
|
/// });
|
||||||
|
/// });
|
||||||
|
/// afterEach(() => {
|
||||||
|
/// clearCityDatabase();
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
RequireHook,
|
||||||
|
style
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Rule for RequireHook {
|
||||||
|
fn from_configuration(value: serde_json::Value) -> Self {
|
||||||
|
let allowed_function_calls = value
|
||||||
|
.get(0)
|
||||||
|
.and_then(|config| config.get("allowedFunctionCalls"))
|
||||||
|
.and_then(serde_json::Value::as_array)
|
||||||
|
.map(|v| {
|
||||||
|
v.iter().filter_map(serde_json::Value::as_str).map(ToString::to_string).collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
Self(Box::new(RequireHookConfig { allowed_function_calls }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||||
|
let kind = node.kind();
|
||||||
|
|
||||||
|
if let AstKind::Program(program) = kind {
|
||||||
|
self.check_block_body(node, &program.body, ctx);
|
||||||
|
} else if let AstKind::CallExpression(call_expr) = kind {
|
||||||
|
if !is_type_of_jest_fn_call(
|
||||||
|
call_expr,
|
||||||
|
&PossibleJestNode { node, original: None },
|
||||||
|
ctx,
|
||||||
|
&[JestFnKind::General(JestGeneralFnKind::Describe)],
|
||||||
|
) || call_expr.arguments.len() < 2
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(Argument::Expression(second_arg_expr)) = call_expr.arguments.get(1) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match second_arg_expr {
|
||||||
|
Expression::FunctionExpression(func_expr) => {
|
||||||
|
if let Some(func_body) = &func_expr.body {
|
||||||
|
self.check_block_body(node, &func_body.statements, ctx);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Expression::ArrowFunctionExpression(arrow_func_expr) => {
|
||||||
|
if !arrow_func_expr.expression {
|
||||||
|
self.check_block_body(node, &arrow_func_expr.body.statements, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequireHook {
|
||||||
|
fn check_block_body<'a>(
|
||||||
|
&self,
|
||||||
|
node: &AstNode<'a>,
|
||||||
|
statements: &'a OxcVec<'a, Statement<'_>>,
|
||||||
|
ctx: &LintContext<'a>,
|
||||||
|
) {
|
||||||
|
for stmt in statements {
|
||||||
|
self.check(node, stmt, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check<'a>(&self, node: &AstNode<'a>, stmt: &'a Statement<'_>, ctx: &LintContext<'a>) {
|
||||||
|
if let Statement::ExpressionStatement(expr_stmt) = stmt {
|
||||||
|
self.check_should_report_in_hook(node, &expr_stmt.expression, ctx);
|
||||||
|
} else if let Statement::Declaration(Declaration::VariableDeclaration(var_decl)) = stmt {
|
||||||
|
if var_decl.kind != VariableDeclarationKind::Const
|
||||||
|
&& var_decl.declarations.iter().any(|decl| {
|
||||||
|
let Some(init_call) = &decl.init else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
!init_call.is_null_or_undefined()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
ctx.diagnostic(UseHook(var_decl.span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_should_report_in_hook<'a>(
|
||||||
|
&self,
|
||||||
|
node: &AstNode<'a>,
|
||||||
|
expr: &'a Expression<'a>,
|
||||||
|
ctx: &LintContext<'a>,
|
||||||
|
) {
|
||||||
|
if let Expression::CallExpression(call_expr) = expr {
|
||||||
|
let name: String = get_node_name(&call_expr.callee);
|
||||||
|
|
||||||
|
if !(parse_jest_fn_call(call_expr, &PossibleJestNode { node, original: None }, ctx)
|
||||||
|
.is_some()
|
||||||
|
|| name.starts_with("jest.")
|
||||||
|
|| self.allowed_function_calls.contains(&name))
|
||||||
|
{
|
||||||
|
ctx.diagnostic(UseHook(call_expr.span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tests() {
|
||||||
|
use crate::tester::Tester;
|
||||||
|
|
||||||
|
let pass = vec![
|
||||||
|
("describe()", None),
|
||||||
|
("describe(\"just a title\")", None),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
describe('a test', () =>
|
||||||
|
test('something', () => {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
test('it', () => {
|
||||||
|
//
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
const { myFn } = require('../functions');
|
||||||
|
|
||||||
|
test('myFn', () => {
|
||||||
|
expect(myFn()).toBe(1);
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
import { myFn } from '../functions';
|
||||||
|
test('myFn', () => {
|
||||||
|
expect(myFn()).toBe(1);
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
class MockLogger {
|
||||||
|
log() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
test('myFn', () => {
|
||||||
|
expect(myFn()).toBe(1);
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
const { myFn } = require('../functions');
|
||||||
|
|
||||||
|
describe('myFn', () => {
|
||||||
|
it('returns one', () => {
|
||||||
|
expect(myFn()).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
const { myFn } = require('../functions');
|
||||||
|
|
||||||
|
describe('myFn', function () {
|
||||||
|
it('returns one', () => {
|
||||||
|
expect(myFn()).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
describe('some tests', () => {
|
||||||
|
it('is true', () => {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
describe('some tests', () => {
|
||||||
|
it('is true', () => {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('more tests', () => {
|
||||||
|
it('is false', () => {
|
||||||
|
expect(true).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
describe('some tests', () => {
|
||||||
|
let consoleLogSpy;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
consoleLogSpy = jest.spyOn(console, 'log');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prints a message', () => {
|
||||||
|
printMessage('hello world');
|
||||||
|
expect(consoleLogSpy).toHaveBeenCalledWith('hello world');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
let consoleErrorSpy = null;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
consoleErrorSpy = jest.spyOn(console, 'error');
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
let consoleErrorSpy = undefined;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
consoleErrorSpy = jest.spyOn(console, 'error');
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
describe('some tests', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
setup();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
beforeEach(() => {
|
||||||
|
initializeCityDatabase();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clearCityDatabase();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('city database has Vienna', () => {
|
||||||
|
expect(isCity('Vienna')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('city database has San Juan', () => {
|
||||||
|
expect(isCity('San Juan')).toBeTruthy();
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
describe('cities', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
initializeCityDatabase();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('city database has Vienna', () => {
|
||||||
|
expect(isCity('Vienna')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('city database has San Juan', () => {
|
||||||
|
expect(isCity('San Juan')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clearCityDatabase();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
enableAutoDestroy(afterEach);
|
||||||
|
|
||||||
|
describe('some tests', () => {
|
||||||
|
it('is false', () => {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
Some(serde_json::json!([{ "allowedFunctionCalls": ["enableAutoDestroy"] }])),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
import { myFn } from '../functions';
|
||||||
|
|
||||||
|
// todo: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56545
|
||||||
|
declare module 'eslint' {
|
||||||
|
namespace ESLint {
|
||||||
|
interface LintResult {
|
||||||
|
fatalErrorCount: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test('myFn', () => {
|
||||||
|
expect(myFn()).toBe(1);
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let fail = vec![
|
||||||
|
("setup();", None),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
describe('some tests', () => {
|
||||||
|
setup();
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
let { setup } = require('./test-utils');
|
||||||
|
|
||||||
|
describe('some tests', () => {
|
||||||
|
setup();
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
describe('some tests', () => {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
it('is true', () => {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('more tests', () => {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
it('is false', () => {
|
||||||
|
expect(true).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
let consoleErrorSpy = jest.spyOn(console, 'error');
|
||||||
|
|
||||||
|
describe('when loading cities from the api', () => {
|
||||||
|
let consoleWarnSpy = jest.spyOn(console, 'warn');
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
let consoleErrorSpy = null;
|
||||||
|
|
||||||
|
describe('when loading cities from the api', () => {
|
||||||
|
let consoleWarnSpy = jest.spyOn(console, 'warn');
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
("let value = 1", None),
|
||||||
|
("let consoleErrorSpy, consoleWarnSpy = jest.spyOn(console, 'error');", None),
|
||||||
|
("let consoleErrorSpy = jest.spyOn(console, 'error'), consoleWarnSpy;", None),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
import { database, isCity } from '../database';
|
||||||
|
import { loadCities } from '../api';
|
||||||
|
|
||||||
|
jest.mock('../api');
|
||||||
|
|
||||||
|
const initializeCityDatabase = () => {
|
||||||
|
database.addCity('Vienna');
|
||||||
|
database.addCity('San Juan');
|
||||||
|
database.addCity('Wellington');
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearCityDatabase = () => {
|
||||||
|
database.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
initializeCityDatabase();
|
||||||
|
|
||||||
|
test('that persists cities', () => {
|
||||||
|
expect(database.cities.length).toHaveLength(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('city database has Vienna', () => {
|
||||||
|
expect(isCity('Vienna')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('city database has San Juan', () => {
|
||||||
|
expect(isCity('San Juan')).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when loading cities from the api', () => {
|
||||||
|
let consoleWarnSpy = jest.spyOn(console, 'warn');
|
||||||
|
|
||||||
|
loadCities.mockResolvedValue(['Wellington', 'London']);
|
||||||
|
|
||||||
|
it('does not duplicate cities', async () => {
|
||||||
|
await database.loadCities();
|
||||||
|
|
||||||
|
expect(database.cities).toHaveLength(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('logs any duplicates', async () => {
|
||||||
|
await database.loadCities();
|
||||||
|
|
||||||
|
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||||
|
'Ignored duplicate cities: Wellington',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
clearCityDatabase();
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
enableAutoDestroy(afterEach);
|
||||||
|
|
||||||
|
describe('some tests', () => {
|
||||||
|
it('is false', () => {
|
||||||
|
expect(true).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
",
|
||||||
|
Some(serde_json::json!([{ "allowedFunctionCalls": ["someOtherName"] }])),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"
|
||||||
|
import { setup } from '../test-utils';
|
||||||
|
|
||||||
|
// todo: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56545
|
||||||
|
declare module 'eslint' {
|
||||||
|
namespace ESLint {
|
||||||
|
interface LintResult {
|
||||||
|
fatalErrorCount: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('some tests', () => {
|
||||||
|
setup();
|
||||||
|
});
|
||||||
|
",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
Tester::new(RequireHook::NAME, pass, fail).test_and_snapshot();
|
||||||
|
}
|
||||||
158
crates/oxc_linter/src/snapshots/require_hook.snap
Normal file
158
crates/oxc_linter/src/snapshots/require_hook.snap
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
---
|
||||||
|
source: crates/oxc_linter/src/tester.rs
|
||||||
|
assertion_line: 157
|
||||||
|
expression: require_hook
|
||||||
|
---
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:1:1]
|
||||||
|
1 │ setup();
|
||||||
|
· ───────
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:3:21]
|
||||||
|
2 │ describe('some tests', () => {
|
||||||
|
3 │ setup();
|
||||||
|
· ───────
|
||||||
|
4 │ });
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:2:17]
|
||||||
|
1 │
|
||||||
|
2 │ let { setup } = require('./test-utils');
|
||||||
|
· ────────────────────────────────────────
|
||||||
|
3 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:5:21]
|
||||||
|
4 │ describe('some tests', () => {
|
||||||
|
5 │ setup();
|
||||||
|
· ───────
|
||||||
|
6 │ });
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:3:21]
|
||||||
|
2 │ describe('some tests', () => {
|
||||||
|
3 │ setup();
|
||||||
|
· ───────
|
||||||
|
4 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:10:25]
|
||||||
|
9 │ describe('more tests', () => {
|
||||||
|
10 │ setup();
|
||||||
|
· ───────
|
||||||
|
11 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:2:17]
|
||||||
|
1 │
|
||||||
|
2 │ let consoleErrorSpy = jest.spyOn(console, 'error');
|
||||||
|
· ───────────────────────────────────────────────────
|
||||||
|
3 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:5:21]
|
||||||
|
4 │ describe('when loading cities from the api', () => {
|
||||||
|
5 │ let consoleWarnSpy = jest.spyOn(console, 'warn');
|
||||||
|
· ─────────────────────────────────────────────────
|
||||||
|
6 │ });
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:5:19]
|
||||||
|
4 │ describe('when loading cities from the api', () => {
|
||||||
|
5 │ let consoleWarnSpy = jest.spyOn(console, 'warn');
|
||||||
|
· ─────────────────────────────────────────────────
|
||||||
|
6 │ });
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:1:1]
|
||||||
|
1 │ let value = 1
|
||||||
|
· ─────────────
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:1:1]
|
||||||
|
1 │ let consoleErrorSpy, consoleWarnSpy = jest.spyOn(console, 'error');
|
||||||
|
· ───────────────────────────────────────────────────────────────────
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:1:1]
|
||||||
|
1 │ let consoleErrorSpy = jest.spyOn(console, 'error'), consoleWarnSpy;
|
||||||
|
· ───────────────────────────────────────────────────────────────────
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:17:17]
|
||||||
|
16 │
|
||||||
|
17 │ initializeCityDatabase();
|
||||||
|
· ────────────────────────
|
||||||
|
18 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:51:17]
|
||||||
|
50 │
|
||||||
|
51 │ clearCityDatabase();
|
||||||
|
· ───────────────────
|
||||||
|
52 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:32:21]
|
||||||
|
31 │ describe('when loading cities from the api', () => {
|
||||||
|
32 │ let consoleWarnSpy = jest.spyOn(console, 'warn');
|
||||||
|
· ─────────────────────────────────────────────────
|
||||||
|
33 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:34:21]
|
||||||
|
33 │
|
||||||
|
34 │ loadCities.mockResolvedValue(['Wellington', 'London']);
|
||||||
|
· ──────────────────────────────────────────────────────
|
||||||
|
35 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:2:17]
|
||||||
|
1 │
|
||||||
|
2 │ enableAutoDestroy(afterEach);
|
||||||
|
· ────────────────────────────
|
||||||
|
3 │
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
|
|
||||||
|
⚠ eslint-plugin-jest(require-hook): Require setup and teardown code to be within a hook.
|
||||||
|
╭─[require_hook.tsx:14:21]
|
||||||
|
13 │ describe('some tests', () => {
|
||||||
|
14 │ setup();
|
||||||
|
· ───────
|
||||||
|
15 │ });
|
||||||
|
╰────
|
||||||
|
help: This should be done within a hook
|
||||||
Loading…
Reference in a new issue