mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-17 23:51:08 +00:00
- Add iconv-lite dependency for encoding conversion - Create src/utils/fileEncoding.ts utility that: - Gets file encoding from VSCode settings (files.encoding) - Provides readFileWithEncoding() and writeFileWithEncoding() functions - Includes fallback to UTF-8 if encoding conversion fails - Update DiffViewProvider.ts to use encoding-aware file operations - Update ApplyPatchTool.ts to use encoding-aware file operations - Add comprehensive tests for the encoding utility Fixes #11002
272 lines
8.6 KiB
TypeScript
272 lines
8.6 KiB
TypeScript
import * as vscode from "vscode"
|
|
import * as fs from "fs/promises"
|
|
import * as iconv from "iconv-lite"
|
|
import {
|
|
getFileEncoding,
|
|
normalizeEncoding,
|
|
isEncodingSupported,
|
|
readFileWithEncoding,
|
|
writeFileWithEncoding,
|
|
} from "../fileEncoding"
|
|
|
|
// Mock vscode module
|
|
vi.mock("vscode", () => ({
|
|
workspace: {
|
|
getConfiguration: vi.fn(),
|
|
},
|
|
Uri: {
|
|
file: vi.fn((path: string) => ({ fsPath: path })),
|
|
},
|
|
}))
|
|
|
|
// Mock fs/promises module
|
|
vi.mock("fs/promises", () => ({
|
|
default: {
|
|
readFile: vi.fn(),
|
|
writeFile: vi.fn(),
|
|
},
|
|
readFile: vi.fn(),
|
|
writeFile: vi.fn(),
|
|
}))
|
|
|
|
// Mock iconv-lite module
|
|
vi.mock("iconv-lite", () => ({
|
|
default: {
|
|
encodingExists: vi.fn(),
|
|
decode: vi.fn(),
|
|
encode: vi.fn(),
|
|
},
|
|
encodingExists: vi.fn(),
|
|
decode: vi.fn(),
|
|
encode: vi.fn(),
|
|
}))
|
|
|
|
describe("fileEncoding", () => {
|
|
const mockedVscode = vi.mocked(vscode)
|
|
const mockedFs = vi.mocked(fs)
|
|
const mockedIconv = vi.mocked(iconv)
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe("getFileEncoding", () => {
|
|
it("should return the configured encoding from VSCode settings", () => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("cp852"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
|
|
const encoding = getFileEncoding("/path/to/file.txt")
|
|
|
|
expect(mockedVscode.Uri.file).toHaveBeenCalledWith("/path/to/file.txt")
|
|
expect(mockedVscode.workspace.getConfiguration).toHaveBeenCalledWith("files", {
|
|
fsPath: "/path/to/file.txt",
|
|
})
|
|
expect(mockConfig.get).toHaveBeenCalledWith("encoding", "utf8")
|
|
expect(encoding).toBe("cp852")
|
|
})
|
|
|
|
it("should return utf8 as default if no encoding is configured", () => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("utf8"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
|
|
const encoding = getFileEncoding("/path/to/file.txt")
|
|
|
|
expect(encoding).toBe("utf8")
|
|
})
|
|
})
|
|
|
|
describe("normalizeEncoding", () => {
|
|
it("should normalize utf-8 to utf8", () => {
|
|
expect(normalizeEncoding("utf-8")).toBe("utf8")
|
|
expect(normalizeEncoding("UTF-8")).toBe("utf8")
|
|
})
|
|
|
|
it("should normalize windows code pages", () => {
|
|
expect(normalizeEncoding("windows1252")).toBe("windows1252")
|
|
expect(normalizeEncoding("windows-1252")).toBe("windows1252")
|
|
})
|
|
|
|
it("should normalize DOS code pages", () => {
|
|
expect(normalizeEncoding("cp852")).toBe("cp852")
|
|
expect(normalizeEncoding("CP852")).toBe("cp852")
|
|
})
|
|
|
|
it("should normalize ISO encodings", () => {
|
|
expect(normalizeEncoding("iso88591")).toBe("iso88591")
|
|
expect(normalizeEncoding("iso-8859-1")).toBe("iso88591")
|
|
})
|
|
|
|
it("should return the original encoding if not in the map", () => {
|
|
expect(normalizeEncoding("unknown-encoding")).toBe("unknown-encoding")
|
|
})
|
|
})
|
|
|
|
describe("isEncodingSupported", () => {
|
|
it("should return true for supported encodings", () => {
|
|
mockedIconv.encodingExists = vi.fn().mockReturnValue(true)
|
|
|
|
expect(isEncodingSupported("utf8")).toBe(true)
|
|
expect(mockedIconv.encodingExists).toHaveBeenCalledWith("utf8")
|
|
})
|
|
|
|
it("should return false for unsupported encodings", () => {
|
|
mockedIconv.encodingExists = vi.fn().mockReturnValue(false)
|
|
|
|
expect(isEncodingSupported("unknown")).toBe(false)
|
|
expect(mockedIconv.encodingExists).toHaveBeenCalledWith("unknown")
|
|
})
|
|
})
|
|
|
|
describe("readFileWithEncoding", () => {
|
|
beforeEach(() => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("utf8"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
})
|
|
|
|
it("should read file with UTF-8 encoding directly", async () => {
|
|
const mockBuffer = Buffer.from("Hello World", "utf8")
|
|
mockedFs.readFile = vi.fn().mockResolvedValue(mockBuffer)
|
|
|
|
const result = await readFileWithEncoding("/path/to/file.txt")
|
|
|
|
expect(result.content).toBe("Hello World")
|
|
expect(result.encoding).toBe("utf8")
|
|
expect(result.usedFallback).toBe(false)
|
|
})
|
|
|
|
it("should read file with CP852 encoding", async () => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("cp852"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
|
|
const mockBuffer = Buffer.from([0x8d, 0x8f, 0xa7]) // Some CP852 bytes
|
|
mockedFs.readFile = vi.fn().mockResolvedValue(mockBuffer)
|
|
mockedIconv.encodingExists = vi.fn().mockReturnValue(true)
|
|
mockedIconv.decode = vi.fn().mockReturnValue("čćž")
|
|
|
|
const result = await readFileWithEncoding("/path/to/file.txt")
|
|
|
|
expect(mockedIconv.decode).toHaveBeenCalledWith(mockBuffer, "cp852")
|
|
expect(result.content).toBe("čćž")
|
|
expect(result.encoding).toBe("cp852")
|
|
expect(result.usedFallback).toBe(false)
|
|
})
|
|
|
|
it("should fall back to UTF-8 if encoding is not supported", async () => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("unsupported-encoding"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
|
|
const mockBuffer = Buffer.from("Hello World", "utf8")
|
|
mockedFs.readFile = vi.fn().mockResolvedValue(mockBuffer)
|
|
mockedIconv.encodingExists = vi.fn().mockReturnValue(false)
|
|
|
|
const result = await readFileWithEncoding("/path/to/file.txt")
|
|
|
|
expect(result.content).toBe("Hello World")
|
|
expect(result.encoding).toBe("unsupported-encoding")
|
|
expect(result.usedFallback).toBe(true)
|
|
})
|
|
|
|
it("should fall back to UTF-8 if decoding fails", async () => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("cp852"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
|
|
const mockBuffer = Buffer.from("Hello World", "utf8")
|
|
mockedFs.readFile = vi.fn().mockResolvedValue(mockBuffer)
|
|
mockedIconv.encodingExists = vi.fn().mockReturnValue(true)
|
|
mockedIconv.decode = vi.fn().mockImplementation(() => {
|
|
throw new Error("Decoding failed")
|
|
})
|
|
|
|
const result = await readFileWithEncoding("/path/to/file.txt")
|
|
|
|
expect(result.content).toBe("Hello World")
|
|
expect(result.encoding).toBe("cp852")
|
|
expect(result.usedFallback).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe("writeFileWithEncoding", () => {
|
|
beforeEach(() => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("utf8"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
})
|
|
|
|
it("should write file with UTF-8 encoding directly", async () => {
|
|
mockedFs.writeFile = vi.fn().mockResolvedValue(undefined)
|
|
|
|
const result = await writeFileWithEncoding("/path/to/file.txt", "Hello World")
|
|
|
|
expect(mockedFs.writeFile).toHaveBeenCalledWith("/path/to/file.txt", "Hello World", "utf8")
|
|
expect(result.encoding).toBe("utf8")
|
|
expect(result.usedFallback).toBe(false)
|
|
})
|
|
|
|
it("should write file with CP852 encoding", async () => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("cp852"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
|
|
const mockBuffer = Buffer.from([0x8d, 0x8f, 0xa7])
|
|
mockedIconv.encodingExists = vi.fn().mockReturnValue(true)
|
|
mockedIconv.encode = vi.fn().mockReturnValue(mockBuffer)
|
|
mockedFs.writeFile = vi.fn().mockResolvedValue(undefined)
|
|
|
|
const result = await writeFileWithEncoding("/path/to/file.txt", "čćž")
|
|
|
|
expect(mockedIconv.encode).toHaveBeenCalledWith("čćž", "cp852")
|
|
expect(mockedFs.writeFile).toHaveBeenCalledWith("/path/to/file.txt", mockBuffer)
|
|
expect(result.encoding).toBe("cp852")
|
|
expect(result.usedFallback).toBe(false)
|
|
})
|
|
|
|
it("should fall back to UTF-8 if encoding is not supported", async () => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("unsupported-encoding"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
|
|
mockedIconv.encodingExists = vi.fn().mockReturnValue(false)
|
|
mockedFs.writeFile = vi.fn().mockResolvedValue(undefined)
|
|
|
|
const result = await writeFileWithEncoding("/path/to/file.txt", "Hello World")
|
|
|
|
expect(mockedFs.writeFile).toHaveBeenCalledWith("/path/to/file.txt", "Hello World", "utf8")
|
|
expect(result.encoding).toBe("unsupported-encoding")
|
|
expect(result.usedFallback).toBe(true)
|
|
})
|
|
|
|
it("should fall back to UTF-8 if encoding fails", async () => {
|
|
const mockConfig = {
|
|
get: vi.fn().mockReturnValue("cp852"),
|
|
}
|
|
mockedVscode.workspace.getConfiguration = vi.fn().mockReturnValue(mockConfig)
|
|
|
|
mockedIconv.encodingExists = vi.fn().mockReturnValue(true)
|
|
mockedIconv.encode = vi.fn().mockImplementation(() => {
|
|
throw new Error("Encoding failed")
|
|
})
|
|
mockedFs.writeFile = vi.fn().mockResolvedValue(undefined)
|
|
|
|
const result = await writeFileWithEncoding("/path/to/file.txt", "Hello World")
|
|
|
|
expect(mockedFs.writeFile).toHaveBeenCalledWith("/path/to/file.txt", "Hello World", "utf8")
|
|
expect(result.encoding).toBe("cp852")
|
|
expect(result.usedFallback).toBe(true)
|
|
})
|
|
})
|
|
})
|