Skip to content

Commit 6e389f5

Browse files
committed
test: add e2e tests for external CLI version display in opencli list
- Add external-cli-version-display.test.ts with 6 e2e tests covering: table format version/isolated display, JSON format version/installType fields, YAML format version display, lock file version caching, non-installed CLI display - Register new test file in vitest.config.ts
1 parent 0bcd896 commit 6e389f5

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* E2E tests for external CLI version display in `opencli list`.
3+
*
4+
* Validates that `opencli list` shows version info and installation type
5+
* for external CLIs, and that version detection/caching works correctly.
6+
*
7+
* Uses a temp HOME directory to isolate from the real ~/.opencli.
8+
*/
9+
10+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
11+
import { runCli, parseJsonOutput } from './helpers.js';
12+
import * as fs from 'node:fs';
13+
import * as path from 'node:path';
14+
import * as os from 'node:os';
15+
16+
const TEMP_HOME = path.join(os.tmpdir(), `opencli-e2e-ver-${Date.now()}`);
17+
18+
function envWithHome(): Record<string, string> {
19+
return { HOME: TEMP_HOME, USERPROFILE: TEMP_HOME };
20+
}
21+
22+
function readLockFile(): Record<string, any> {
23+
const lockPath = path.join(TEMP_HOME, '.opencli', 'external.lock.json');
24+
if (!fs.existsSync(lockPath)) return {};
25+
return JSON.parse(fs.readFileSync(lockPath, 'utf8'));
26+
}
27+
28+
describe('external CLI version display E2E', () => {
29+
beforeAll(async () => {
30+
fs.mkdirSync(TEMP_HOME, { recursive: true });
31+
32+
// Register and install cowsay in isolated mode for version tests
33+
await runCli(
34+
['register', 'cowsay', '--binary', 'cowsay', '--install', 'npm install -g cowsay', '--desc', 'ASCII art cow'],
35+
{ env: envWithHome(), timeout: 15_000 },
36+
);
37+
await runCli(
38+
['install', 'cowsay', '--isolated', '--version', '1.5.0'],
39+
{ env: envWithHome(), timeout: 60_000 },
40+
);
41+
}, 90_000);
42+
43+
afterAll(() => {
44+
try {
45+
fs.rmSync(TEMP_HOME, { recursive: true, force: true });
46+
} catch {}
47+
});
48+
49+
// ── list table format with version info ──
50+
it('list table format shows external CLIs section', async () => {
51+
const { stdout, code } = await runCli(
52+
['list'],
53+
{ env: envWithHome(), timeout: 30_000 },
54+
);
55+
expect(code).toBe(0);
56+
// Should have external CLIs section
57+
expect(stdout).toContain('external CLIs');
58+
});
59+
60+
it('list table format shows version for isolated install', async () => {
61+
const { stdout, code } = await runCli(
62+
['list'],
63+
{ env: envWithHome(), timeout: 30_000 },
64+
);
65+
expect(code).toBe(0);
66+
// cowsay should appear with version and isolated marker
67+
expect(stdout).toContain('cowsay');
68+
// Should show the version number
69+
expect(stdout).toContain('1.5.0');
70+
// Should show installation type
71+
expect(stdout).toContain('isolated');
72+
});
73+
74+
// ── list JSON format with version info ──
75+
it('list -f json includes version and installType for external CLIs', async () => {
76+
const { stdout, code } = await runCli(
77+
['list', '-f', 'json'],
78+
{ env: envWithHome(), timeout: 30_000 },
79+
);
80+
expect(code).toBe(0);
81+
const data = parseJsonOutput(stdout);
82+
expect(Array.isArray(data)).toBe(true);
83+
84+
// Find the cowsay entry
85+
const cowsayEntry = data.find((e: any) => e.command === 'cowsay');
86+
expect(cowsayEntry).toBeDefined();
87+
expect(cowsayEntry.version).toBe('1.5.0');
88+
expect(cowsayEntry.installType).toBe('isolated');
89+
expect(cowsayEntry.installed).toBe(true);
90+
});
91+
92+
// ── list YAML format includes external CLIs ──
93+
it('list -f yaml includes external CLI entries', async () => {
94+
const { stdout, code } = await runCli(
95+
['list', '-f', 'yaml'],
96+
{ env: envWithHome(), timeout: 30_000 },
97+
);
98+
expect(code).toBe(0);
99+
expect(stdout).toContain('cowsay');
100+
expect(stdout).toContain('1.5.0');
101+
});
102+
103+
// ── version caching in lock file ──
104+
it('lock file stores version info for isolated installs', async () => {
105+
const lock = readLockFile();
106+
expect(lock['cowsay']).toBeDefined();
107+
expect(lock['cowsay'].installType).toBe('isolated');
108+
const currentVersion = lock['cowsay'].versions.find((v: any) => v.current);
109+
expect(currentVersion).toBeDefined();
110+
expect(currentVersion.version).toBe('1.5.0');
111+
});
112+
113+
// ── list shows non-installed external CLIs with auto-install tag ──
114+
it('list shows non-installed external CLIs', async () => {
115+
const { stdout, code } = await runCli(
116+
['list'],
117+
{ env: envWithHome(), timeout: 30_000 },
118+
);
119+
expect(code).toBe(0);
120+
// Built-in external CLIs that aren't installed should still appear
121+
// (they'll show auto-install or installed depending on global availability)
122+
expect(stdout).toContain('external CLIs');
123+
});
124+
});

vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default defineConfig({
3030
'tests/e2e/output-formats.test.ts',
3131
'tests/e2e/plugin-management.test.ts',
3232
'tests/e2e/external-cli-management.test.ts',
33+
'tests/e2e/external-cli-version-display.test.ts',
3334
// Extended browser tests (20+ sites) — opt-in only:
3435
// OPENCLI_E2E=1 npx vitest run
3536
...(includeExtendedE2e ? ['tests/e2e/browser-public-extended.test.ts', 'tests/e2e/browser-auth.test.ts'] : []),

0 commit comments

Comments
 (0)