Skip to content

Commit 3ed58d9

Browse files
author
Simon Heather
committed
chore(tests): lint Jest tests with eslint-plugin-jest
1 parent 9083b1a commit 3ed58d9

24 files changed

Lines changed: 220 additions & 208 deletions

‎eslint.config.mjs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import nx from "@nx/eslint-plugin";
77
import tsParser from "@typescript-eslint/parser";
88
import eslint from "@eslint/js";
99
import tseslint from "typescript-eslint";
10+
import jest from "eslint-plugin-jest";
1011
import prettierConfig from "eslint-plugin-prettier/recommended";
1112

1213
export const baseConfig = [
@@ -78,6 +79,10 @@ export const baseConfig = [
7879
],
7980
},
8081
},
82+
{
83+
...jest.configs["flat/recommended"],
84+
files: ["**/*.test.ts"],
85+
},
8186
];
8287

8388
export default [...baseConfig, prettierConfig];

‎package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"eslint": "^9.39.4",
5959
"eslint-config-prettier": "^10.1.8",
6060
"eslint-formatter-gha": "^2.0.1",
61+
"eslint-plugin-jest": "^29.17.0",
6162
"eslint-plugin-jsdoc": "^50.8.0",
6263
"eslint-plugin-no-instanceof": "1.0.1",
6364
"eslint-plugin-prettier": "^5.5.6",

‎packages/@cdktn/cli-core/eslint.config.mjs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,15 @@ export default [
1515
],
1616
},
1717
...rootConfig,
18+
{
19+
files: ["**/*.test.ts"],
20+
21+
rules: {
22+
// Treat xstate's waitFor as an assertion.
23+
"jest/expect-expect": [
24+
"warn",
25+
{ assertFunctionNames: ["expect", "waitFor"] },
26+
],
27+
},
28+
},
1829
];

‎packages/@cdktn/cli-core/src/test/error-reporting.test.ts‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -329,13 +329,11 @@ describe("consent gating (initializErrorReporting)", () => {
329329

330330
expect(crashPrompt).not.toHaveBeenCalled();
331331
expect(usagePrompt).not.toHaveBeenCalled();
332-
if (hasProject) {
333-
const persisted = fs.readJsonSync(path.join(workdir, "cdktf.json"));
334-
expect(persisted.sendCrashReports).toBeUndefined();
335-
expect(persisted.sendUsageTelemetry).toBeUndefined();
336-
} else {
337-
expect(fs.existsSync(path.join(workdir, "cdktf.json"))).toBe(false);
338-
}
332+
const configPath = path.join(workdir, "cdktf.json");
333+
expect(fs.existsSync(configPath)).toBe(hasProject);
334+
const persisted = hasProject ? fs.readJsonSync(configPath) : {};
335+
expect(persisted.sendCrashReports).toBeUndefined();
336+
expect(persisted.sendUsageTelemetry).toBeUndefined();
339337
expect(Sentry.init).toHaveBeenCalledTimes(1);
340338
},
341339
);

‎packages/@cdktn/cli-core/src/test/lib/watch.test.ts‎

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -147,27 +147,35 @@ describe("watch", () => {
147147
await tick();
148148
});
149149

150-
it.each(["a failure", "an abort"])(
151-
"starts nothing for a change that arrives after %s",
152-
async (ending) => {
153-
const { ac, session } = startWatch();
154-
deploys[0].resolve();
155-
await tick();
156-
157-
if (ending === "an abort") {
158-
ac.abort();
159-
await session;
160-
} else {
161-
watcher.emit("all");
162-
deploys[1].reject(new Error("terraform apply failed"));
163-
await expect(session).rejects.toThrow("terraform apply failed");
164-
}
165-
const started = deploys.length;
166-
167-
watcher.emit("all");
168-
await tick();
169-
170-
expect(deploys).toHaveLength(started);
171-
},
172-
);
150+
async function deploysStartedByLaterChange() {
151+
const started = deploys.length;
152+
153+
watcher.emit("all");
154+
await tick();
155+
156+
return deploys.length - started;
157+
}
158+
159+
it("starts nothing for a change that arrives after a failure", async () => {
160+
const { session } = startWatch();
161+
deploys[0].resolve();
162+
await tick();
163+
164+
watcher.emit("all");
165+
deploys[1].reject(new Error("terraform apply failed"));
166+
await expect(session).rejects.toThrow("terraform apply failed");
167+
168+
expect(await deploysStartedByLaterChange()).toBe(0);
169+
});
170+
171+
it("starts nothing for a change that arrives after an abort", async () => {
172+
const { ac, session } = startWatch();
173+
deploys[0].resolve();
174+
await tick();
175+
176+
ac.abort();
177+
await session;
178+
179+
expect(await deploysStartedByLaterChange()).toBe(0);
180+
});
173181
});

