Skip to content

Commit ac674a1

Browse files
committed
Add comprehensive unit tests for ExecaTerminalProcess UTF-8 encoding fix
- Tests verify LANG and LC_ALL are set to en_US.UTF-8 - Tests ensure existing environment variables are preserved - Tests confirm UTF-8 settings override conflicting locale values - Addresses PR feedback requesting test coverage for encoding fix - All 7 tests passing with proper mocking of execa and ps-tree
1 parent 0fa5c3c commit ac674a1

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// npx vitest run integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
2+
import { vitest, describe, it, expect, beforeEach, afterEach } from "vitest"
3+
4+
const mockPid = 12345
5+
6+
vitest.mock("execa", () => {
7+
const mockKill = vitest.fn()
8+
const execa = vitest.fn((options: any) => {
9+
return (_template: TemplateStringsArray, ...args: any[]) => ({
10+
pid: mockPid,
11+
iterable: (_opts: any) =>
12+
(async function* () {
13+
yield "test output\n"
14+
})(),
15+
kill: mockKill,
16+
})
17+
})
18+
return { execa, ExecaError: class extends Error {} }
19+
})
20+
21+
vitest.mock("ps-tree", () => ({
22+
default: vitest.fn((_: number, cb: any) => cb(null, [])),
23+
}))
24+
25+
import { execa } from "execa"
26+
import { ExecaTerminalProcess } from "../ExecaTerminalProcess"
27+
import type { RooTerminal } from "../types"
28+
29+
describe("ExecaTerminalProcess", () => {
30+
let mockTerminal: RooTerminal
31+
let terminalProcess: ExecaTerminalProcess
32+
let originalEnv: NodeJS.ProcessEnv
33+
34+
beforeEach(() => {
35+
originalEnv = { ...process.env }
36+
mockTerminal = {
37+
provider: "execa",
38+
id: 1,
39+
busy: false,
40+
running: false,
41+
getCurrentWorkingDirectory: vitest.fn().mockReturnValue("/test/cwd"),
42+
isClosed: vitest.fn().mockReturnValue(false),
43+
runCommand: vitest.fn(),
44+
setActiveStream: vitest.fn(),
45+
shellExecutionComplete: vitest.fn(),
46+
getProcessesWithOutput: vitest.fn().mockReturnValue([]),
47+
getUnretrievedOutput: vitest.fn().mockReturnValue(""),
48+
getLastCommand: vitest.fn().mockReturnValue(""),
49+
cleanCompletedProcessQueue: vitest.fn(),
50+
} as unknown as RooTerminal
51+
terminalProcess = new ExecaTerminalProcess(mockTerminal)
52+
})
53+
54+
afterEach(() => {
55+
process.env = originalEnv
56+
vitest.clearAllMocks()
57+
})
58+
59+
describe("UTF-8 encoding fix", () => {
60+
it("should set LANG and LC_ALL to en_US.UTF-8", async () => {
61+
await terminalProcess.run("echo test")
62+
const execaMock = vitest.mocked(execa)
63+
expect(execaMock).toHaveBeenCalledWith(
64+
expect.objectContaining({
65+
shell: true,
66+
cwd: "/test/cwd",
67+
all: true,
68+
env: expect.objectContaining({
69+
LANG: "en_US.UTF-8",
70+
LC_ALL: "en_US.UTF-8",
71+
}),
72+
}),
73+
)
74+
})
75+
76+
it("should preserve existing environment variables", async () => {
77+
process.env.EXISTING_VAR = "existing"
78+
terminalProcess = new ExecaTerminalProcess(mockTerminal)
79+
await terminalProcess.run("echo test")
80+
const execaMock = vitest.mocked(execa)
81+
const calledOptions = execaMock.mock.calls[0][0] as any
82+
expect(calledOptions.env.EXISTING_VAR).toBe("existing")
83+
})
84+
85+
it("should override existing LANG and LC_ALL values", async () => {
86+
process.env.LANG = "C"
87+
process.env.LC_ALL = "POSIX"
88+
terminalProcess = new ExecaTerminalProcess(mockTerminal)
89+
await terminalProcess.run("echo test")
90+
const execaMock = vitest.mocked(execa)
91+
const calledOptions = execaMock.mock.calls[0][0] as any
92+
expect(calledOptions.env.LANG).toBe("en_US.UTF-8")
93+
expect(calledOptions.env.LC_ALL).toBe("en_US.UTF-8")
94+
})
95+
})
96+
97+
describe("basic functionality", () => {
98+
it("should create instance with terminal reference", () => {
99+
expect(terminalProcess).toBeInstanceOf(ExecaTerminalProcess)
100+
expect(terminalProcess.terminal).toBe(mockTerminal)
101+
})
102+
103+
it("should emit shell_execution_complete with exitCode 0", async () => {
104+
const spy = vitest.fn()
105+
terminalProcess.on("shell_execution_complete", spy)
106+
await terminalProcess.run("echo test")
107+
expect(spy).toHaveBeenCalledWith({ exitCode: 0 })
108+
})
109+
110+
it("should emit completed event with full output", async () => {
111+
const spy = vitest.fn()
112+
terminalProcess.on("completed", spy)
113+
await terminalProcess.run("echo test")
114+
expect(spy).toHaveBeenCalledWith("test output\n")
115+
})
116+
117+
it("should set and clear active stream", async () => {
118+
await terminalProcess.run("echo test")
119+
expect(mockTerminal.setActiveStream).toHaveBeenCalledWith(expect.any(Object), mockPid)
120+
expect(mockTerminal.setActiveStream).toHaveBeenLastCalledWith(undefined)
121+
})
122+
})
123+
})

0 commit comments

Comments
 (0)