Skip to content

fix(linter/no-standalone-expect): allows expect in wrapper functions passed to test blocks#17427

Merged
camc314 merged 3 commits intomainfrom
copilot/fix-no-standalone-expect-rule
Dec 29, 2025
Merged

fix(linter/no-standalone-expect): allows expect in wrapper functions passed to test blocks#17427
camc314 merged 3 commits intomainfrom
copilot/fix-no-standalone-expect-rule

Conversation

Copy link
Contributor

Copilot AI commented Dec 28, 2025

The no-standalone-expect rule incorrectly flagged expect calls inside arrow functions passed to wrapper functions (e.g., Angular's fakeAsync()) when those wrapper functions were arguments to test blocks.

// Previously failed, now passes
it('test', fakeAsync(() => {
  expect(true).toBeTruthy();
}));

// Still correctly fails
describe('suite', () => {
  fakeAsync(() => {
    expect(true).toBeTruthy();
  });
});

Changes

  • Modified is_var_declarator_or_test_block: When a CallExpression is not a test block, recursively check if its parent CallExpression is one. This handles cases where wrapper functions are nested as arguments to test blocks.

  • Added test coverage: Test cases for both jest and vitest plugins covering the fakeAsync pattern and multi-level wrapper nesting.

The fix aligns oxlint's behavior with eslint-plugin-jest, which uses a stack-based approach that implicitly allows wrapper functions as arguments to test blocks.

Original prompt

This section details on the original issue you should resolve

<issue_title>linter: behavior of no-standalone-expect rule differs between eslint and oxlint</issue_title>
<issue_description>### What version of Oxlint are you using?

1.35.0

What command did you run?

oxlint

What does your .oxlintrc.json config file look like?

{
  "plugins": ["jest", "vitest"],
  "rules": {
    "vitest/no-standalone-expect": "error"
  }
}

What happened?

A similar situation occurs in vitest/no-standalone-expect and jest/no-standalone-expect

I use the no-standalone-expect rule for tests inside Angular projects. The key feature of these tests is that they allow the use of built-in Angular testing capabilities. For example, the fakeAsync function from the @angular/core/testing library. It allows you to stabilize asynchronous tests.

But I ran into a problem that the default behavior of the no-standalone-expect rule in Eslint and Oxlint is different.

Eslint config:

import {defineConfig} from 'eslint/config'
import vitest from '@vitest/eslint-plugin'

export default defineConfig({
  plugins: {
    vitest,
  },
  rules: {
    'vitest/no-standalone-expect': 'error'
  },
})
@vitest/eslint-plugin: 1.6.4
eslint: 9.39.2

I have a test written in my code like this:

import {fakeAsync} from '@angular/core/testing';

describe('App', () => {
  it('should create the app', fakeAsync(() => {
    expect(true).toBeTruthy();
  }));
});

I'm getting an error in oxlint:

  × eslint-plugin-jest(no-standalone-expect): `expect` must be inside of a test block.
   ╭─[file:///~/oxlint-jest-issue/src/app/app.spec.ts:5:5]
 4 │   it('should create the app', fakeAsync(() => {
 5 │     expect(true).toBeTruthy();
   ·     ──────
 6 │   }));
   ╰────
  help: Did you forget to wrap `expect` in a `test` or `it` block?

This is correct from the logic point of view of the rule, but the main problem is that the original eslint rule does not throw an error for this case. I know about additionalTestBlockFunctions, but I assume (I could be wrong) that oxlint rules should perfectly match eslint rules in behavior. Or at least have some documentation indicating that the behavior in this rule may differ.

You can visually check the behavior in this repository - https://github.com/lld4n/oxlint-vitest-issue</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Fix linter rule behavior for no-standalone-expect fix(linter): no-standalone-expect allows expect in wrapper functions passed to test blocks Dec 28, 2025
Copilot AI requested a review from camc314 December 28, 2025 15:19
@github-actions github-actions bot added A-linter Area - Linter C-bug Category - Bug labels Dec 29, 2025
@camc314 camc314 marked this pull request as ready for review December 29, 2025 11:15
Copilot AI review requested due to automatic review settings December 29, 2025 11:15
Copilot AI and others added 2 commits December 29, 2025 11:16
…th wrapper functions

The rule now correctly allows expect calls inside arrow functions that are
passed to wrapper functions like fakeAsync(), as long as those wrapper
functions are arguments to test blocks (it/test).

This fixes the issue where oxlint incorrectly flagged code like:
  it('test', fakeAsync(() => { expect(...) }))

The fix recursively checks if a CallExpression is an argument to a test block,
matching the behavior of eslint-plugin-jest.

Co-authored-by: camc314 <18101008+camc314@users.noreply.github.com>
@camc314 camc314 force-pushed the copilot/fix-no-standalone-expect-rule branch from 843c324 to 3764fa0 Compare December 29, 2025 11:16
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes the no-standalone-expect rule to allow expect calls inside arrow functions passed to wrapper functions (e.g., Angular's fakeAsync()) when those wrapper functions are arguments to test blocks. This aligns oxlint's behavior with eslint-plugin-jest, which uses a stack-based approach that implicitly handles this pattern.

Key Changes

  • Modified is_var_declarator_or_test_block function to recursively check parent CallExpressions when a CallExpression is not itself a test block
  • Added test coverage for the fakeAsync pattern and basic wrapper function nesting for both jest and vitest plugins

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/oxc_linter/src/rules/jest/no_standalone_expect/mod.rs Added recursive parent checking logic to handle wrapper functions passed as arguments to test blocks
crates/oxc_linter/src/rules/jest/no_standalone_expect/tests/jest.rs Added passing test cases for fakeAsync wrapper and generic wrapper functions, plus one failing test case for wrappers not in test blocks
crates/oxc_linter/src/rules/jest/no_standalone_expect/tests/vitest.rs Added passing test cases for fakeAsync wrapper and generic wrapper functions for vitest plugin
crates/oxc_linter/src/snapshots/jest_no_standalone_expect.snap Updated snapshot to include expected failure for wrapper function not passed to test block
crates/oxc_linter/src/snapshots/eslint_object_shorthand.snap Unrelated file - Contains snapshots for object-shorthand rule, not related to this PR

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 29, 2025

CodSpeed Performance Report

Merging #17427 will not alter performance

Comparing copilot/fix-no-standalone-expect-rule (c1ef270) with main (dab232f)

Summary

✅ 4 untouched
⏩ 41 skipped1

Footnotes

  1. 41 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@camc314 camc314 changed the title fix(linter): no-standalone-expect allows expect in wrapper functions passed to test blocks fix(linter/no-standalone-expect): allows expect in wrapper functions passed to test blocks Dec 29, 2025
@camc314 camc314 merged commit ff70fe9 into main Dec 29, 2025
21 checks passed
@camc314 camc314 deleted the copilot/fix-no-standalone-expect-rule branch December 29, 2025 11:32
graphite-app bot pushed a commit that referenced this pull request Jan 5, 2026
# Oxlint
### 💥 BREAKING CHANGES

- f7da875 oxlint: [**BREAKING**] Remove oxc_language_server binary (#17457) (Boshen)

### 🚀 Features

- 659c23e linter: Init note field boilerplate  (#17589) (Shrey Sudhir)
- 6870b64 parser: Add TS1363 error code (#17609) (Sysix)
- 6154c8c linter/eslint-plugin-vitest: Implemented vitest/warn-todo rule (#17228) (Said Atrahouch)
- 0043cd6 linter/eslint-plugin-vitest: Implement consistent-vitest-vi rule (#17389) (Said Atrahouch)
- a6d773d linter: Add full TS support to eslint/no-useless-constructor (#17592) (camc314)
- f02c0e7 linter/eslint: Implement complexity (#17569) (Nguyen Tran)
- bc7aae7 linter/no-unused-vars: Add fixer to remove unused catch bindings (#17567) (Don Isaac)
- 9e8ec78 linter/only-throw-error rule: Add `allowRethrowing` option for  (#17554) (camc314)
- b67e819 linter: Add fixer for `unicorn/prefer-response-static-json` rule (#17559) (Mikhail Baev)
- 44b0361 linter/vue: Implement no-this-in-before-route-enter (#17525) (yefan)
- ee34716 linter/react: Implement no-will-update-set-state (#17530) (Kenzo Wada)
- 3088e1d linter/react: Implement no-this-in-sfc (#17535) (Kenzo Wada)
- 29a2868 linter/jsx-a11y: Implement no-static-element-interactions (#17538) (Kenzo Wada)
- eadf057 linter: Enable tsconfig auto discovery by default (#17489) (Boshen)
- 12a7d6e website_linter: Add a count of rules with fixes available to rules table. (#17476) (Connor Shea)

### 🐛 Bug Fixes

- a702f13 oxlint/lsp: Correct position for "disable for this file" with shebang (#17613) (Sysix)
- 19fdfb6 linter: Panic in `sort-keys` rule with Unicode numeric characters (#17629) (Adel Rodríguez)
- 2e8f469 vscode: Search for `node_modules/.bin/oxlint.exe` too (bun setup) (#17597) (Sysix)
- be39906 linter/aria-proptypes: Allow template literals with expressions for string-type ARIA props (#17460) (Jökull Sólberg Auðunsson)
- 529901c linter: Include JS plugin rules when calculating total rule count (#17520) (connorshea)
- 96ef2cc linter: Print total rule # when using a single nested config (#17517) (connorshea)
- 9ad0f29 oxlint: Do not enable external plugin store when no external linter is passed (#17498) (Sysix)
- 174375d oxfmt,oxlint: Disable mimalloc for 32-bit Arm targets (#17473) (Yaksh Bariya)
- ff70fe9 linter/no-standalone-expect: Allows expect in wrapper functions passed to test blocks (#17427) (Copilot)
- dab232f linter/catch-or-return: Handle arrow functions with implicit returns correctly (#17440) (Copilot)
- a38892a linter: Update no-unnecessary-template-expression docs and test case (#17453) (camc314)

### ⚡ Performance

- 605dbf1 vscode: Restrict searching for oxlint/oxfmt binaries only 3 levels deep + 10s timeout (#17345) (Sysix)

### 📚 Documentation

- 884fb63 linter/react: Improve docs for jsx-curly-brace-presence (#17579) (connorshea)
- 1d3ee07 linter: Improve rule explanation for `vue/no-this-in-before-route-enter`. (#17581) (connorshea)
- 5f189f8 linter/arrow-body-style: Correctly document default mode option (#17566) (Rägnar O'ock)
- bb2e8e4 linter: Add a note to the `typescript/no-var-requires` rule about the missing `allow` option (#17551) (connorshea)
- 655afc1 linter: Improve docs for `import/extensions` and add a few more tests (#17539) (connorshea)
- 7e5fc90 linter: Update list of plugins that are reserved. (#17516) (connorshea)
# Oxfmt
### 💥 BREAKING CHANGES

- f7da875 oxlint: [**BREAKING**] Remove oxc_language_server binary (#17457) (Boshen)

### 🚀 Features

- 8fd4ea9 oxfmt: `options.embeddedLanguageFormatting` is now `"auto"` by default (#17649) (leaysgur)

### 🐛 Bug Fixes

- c9b5d7d formatter/sort_imports: Handle alignable_comment correctly (#17646) (leaysgur)
- 453222d formatter: Missing comment handling for end-of-line comments in member chains (#17659) (Dunqing)
- 0805ff2 formatter: Incorrect inline comment placement in try-catch (#17657) (Dunqing)
- 3a0c782 formatter: Don't move comments into optional call parentheses (#17582) (magic-akari)
- 174375d oxfmt,oxlint: Disable mimalloc for 32-bit Arm targets (#17473) (Yaksh Bariya)

### ⚡ Performance

- abb28dc oxfmt: Turn of pretty print from sort-package-json (#17452) (Boshen)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-linter Area - Linter C-bug Category - Bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

linter: behavior of no-standalone-expect rule differs between eslint and oxlint

3 participants