Skip to content
Source
Sign inSign up

check-changed-command.test.ts

122 lines4.4 KB

1// @effect-diagnostics nodeBuiltinImport:off
2import * as NodeChildProcess from "node:child_process";
3import * as NodeFS from "node:fs";
4import * as NodeOS from "node:os";
5import * as NodePath from "node:path";
6import { expect, it } from "vite-plus/test";
7​
8import { vitePlusCommand } from "./check-changed-command.ts";
9​
10function fixture() {
11 const root = NodeFS.realpathSync(
12 NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3 check & (vp) % ! ")),
13 );
14 const write = (file: string, content: string) => {
15 const path = NodePath.join(root, file);
16 NodeFS.mkdirSync(NodePath.dirname(path), { recursive: true });
17 NodeFS.writeFileSync(path, content);
18 };
19 NodeFS.copyFileSync(
20 NodePath.join(import.meta.dirname, "check-changed-command.ts"),
21 NodePath.join(root, "command.ts"),
22 );
23 write("package.json", JSON.stringify({ type: "module" }));
24 write(
25 "node_modules/vite-plus/package.json",
26 JSON.stringify({ type: "module", exports: { "./bin": "./cli.js" } }),
27 );
28 write(
29 "node_modules/vite-plus/cli.js",
30 `import fs from 'node:fs';
31 fs.writeFileSync(process.env.CALL_FILE, JSON.stringify({
32 args: process.argv.slice(2), cwd: process.cwd(), marker: process.env.ARG_MARKER
33 }));
34 process.stdout.write('vp stdout');
35 process.stderr.write('vp stderr');
36 process.exit(Number(process.env.EXIT_STATUS));`,
37 );
38 write(
39 "run.mjs",
40 `import { spawn, spawnSync } from 'node:child_process';
41 import { vitePlusCommand } from './command.ts';
42 const [command, args] = vitePlusCommand(process.argv.slice(3), 'win32');
43 const options = { cwd: process.env.CHILD_CWD, env: process.env };
44 if (process.argv[2] === 'sync') {
45 const result = spawnSync(command, args, { ...options, encoding: 'utf8' });
46 process.stdout.write(result.stdout ?? '');
47 process.stderr.write(result.stderr ?? '');
48 if (result.error) console.error(result.error.message);
49 process.exitCode = result.status ?? 1;
50 } else {
51 const child = spawn(command, args, options);
52 child.stdout.pipe(process.stdout);
53 child.stderr.pipe(process.stderr);
54 child.on('error', error => console.error(error.message));
55 child.on('close', status => { process.exitCode = status ?? 1; });
56 }`,
57 );
58 const cwd = NodePath.join(root, "temporary install");
59 NodeFS.mkdirSync(cwd);
60 return {
61 cwd,
62 run(mode: string, args: Array<string>, status: number) {
63 return NodeChildProcess.spawnSync(
64 process.execPath,
65 [NodePath.join(root, "run.mjs"), mode, ...args],
66 {
67 cwd: NodeOS.tmpdir(),
68 encoding: "utf8",
69 env: {
70 ...process.env,
71 // No vp, vp.cmd or node launcher can be found on PATH.
72 PATH: root,
73 Path: root,
74 CALL_FILE: NodePath.join(root, "call.json"),
75 CHILD_CWD: cwd,
76 ARG_MARKER: "inherited",
77 EXIT_STATUS: String(status),
78 },
79 },
80 );
81 },
82 call: () => JSON.parse(NodeFS.readFileSync(NodePath.join(root, "call.json"), "utf8")),
83 [Symbol.dispose]() {
84 NodeFS.rmSync(root, { recursive: true, force: true });
85 },
86 };
88​
89it.each(["linux", "darwin"] as const)("keeps the existing %s launcher", (platform) => {
90 const args = ["fmt", "--check", "a file.ts"];
91 expect(vitePlusCommand(args, platform)).toEqual(["vp", args]);
92});
93​
94it.each(["sync", "async"])("launches the Windows CLI without a shim (%s)", (mode) => {
95 using project = fixture();
96 const args = [
97 "fmt",
98 "--check",
99 "space & (draft) %PATH% ! ^ 研究.ts",
100 'embedded"quote',
101 "trailing\\",
102 "",
103 "& echo injected>injected.txt",
104 ];
105 const result = project.run(mode, args, 0);
106 expect(result.error).toBeUndefined();
107 expect(result.status).toBe(0);
108 expect(result.stdout).toBe("vp stdout");
109 expect(result.stderr).toBe("vp stderr");
110 expect(project.call()).toEqual({ args, cwd: project.cwd, marker: "inherited" });
111 expect(NodeFS.existsSync(NodePath.join(project.cwd, "injected.txt"))).toBe(false);
112});
113​
114it.each(["sync", "async"])("preserves a failing CLI's output and status (%s)", (mode) => {
115 using project = fixture();
116 const args = ["install", "--lockfile-only", "--ignore-scripts"];
117 const result = project.run(mode, args, 23);
118 expect(result.status).toBe(23);
119 expect(result.stdout).toBe("vp stdout");
120 expect(result.stderr).toBe("vp stderr");
121 expect(project.call()).toEqual({ args, cwd: project.cwd, marker: "inherited" });
122});