Skip to content

Commit 622ceaa

Browse files
committed
refactor(create-sei): extract lib and expand test coverage
1 parent 184d715 commit 622ceaa

7 files changed

Lines changed: 693 additions & 177 deletions

File tree

packages/create-sei/.gitignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
node_modules
22
dist
3-
4-
test
5-
```
3+
test-output-*
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2+
import * as fs from "node:fs";
3+
import path from "node:path";
4+
5+
const packageDir = path.resolve(import.meta.dir, "../..");
6+
const cliPath = path.join(packageDir, "dist", "main.js");
7+
const e2eDir = path.join(packageDir, "test-output-e2e");
8+
9+
async function runCli(
10+
args: string[],
11+
cwd: string,
12+
): Promise<{ stdout: string; stderr: string; exitCode: number }> {
13+
const proc = Bun.spawn(["node", cliPath, ...args], {
14+
cwd,
15+
stdout: "pipe",
16+
stderr: "pipe",
17+
env: { ...process.env, NO_COLOR: "1" },
18+
});
19+
20+
const [stdout, stderr] = await Promise.all([
21+
new Response(proc.stdout).text(),
22+
new Response(proc.stderr).text(),
23+
]);
24+
const exitCode = await proc.exited;
25+
26+
return { stdout, stderr, exitCode };
27+
}
28+
29+
describe("create-sei CLI e2e", () => {
30+
beforeAll(async () => {
31+
await fs.promises.rm(e2eDir, { recursive: true, force: true });
32+
await fs.promises.mkdir(e2eDir, { recursive: true });
33+
34+
// Ensure dist is built
35+
const distExists = await fs.promises
36+
.access(cliPath)
37+
.then(() => true)
38+
.catch(() => false);
39+
if (!distExists) {
40+
throw new Error(
41+
"dist/main.js not found. Run `bun run build` in packages/create-sei first.",
42+
);
43+
}
44+
}, 30_000);
45+
46+
afterAll(async () => {
47+
await fs.promises.rm(e2eDir, { recursive: true, force: true });
48+
}, 30_000);
49+
50+
test("app --name creates a project directory", async () => {
51+
const { exitCode } = await runCli(["app", "--name", "e2e-basic"], e2eDir);
52+
expect(exitCode).toBe(0);
53+
54+
const projectDir = path.join(e2eDir, "e2e-basic");
55+
const exists = await fs.promises
56+
.access(projectDir)
57+
.then(() => true)
58+
.catch(() => false);
59+
expect(exists).toBe(true);
60+
});
61+
62+
test("generated project has valid package.json", async () => {
63+
const pkgPath = path.join(e2eDir, "e2e-basic", "package.json");
64+
const raw = await fs.promises.readFile(pkgPath, "utf-8");
65+
const pkg = JSON.parse(raw);
66+
67+
expect(pkg.scripts).toBeDefined();
68+
expect(pkg.scripts.dev).toBe("next dev");
69+
expect(pkg.scripts.build).toBe("next build");
70+
expect(pkg.dependencies).toBeDefined();
71+
expect(pkg.dependencies.next).toBeDefined();
72+
expect(pkg.dependencies.react).toBeDefined();
73+
expect(pkg.dependencies.viem).toBeDefined();
74+
});
75+
76+
test("generated project has expected file structure", async () => {
77+
const projectDir = path.join(e2eDir, "e2e-basic");
78+
const expectedFiles = [
79+
"package.json",
80+
"tsconfig.json",
81+
"next.config.mjs",
82+
"src",
83+
];
84+
85+
for (const file of expectedFiles) {
86+
const exists = await fs.promises
87+
.access(path.join(projectDir, file))
88+
.then(() => true)
89+
.catch(() => false);
90+
expect(exists).toBe(true);
91+
}
92+
});
93+
94+
test("generated project can install dependencies", async () => {
95+
const projectDir = path.join(e2eDir, "e2e-basic");
96+
97+
const proc = Bun.spawn(["bun", "install"], {
98+
cwd: projectDir,
99+
stdout: "pipe",
100+
stderr: "pipe",
101+
});
102+
103+
await proc.exited;
104+
const exitCode = proc.exitCode;
105+
expect(exitCode).toBe(0);
106+
107+
// node_modules should exist
108+
const nmExists = await fs.promises
109+
.access(path.join(projectDir, "node_modules"))
110+
.then(() => true)
111+
.catch(() => false);
112+
expect(nmExists).toBe(true);
113+
}, 60_000);
114+
115+
test("generated project can build successfully", async () => {
116+
const projectDir = path.join(e2eDir, "e2e-basic");
117+
118+
const proc = Bun.spawn(["bun", "run", "build"], {
119+
cwd: projectDir,
120+
stdout: "pipe",
121+
stderr: "pipe",
122+
});
123+
124+
const [stdout, stderr] = await Promise.all([
125+
new Response(proc.stdout).text(),
126+
new Response(proc.stderr).text(),
127+
]);
128+
await proc.exited;
129+
const exitCode = proc.exitCode;
130+
131+
if (exitCode !== 0) {
132+
console.error("Build stdout:", stdout);
133+
console.error("Build stderr:", stderr);
134+
}
135+
expect(exitCode).toBe(0);
136+
}, 120_000);
137+
138+
test("app --name --extension precompiles creates project with extension", async () => {
139+
const { exitCode, stdout } = await runCli(
140+
["app", "--name", "e2e-precompiles", "--extension", "precompiles"],
141+
e2eDir,
142+
);
143+
expect(exitCode).toBe(0);
144+
expect(stdout).toContain("Applied extension: precompiles");
145+
146+
// Extension should have overwritten package.json
147+
const pkgPath = path.join(e2eDir, "e2e-precompiles", "package.json");
148+
const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8"));
149+
expect(pkg.name).toBe("template-next-create-sei-app-precompiles");
150+
});
151+
152+
test("extension project can install dependencies", async () => {
153+
const projectDir = path.join(e2eDir, "e2e-precompiles");
154+
155+
const proc = Bun.spawn(["bun", "install"], {
156+
cwd: projectDir,
157+
stdout: "pipe",
158+
stderr: "pipe",
159+
});
160+
161+
await proc.exited;
162+
expect(proc.exitCode).toBe(0);
163+
}, 60_000);
164+
165+
test("extension project can build successfully", async () => {
166+
const projectDir = path.join(e2eDir, "e2e-precompiles");
167+
168+
const proc = Bun.spawn(["bun", "run", "build"], {
169+
cwd: projectDir,
170+
stdout: "pipe",
171+
stderr: "pipe",
172+
});
173+
174+
const [stdout, stderr] = await Promise.all([
175+
new Response(proc.stdout).text(),
176+
new Response(proc.stderr).text(),
177+
]);
178+
await proc.exited;
179+
180+
if (proc.exitCode !== 0) {
181+
console.error("Build stdout:", stdout);
182+
console.error("Build stderr:", stderr);
183+
}
184+
expect(proc.exitCode).toBe(0);
185+
}, 120_000);
186+
187+
test("list-extensions command outputs available extensions", async () => {
188+
const { exitCode, stdout } = await runCli(["list-extensions"], e2eDir);
189+
expect(exitCode).toBe(0);
190+
expect(stdout).toContain("Available extensions:");
191+
expect(stdout).toContain("precompiles");
192+
});
193+
194+
test("app with invalid name does not create directory", async () => {
195+
const { exitCode, stdout } = await runCli(
196+
["app", "--name", "INVALID NAME!"],
197+
e2eDir,
198+
);
199+
expect(exitCode).toBe(0);
200+
expect(stdout).toContain("Invalid package name");
201+
202+
const exists = await fs.promises
203+
.access(path.join(e2eDir, "INVALID NAME!"))
204+
.then(() => true)
205+
.catch(() => false);
206+
expect(exists).toBe(false);
207+
});
208+
209+
test("app with nonexistent extension falls back to base template", async () => {
210+
const { exitCode, stdout } = await runCli(
211+
["app", "--name", "e2e-fallback", "--extension", "does-not-exist"],
212+
e2eDir,
213+
);
214+
expect(exitCode).toBe(0);
215+
expect(stdout).toContain("Warning");
216+
expect(stdout).toContain("does-not-exist");
217+
218+
// Should still have created the project from base template
219+
const pkgPath = path.join(e2eDir, "e2e-fallback", "package.json");
220+
const pkg = JSON.parse(await fs.promises.readFile(pkgPath, "utf-8"));
221+
expect(pkg.scripts.dev).toBe("next dev");
222+
});
223+
});

0 commit comments

Comments
 (0)