Skip to content
Draft
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
6 changes: 6 additions & 0 deletions .changeset/many-needles-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@flint.fyi/rule-data": patch
"@flint.fyi/vitest": patch
---

[titleValidity] add rule.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
[titleValidity] add rule.
Added `titleValidity` rule, which aims to enforce valid titles for `describe()`, `it()` and `test()` titles.

3 changes: 2 additions & 1 deletion packages/rule-data/src/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -31557,7 +31557,8 @@
"flint": {
"name": "titleValidity",
"plugin": "vitest",
"preset": "logical"
"preset": "logical",
"status": "implemented"
},
"oxlint": [
{
Expand Down
217 changes: 217 additions & 0 deletions packages/site/src/content/docs/rules/vitest/titleValidity.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
---
description: "This rule aims to enforce valid titles for `describe()`, `it()` and `test()` titles."
title: "titleValidity"
topic: "rules"
---

import { TabItem, Tabs } from "@astrojs/starlight/components";

import { RuleEquivalents } from "~/components/RuleEquivalents";
import RuleSummary from "~/components/RuleSummary.astro";

<RuleSummary plugin="vitest" rule="titleValidity" />

Reports `describe()`, `it()`, and `test()` calls with invalid titles.

## Examples

<Tabs>
<TabItem label="❌ Incorrect">

```ts
describe(1, () => {
// ...
});
```

```ts
it("", () => {
// ...
});
```

```ts
it("it returns a number", () => {
// ...
});
```

```ts
it(" returns a number ", () => {
// ...
});
```

</TabItem>
<TabItem label="✅ Correct">

```ts
describe("1", () => {
// ...
});
```

```ts
it("returns a number", () => {
// ...
});
```

```ts
declare function getValue(): number;

describe(getValue, () => {
// ...
});
```

</TabItem>
</Tabs>

## Options

### `allowArguments`

Whether to allow identifiers as titles.
Defaults to `false`.

Only identifiers are skipped by this option.
Function calls such as `getTitle()` and property accesses such as `config.title` are not skipped, and are still reported.

Examples of **incorrect** code for this rule with the `{ "allowArguments": true }` option:

```ts
declare function getTitle(): unknown;

it(getTitle(), () => {
// ...
});
```

```ts
declare const config: { title: unknown };

it(config.title, () => {
// ...
});
```

Examples of **correct** code for this rule with the `{ "allowArguments": true }` option:

```ts
declare const title: unknown;

it(title, () => {
// ...
});
```

### `disallowedWords`

Words that are not allowed to appear in titles.
Defaults to `[]`.

Words are matched case-insensitively on word boundaries, against the title string only.
Because of the word boundaries, `"skip"` does not match a title containing `skipped`.

Examples of **incorrect** code for this rule with the `{ "disallowedWords": ["skips"] }` option:

```ts
it("skips the empty case", () => {
// ...
});
```

Examples of **correct** code for this rule with the `{ "disallowedWords": ["skips"] }` option:

```ts
it("ignores the empty case", () => {
// ...
});
```

### `ignoreTypeOfDescribeName`

Whether to skip checking the type of `describe()` titles.
Defaults to `false`.

Examples of **correct** code for this rule with the `{ "ignoreTypeOfDescribeName": true }` option:

```ts
declare const value: unknown;

describe(typeof value, () => {
// ...
});
Comment on lines +141 to +145

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This example is not what the vitest equivalent option permits (or what the description above describes.

```

### `mustMatch`

Regular expressions that titles must match, optionally with a custom message.
Not set by default.

A string or a `[pattern, message]` pair applies to `describe`, `it`, and `test` alike.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there a reason we've deviated from the API of the eslint equivalent of this option? I.e. having a pattern/message array instead of having an array of regexes?

An object applies a separate pattern per function:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
An object applies a separate pattern per function:
Using an object with `describe`, `it`, and/or `test` properties, you can apply a different match pattern for each function:


```json
{
"mustMatch": {
"it": ["^should ", "Start test titles with \"should\"."]
}
}
```

Aliases map onto those three keys: `xdescribe` uses `describe`, `fit` and `xit` use `it`, and `xtest` uses `test`.

Examples of **incorrect** code for this rule with the `{ "mustMatch": "^should " }` option:

```ts
it("returns a number", () => {
// ...
});
```

Examples of **correct** code for this rule with the `{ "mustMatch": "^should " }` option:

```ts
it("should return a number", () => {
// ...
});
```

### `mustNotMatch`

Regular expressions that titles must not match, optionally with a custom message.
Not set by default.

It takes the same shapes as `mustMatch`.
When a title matches `mustNotMatch`, that is reported and `mustMatch` is not checked.

Examples of **incorrect** code for this rule with the `{ "mustNotMatch": "^should " }` option:

```ts
it("should return a number", () => {
// ...
});
```

Examples of **correct** code for this rule with the `{ "mustNotMatch": "^should " }` option:

```ts
it("returns a number", () => {
// ...
});
```

## When Not To Use It

Projects that lean on generated titles, or that have a large existing suite whose titles would be disruptive to rename, might prefer to enable only the options they need rather than the whole rule.

## Further Reading

- [Vitest API: Describe](https://vitest.dev/api/describe)
- [Vitest API: Test](https://vitest.dev/api/test)

## Equivalents in Other Linters

<RuleEquivalents pluginId="vitest" ruleId="titleValidity" />
3 changes: 2 additions & 1 deletion packages/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"dependencies": {
"@flint.fyi/core": "workspace:^",
"@flint.fyi/typescript-language": "workspace:^",
"typescript": "^6.0.0"
"typescript": "^6.0.0",
"zod": "^4.3.6"
},
"devDependencies": {
"@flint.fyi/build": "workspace:^",
Expand Down
2 changes: 2 additions & 0 deletions packages/vitest/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import expectGroupPaddingLines from "./rules/expectGroupPaddingLines.ts";
import nodeTestImports from "./rules/nodeTestImports.ts";
import testCasePaddingLines from "./rules/testCasePaddingLines.ts";
import testCasesWithinDescribes from "./rules/testCasesWithinDescribes.ts";
import titleValidity from "./rules/titleValidity.ts";

export const vitest = createPlugin({
files: {
Expand All @@ -29,5 +30,6 @@ export const vitest = createPlugin({
nodeTestImports,
testCasePaddingLines,
testCasesWithinDescribes,
titleValidity,
],
});
Loading
Loading