Skip to content

Commit e166bf9

Browse files
committed
test: make issue list output deterministic
1 parent 6fdd1e0 commit e166bf9

3 files changed

Lines changed: 87 additions & 57 deletions

File tree

deno.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/commands/issue/issue-list.test.ts

Lines changed: 75 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { snapshotTest } from "@cliffy/testing"
2-
import { assertThrows } from "@std/assert"
1+
import { snapshotTest as cliffySnapshotTest } from "@cliffy/testing"
2+
import { assertEquals, assertThrows } from "@std/assert"
3+
import { stub } from "@std/testing/mock"
34
import { listCommand } from "../../../src/commands/issue/issue-list.ts"
45
import { parseDateFilter } from "../../../src/utils/linear.ts"
56
import { ValidationError } from "../../../src/utils/errors.ts"
@@ -9,7 +10,7 @@ import {
910
} from "../../utils/test-helpers.ts"
1011

1112
// Test help output
12-
await snapshotTest({
13+
await cliffySnapshotTest({
1314
name: "Issue List Command - Help Text",
1415
meta: import.meta,
1516
colors: false,
@@ -20,66 +21,85 @@ await snapshotTest({
2021
},
2122
})
2223

23-
await snapshotTest({
24-
name: "Issue List Command - Filter By Label",
25-
meta: import.meta,
26-
colors: false,
27-
args: ["--label", "Bug", "--team", "ENG", "--sort", "priority"],
28-
denoArgs: commonDenoArgs,
29-
async fn() {
30-
const { cleanup } = await setupMockLinearServer([
31-
{
32-
queryName: "GetTeamIdByKey",
33-
variables: { team: "ENG" },
34-
response: {
35-
data: {
36-
teams: {
37-
nodes: [{ id: "team-eng-id" }],
38-
},
24+
Deno.test("Issue List Command - Filter By Label", async () => {
25+
const fixedNow = new Date("2026-03-30T10:00:00.000Z")
26+
const RealDate = Date
27+
class MockDate extends RealDate {
28+
constructor(value?: string | number | Date) {
29+
super(value == null ? fixedNow.toISOString() : value)
30+
}
31+
32+
static override now(): number {
33+
return fixedNow.getTime()
34+
}
35+
}
36+
globalThis.Date = MockDate as DateConstructor
37+
38+
const { cleanup } = await setupMockLinearServer([
39+
{
40+
queryName: "GetTeamIdByKey",
41+
variables: { team: "ENG" },
42+
response: {
43+
data: {
44+
teams: {
45+
nodes: [{ id: "team-eng-id" }],
3946
},
4047
},
4148
},
42-
{
43-
queryName: "GetIssuesForState",
44-
response: {
45-
data: {
46-
issues: {
47-
nodes: [
48-
{
49-
id: "issue-1",
50-
identifier: "ENG-101",
51-
title: "Fix login bug",
52-
priority: 1,
53-
estimate: 3,
54-
assignee: { initials: "MC" },
55-
state: {
56-
id: "state-1",
57-
name: "In Progress",
58-
color: "#f2c94c",
59-
},
60-
labels: {
61-
nodes: [{
62-
id: "label-1",
63-
name: "Bug",
64-
color: "#eb5757",
65-
}],
66-
},
67-
updatedAt: "2026-03-13T10:00:00.000Z",
49+
},
50+
{
51+
queryName: "GetIssuesForState",
52+
response: {
53+
data: {
54+
issues: {
55+
nodes: [
56+
{
57+
id: "issue-1",
58+
identifier: "ENG-101",
59+
title: "Fix login bug",
60+
priority: 1,
61+
estimate: 3,
62+
assignee: { initials: "MC" },
63+
state: {
64+
id: "state-1",
65+
name: "In Progress",
66+
color: "#f2c94c",
67+
},
68+
labels: {
69+
nodes: [{
70+
id: "label-1",
71+
name: "Bug",
72+
color: "#eb5757",
73+
}],
6874
},
69-
],
70-
pageInfo: { hasNextPage: false, endCursor: null },
71-
},
75+
updatedAt: "2026-03-13T10:00:00.000Z",
76+
},
77+
],
78+
pageInfo: { hasNextPage: false, endCursor: null },
7279
},
7380
},
7481
},
75-
], { LINEAR_TEAM_ID: "ENG", LINEAR_ISSUE_SORT: "priority" })
82+
},
83+
], { LINEAR_TEAM_ID: "ENG", LINEAR_ISSUE_SORT: "priority", NO_COLOR: "true" })
7684

77-
try {
78-
await listCommand.parse()
79-
} finally {
80-
await cleanup()
81-
}
82-
},
85+
const logs: string[] = []
86+
const logStub = stub(console, "log", (...args: unknown[]) => {
87+
logs.push(args.map(String).join(" "))
88+
})
89+
90+
try {
91+
await listCommand.parse(["--label", "Bug", "--team", "ENG", "--sort", "priority"])
92+
93+
assertEquals(
94+
logs.join("\n") + "\n",
95+
"◌ ID TITLE LABELS E STATE UPDATED \n" +
96+
"⚠⚠⚠ ENG-101 Fix login bug Bug 3 In Progress 17 days ago\n",
97+
)
98+
} finally {
99+
logStub.restore()
100+
globalThis.Date = RealDate
101+
await cleanup()
102+
}
83103
})
84104

85105
// parseDateFilter unit tests

test/utils/mock_linear_server.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,19 @@ interface MockResponse {
2020

2121
export class MockLinearServer {
2222
private server?: Deno.HttpServer
23-
private port = 3333
23+
private port = 0
2424
private mockResponses: MockResponse[]
2525

2626
constructor(responses: MockResponse[] = []) {
2727
this.mockResponses = responses
2828
}
2929

3030
async start(): Promise<void> {
31-
this.server = Deno.serve({ port: this.port }, (request) => {
31+
this.server = Deno.serve({
32+
hostname: "127.0.0.1",
33+
port: this.port,
34+
onListen: () => {},
35+
}, (request) => {
3236
// Handle CORS preflight
3337
if (request.method === "OPTIONS") {
3438
return new Response(null, {
@@ -52,6 +56,10 @@ export class MockLinearServer {
5256
return new Response("Not Found", { status: 404 })
5357
})
5458

59+
if ("port" in this.server.addr) {
60+
this.port = this.server.addr.port
61+
}
62+
5563
// Wait a bit for server to start
5664
await new Promise((resolve) => setTimeout(resolve, 100))
5765
}

0 commit comments

Comments
 (0)