Why is this an issue?

Testing libraries often provide both generic assertions and dedicated matchers or chainers for common checks such as nullish values, comparisons, collection length, containment, and DOM state.

Both forms can express the same intent, but the dedicated assertion is usually easier to read and produces a more focused failure message. In browser-oriented APIs such as Playwright locators, dedicated assertions can also use framework-specific behavior such as automatic retries.

When a testing library already provides a dedicated assertion for the check being performed, use that assertion instead of expressing the same condition through a more generic one.

Code examples

Noncompliant code example

import { expect, test } from "vitest";

test("uses generic assertions", () => {
  expect(error).toBe(null); // Noncompliant: toBeNull() is the dedicated matcher for this check
  expect(items.length).toBe(3); // Noncompliant: toHaveLength(3) expresses the intent more clearly
});

Compliant solution

import { expect, test } from "vitest";

test("uses dedicated assertions", () => {
  expect(error).toBeNull(); // Compliant: uses the dedicated matcher for null
  expect(items).toHaveLength(3); // Compliant: uses the dedicated matcher for collection length
});

Noncompliant code example

import { expect, test } from "@playwright/test";

test("checks the page", async ({ page }) => {
  const banner = page.getByRole("status");
  expect(await banner.isVisible()).toBe(true); // Noncompliant: Playwright provides a dedicated locator assertion
});

Compliant solution

import { expect, test } from "@playwright/test";

test("checks the page", async ({ page }) => {
  const banner = page.getByRole("status");
  await expect(banner).toBeVisible(); // Compliant: uses the dedicated web-first assertion
});

Resources

Documentation

Related rules