Skip to content

JS-1784 Fix S2699 false positive for uvu/assert and expect.poll#7511

Open
sonar-nigel[bot] wants to merge 6 commits into
masterfrom
fix/JS-1784-fix-fp-on-s2699-assertion-library-method-calls-not-recognized-as-assertions-gpt-5.5
Open

JS-1784 Fix S2699 false positive for uvu/assert and expect.poll#7511
sonar-nigel[bot] wants to merge 6 commits into
masterfrom
fix/JS-1784-fix-fp-on-s2699-assertion-library-method-calls-not-recognized-as-assertions-gpt-5.5

Conversation

@sonar-nigel

@sonar-nigel sonar-nigel Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Relates to JS-1784

S2699 now recognizes the remaining documented uvu/assert assertion methods and Playwright expect.poll(...).matcher() chains that were previously reported as assertion-less tests.

  • Keeps uvu/assert bounded to its documented method whitelist.
  • Handles imported, renamed, and direct CommonJS Playwright expect.poll forms.
  • Preserves existing noncompliant behavior for bare expect.poll(...) and query-only Cypress chains.

Sonar Vibe Bot added 3 commits July 9, 2026 07:38
Tests cover uvu/assert whitelisted methods and Playwright expect.poll
matcher chains being treated as assertions. They also verify bare
expect.poll and Cypress query-only chains remain noncompliant.

Relates to JS-1784
Recognize documented uvu/assert assertion methods and Playwright
expect.poll(...).matcher() chains, including renamed Playwright expect imports,
when S2699 scans test callbacks for assertions. The implementation follows the
approved proposal by keeping Cypress query chains and bare expect.poll calls
non-asserting, using the existing FQN helpers, and documenting uvu/assert in
rspec.

Relates to JS-1784
@sonar-nigel

sonar-nigel Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor Author

Rule Profile

Field Value
Rule S2699 — Tests should include assertions
Severity / type Blocker CODE_SMELL
Sonar Way Yes
Scope Test
test('has no assertions', () => {
  const x = 1 + 2;
});

False Positive Pattern

S2699 was reporting tests that do contain real assertions because the shared assertion detector did not recognize uvu/assert calls such as assert.instance, assert.is, and assert.ok, and because Playwright polling assertions expose the assertion entry point inside expect.poll(...).matcher() chains.

test('ChatwootPlugin exposes getApi()', () => {
  assert.instance(createChatwootPlugin(MOCK_CONFIG).getApi(), ChatwootApi)
})
test('attach() registers webhook when webhookUrl is configured', async () => {
  const { mock, calls } = makeSmartFetch()
  const plugin = createChatwootPlugin({ ...MOCK_CONFIG, webhookUrl: 'https://bot.example.com/chatwoot' })
  const bot = makeMockBot()
  await withFetch(mock, () => plugin.attach(bot as any))
  assert.ok(
    calls.some((c) => c.startsWith('POST') && c.includes('/webhooks')),
    'POST /webhooks was called to register the webhook'
  )
})
require('@playwright/test').expect.poll(() => 'ready').toBe('ready');

False Negative Risk

The risk is bounded by explicit call-shape and library checks. uvu/assert recognition is limited to the documented method whitelist, and Playwright polling only counts as an assertion when expect.poll(...) is followed by a matcher; bare polling remains noncompliant. This can suppress a real issue only if code calls one of those recognized library APIs in a test without intending it as an assertion, which is unlikely for the supported assertion methods.

Fix Summary

The implementation extends the existing assertion-detection layer instead of adding a separate S2699 path. It adds uvu/assert to the supported assertion libraries with a documented method whitelist, detects Playwright expect.poll(...).matcher() through FQN helpers, and includes regression coverage for JavaScript and TypeScript paths.

  • Covers representative uvu/assert methods, including equal, match, throws, and not.* forms.
  • Covers direct, renamed, and direct CommonJS Playwright expect.poll forms.
  • Keeps cy.contains(...), cy.contains(...).click(), query-only Cypress chains, and bare expect.poll(...) noncompliant.

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Jul 9, 2026

