204 lines
8.6 KiB
TypeScript
204 lines
8.6 KiB
TypeScript
import { describe, test, expect } from "vitest";
|
|
import { cleanNumericInput } from "./strings";
|
|
|
|
// Simulate what onChange does: clean → Number()
|
|
function simulateOnChange(raw: string): { display: string; value: number | undefined } {
|
|
const cleaned = cleanNumericInput(raw);
|
|
if (cleaned === "" || cleaned === ".") {
|
|
return { display: cleaned, value: undefined };
|
|
}
|
|
const n = Number(cleaned);
|
|
return { display: cleaned, value: isNaN(n) ? undefined : n };
|
|
}
|
|
|
|
// Simulate what onBlur does: normalize display string
|
|
function simulateOnBlur(displayValue: string): { display: string; value: number | undefined } {
|
|
const trimmed = displayValue.trim();
|
|
if (trimmed === "" || trimmed === ".") {
|
|
return { display: "", value: undefined };
|
|
}
|
|
const num = Number(trimmed);
|
|
if (!isNaN(num)) {
|
|
return { display: String(num), value: num };
|
|
}
|
|
return { display: "", value: undefined };
|
|
}
|
|
|
|
describe("cleanNumericInput", () => {
|
|
// Basic valid inputs
|
|
test("empty string", () => expect(cleanNumericInput("")).toBe(""));
|
|
test("single digit", () => expect(cleanNumericInput("5")).toBe("5"));
|
|
test("multiple digits", () => expect(cleanNumericInput("123")).toBe("123"));
|
|
test("decimal number", () => expect(cleanNumericInput("1.5")).toBe("1.5"));
|
|
test("leading decimal", () => expect(cleanNumericInput(".5")).toBe(".5"));
|
|
test("trailing decimal", () => expect(cleanNumericInput("5.")).toBe("5."));
|
|
test("zero", () => expect(cleanNumericInput("0")).toBe("0"));
|
|
test("decimal zero", () => expect(cleanNumericInput("0.0")).toBe("0.0"));
|
|
test("long decimal", () => expect(cleanNumericInput("123.456")).toBe("123.456"));
|
|
|
|
// Comma-separated (thousands)
|
|
test("1,000", () => expect(cleanNumericInput("1,000")).toBe("1000"));
|
|
test("1,000,000", () => expect(cleanNumericInput("1,000,000")).toBe("1000000"));
|
|
test("1,234.56", () => expect(cleanNumericInput("1,234.56")).toBe("1234.56"));
|
|
|
|
// Underscore-separated (programming style)
|
|
test("1_000", () => expect(cleanNumericInput("1_000")).toBe("1000"));
|
|
test("1_000_000", () => expect(cleanNumericInput("1_000_000")).toBe("1000000"));
|
|
|
|
// Space-separated
|
|
test("1 000", () => expect(cleanNumericInput("1 000")).toBe("1000"));
|
|
test("1 000 000", () => expect(cleanNumericInput("1 000 000")).toBe("1000000"));
|
|
|
|
// Alphabetic characters — should stop
|
|
test("1abc", () => expect(cleanNumericInput("1abc")).toBe("1"));
|
|
test("123abc456", () => expect(cleanNumericInput("123abc456")).toBe("123"));
|
|
test("abc123", () => expect(cleanNumericInput("abc123")).toBe(""));
|
|
|
|
// Multiple consecutive separators — should stop
|
|
test("1,,000", () => expect(cleanNumericInput("1,,000")).toBe("1"));
|
|
test("1..5", () => expect(cleanNumericInput("1..5")).toBe("1.5"));
|
|
test("1 000", () => expect(cleanNumericInput("1 000")).toBe("1"));
|
|
|
|
// Multiple decimal points — second dot treated as separator
|
|
test("12.3.4", () => expect(cleanNumericInput("12.3.4")).toBe("12.34"));
|
|
test("1.2.3.4", () => expect(cleanNumericInput("1.2.3.4")).toBe("1.234"));
|
|
|
|
// Currency symbols and special chars
|
|
test("$100", () => expect(cleanNumericInput("$100")).toBe("100"));
|
|
test("€1,000", () => expect(cleanNumericInput("€1,000")).toBe("1000"));
|
|
test("100%", () => expect(cleanNumericInput("100%")).toBe("100"));
|
|
|
|
// Trailing separator with no digit after
|
|
test("100,", () => expect(cleanNumericInput("100,")).toBe("100"));
|
|
test("100.", () => expect(cleanNumericInput("100.")).toBe("100."));
|
|
|
|
// Just a dot
|
|
test(".", () => expect(cleanNumericInput(".")).toBe("."));
|
|
|
|
// Negative sign (non-alpha, stripped as separator if digit follows)
|
|
test("-5", () => expect(cleanNumericInput("-5")).toBe("5"));
|
|
test("-1,000", () => expect(cleanNumericInput("-1,000")).toBe("1000"));
|
|
|
|
// Whitespace
|
|
test(" 123 ", () => expect(cleanNumericInput(" 123 ")).toBe("123"));
|
|
test("1 0 0", () => expect(cleanNumericInput("1 0 0")).toBe("100"));
|
|
test(" 1,000 ", () => expect(cleanNumericInput(" 1,000 ")).toBe("1000"));
|
|
test(" .5 ", () => expect(cleanNumericInput(" .5 ")).toBe(".5"));
|
|
|
|
// Tab and mixed whitespace
|
|
test("tab 123", () => expect(cleanNumericInput("\t123\t")).toBe("123"));
|
|
test("newline 123", () => expect(cleanNumericInput("\n123\n")).toBe("123"));
|
|
|
|
// Plus sign
|
|
test("+5", () => expect(cleanNumericInput("+5")).toBe("5"));
|
|
test("+1,000", () => expect(cleanNumericInput("+1,000")).toBe("1000"));
|
|
|
|
// Mixed separators
|
|
test("1_000,000.50", () => expect(cleanNumericInput("1_000,000.50")).toBe("1000000.50"));
|
|
|
|
// Parentheses (accounting negative)
|
|
test("(100)", () => expect(cleanNumericInput("(100)")).toBe("100"));
|
|
|
|
// Only separators
|
|
test(",", () => expect(cleanNumericInput(",")).toBe(""));
|
|
test(",,", () => expect(cleanNumericInput(",,")).toBe(""));
|
|
test("_", () => expect(cleanNumericInput("_")).toBe(""));
|
|
|
|
// Only alpha
|
|
test("abc", () => expect(cleanNumericInput("abc")).toBe(""));
|
|
test("NaN", () => expect(cleanNumericInput("NaN")).toBe(""));
|
|
test("Infinity", () => expect(cleanNumericInput("Infinity")).toBe(""));
|
|
|
|
// Very large numbers
|
|
test("999,999,999.99", () => expect(cleanNumericInput("999,999,999.99")).toBe("999999999.99"));
|
|
test("1_000_000_000", () => expect(cleanNumericInput("1_000_000_000")).toBe("1000000000"));
|
|
|
|
// Pasted from spreadsheet with trailing whitespace/newline
|
|
test("1234\\n", () => expect(cleanNumericInput("1234\n")).toBe("1234"));
|
|
test("\\t5000\\t", () => expect(cleanNumericInput("\t5000\t")).toBe("5000"));
|
|
|
|
// Unicode non-breaking space
|
|
test("1\u00A0000", () => expect(cleanNumericInput("1\u00A0000")).toBe("1000"));
|
|
|
|
// Hash, slash, other symbols before digits
|
|
test("#100", () => expect(cleanNumericInput("#100")).toBe("100"));
|
|
test("/100", () => expect(cleanNumericInput("/100")).toBe("100"));
|
|
|
|
// Zero edge cases
|
|
test("0.00", () => expect(cleanNumericInput("0.00")).toBe("0.00"));
|
|
test("00.5", () => expect(cleanNumericInput("00.5")).toBe("00.5"));
|
|
test("000", () => expect(cleanNumericInput("000")).toBe("000"));
|
|
test("0,000", () => expect(cleanNumericInput("0,000")).toBe("0000"));
|
|
});
|
|
|
|
describe("simulateOnChange (clean + Number)", () => {
|
|
test("empty → undefined", () => {
|
|
expect(simulateOnChange("")).toEqual({ display: "", value: undefined });
|
|
});
|
|
test("just dot → undefined", () => {
|
|
expect(simulateOnChange(".")).toEqual({ display: ".", value: undefined });
|
|
});
|
|
test("123 → 123", () => {
|
|
expect(simulateOnChange("123")).toEqual({ display: "123", value: 123 });
|
|
});
|
|
test("1.5 → 1.5", () => {
|
|
expect(simulateOnChange("1.5")).toEqual({ display: "1.5", value: 1.5 });
|
|
});
|
|
test("0.0 → display 0.0, value 0", () => {
|
|
expect(simulateOnChange("0.0")).toEqual({ display: "0.0", value: 0 });
|
|
});
|
|
test("1,000 → 1000", () => {
|
|
expect(simulateOnChange("1,000")).toEqual({ display: "1000", value: 1000 });
|
|
});
|
|
test("1_000 → 1000", () => {
|
|
expect(simulateOnChange("1_000")).toEqual({ display: "1000", value: 1000 });
|
|
});
|
|
test("1abc → 1", () => {
|
|
expect(simulateOnChange("1abc")).toEqual({ display: "1", value: 1 });
|
|
});
|
|
test("$100 → 100", () => {
|
|
expect(simulateOnChange("$100")).toEqual({ display: "100", value: 100 });
|
|
});
|
|
test("1,234.56 → 1234.56", () => {
|
|
expect(simulateOnChange("1,234.56")).toEqual({ display: "1234.56", value: 1234.56 });
|
|
});
|
|
test(" 500 → 500 (trimmed)", () => {
|
|
expect(simulateOnChange(" 500 ")).toEqual({ display: "500", value: 500 });
|
|
});
|
|
test("abc → empty, undefined", () => {
|
|
expect(simulateOnChange("abc")).toEqual({ display: "", value: undefined });
|
|
});
|
|
test("999,999,999.99 → 999999999.99", () => {
|
|
expect(simulateOnChange("999,999,999.99")).toEqual({ display: "999999999.99", value: 999999999.99 });
|
|
});
|
|
test("0.01 → 0.01", () => {
|
|
expect(simulateOnChange("0.01")).toEqual({ display: "0.01", value: 0.01 });
|
|
});
|
|
test("-5 → 5 (negative sign stripped)", () => {
|
|
expect(simulateOnChange("-5")).toEqual({ display: "5", value: 5 });
|
|
});
|
|
});
|
|
|
|
describe("simulateOnBlur (normalize display)", () => {
|
|
test("empty → empty, undefined", () => {
|
|
expect(simulateOnBlur("")).toEqual({ display: "", value: undefined });
|
|
});
|
|
test("dot → empty, undefined", () => {
|
|
expect(simulateOnBlur(".")).toEqual({ display: "", value: undefined });
|
|
});
|
|
test("0. → 0", () => {
|
|
expect(simulateOnBlur("0.")).toEqual({ display: "0", value: 0 });
|
|
});
|
|
test("1.0 → 1 (Number normalizes trailing zero)", () => {
|
|
expect(simulateOnBlur("1.0")).toEqual({ display: "1", value: 1 });
|
|
});
|
|
test("1.50 → 1.5", () => {
|
|
expect(simulateOnBlur("1.50")).toEqual({ display: "1.5", value: 1.5 });
|
|
});
|
|
test("0100 → 100 (leading zero stripped)", () => {
|
|
expect(simulateOnBlur("0100")).toEqual({ display: "100", value: 100 });
|
|
});
|
|
test("1000 → 1000", () => {
|
|
expect(simulateOnBlur("1000")).toEqual({ display: "1000", value: 1000 });
|
|
});
|
|
}); |