Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 52 additions & 39 deletions crates/oxc_linter/src/rules/jest/no_standalone_expect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,58 +143,71 @@ fn is_correct_place_to_call_expect<'a>(
id_nodes_mapping: &FxHashMap<NodeId, &PossibleJestNode<'a, '_>>,
ctx: &LintContext<'a>,
) -> Option<()> {
let mut parent = ctx.nodes().parent_node(node.id());
let mut current_node = node;

// loop until find the closest function body
while !matches!(parent.kind(), AstKind::FunctionBody(_) | AstKind::Program(_)) {
parent = ctx.nodes().parent_node(parent.id());
}
// Walk up the tree, checking each function scope
loop {
let mut current = ctx.nodes().parent_node(current_node.id());

let parent = ctx.nodes().parent_node(parent.id());
// loop until find the closest function body
while !matches!(current.kind(), AstKind::FunctionBody(_) | AstKind::Program(_)) {
current = ctx.nodes().parent_node(current.id());
}

match parent.kind() {
AstKind::Function(function) => {
// `function foo() { expect(1).toBe(1); }`
if function.is_function_declaration() {
return Some(());
}
// If we reached the program root without finding a test block, it's invalid
if matches!(current.kind(), AstKind::Program(_)) {
return None;
}

if function.is_expression() {
let grandparent = ctx.nodes().parent_node(parent.id());
let parent = ctx.nodes().parent_node(current.id());

match parent.kind() {
AstKind::Function(function) => {
// `function foo() { expect(1).toBe(1); }`
if function.is_function_declaration() {
return Some(());
}

if function.is_expression() {
let grandparent = ctx.nodes().parent_node(parent.id());

// `test('foo', function () { expect(1).toBe(1) })`
// `const foo = function() {expect(1).toBe(1)}`
if is_var_declarator_or_test_block(
grandparent,
additional_test_block_functions,
id_nodes_mapping,
ctx,
) {
return Some(());
}

// `test('foo', function () { expect(1).toBe(1) })`
// `const foo = function() {expect(1).toBe(1)}`
return if is_var_declarator_or_test_block(
// Continue checking parent scopes
current_node = parent;
} else {
// Function that's neither a declaration nor expression - shouldn't reach here
return None;
}
}
AstKind::ArrowFunctionExpression(_) => {
let grandparent = ctx.nodes().parent_node(parent.id());
// `test('foo', () => expect(1).toBe(1))`
// `const foo = () => expect(1).toBe(1)`
if is_var_declarator_or_test_block(
grandparent,
additional_test_block_functions,
id_nodes_mapping,
ctx,
) {
Some(())
} else {
None
};
return Some(());
}

// Continue checking parent scopes
current_node = parent;
}
_ => return None,
}
AstKind::ArrowFunctionExpression(_) => {
let grandparent = ctx.nodes().parent_node(parent.id());
// `test('foo', () => expect(1).toBe(1))`
// `const foo = () => expect(1).toBe(1)`
return if is_var_declarator_or_test_block(
grandparent,
additional_test_block_functions,
id_nodes_mapping,
ctx,
) {
Some(())
} else {
None
};
}
_ => {}
}

None
}

fn is_var_declarator_or_test_block<'a>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,48 @@ fn test() {
",
Some(serde_json::json!([{ "additionalTestBlockFunctions": ["each.test"] }])),
),
// (
// r"function funcWithCallback(callback) { callback(5); }
// describe('testWithCallback', () => {
// it('should call the callback', (done) => {
// funcWithCallback((result) => {
// expect(result).toBe(5);
// done();
// });
// });
// });",
// None,
// ),
(
r"function funcWithCallback(callback) { callback(5); }
describe('testWithCallback', () => {
it('should call the callback', (done) => {
funcWithCallback((result) => {
expect(result).toBe(5);
done();
});
});
});",
None,
),
(
r"it('should do something', async () => {
render();

await waitFor(() => {
expect(screen.getByText('Option 2')).toBeInTheDocument();
});
});",
None,
),
(
r"it('should do something', () => {
waitFor(() => {
expect(screen.getByText('Option 2')).toBeInTheDocument();
});
});",
None,
),
(
r"describe('test suite', () => {
it('should work with nested callbacks', () => {
someFunction(() => {
anotherFunction(() => {
expect(true).toBe(true);
});
});
});
});",
None,
),
];

let fail = vec![
Expand Down
Loading