Skip to content

Commit 5feb36e

Browse files
committed
test(e2e): strengthen mod happy-path test to cover the full clone flow
The previous version of this test verified `exitCode === 0` and that a slug-prefix directory existed with a `package.json`. That assertion would pass even if `dot mod` invented an arbitrary `package.json` from thin air and silently skipped both setup.sh and the side effects of the source-download step. `dot mod`'s flow has three steps (see src/commands/mod/SetupScreen.tsx): 1. fetch app metadata 2. download source — extract tarball + writeDotJson + ignoreModLogs + stripPostinstall + createOptionalGitBaseline 3. run setup.sh (or warn if absent) Steps 2's side effects and step 3 weren't observed at all. Strengthen the test to assert on: - multiple specific files from the upstream paritytech/Rock-Paper- Scissors fixture (README.md, setup.sh, src/, vite.config.ts) — proves the GitHub tarball was actually fetched and extracted, not invented. - dot.json was written with the expected `domain` (relative slug-suffix path, per writeDotJson()) and `name` fields. - .gitignore contains the mod-log entries appended by ignoreModLogs(). - .dot-mod-setup.log exists with `[setup]`-prefixed content from the fixture's setup.sh — proves step 3 ran, not just that the file was copied across. Bumped the per-test timeout 180s → 240s to give setup.sh's `npm install` enough headroom on a cold cache. Same fixture, no new registration needed.
1 parent 1dee469 commit 5feb36e

1 file changed

Lines changed: 59 additions & 6 deletions

File tree

e2e/cli/mod.test.ts

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
import { describe, test, expect, afterEach } from "vitest";
14-
import { existsSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
14+
import { existsSync, mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
1515
import { join } from "node:path";
1616
import { tmpdir } from "node:os";
1717
import { dot } from "./helpers/dot.js";
@@ -37,16 +37,20 @@ afterEach(() => {
3737

3838
describe("dot mod — clone", () => {
3939
test.skipIf(!TEST_DOMAIN)(
40-
"clones the registered template into a fresh directory",
41-
{ timeout: 180_000 },
40+
"full clone flow: fetches source, runs setup.sh, writes dot.json",
41+
{ timeout: 240_000 },
4242
async () => {
4343
const cwd = makeTempDir("dot-e2e-mod-cwd-");
4444
const result = await dot(["mod", TEST_DOMAIN, "--suri", ALICE.suri], {
4545
cwd,
46-
timeout: 180_000,
46+
timeout: 240_000,
4747
});
4848

49-
expect(result.exitCode).toBe(0);
49+
expect(
50+
result.exitCode,
51+
`mod failed:\n${result.stdout}\n${result.stderr}`,
52+
).toBe(0);
53+
5054
// defaultRepoName slugifies the domain and appends a 6-hex suffix.
5155
const slug = TEST_DOMAIN.replace(/\.dot$/, "")
5256
.toLowerCase()
@@ -55,7 +59,56 @@ describe("dot mod — clone", () => {
5559
(name) => name.startsWith(`${slug}-`) && /-[0-9a-f]{6}$/.test(name),
5660
);
5761
expect(created).toHaveLength(1);
58-
expect(existsSync(join(cwd, created[0]!, "package.json"))).toBe(true);
62+
const projectDir = join(cwd, created[0]!);
63+
64+
// ── Step 2 (download source) — content from GitHub ─────────────
65+
// Asserting on multiple specific files proves the tarball was
66+
// actually fetched and extracted, not invented from thin air.
67+
// All four files are committed to paritytech/Rock-Paper-Scissors's
68+
// default branch — if any go missing, the upstream fixture has
69+
// been restructured and this test needs updating to match.
70+
expect(existsSync(join(projectDir, "package.json"))).toBe(true);
71+
expect(existsSync(join(projectDir, "README.md"))).toBe(true);
72+
expect(existsSync(join(projectDir, "setup.sh"))).toBe(true);
73+
expect(existsSync(join(projectDir, "vite.config.ts"))).toBe(true);
74+
75+
// ── Step 2 side effect: writeDotJson() ─────────────────────────
76+
const dotJsonPath = join(projectDir, "dot.json");
77+
expect(existsSync(dotJsonPath)).toBe(true);
78+
const dotJson = JSON.parse(readFileSync(dotJsonPath, "utf-8")) as {
79+
domain?: string;
80+
name?: string;
81+
};
82+
// `domain` is set to `targetDir` in writeDotJson() — and
83+
// `targetDir` flows from defaultRepoName(), which returns a
84+
// RELATIVE slug-suffix path (e.g. `dot-cli-mod-fixture-a3b2c1`),
85+
// not an absolute one. So dotJson.domain should equal the leaf
86+
// directory name, NOT projectDir. (Field name is unusual but
87+
// documented in src/commands/mod/SetupScreen.tsx.)
88+
expect(dotJson.domain).toBe(created[0]);
89+
expect(dotJson.name).toBeDefined();
90+
91+
// ── Step 2 side effect: ignoreModLogs() ────────────────────────
92+
const gitignore = readFileSync(
93+
join(projectDir, ".gitignore"),
94+
"utf-8",
95+
);
96+
expect(gitignore).toContain(".dot-mod-setup.log");
97+
expect(gitignore).toContain(".dot-mod-source.log");
98+
99+
// ── Step 3 (run setup.sh) ──────────────────────────────────────
100+
// The script's first line of substantive output is
101+
// `echo "[setup] Rock Paper Scissors tutorial"`. If the log file
102+
// exists with that prefix, setup.sh actually ran. (We can't
103+
// assert exit 0 of the script here — exit-code propagation is
104+
// already covered by the outer `result.exitCode` check above.)
105+
const setupLog = join(projectDir, ".dot-mod-setup.log");
106+
expect(
107+
existsSync(setupLog),
108+
`setup.sh log not found — step 3 did not run.\n${result.stdout}\n${result.stderr}`,
109+
).toBe(true);
110+
const setupLogContent = readFileSync(setupLog, "utf-8");
111+
expect(setupLogContent).toContain("[setup]");
59112
},
60113
);
61114

0 commit comments

Comments
 (0)