Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/tests/unit/windows-exec.test.ts
6449 views
1
/*
2
* windows-exec.test.ts
3
*
4
* Tests for Windows command execution utilities.
5
* Validates fix for https://github.com/quarto-dev/quarto-cli/issues/13997
6
*
7
* Copyright (C) 2020-2025 Posit Software, PBC
8
*/
9
10
import { unitTest } from "../test.ts";
11
import { assert, assertEquals } from "testing/asserts";
12
import { isWindows } from "../../src/deno_ral/platform.ts";
13
import { join } from "../../src/deno_ral/path.ts";
14
import { requireQuoting, safeWindowsExec } from "../../src/core/windows.ts";
15
import { execProcess } from "../../src/core/process.ts";
16
17
// Test that requireQuoting correctly quotes paths with spaces
18
unitTest(
19
"requireQuoting - quotes paths with spaces",
20
// deno-lint-ignore require-await
21
async () => {
22
const result = requireQuoting([
23
"C:\\Program Files\\dart-sass\\sass.bat",
24
"C:\\My Project\\style.scss",
25
"C:\\My Project\\style.css",
26
]);
27
28
assert(result.status === true, "Should indicate quoting was required");
29
assertEquals(result.args[0], '"C:\\Program Files\\dart-sass\\sass.bat"');
30
assertEquals(result.args[1], '"C:\\My Project\\style.scss"');
31
assertEquals(result.args[2], '"C:\\My Project\\style.css"');
32
},
33
{ ignore: !isWindows },
34
);
35
36
// Test that requireQuoting does not quote clean paths (no special chars)
37
// Note: Windows paths with drive letters (C:) ARE quoted due to the colon
38
unitTest(
39
"requireQuoting - no quoting for clean paths",
40
// deno-lint-ignore require-await
41
async () => {
42
const result = requireQuoting([
43
"sass.bat",
44
"input.scss",
45
"output.css",
46
]);
47
48
assert(result.status === false, "Should indicate no quoting needed");
49
assertEquals(result.args[0], "sass.bat");
50
assertEquals(result.args[1], "input.scss");
51
assertEquals(result.args[2], "output.css");
52
},
53
{ ignore: !isWindows },
54
);
55
56
// Test that safeWindowsExec passes arguments with spaces correctly
57
unitTest(
58
"safeWindowsExec - passes spaced args correctly",
59
async () => {
60
const tempDir = Deno.makeTempDirSync({ prefix: "quarto-test" });
61
62
try {
63
// Create batch file that echoes args (use %~1 to strip quotes)
64
const echoArgs = join(tempDir, "echo-args.bat");
65
Deno.writeTextFileSync(
66
echoArgs,
67
`@echo off
68
echo ARG1: %~1
69
echo ARG2: %~2
70
`,
71
);
72
73
const spaced1 = "C:\\My Project\\input.scss";
74
const spaced2 = "C:\\My Project\\output.css";
75
const quoted = requireQuoting([echoArgs, spaced1, spaced2]);
76
77
const result = await safeWindowsExec(
78
quoted.args[0],
79
quoted.args.slice(1),
80
(cmd) =>
81
execProcess({
82
cmd: cmd[0],
83
args: cmd.slice(1),
84
stdout: "piped",
85
stderr: "piped",
86
}),
87
);
88
89
assert(result.success, "Should execute successfully");
90
assert(
91
result.stdout?.includes(spaced1),
92
`Arg1 should be passed correctly. Got: ${result.stdout}`,
93
);
94
assert(
95
result.stdout?.includes(spaced2),
96
`Arg2 should be passed correctly. Got: ${result.stdout}`,
97
);
98
} finally {
99
Deno.removeSync(tempDir, { recursive: true });
100
}
101
},
102
{ ignore: !isWindows },
103
);
104
105
// Test that safeWindowsExec handles program paths with spaces (issue #13997)
106
// This is the core bug: Deno has issues executing .bat files when both the
107
// command path AND arguments contain spaces.
108
unitTest(
109
"safeWindowsExec - handles program path with spaces (issue #13997)",
110
async () => {
111
const tempDir = Deno.makeTempDirSync({ prefix: "quarto-test" });
112
113
try {
114
// Create directory with spaces (simulates C:\Program Files\)
115
const spacedDir = join(tempDir, "Program Files", "tool");
116
Deno.mkdirSync(spacedDir, { recursive: true });
117
118
// Create batch file in spaced path
119
const program = join(spacedDir, "echo-success.bat");
120
Deno.writeTextFileSync(
121
program,
122
`@echo off
123
echo SUCCESS
124
echo ARG: %~1
125
`,
126
);
127
128
const spacedArg = "C:\\My Project\\file.txt";
129
const quoted = requireQuoting([program, spacedArg]);
130
131
const result = await safeWindowsExec(
132
quoted.args[0],
133
quoted.args.slice(1),
134
(cmd) =>
135
execProcess({
136
cmd: cmd[0],
137
args: cmd.slice(1),
138
stdout: "piped",
139
stderr: "piped",
140
}),
141
);
142
143
assert(result.success, "Should execute program in spaced path");
144
assert(
145
result.stdout?.includes("SUCCESS"),
146
`Program should run. Got: ${result.stdout}`,
147
);
148
assert(
149
result.stdout?.includes(spacedArg),
150
`Arg should be passed correctly. Got: ${result.stdout}`,
151
);
152
} finally {
153
Deno.removeSync(tempDir, { recursive: true });
154
}
155
},
156
{ ignore: !isWindows },
157
);
158
159