Files
claude-code/packages/builtin-tools/src/tools/FileEditTool/__tests__/utils.test.ts
claude-code-best a91653a0dd fix: 删除 edit tool 中的旧逻辑处理, 现在已经不需要这些处理了, 大模型够屌 (#1251)
* refactor: remove tab/quote normalization from FileEditTool

* fix: resolve pre-existing typecheck errors (zod v4 compat + RCS web exclude)
2026-05-28 21:52:31 +08:00

123 lines
4.1 KiB
TypeScript

import { mock, describe, expect, test } from 'bun:test'
import { logMock } from '../../../../../../tests/mocks/log'
// Mock log.ts to cut the heavy dependency chain
mock.module('src/utils/log.ts', logMock)
const { stripTrailingWhitespace, findActualString, applyEditToFile } =
await import('../utils')
// ─── stripTrailingWhitespace ────────────────────────────────────────────
describe('stripTrailingWhitespace', () => {
test('strips trailing spaces from lines', () => {
expect(stripTrailingWhitespace('hello \nworld ')).toBe('hello\nworld')
})
test('strips trailing tabs', () => {
expect(stripTrailingWhitespace('hello\t\nworld\t')).toBe('hello\nworld')
})
test('preserves leading whitespace', () => {
expect(stripTrailingWhitespace(' hello \n world ')).toBe(
' hello\n world',
)
})
test('handles empty string', () => {
expect(stripTrailingWhitespace('')).toBe('')
})
test('handles CRLF line endings', () => {
expect(stripTrailingWhitespace('hello \r\nworld ')).toBe(
'hello\r\nworld',
)
})
test('handles no trailing whitespace', () => {
expect(stripTrailingWhitespace('hello\nworld')).toBe('hello\nworld')
})
test('handles CR-only line endings', () => {
expect(stripTrailingWhitespace('hello \rworld ')).toBe('hello\rworld')
})
test('handles content with no trailing newline', () => {
expect(stripTrailingWhitespace('hello ')).toBe('hello')
})
})
// ─── findActualString ───────────────────────────────────────────────────
describe('findActualString', () => {
test('finds exact match', () => {
expect(findActualString('hello world', 'hello')).toBe('hello')
})
test('returns null when not found', () => {
expect(findActualString('hello world', 'xyz')).toBeNull()
})
test('returns null for empty search in non-empty content', () => {
// Empty string is always found at index 0 via includes()
const result = findActualString('hello', '')
expect(result).toBe('')
})
// ── CJK / UTF-8 characters ──
test('finds match with CJK characters in content', () => {
const fileContent = 'input int x = 620; // 止盈点数(点) — 32个pip=320点'
const result = findActualString(fileContent, fileContent)
expect(result).toBe(fileContent)
})
})
// ─── applyEditToFile ────────────────────────────────────────────────────
describe('applyEditToFile', () => {
test('replaces first occurrence by default', () => {
expect(applyEditToFile('foo bar foo', 'foo', 'baz')).toBe('baz bar foo')
})
test('replaces all occurrences with replaceAll=true', () => {
expect(applyEditToFile('foo bar foo', 'foo', 'baz', true)).toBe(
'baz bar baz',
)
})
test('handles deletion (empty newString) with trailing newline', () => {
const result = applyEditToFile('line1\nline2\nline3\n', 'line2', '')
expect(result).toBe('line1\nline3\n')
})
test('handles deletion without trailing newline', () => {
const result = applyEditToFile('foobar', 'foo', '')
expect(result).toBe('bar')
})
test('handles no match (returns original)', () => {
expect(applyEditToFile('hello world', 'xyz', 'abc')).toBe('hello world')
})
test('handles empty original content with insertion', () => {
expect(applyEditToFile('', '', 'new content')).toBe('new content')
})
test('handles multiline oldString and newString', () => {
const content = 'line1\nline2\nline3\n'
const result = applyEditToFile(content, 'line2\nline3', 'replaced')
expect(result).toBe('line1\nreplaced\n')
})
test('handles multiline replacement across multiple lines', () => {
const content = 'header\nold line A\nold line B\nfooter\n'
const result = applyEditToFile(
content,
'old line A\nold line B',
'new line X\nnew line Y',
)
expect(result).toBe('header\nnew line X\nnew line Y\nfooter\n')
})
})