|
3 | 3 | */ |
4 | 4 | import { describe, expect, it } from 'vitest' |
5 | 5 | import { |
| 6 | + compareStrings, |
| 7 | + escapeRegExp, |
6 | 8 | forEachSearchOccurrence, |
7 | 9 | formatQuotedNameList, |
8 | 10 | isVersionedType, |
@@ -246,3 +248,41 @@ describe('forEachSearchOccurrence', () => { |
246 | 248 | expect(spans('\u0130xyz target', 'target')).toEqual(['target']) |
247 | 249 | }) |
248 | 250 | }) |
| 251 | + |
| 252 | +describe('escapeRegExp', () => { |
| 253 | + it('escapes every regex metacharacter', () => { |
| 254 | + const metacharacters = `.*+?^$${'{}'}()|[]\\` |
| 255 | + expect(escapeRegExp(metacharacters)).toBe('\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\\\') |
| 256 | + }) |
| 257 | + |
| 258 | + it('leaves ordinary text untouched', () => { |
| 259 | + expect(escapeRegExp('plain text 42')).toBe('plain text 42') |
| 260 | + }) |
| 261 | + |
| 262 | + it('matches the literal value once interpolated', () => { |
| 263 | + const pattern = new RegExp(escapeRegExp('a.b')) |
| 264 | + expect(pattern.test('a.b')).toBe(true) |
| 265 | + expect(pattern.test('axb')).toBe(false) |
| 266 | + }) |
| 267 | + |
| 268 | + it('escapes every occurrence, not just the first', () => { |
| 269 | + expect(escapeRegExp('a.b.c')).toBe('a\\.b\\.c') |
| 270 | + }) |
| 271 | +}) |
| 272 | + |
| 273 | +describe('compareStrings', () => { |
| 274 | + it('orders by code unit', () => { |
| 275 | + expect(compareStrings('a', 'b')).toBe(-1) |
| 276 | + expect(compareStrings('b', 'a')).toBe(1) |
| 277 | + expect(compareStrings('a', 'a')).toBe(0) |
| 278 | + }) |
| 279 | + |
| 280 | + it('sorts uppercase before lowercase, unlike localeCompare', () => { |
| 281 | + expect(compareStrings('Z', 'a')).toBe(-1) |
| 282 | + expect(['a', 'Z'].sort(compareStrings)).toEqual(['Z', 'a']) |
| 283 | + }) |
| 284 | + |
| 285 | + it('orders digit-led keys lexically, not numerically', () => { |
| 286 | + expect(['2', '10'].sort(compareStrings)).toEqual(['10', '2']) |
| 287 | + }) |
| 288 | +}) |
0 commit comments