Skip to content

Commit 92cfb9a

Browse files
authored
fix(shadcn): flaky create-project tests (#7590)
* fix(shadcn): flaky create-project tests * fix * fix
1 parent c5d90c7 commit 92cfb9a

2 files changed

Lines changed: 90 additions & 4 deletions

File tree

.changeset/tricky-terms-smell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"shadcn": patch
3+
---
4+
5+
fix flacky tests

packages/shadcn/src/utils/create-project.test.ts

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import { fetchRegistry } from "@/src/registry/api"
2+
import { spinner } from "@/src/utils/spinner"
23
import { execa } from "execa"
34
import fs from "fs-extra"
45
import prompts from "prompts"
5-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
6+
import {
7+
afterEach,
8+
beforeEach,
9+
describe,
10+
expect,
11+
it,
12+
vi,
13+
type MockInstance,
14+
} from "vitest"
615

716
import { TEMPLATES, createProject } from "./create-project"
817

@@ -14,16 +23,85 @@ vi.mock("@/src/registry/api")
1423
vi.mock("@/src/utils/get-package-manager", () => ({
1524
getPackageManager: vi.fn().mockResolvedValue("npm"),
1625
}))
26+
vi.mock("@/src/utils/spinner")
27+
vi.mock("@/src/utils/logger", () => ({
28+
logger: {
29+
break: vi.fn(),
30+
error: vi.fn(),
31+
info: vi.fn(),
32+
},
33+
}))
1734

1835
describe("createProject", () => {
36+
let mockExit: MockInstance
37+
1938
beforeEach(() => {
2039
vi.clearAllMocks()
40+
41+
// Reset all fs mocks
2142
vi.mocked(fs.access).mockResolvedValue(undefined)
2243
vi.mocked(fs.existsSync).mockReturnValue(false)
44+
vi.mocked(fs.ensureDir).mockResolvedValue(undefined)
45+
vi.mocked(fs.writeFile).mockResolvedValue(undefined)
46+
vi.mocked(fs.move).mockResolvedValue(undefined)
47+
vi.mocked(fs.remove).mockResolvedValue(undefined)
48+
49+
// Mock execa to resolve immediately without actual execution
50+
vi.mocked(execa).mockResolvedValue({
51+
stdout: "",
52+
stderr: "",
53+
exitCode: 0,
54+
signal: undefined,
55+
signalDescription: undefined,
56+
command: "",
57+
escapedCommand: "",
58+
failed: false,
59+
timedOut: false,
60+
isCanceled: false,
61+
killed: false,
62+
} as any)
63+
64+
// Mock fetch for monorepo template
65+
global.fetch = vi.fn().mockResolvedValue({
66+
ok: true,
67+
arrayBuffer: () => Promise.resolve(new ArrayBuffer(0)),
68+
} as any)
69+
70+
// Reset prompts mock
71+
vi.mocked(prompts).mockResolvedValue({ type: "next", name: "my-app" })
72+
73+
// Reset registry mock
74+
vi.mocked(fetchRegistry).mockResolvedValue([])
75+
76+
// Mock spinner function
77+
const mockSpinner = {
78+
start: vi.fn().mockReturnThis(),
79+
succeed: vi.fn().mockReturnThis(),
80+
fail: vi.fn().mockReturnThis(),
81+
stop: vi.fn().mockReturnThis(),
82+
text: "",
83+
prefixText: "",
84+
suffixText: "",
85+
color: "cyan" as const,
86+
indent: 0,
87+
spinner: "dots" as const,
88+
isSpinning: false,
89+
interval: 100,
90+
stream: process.stderr,
91+
clear: vi.fn(),
92+
render: vi.fn(),
93+
frame: vi.fn(),
94+
stopAndPersist: vi.fn(),
95+
warn: vi.fn(),
96+
info: vi.fn(),
97+
}
98+
vi.mocked(spinner).mockReturnValue(mockSpinner as any)
2399
})
24100

25101
afterEach(() => {
26102
vi.resetAllMocks()
103+
mockExit?.mockRestore()
104+
delete (global as any).fetch
27105
})
28106

29107
it("should create a Next.js project with default options", async () => {
@@ -84,10 +162,13 @@ describe("createProject", () => {
84162
})
85163

86164
it("should throw error if project path already exists", async () => {
87-
vi.mocked(fs.existsSync).mockReturnValue(true)
165+
// Mock fs.existsSync to return true only for the specific package.json path
166+
vi.mocked(fs.existsSync).mockImplementation((path: any) => {
167+
return path.toString().includes("existing-app/package.json")
168+
})
88169
vi.mocked(prompts).mockResolvedValue({ type: "next", name: "existing-app" })
89170

90-
const mockExit = vi
171+
mockExit = vi
91172
.spyOn(process, "exit")
92173
.mockImplementation(() => undefined as never)
93174

@@ -103,7 +184,7 @@ describe("createProject", () => {
103184
vi.mocked(fs.access).mockRejectedValue(new Error("Permission denied"))
104185
vi.mocked(prompts).mockResolvedValue({ type: "next", name: "my-app" })
105186

106-
const mockExit = vi
187+
mockExit = vi
107188
.spyOn(process, "exit")
108189
.mockImplementation(() => undefined as never)
109190

0 commit comments

Comments
 (0)