Open History to see the last commit
1import { assert, describe } from "@effect/vitest";
2
3import { createOxlintRuleHarness } from "../test/utils.ts";
4
5const RULE = "t3code/no-test-timeout-or-unreasoned-skip";
6const rule = createOxlintRuleHarness(RULE, { filename: "fixture.test.ts" });
7
8const assertTimeoutMessage = (output: string) =>
9 assert.match(output, /project timeout is the ceiling/);
10const assertSkipMessage = (output: string) => assert.match(output, /quarantine: #1234/);
11
12describe(RULE, () => {
13 rule.valid(
14 "allows tests without overrides, conditional skips, and waitFor timeouts",
15 `
16 import { describe, it, vi } from "vitest";
17
18 describe.skipIf(process.env.CI)("suite", () => {
19 it.runIf(true)("runs", async () => {
20 await vi.waitFor(() => {}, { timeout: 10_000 });
21 });
22 it.each([1, 2])("handles %s", (value) => {});
23 });
24 vi.setConfig({ restoreMocks: true });
25 `,
26 );
27
28 rule.valid(
29 "allows a skip with a quarantine issue or a GitHub issue URL",
30 `
31 import { describe, it } from "vitest";
32
33 // quarantine: #1234 flakes on Windows until the pty fix lands
34 it.skip("pending", () => {});
35
36 // https://github.com/pingdotgg/t3code/issues/42
37 describe.only("focused", () => {});
38 `,
39 );
40
41 rule.invalid(
42 "reports a numeric timeout on it.effect",
43 `
44 import { it } from "@effect/vitest";
45 import * as Effect from "effect/Effect";
46
47 it.effect("slow", () => Effect.void, 20_000);
48 `,
49 assertTimeoutMessage,
50 );
51
52 rule.invalid(
53 "reports a timeout through .each and .skipIf chains",
54 `
55 import { it } from "@effect/vitest";
56 import * as Effect from "effect/Effect";
57
58 it.live.skipIf(false)("slow", () => Effect.void, 5_000);
59 it.effect.each([1])("case %s", () => Effect.void, { timeout: 1_000 });
60 `,
61 (output) => {
62 assertTimeoutMessage(output);
63 assert.equal(output.match(/no-test-timeout-or-unreasoned-skip/g)?.length, 2);
64 },
65 );
66
67 rule.invalid(
68 "reports retry options, options-first timeouts, and hook timeouts",
69 `
70 import { beforeAll, describe, it } from "vitest";
71
72 describe("suite", () => {
73 beforeAll(() => {}, 60_000);
74 it("flaky", () => {}, { retry: 3 });
75 it("slow", { timeout: 30_000 }, () => {});
76 });
77 `,
78 (output) => {
79 assertTimeoutMessage(output);
80 assert.equal(output.match(/no-test-timeout-or-unreasoned-skip/g)?.length, 3);
81 },
82 );
83
84 rule.invalid(
85 "reports vi.setConfig timeouts",
86 `
87 import { vi } from "vitest";
88
89 vi.setConfig({ testTimeout: 30_000 });
90 `,
91 assertTimeoutMessage,
92 );
93
94 rule.invalid(
95 "reports a skip whose comment has no issue",
96 `
97 import { it } from "@effect/vitest";
98 import * as Effect from "effect/Effect";
99
100 // flaky, will fix later
101 it.effect.skip("pending", () => Effect.void);
102 `,
103 assertSkipMessage,
104 );
105
106 rule.invalid(
107 "reports todo, only, and the x/f aliases",
108 `
109 import { describe, it, test } from "vitest";
110
111 test.todo("later");
112 describe.only("focused", () => {});
113 xit("disabled", () => {});
114 fdescribe("focused", () => {});
115 `,
116 (output) => {
117 assertSkipMessage(output);
118 assert.equal(output.match(/no-test-timeout-or-unreasoned-skip/g)?.length, 4);
119 },
120 );
121 rule.invalid(
122 "flags an options-object timeout when the callback is a reference",
123 `
124 import { it } from "vitest";
125 const run = async () => {};
126 it("slow", { timeout: 30_000 }, run);
127 `,
128 assertTimeoutMessage,
129 );
130
131 rule.valid(
132 "allows a computed skip option, which is a platform gate like skipIf",
133 `
134 import { it } from "vitest";
135 const windowsHost = process.platform === "win32";
136 it.each([{ name: "a" }])("x $name", { skip: windowsHost }, () => {});
137 `,
138 );
139
140 rule.invalid(
141 "flags the suite alias and option-object skips",
142 `
143 import { suite, test } from "vitest";
144 suite.skip("later", () => {});
145 `,
146 assertSkipMessage,
147 );
148
149 rule.invalid(
150 "flags a skip option without a quarantine issue",
151 `
152 import { test } from "vitest";
153 test("later", { skip: true }, () => {});
154 `,
155 assertSkipMessage,
156 );
157
158 rule.invalid(
159 "flags a bracket-access skip without a quarantine issue",
160 `
161 import { it } from "vitest";
162 it["skip"]("later", () => {});
163 `,
164 assertSkipMessage,
165 );
166});
167
168createOxlintRuleHarness(RULE, { filename: "Provider.live.test.ts" }).invalid(
169 "still flags an option-object skip in a live file",
170 `
171 import { it } from "vitest";
172 it("later", { skip: true }, () => {});
173 `,
174 assertSkipMessage,
175);
176
177createOxlintRuleHarness(RULE, { filename: "Provider.live.test.ts" }).valid(
178 "allows timeouts in opt-in live provider suites",
179 `
180 import { it } from "@effect/vitest";
181 import * as Effect from "effect/Effect";
182
183 it.live("real turn", () => Effect.void, 360_000);
184 `,
185);
186
187createOxlintRuleHarness(RULE).valid(
188 "ignores non-test files",
189 `
190 const it = (_name: string, _fn: () => void, _ms: number) => {};
191 it("x", () => {}, 1_000);
192 `,
193);