Copy link
Copy Markdown

JS-1784

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Ruling Report

Code no longer flagged (2 issues)

S6551

vitest/packages/vitest/src/node/cli/cli-config.ts:785

   783 |           if (typeof value !== 'object') {
   784 |             throw new TypeError(
>  785 |               `Unexpected value for --expect.poll: ${value}. If you need to configure timeout, use --expect.poll.timeout=<timeout>`,
   786 |             )
   787 |           }

vitest/packages/vitest/src/node/cli/cli-config.ts:795

   793 |       if (typeof value !== 'object') {
   794 |         throw new TypeError(
>  795 |           `Unexpected value for --expect: ${value}. If you need to configure expect options, use --expect.{name}=<value> syntax`,
   796 |         )
   797 |       }

New issues flagged (2 issues)

S8982

vuetify/packages/docs/src/components/app/search/SearchResults.vue:12

    10 |       />
    11 | 
>   12 |       <div class="text-high-emphasis font-weight-black text-uppercase">
    13 |         {{ group.name }}
    14 |       </div>

S8984

vuetify/packages/docs/src/components/app/search/SearchResults.vue:7

     5 |   <v-list ref="rootEl" density="compact" nav bg-color="transparent">
     6 |     <template v-for="(group, i) in props.groups">
>    7 |       <v-divider
     8 |         v-if="i !== 0"
     9 |         class="mb-2 mt-2 ms-2 me-n2"

Ruling passed with these expected-result updates already present in the branch. No fix PR was needed.

Replace repeated Playwright module literals in the shared assertion-detection
helper with a single constant, addressing the S1192 issue introduced by the
S2699 Playwright expect.poll support.

No suppressions were added.
@sonar-nigel
sonar-nigel Bot marked this pull request as ready for review July 10, 2026 05:24
@francois-mora-sonarsource

Copy link
Copy Markdown
Contributor

Improvement request: please extend the new Playwright expect.poll detection to handle direct CommonJS require chains, for example:

require('@playwright/test').expect.poll(() => 'ready').toBe('ready');

I checked the helper behavior on that AST shape: getFullyQualifiedName(context, innerPollCall) resolves it as @playwright/test.expect.poll, while getFullyQualifiedName(context, innerPollCall.callee) returns null. The current implementation compares getFullyQualifiedName(context, callee) with the dotted Playwright import FQN and only falls back for bare expect.poll, so this direct-require form can still leave S2699 reporting the test as assertion-less.

Please add coverage for the direct-require form and either accept both Playwright FQN spellings (@playwright.test.expect.poll and @playwright/test.expect.poll) or normalize the module name before comparison.

Sonar Vibe Bot and others added 2 commits July 10, 2026 10:54
Comment: Improvement request: please extend the new Playwright `expect.poll` detection to handle direct CommonJS require chains, for example:

```js
require('@playwright/test').expect.poll(() => 'ready').toBe('ready');
```

I checked the helper behavior on that AST shape: `getFullyQualifiedName(context, innerPollCall)` resolves it as `@playwright/test.expect.poll`, while `getFullyQualifiedName(context, innerPollCall.callee)` returns `null`. The current implementation compares `getFullyQualifiedName(context, callee)` with the dotted Playwright import FQN and only falls back for bare `expect.poll`, so this direct-require form can still leave S2699 reporting the test as assertion-less.

Please add coverage for the direct-require form and either accept both Playwright FQN spellings (`@playwright.test.expect.poll` and `@playwright/test.expect.poll`) or normalize the module name before comparison.
S2699 is a native rule and does not appear in the external/decorated
ESLint rule tables, so this PR should introduce no README changes. Revert
the drifted external-rule rows to match master per repo guidance on not
committing unrelated regenerated metadata.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqube-next

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant