Skip to content

Commit dae6ae7

Browse files
committed
update
1 parent 11d2b2b commit dae6ae7

File tree

3 files changed

+509
-1
lines changed

3 files changed

+509
-1
lines changed

src/bash.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,14 @@ __${nameForVar}_complete() {
4242
local requestComp out directive
4343
4444
# Build the command to get completions
45-
requestComp="${exec} complete -- \${words[@]:1}"
45+
# Use a more compatible approach instead of \${words[@]:1}
46+
local args=""
47+
for (( i=1; i<cword+1; i++ )); do
48+
if [[ -n "\${words[i]}" ]]; then
49+
args="$args \\"\${words[i]}\\""
50+
fi
51+
done
52+
requestComp="${exec} complete --$args"
4653
4754
# Add an empty parameter if the last parameter is complete
4855
if [[ -z "$cur" ]]; then
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
import { describe, it, expect } from 'vitest';
2+
import { promisify } from 'node:util';
3+
import { exec as execCb } from 'node:child_process';
4+
import { writeFile, unlink } from 'node:fs/promises';
5+
import { join } from 'node:path';
6+
7+
const exec = promisify(execCb);
8+
9+
describe('package manager integration tests', () => {
10+
const packageManagers = ['npm', 'pnpm', 'yarn', 'bun'];
11+
const shells = ['zsh', 'bash', 'fish', 'powershell'];
12+
13+
describe('package manager completion script generation', () => {
14+
packageManagers.forEach((pm) => {
15+
shells.forEach((shell) => {
16+
it(`should generate ${shell} completion script for ${pm}`, async () => {
17+
const command = `pnpm tsx bin/cli.ts ${pm} ${shell}`;
18+
19+
try {
20+
const { stdout, stderr } = await exec(command);
21+
22+
// Should have output
23+
expect(stdout).toBeTruthy();
24+
expect(stdout.length).toBeGreaterThan(100);
25+
26+
// Should contain shell-specific markers
27+
expect(stdout).toContain(`# ${shell} completion for`);
28+
29+
// Should contain package manager name
30+
expect(stdout).toContain(pm);
31+
32+
// Should not have errors
33+
expect(stderr).toBe('');
34+
} catch (error) {
35+
throw new Error(
36+
`Failed to generate ${shell} script for ${pm}: ${error}`
37+
);
38+
}
39+
});
40+
});
41+
});
42+
});
43+
44+
// Test package manager completion functionality
45+
describe('package manager completion functionality', () => {
46+
packageManagers.forEach((pm) => {
47+
describe(`${pm} completion`, () => {
48+
it('should handle basic command completion', async () => {
49+
const command = `pnpm tsx bin/cli.ts ${pm} complete -- install`;
50+
51+
try {
52+
const { stdout, stderr } = await exec(command);
53+
54+
// Should have some output (completions or directive)
55+
expect(stdout).toBeTruthy();
56+
57+
// Should end with completion directive
58+
expect(stdout.trim()).toMatch(/:\d+$/);
59+
60+
// Should not have errors
61+
expect(stderr).toBe('');
62+
} catch (error) {
63+
throw new Error(`Failed ${pm} completion test: ${error}`);
64+
}
65+
});
66+
67+
it('should handle script completion', async () => {
68+
const command = `pnpm tsx bin/cli.ts ${pm} complete -- run`;
69+
70+
try {
71+
const { stdout, stderr } = await exec(command);
72+
73+
// Should have output
74+
expect(stdout).toBeTruthy();
75+
expect(stdout.trim()).toMatch(/:\d+$/);
76+
expect(stderr).toBe('');
77+
} catch (error) {
78+
throw new Error(`Failed ${pm} script completion: ${error}`);
79+
}
80+
});
81+
82+
it('should handle no match scenarios', async () => {
83+
const command = `pnpm tsx bin/cli.ts ${pm} complete -- nonexistentcommand`;
84+
85+
try {
86+
const { stdout, stderr } = await exec(command);
87+
88+
// Should have output (even if no matches)
89+
expect(stdout).toBeTruthy();
90+
expect(stdout.trim()).toMatch(/:\d+$/);
91+
expect(stderr).toBe('');
92+
} catch (error) {
93+
throw new Error(`Failed ${pm} no-match test: ${error}`);
94+
}
95+
});
96+
});
97+
});
98+
});
99+
100+
// Test bash-specific package manager issues
101+
describe('bash package manager completion validation', () => {
102+
packageManagers.forEach((pm) => {
103+
it(`should generate syntactically valid bash completion for ${pm}`, async () => {
104+
const command = `pnpm tsx bin/cli.ts ${pm} bash`;
105+
const { stdout } = await exec(command);
106+
107+
// Write script to temp file
108+
const scriptPath = join(process.cwd(), `temp-${pm}-bash-completion.sh`);
109+
await writeFile(scriptPath, stdout);
110+
111+
try {
112+
// Test bash syntax with -n flag (syntax check only)
113+
await exec(`bash -n ${scriptPath}`);
114+
// If we get here, syntax is valid
115+
expect(true).toBe(true);
116+
} catch (error) {
117+
throw new Error(`${pm} bash script has syntax errors: ${error}`);
118+
} finally {
119+
// Clean up
120+
await unlink(scriptPath).catch(() => {});
121+
}
122+
});
123+
124+
it(`should generate bash completion for ${pm} without problematic syntax`, async () => {
125+
const command = `pnpm tsx bin/cli.ts ${pm} bash`;
126+
const { stdout } = await exec(command);
127+
128+
// Check for the actual problematic bash syntax in the requestComp assignment
129+
expect(stdout).not.toMatch(/requestComp="[^"]*\$\{words\[@\]:1\}/); // Should not use ${words[@]:1} in requestComp
130+
expect(stdout).toContain('complete -F'); // Should register completion properly
131+
132+
// Should contain package manager specific function
133+
expect(stdout).toMatch(
134+
new RegExp(`__${pm.replace('-', '_')}_complete\\(\\)`)
135+
);
136+
});
137+
});
138+
});
139+
140+
describe('package manager completion registration', () => {
141+
shells.forEach((shell) => {
142+
it(`should generate ${shell} completion with proper registration for all package managers`, async () => {
143+
for (const pm of packageManagers) {
144+
const command = `pnpm tsx bin/cli.ts ${pm} ${shell}`;
145+
const { stdout } = await exec(command);
146+
147+
switch (shell) {
148+
case 'bash':
149+
expect(stdout).toMatch(/complete -F __\w+_complete/);
150+
expect(stdout).toContain(pm); // Should register for the specific package manager
151+
break;
152+
case 'zsh':
153+
expect(stdout).toMatch(/#compdef \w+/);
154+
expect(stdout).toMatch(/compdef _\w+ \w+/);
155+
break;
156+
case 'fish':
157+
expect(stdout).toContain('complete -c');
158+
expect(stdout).toContain(pm);
159+
break;
160+
case 'powershell':
161+
expect(stdout).toContain('Register-ArgumentCompleter');
162+
expect(stdout).toContain(pm);
163+
break;
164+
}
165+
}
166+
});
167+
});
168+
});
169+
170+
// Test error handling
171+
describe('package manager error handling', () => {
172+
it('should handle unsupported package manager', async () => {
173+
const command = `pnpm tsx bin/cli.ts unsupported complete -- test`;
174+
175+
try {
176+
await exec(command);
177+
// Should not reach here
178+
expect(true).toBe(false);
179+
} catch (error) {
180+
const errorMessage =
181+
error instanceof Error ? error.message : String(error);
182+
expect(errorMessage).toContain('Unsupported package manager');
183+
}
184+
});
185+
186+
it('should handle missing -- separator', async () => {
187+
const command = `pnpm tsx bin/cli.ts pnpm complete test`;
188+
189+
try {
190+
await exec(command);
191+
// Should not reach here
192+
expect(true).toBe(false);
193+
} catch (error) {
194+
const errorMessage =
195+
error instanceof Error ? error.message : String(error);
196+
expect(errorMessage).toContain("Expected '--' followed by command");
197+
}
198+
});
199+
});
200+
201+
// Test specific package manager features
202+
describe('package manager specific features', () => {
203+
it('should handle pnpm-specific commands', async () => {
204+
const command = `pnpm tsx bin/cli.ts pnpm complete -- dlx`;
205+
206+
try {
207+
const { stdout, stderr } = await exec(command);
208+
expect(stdout).toBeTruthy();
209+
expect(stdout.trim()).toMatch(/:\d+$/);
210+
expect(stderr).toBe('');
211+
} catch (error) {
212+
throw new Error(`Failed pnpm dlx completion: ${error}`);
213+
}
214+
});
215+
216+
it('should handle yarn-specific commands', async () => {
217+
const command = `pnpm tsx bin/cli.ts yarn complete -- workspace`;
218+
219+
try {
220+
const { stdout, stderr } = await exec(command);
221+
expect(stdout).toBeTruthy();
222+
expect(stdout.trim()).toMatch(/:\d+$/);
223+
expect(stderr).toBe('');
224+
} catch (error) {
225+
throw new Error(`Failed yarn workspace completion: ${error}`);
226+
}
227+
});
228+
229+
it('should handle npm-specific commands', async () => {
230+
const command = `pnpm tsx bin/cli.ts npm complete -- audit`;
231+
232+
try {
233+
const { stdout, stderr } = await exec(command);
234+
expect(stdout).toBeTruthy();
235+
expect(stdout.trim()).toMatch(/:\d+$/);
236+
expect(stderr).toBe('');
237+
} catch (error) {
238+
throw new Error(`Failed npm audit completion: ${error}`);
239+
}
240+
});
241+
242+
it('should handle bun-specific commands', async () => {
243+
const command = `pnpm tsx bin/cli.ts bun complete -- create`;
244+
245+
try {
246+
const { stdout, stderr } = await exec(command);
247+
expect(stdout).toBeTruthy();
248+
expect(stdout.trim()).toMatch(/:\d+$/);
249+
expect(stderr).toBe('');
250+
} catch (error) {
251+
throw new Error(`Failed bun create completion: ${error}`);
252+
}
253+
});
254+
});
255+
});

0 commit comments

Comments
 (0)