forked from Yara724/api
132 lines
3.6 KiB
TypeScript
132 lines
3.6 KiB
TypeScript
import { PlateNormalizerService, normalizePlateText } from "./plate-normalizer.service";
|
||
|
||
describe("normalizePlateText (pure function)", () => {
|
||
it("returns unchanged text for a clean plate letter", () => {
|
||
expect(normalizePlateText("ه")).toBe("ه");
|
||
expect(normalizePlateText("الف")).toBe("الف");
|
||
});
|
||
|
||
it("strips Zero-Width Joiner (U+200D)", () => {
|
||
// ه + ZWJ — the exact scenario from the issue
|
||
const input = "ه\u200D";
|
||
expect(normalizePlateText(input)).toBe("ه");
|
||
});
|
||
|
||
it("strips Zero-Width Non-Joiner (U+200C)", () => {
|
||
expect(normalizePlateText("ه\u200C")).toBe("ه");
|
||
});
|
||
|
||
it("strips Left-to-Right Mark (U+200E)", () => {
|
||
expect(normalizePlateText("ه\u200E")).toBe("ه");
|
||
});
|
||
|
||
it("strips Right-to-Left Mark (U+200F)", () => {
|
||
expect(normalizePlateText("ه\u200F")).toBe("ه");
|
||
});
|
||
|
||
it("strips BOM (U+FEFF)", () => {
|
||
expect(normalizePlateText("\uFEFFه")).toBe("ه");
|
||
});
|
||
|
||
it("converts Arabic ي (U+064A) to Persian ی (U+06CC)", () => {
|
||
expect(normalizePlateText("\u064A")).toBe("\u06CC");
|
||
});
|
||
|
||
it("does not alter already-Persian ی (U+06CC)", () => {
|
||
expect(normalizePlateText("\u06CC")).toBe("\u06CC");
|
||
});
|
||
|
||
it("handles a full plate string: 339ه\u200D77 → 339ه77", () => {
|
||
expect(normalizePlateText("339ه\u200D77")).toBe("339ه77");
|
||
});
|
||
|
||
it("handles multiple invisible chars interleaved", () => {
|
||
const input = "\u200F339\u200Cه\u200D\uFEFF77\u200E";
|
||
expect(normalizePlateText(input)).toBe("339ه77");
|
||
});
|
||
|
||
it("trims whitespace", () => {
|
||
expect(normalizePlateText(" ه ")).toBe("ه");
|
||
});
|
||
|
||
it("applies NFKC normalization", () => {
|
||
// Full-width digit 3 (U+FF13) → ASCII 3
|
||
expect(normalizePlateText("3")).toBe("3");
|
||
});
|
||
|
||
it("returns empty string for empty input", () => {
|
||
expect(normalizePlateText("")).toBe("");
|
||
});
|
||
});
|
||
|
||
describe("PlateNormalizerService", () => {
|
||
let service: PlateNormalizerService;
|
||
|
||
beforeEach(() => {
|
||
service = new PlateNormalizerService();
|
||
});
|
||
|
||
describe("normalizePlateText", () => {
|
||
it("delegates to the pure function", () => {
|
||
expect(service.normalizePlateText("ه\u200D")).toBe("ه");
|
||
});
|
||
});
|
||
|
||
describe("resolvePlateLetterCode", () => {
|
||
const PLATE_LETTER_MAP: Record<string, number> = {
|
||
"\u0627\u0644\u0641": 1, // الف
|
||
"\u0628": 2,
|
||
"\u067E": 3,
|
||
"\u062C": 4,
|
||
"\u062F": 5,
|
||
"\u0633": 6,
|
||
"\u0635": 7,
|
||
"\u0637": 8,
|
||
"\u0639": 9,
|
||
"\u0642": 10,
|
||
"\u0644": 11,
|
||
"\u0645": 12,
|
||
"\u0646": 13,
|
||
"\u0648": 14,
|
||
"\u0647": 15, // ه
|
||
"\u06CC": 16, // ی
|
||
"\u06A9": 17,
|
||
"\u0698": 18,
|
||
"\u062A": 19,
|
||
"\u062B": 20,
|
||
"\u0632": 21,
|
||
"\u0634": 22,
|
||
"\u0641": 23,
|
||
"\u06AF": 24,
|
||
};
|
||
|
||
it("returns 15 for clean ه", () => {
|
||
expect(service.resolvePlateLetterCode("ه", PLATE_LETTER_MAP)).toBe(15);
|
||
});
|
||
|
||
it("returns 15 for ه + ZWJ (the bug scenario)", () => {
|
||
expect(service.resolvePlateLetterCode("ه\u200D", PLATE_LETTER_MAP)).toBe(
|
||
15,
|
||
);
|
||
});
|
||
|
||
it("returns 16 for Arabic ي (mapped to Persian ی)", () => {
|
||
expect(
|
||
service.resolvePlateLetterCode("\u064A", PLATE_LETTER_MAP),
|
||
).toBe(16);
|
||
});
|
||
|
||
it("returns null for an unknown letter and logs it", () => {
|
||
const spy = jest.spyOn(
|
||
(service as any).logger,
|
||
"debug",
|
||
);
|
||
expect(service.resolvePlateLetterCode("؟", PLATE_LETTER_MAP)).toBeNull();
|
||
expect(spy).toHaveBeenCalledWith(
|
||
expect.stringContaining("Unknown plate letter"),
|
||
);
|
||
spy.mockRestore();
|
||
});
|
||
});
|
||
});
|