Skip to content

Commit 508d8fb

Browse files
Add test shell
1 parent 6b47c15 commit 508d8fb

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed

tests/shell.test.js

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2+
import { execCommand, getPrompt } from '../src/executor.js';
3+
import { getProfileModule } from '../src/modules/ProfileModule.js';
4+
import readline from 'node:readline';
5+
6+
vi.mock('../src/executor.js');
7+
vi.mock('../src/modules/ProfileModule.js');
8+
vi.mock('node:readline');
9+
vi.mock('conf', () => {
10+
const Conf = vi.fn(() => ({
11+
get: vi.fn(),
12+
set: vi.fn(),
13+
}));
14+
return { default: Conf };
15+
});
16+
17+
vi.spyOn(console, 'log').mockImplementation(() => { });
18+
vi.spyOn(console, 'error').mockImplementation(() => { });
19+
20+
let updatePrompt;
21+
let startShell;
22+
23+
beforeEach(async () => {
24+
vi.resetModules();
25+
vi.clearAllMocks();
26+
27+
vi.mocked(getPrompt).mockReturnValue('puter@/> ');
28+
vi.mocked(getProfileModule).mockReturnValue({
29+
checkLogin: vi.fn(),
30+
});
31+
32+
const mockOn = vi.fn().mockReturnThis();
33+
vi.mocked(readline.createInterface).mockReturnValue({
34+
setPrompt: vi.fn(),
35+
prompt: vi.fn(),
36+
on: mockOn,
37+
});
38+
39+
const module = await import('../src/commands/shell.js');
40+
updatePrompt = module.updatePrompt;
41+
startShell = module.startShell;
42+
});
43+
44+
describe('updatePrompt', () => {
45+
it('should call rl.setPrompt with getPrompt result', async () => {
46+
await startShell();
47+
const rl = readline.createInterface.mock.results[0].value;
48+
rl.setPrompt.mockClear();
49+
50+
updatePrompt('/test/path');
51+
52+
expect(rl.setPrompt).toHaveBeenCalledWith('puter@/> ');
53+
});
54+
});
55+
56+
describe('startShell', () => {
57+
it('should call checkLogin', async () => {
58+
await startShell();
59+
expect(getProfileModule().checkLogin).toHaveBeenCalled();
60+
});
61+
62+
describe('with command argument', () => {
63+
let mockExit;
64+
65+
beforeEach(() => {
66+
mockExit = vi.spyOn(process, 'exit').mockImplementation(() => {
67+
throw new Error('process.exit called');
68+
});
69+
});
70+
71+
afterEach(() => {
72+
mockExit.mockRestore();
73+
});
74+
75+
it('should execute command and exit when command is provided', async () => {
76+
await expect(startShell('help')).rejects.toThrow('process.exit called');
77+
expect(execCommand).toHaveBeenCalledWith('help');
78+
expect(mockExit).toHaveBeenCalledWith(0);
79+
});
80+
81+
it('should not create readline interface when command is provided', async () => {
82+
vi.mocked(readline.createInterface).mockClear();
83+
await expect(startShell('help')).rejects.toThrow('process.exit called');
84+
expect(readline.createInterface).not.toHaveBeenCalled();
85+
});
86+
});
87+
88+
describe('without command argument (interactive mode)', () => {
89+
it('should create readline interface', async () => {
90+
await startShell();
91+
expect(readline.createInterface).toHaveBeenCalledWith({
92+
input: process.stdin,
93+
output: process.stdout,
94+
prompt: null,
95+
});
96+
});
97+
98+
it('should display welcome message', async () => {
99+
await startShell();
100+
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Welcome to Puter-CLI'));
101+
});
102+
103+
it('should set prompt and call prompt()', async () => {
104+
await startShell();
105+
const rl = readline.createInterface.mock.results[0].value;
106+
expect(rl.setPrompt).toHaveBeenCalledWith('puter@/> ');
107+
expect(rl.prompt).toHaveBeenCalled();
108+
});
109+
110+
it('should register line and close event handlers', async () => {
111+
await startShell();
112+
const rl = readline.createInterface.mock.results[0].value;
113+
expect(rl.on).toHaveBeenCalledWith('line', expect.any(Function));
114+
expect(rl.on).toHaveBeenCalledWith('close', expect.any(Function));
115+
});
116+
});
117+
118+
describe('line event handler', () => {
119+
let lineHandler;
120+
let rl;
121+
122+
beforeEach(async () => {
123+
await startShell();
124+
rl = readline.createInterface.mock.results[0].value;
125+
lineHandler = rl.on.mock.calls.find(call => call[0] === 'line')[1];
126+
});
127+
128+
it('should execute trimmed command', async () => {
129+
await lineHandler(' ls ');
130+
expect(execCommand).toHaveBeenCalledWith('ls');
131+
});
132+
133+
it('should not execute empty line', async () => {
134+
vi.mocked(execCommand).mockClear();
135+
await lineHandler(' ');
136+
expect(execCommand).not.toHaveBeenCalled();
137+
});
138+
139+
it('should call prompt after executing command', async () => {
140+
rl.prompt.mockClear();
141+
await lineHandler('ls');
142+
expect(rl.prompt).toHaveBeenCalled();
143+
});
144+
145+
it('should log error message when execCommand throws', async () => {
146+
vi.mocked(execCommand).mockRejectedValueOnce(new Error('Command failed'));
147+
await lineHandler('badcmd');
148+
expect(console.error).toHaveBeenCalledWith(expect.stringContaining('Command failed'));
149+
});
150+
151+
it('should still call prompt after error', async () => {
152+
vi.mocked(execCommand).mockRejectedValueOnce(new Error('Command failed'));
153+
rl.prompt.mockClear();
154+
await lineHandler('badcmd');
155+
expect(rl.prompt).toHaveBeenCalled();
156+
});
157+
});
158+
159+
describe('close event handler', () => {
160+
let closeHandler;
161+
let mockExit;
162+
163+
beforeEach(async () => {
164+
mockExit = vi.spyOn(process, 'exit').mockImplementation(() => { });
165+
await startShell();
166+
const rl = readline.createInterface.mock.results[0].value;
167+
closeHandler = rl.on.mock.calls.find(call => call[0] === 'close')[1];
168+
});
169+
170+
afterEach(() => {
171+
mockExit.mockRestore();
172+
});
173+
174+
it('should display goodbye message', () => {
175+
closeHandler();
176+
expect(console.log).toHaveBeenCalledWith(expect.stringContaining('Goodbye'));
177+
});
178+
179+
it('should exit with code 0', () => {
180+
closeHandler();
181+
expect(mockExit).toHaveBeenCalledWith(0);
182+
});
183+
});
184+
});

0 commit comments

Comments
 (0)