‎packages/@cdktn/cli-core/src/test/models/deploy-machine.test.ts‎

Lines changed: 49 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: MPL-2.0
44
*/
55

6-
import { createActor, fromCallback, getNextSnapshot } from "xstate";
6+
import { createActor, fromCallback, getNextSnapshot, waitFor } from "xstate";
77
import { spawnInteractive } from "../../lib/models/interactive-process";
88
import {
99
deployMachine,
@@ -60,7 +60,7 @@ describe("transitions", () => {
6060
expect(actualState.matches("running")).toBe(true);
6161
});
6262

63-
it("transitions to processing upon external approval", (done) => {
63+
it("transitions to processing upon external approval", async () => {
6464
const mockDeployMachine = deployMachine.provide({
6565
actors: {
6666
runTerraformInPty: fromCallback<DeployEvent, PtyActorInput>(
@@ -90,14 +90,12 @@ describe("transitions", () => {
9090
},
9191
});
9292

93-
ptyService.subscribe((state) => {
94-
if (
93+
const processing = waitFor(
94+
ptyService,
95+
(state) =>
9596
state.matches({ running: "processing" }) &&
96-
lastEvent === "APPROVED_EXTERNALLY"
97-
) {
98-
done();
99-
}
100-
});
97+
lastEvent === "APPROVED_EXTERNALLY",
98+
);
10199

102100
ptyService.start();
103101

@@ -111,6 +109,8 @@ describe("transitions", () => {
111109
},
112110
},
113111
});
112+
113+
await processing;
114114
});
115115
});
116116

@@ -143,8 +143,7 @@ function mockPty(ptyEvents: string[]): typeof spawnInteractive {
143143
}
144144

145145
describe("pty events", () => {
146-
it("transitions when pty receives an approval question", (done) => {
147-
let isDone = false;
146+
it("transitions when pty receives an approval question", async () => {
148147
const mockDeployMachine = deployMachine.provide({
149148
actors: {
150149
runTerraformInPty: makeTerraformPtyService(
@@ -154,12 +153,9 @@ describe("pty events", () => {
154153
});
155154

156155
const ptyService = createActor(mockDeployMachine);
157-
ptyService.subscribe((state) => {
158-
if (state.matches({ running: "awaiting_approval" }) && !isDone) {
159-
isDone = true;
160-
done();
161-
}
162-
});
156+
const awaitingApproval = waitFor(ptyService, (state) =>
157+
state.matches({ running: "awaiting_approval" }),
158+
);
163159

164160
ptyService.start();
165161

@@ -173,10 +169,11 @@ describe("pty events", () => {
173169
},
174170
},
175171
});
172+
173+
await awaitingApproval;
176174
});
177175

178-
it("transitions when pty receives an approval question that is split across two buffers", (done) => {
179-
let isDone = false;
176+
it("transitions when pty receives an approval question that is split across two buffers", async () => {
180177
const mockDeployMachine = deployMachine.provide({
181178
actors: {
182179
runTerraformInPty: makeTerraformPtyService(
@@ -186,12 +183,9 @@ describe("pty events", () => {
186183
});
187184

188185
const ptyService = createActor(mockDeployMachine);
189-
ptyService.subscribe((state) => {
190-
if (state.matches({ running: "awaiting_approval" }) && !isDone) {
191-
isDone = true;
192-
done();
193-
}
194-
});
186+
const awaitingApproval = waitFor(ptyService, (state) =>
187+
state.matches({ running: "awaiting_approval" }),
188+
);
195189

196190
ptyService.start();
197191

@@ -205,9 +199,11 @@ describe("pty events", () => {
205199
},
206200
},
207201
});
202+
203+
await awaitingApproval;
208204
});
209205

210-
it("transitions when pty receives an override question", (done) => {
206+
it("transitions when pty receives an override question", async () => {
211207
const mockDeployMachine = deployMachine.provide({
212208
actors: {
213209
runTerraformInPty: makeTerraformPtyService(
@@ -222,11 +218,9 @@ describe("pty events", () => {
222218
});
223219

224220
const ptyService = createActor(mockDeployMachine);
225-
ptyService.subscribe((state) => {
226-
if (state.matches({ running: "awaiting_sentinel_override" })) {
227-
done();
228-
}
229-
});
221+
const awaitingOverride = waitFor(ptyService, (state) =>
222+
state.matches({ running: "awaiting_sentinel_override" }),
223+
);
230224

231225
ptyService.start();
232226

@@ -240,9 +234,11 @@ describe("pty events", () => {
240234
},
241235
},
242236
});
237+
238+
await awaitingOverride;
243239
});
244240

245-
it("waits for terraform to exit on STOP rather than stopping immediately", (done) => {
241+
it("waits for terraform to exit on STOP rather than stopping immediately", async () => {
246242
// The pty stays alive until we resolve its exit, letting us assert the machine waits for terraform's own exit
247243
// before reaching "stopped".
248244
const stop = jest.fn();
@@ -260,22 +256,10 @@ describe("pty events", () => {
260256
},
261257
});
262258

263-
let interrupted = false;
264259
const ptyService = createActor(mockDeployMachine);
265-
ptyService.subscribe((state) => {
266-
if (state.matches({ running: "stopping" }) && !interrupted) {
267-
interrupted = true;
268-
// We are waiting for terraform to exit, not yet at the final "stopped" state, and we have not re-signalled it.
269-
expect(state.matches("stopped")).toBe(false);
270-
expect(stop).not.toHaveBeenCalled();
271-
setTimeout(() => resolveExit(0), 50); // terraform finishes exiting, releasing its lock
272-
}
273-
274-
if (state.matches("stopped")) {
275-
expect(state.context.cancelled).toBe(true);
276-
done();
277-
}
278-
});
260+
const stopping = waitFor(ptyService, (state) =>
261+
state.matches({ running: "stopping" }),
262+
);
279263

280264
ptyService.start();
281265

@@ -286,9 +270,17 @@ describe("pty events", () => {
286270

287271
// Give the pty service a tick to start before requesting a stop.
288272
setTimeout(() => ptyService.send({ type: "STOP" }), 50);
273+
274+
// We are waiting for terraform to exit, not yet at the final "stopped" state, and we have not re-signalled it.
275+
expect((await stopping).matches("stopped")).toBe(false);
276+
expect(stop).not.toHaveBeenCalled();
277+
278+
const stopped = waitFor(ptyService, (state) => state.matches("stopped"));
279+
setTimeout(() => resolveExit(0), 50); // terraform finishes exiting, releasing its lock
280+
expect((await stopped).context.cancelled).toBe(true);
289281
});
290282

291-
it("transitions to rejected state when done externally", (done) => {
283+
it("transitions to rejected state when done externally", async () => {
292284
const mockDeployMachine = deployMachine.provide({
293285
actors: {
294286
runTerraformInPty: makeTerraformPtyService(
@@ -304,7 +296,6 @@ describe("pty events", () => {
304296
});
305297

306298
// Record events seen by the machine via inspection.
307-
let enteredAwaiting = false;
308299
const states: string[] = [];
309300
const ptyService = createActor(mockDeployMachine, {
310301
inspect: (inspectionEvent) => {
@@ -317,17 +308,9 @@ describe("pty events", () => {
317308
},
318309
});
319310

320-
ptyService.subscribe((state) => {
321-
if (enteredAwaiting && state.matches("exited")) {
322-
expect(states).toEqual(
323-
expect.arrayContaining(["OVERRIDE_REJECTED_EXTERNALLY"]),
324-
);
325-
done();
326-
}
327-
if (state.matches({ running: "awaiting_sentinel_override" })) {
328-
enteredAwaiting = true;
329-
}
330-
});
311+
const awaitingOverride = waitFor(ptyService, (state) =>
312+
state.matches({ running: "awaiting_sentinel_override" }),
313+
);
331314

332315
ptyService.start();
333316

@@ -341,6 +324,12 @@ describe("pty events", () => {
341324
},
342325
},
343326
});
327+
328+
await awaitingOverride;
329+
await waitFor(ptyService, (state) => state.matches("exited"));
330+
expect(states).toEqual(
331+
expect.arrayContaining(["OVERRIDE_REJECTED_EXTERNALLY"]),
332+
);
344333
});
345334
});
346335

‎packages/@cdktn/commons/src/config.test.ts‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) HashiCorp, Inc
22
// SPDX-License-Identifier: MPL-2.0
33
import { parseConfig } from "./config";
4-
import * as fs from "fs-extra";
5-
import * as os from "os";
64
import * as path from "path";
75

86
/**
@@ -14,15 +12,6 @@ function toPosixPath(p: string): string {
1412
return p.split(path.sep).join(path.posix.sep);
1513
}
1614

17-
export async function mkdtemp(closure: (dir: string) => Promise<void>) {
18-
const workdir = await fs.mkdtemp(path.join(os.tmpdir(), "cdktf."));
19-
try {
20-
await closure(workdir);
21-
} finally {
22-
await fs.remove(workdir);
23-
}
24-
}
25-
2615
describe("parseConfig", () => {
2716
it("provides default with no input", async () => {
2817
expect(parseConfig()).toMatchInlineSnapshot(`

0 commit comments

Comments
 (0)