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
17 changes: 0 additions & 17 deletions .changeset/code-quality-test-discipline.md

This file was deleted.

18 changes: 18 additions & 0 deletions packages/eslint-plugin-code-quality/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# @noctcore/eslint-plugin-code-quality

## 0.4.0

### Minor Changes

- [#53](https://github.com/noctcore/eslint-plugins/pull/53) [`954f5b2`](https://github.com/noctcore/eslint-plugins/commit/954f5b245b86ebf619556b17dc1d126817f2dbe8) Thanks [@Shironex](https://github.com/Shironex)! - Four new opt-in test-discipline rules and two stricter existing rules.

**A project that spreads `recommended` sees new errors from two rules it already runs:**

- `no-vacuous-expect` now reports a test whose only assertion is that the render root is present (`soleRenderRootExpect`): `expect(container).not.toBeEmptyDOMElement()`, `expect(container.firstChild).toBeInTheDocument()`, `expect(container.innerHTML).not.toBe('')` and the like, on a `container` or `baseElement` bound from a call (`const { container } = render(...)`, `view.container`, `render(...).container`). Such a test passes for anything that renders, an error fallback included. Assert on a role, a label or a text instead, or set the new `renderRoots` option to `[]` to keep the old behaviour.
- `skipped-tests-need-tracking` now also reads `node:test` skips: a `skip` or `todo` option whose value is a truthy literal (`{ skip: true }`, `{ skip: 'reason' }`, `{ todo: 1 }`) on `test` / `it` / `describe` / `suite` (and a `t.test` subtest), and `t.skip()` / `t.todo()` (awaited or not) as a statement of the test callback's own body. Only unconditional skips are reported: a computed value (`{ skip: process.platform === 'win32' }`, `{ skip: !ready }`) or a `t.skip(...)` inside an `if` is a platform guard and stays silent. Add an issue URL or `TODO(@owner)` near the skip, as for `.skip(`.

**New rules, left out of `recommended`** (each needs a per-project fact; enabling them is up to you):

- `no-message-only-throw-assertion`: `toThrow()` / `toThrowError()` with no argument, or with only a string, template or regex, and the message snapshots `toThrowErrorMatchingSnapshot()` / `toThrowErrorMatchingInlineSnapshot()`, sync or after `.rejects`. Any error passes those, including a `TypeError` from a broken mock. A class argument, an asymmetric matcher, `.rejects.toMatchObject(...)` and `.not.toThrow()` are fine, and a message-only assertion is accepted when the same test pins the class of the same subject in the same or an enclosing block (a bare `toThrow()` is never excused that way). Options: `throwMatchers`, `allowMessageOnly` (report only the argless form), `trustErrorInstances` (set `false` under Jest, where `toThrow(new X('m'))` and `.rejects.toEqual(new X('m'))` compare only the message; a `.rejects.toMatchObject({ message })` or `toHaveProperty('message')` is never a class pin) and `assertionHelpers`.
- `no-sleep-in-unit-tests`: a real sleep in a unit test file: `new Promise((r) => setTimeout(r, n))`, `setTimeout` from `timers/promises`, and `promisify(setTimeout)`. A zero or omitted delay (`allowZeroDelay`, on by default) a reject-only timeout guard and a deadline whose kept handle the file passes to `clearTimeout` are not sleeps, and a file that installs fake timers (`fakeTimerMethods`, default `useFakeTimers`) is not checked, since its waits are virtual. Same `testFileSuffixes` / `integrationMarkers` options as `no-real-network-in-unit-tests`.
- `no-real-clock-in-unit-tests`: `Date.now()`, an argless `new Date()` and `Date()` in a unit test file that never fakes the clock (`useFakeTimers`, `setSystemTime`, a `Date.now` spy, a stubbed or replaced global `Date`, or a mocked module matching `clockModules`). An offset from now (`Date.now() + 60_000`, `new Date().getTime() - 1000`) is allowed.
- `typed-mock-over-double-cast`: an object literal containing `jest.fn()` / `vi.fn()` cast `as unknown as T` (or through `any` or `never`, in `as` or angle-bracket form). Type it as `jest.Mocked<Pick<T, ...>>` or check it with `satisfies`. Options: `mockFactories`, `allowTargets` for types too wide to `Pick` from.

## 0.3.2

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-code-quality/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@noctcore/eslint-plugin-code-quality",
"version": "0.3.2",
"version": "0.4.0",
"description": "Portable code-quality, comment-hygiene and test-discipline ESLint rules: guard clauses, deterministic time, no stray process.exit, and tests that cannot pass vacuously.",
"license": "MIT",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-code-quality/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { rules } from './rules';

/** Flat-config namespace: rule ids are keyed `noctcore-code-quality/<rule>`. */
const NAMESPACE = 'noctcore-code-quality';
const VERSION = '0.3.2';
const VERSION = '0.4.0';

const plugin = {
meta: { name: '@noctcore/eslint-plugin-code-quality', version: VERSION },
Expand Down
Loading