From 25c4a4801700e4087d9e0c07ce6a2d2a852407cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 17:29:07 +0000 Subject: [PATCH] Version Packages --- .changeset/code-quality-test-discipline.md | 17 ----------------- .../eslint-plugin-code-quality/CHANGELOG.md | 18 ++++++++++++++++++ .../eslint-plugin-code-quality/package.json | 2 +- .../eslint-plugin-code-quality/src/index.ts | 2 +- 4 files changed, 20 insertions(+), 19 deletions(-) delete mode 100644 .changeset/code-quality-test-discipline.md diff --git a/.changeset/code-quality-test-discipline.md b/.changeset/code-quality-test-discipline.md deleted file mode 100644 index c4b11aa..0000000 --- a/.changeset/code-quality-test-discipline.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -'@noctcore/eslint-plugin-code-quality': minor ---- - -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>` or check it with `satisfies`. Options: `mockFactories`, `allowTargets` for types too wide to `Pick` from. diff --git a/packages/eslint-plugin-code-quality/CHANGELOG.md b/packages/eslint-plugin-code-quality/CHANGELOG.md index 3c51f54..2fa17d5 100644 --- a/packages/eslint-plugin-code-quality/CHANGELOG.md +++ b/packages/eslint-plugin-code-quality/CHANGELOG.md @@ -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>` or check it with `satisfies`. Options: `mockFactories`, `allowTargets` for types too wide to `Pick` from. + ## 0.3.2 ### Patch Changes diff --git a/packages/eslint-plugin-code-quality/package.json b/packages/eslint-plugin-code-quality/package.json index 5082422..ee7e97e 100644 --- a/packages/eslint-plugin-code-quality/package.json +++ b/packages/eslint-plugin-code-quality/package.json @@ -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", diff --git a/packages/eslint-plugin-code-quality/src/index.ts b/packages/eslint-plugin-code-quality/src/index.ts index 451b14e..7b62386 100644 --- a/packages/eslint-plugin-code-quality/src/index.ts +++ b/packages/eslint-plugin-code-quality/src/index.ts @@ -3,7 +3,7 @@ import { rules } from './rules'; /** Flat-config namespace: rule ids are keyed `noctcore-code-quality/`. */ 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 },