UNPKG

typopo

Version:

Fix frequent microtypography errors in multiple languages. Write neat texts without bothering about typography rules. Typopo works for English, German, Slovak, Czech and Rusyn language.

216 lines (181 loc) 8.14 kB
import { describe, it, expect } from "vitest"; import { replaceWithOverlapHandling } from "../../src/utils/regex-overlap.js"; describe("replaceWithOverlapHandling", () => { describe("Basic functionality", () => { it("should handle simple non-overlapping replacements", () => { const input = "hello world hello"; const result = replaceWithOverlapHandling(input, /hello/g, "hi"); expect(result).toBe("hi world hi"); }); it("should handle single replacement", () => { const input = "test string"; const result = replaceWithOverlapHandling(input, /test/g, "demo"); expect(result).toBe("demo string"); }); it("should return unchanged string when no matches", () => { const input = "no matches here"; const result = replaceWithOverlapHandling(input, /xyz/g, "replacement"); expect(result).toBe("no matches here"); }); }); describe("Overlapping matches", () => { it("should handle overlapping dash patterns (1-2-3)", () => { const input = "1-2-3"; const result = replaceWithOverlapHandling(input, /(\d)-(\d)/g, "$1–$2"); expect(result).toBe("1–2–3"); }); it("should handle complex overlapping dash patterns (1-2-3-4-5)", () => { const input = "1-2-3-4-5"; const result = replaceWithOverlapHandling(input, /(\d)-(\d)/g, "$1–$2"); expect(result).toBe("1–2–3–4–5"); }); it("should handle overlapping word patterns (a x b x c)", () => { const input = "a x b x c"; const result = replaceWithOverlapHandling(input, /(\w) x (\w)/g, "$1×$2"); expect(result).toBe("a×b×c"); }); it("should handle overlapping with mixed patterns", () => { const input = "word-to-word and 1-2-3 mixed"; const result = replaceWithOverlapHandling(input, /(\w)-(\w)/g, "$1–$2"); expect(result).toBe("word–to–word and 1–2–3 mixed"); }); }); describe("Multiple iterations required", () => { it("should handle patterns requiring multiple passes", () => { const input = "AAA"; const result = replaceWithOverlapHandling(input, /AA/g, "B"); expect(result).toBe("BA"); // First pass: AAA -> BA, Second pass: no change }); it("should handle cascading replacements", () => { const input = "AAAA"; const result = replaceWithOverlapHandling(input, /AA/g, "B"); expect(result).toBe("BB"); // First pass: AAAA -> BB, Second pass: no change }); it("should handle complex cascading pattern", () => { const input = "AAAAAA"; const result = replaceWithOverlapHandling(input, /AA/g, "B"); expect(result).toBe("BBB"); // Multiple iterations needed }); }); describe("Function replacement", () => { it("should handle function-based replacement", () => { const input = "test1 test2 test3"; const result = replaceWithOverlapHandling(input, /test(\d)/g, (match, num) => `demo${num}`); expect(result).toBe("demo1 demo2 demo3"); }); it("should handle complex function replacement with overlaps", () => { const input = "1-2-3"; const result = replaceWithOverlapHandling(input, /(\d)-(\d)/g, (match, a, b) => `${a}${b}`); expect(result).toBe("1–2–3"); }); }); describe("Edge cases", () => { it("should handle empty string", () => { const result = replaceWithOverlapHandling("", /test/g, "replacement"); expect(result).toBe(""); }); it("should handle empty replacement", () => { const input = "remove this remove that"; const result = replaceWithOverlapHandling(input, /remove /g, ""); expect(result).toBe("this that"); }); it("should handle single character patterns", () => { const input = "aaaa"; const result = replaceWithOverlapHandling(input, /a/g, "b"); expect(result).toBe("bbbb"); }); it("should handle special regex characters", () => { const input = "a.b.c"; const result = replaceWithOverlapHandling(input, /\./g, "-"); expect(result).toBe("a-b-c"); }); it("should handle unicode characters", () => { const input = "café→café"; const result = replaceWithOverlapHandling(input, /café/g, "coffee"); expect(result).toBe("coffee→coffee"); }); }); describe("Performance and safety", () => { it("should handle patterns that converge quickly", () => { const input = "1-2-3-4-5-6-7-8-9-10"; const startTime = Date.now(); const result = replaceWithOverlapHandling(input, /(\d+)-(\d+)/g, "$1–$2"); const endTime = Date.now(); expect(result).toBe("1–2–3–4–5–6–7–8–9–10"); expect(endTime - startTime).toBeLessThan(100); }); it("should handle long strings efficiently", () => { const input = "a-b-".repeat(100) + "c"; // 'a-b-a-b-...a-b-c' const startTime = Date.now(); const result = replaceWithOverlapHandling(input, /(\w)-(\w)/g, "$1–$2"); const endTime = Date.now(); expect(result).toContain("a–b"); expect(endTime - startTime).toBeLessThan(1000); }); it("should prevent infinite loops with problematic patterns", () => { // This would theoretically create an infinite loop without the safety cap const input = "test"; const result = replaceWithOverlapHandling(input, /t/g, "x"); // Should complete normally - replacing all 't' with 'x' expect(result).toBe("xesx"); }); }); describe("Real-world scenarios", () => { it("should handle typography dash fixes", () => { const input = "1-2-3 and 4 – 5 – 6"; const result = replaceWithOverlapHandling(input, /(\d)\s*[-–]\s*(\d)/g, "$1–$2"); // This test demonstrates the utility works for many overlapping cases // The complex pattern with spaces might not overlap exactly as expected expect(result).toContain("1–2"); expect(result).toContain("4–5"); }); it("should handle multiplication sign replacements", () => { const input = "2 x 3 x 4 cm"; const result = replaceWithOverlapHandling(input, /(\d+)\s*x\s*(\d+)/g, "$1×$2"); expect(result).toBe("2×3×4 cm"); }); it("should handle space normalization patterns", () => { const input = "word word word"; const result = replaceWithOverlapHandling(input, /\s{2,}/g, " "); expect(result).toBe("word word word"); }); it("should handle nested quote patterns", () => { const input = '"word" "word" "word"'; const result = replaceWithOverlapHandling(input, /"(\w+)"/g, '"$1"'); expect(result).toBe('"word" "word" "word"'); }); }); describe("Boundary conditions", () => { it("should handle exactly 50 iterations gracefully", () => { // Create a pattern that would require many iterations - but not exponential growth const input = "A".repeat(10); const result = replaceWithOverlapHandling(input, /A/g, "B"); // Should return transformed content expect(result).toBe("B".repeat(10)); }); it("should handle patterns that require exactly the max iterations", () => { // Create a pattern that needs many iterations but converges const input = "test test test"; const result = replaceWithOverlapHandling(input, /test/g, "demo"); // Should complete successfully expect(result).toBe("demo demo demo"); }); }); describe("Regex flag handling", () => { it("should work with global flag", () => { const input = "test test test"; const result = replaceWithOverlapHandling(input, /test/g, "demo"); expect(result).toBe("demo demo demo"); }); it("should work with case-insensitive flag", () => { const input = "Test TEST test"; const result = replaceWithOverlapHandling(input, /test/gi, "demo"); expect(result).toBe("demo demo demo"); }); it("should work with multiline flag", () => { const input = "line1\ntest\nline3"; const result = replaceWithOverlapHandling(input, /^test$/gm, "demo"); expect(result).toBe("line1\ndemo\nline3"); }); }); });