test(web): add 23 edge-case tests across UI components and lib utils
Covers: aria-sort/aria-labelledby attributes, non-Error throws in ErrorBoundary, NaN/MAX_SAFE_INTEGER in formatCents, invalid dates, carriage returns in CSV, self-closing HTML tags in sanitize, non-digit input in DateInput, panel-click-not-dismissing in ConfirmDialog, role="search" on FilterBar. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -126,4 +126,21 @@ describe("generateCsv", () => {
|
||||
expect(lines[1]).toBe("abc");
|
||||
expect(lines[2]).toBe("abc");
|
||||
});
|
||||
|
||||
it("does NOT quote a cell value that contains only a carriage return (\\r is not in the escape list)", () => {
|
||||
// The escapeCsvValue function checks for '\n' but not '\r'.
|
||||
// A bare \r is therefore left unquoted.
|
||||
const crCols = [{ header: "Field", accessor: (_r: unknown) => "line1\rline2" }];
|
||||
const csv = generateCsv([{}], crCols);
|
||||
const dataLine = csv.split("\n")[1];
|
||||
// Because \r is not special-cased, the value is NOT wrapped in quotes
|
||||
expect(dataLine).toBe("line1\rline2");
|
||||
});
|
||||
|
||||
it("empty columns array produces just a newline for no rows", () => {
|
||||
// header = "" (no columns), body = "" (no rows)
|
||||
// result = "" + "\n" + "" = "\n"
|
||||
const csv = generateCsv([], []);
|
||||
expect(csv).toBe("\n");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -239,3 +239,36 @@ describe("formatCents", () => {
|
||||
expect(formatCents(5)).toBe("0,05");
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// edge cases
|
||||
// ---------------------------------------------------------------------------
|
||||
describe("edge cases", () => {
|
||||
it("formatCents with NaN input — passes through toLocaleString which returns 'NaN'", () => {
|
||||
// NaN == null is false, so it reaches the toLocaleString branch
|
||||
// NaN / 100 is NaN; de-DE toLocaleString of NaN returns "NaN"
|
||||
const result = formatCents(NaN);
|
||||
expect(result).toBe("NaN");
|
||||
});
|
||||
|
||||
it("formatCents with Number.MAX_SAFE_INTEGER — no precision loss in string output", () => {
|
||||
// Verify it returns a non-empty, numeric-looking string and not '-'
|
||||
const result = formatCents(Number.MAX_SAFE_INTEGER);
|
||||
expect(result).not.toBe("-");
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
// The integer part should contain the expected leading digits (90071992547409)
|
||||
expect(result).toContain("90.071.992.547.409");
|
||||
});
|
||||
|
||||
it("toDateInputValue with an invalid date string — returns 'NaN-NaN-NaN'", () => {
|
||||
// new Date("not-a-date") produces an Invalid Date; getFullYear() etc. return NaN
|
||||
expect(toDateInputValue("not-a-date")).toBe("NaN-NaN-NaN");
|
||||
});
|
||||
|
||||
it("formatMoney with 0 cents — returns the zero euro representation", () => {
|
||||
const result = formatMoney(0);
|
||||
// de-DE locale: "0 €" (with non-breaking space before €)
|
||||
expect(result).toContain("0");
|
||||
expect(result).toContain("€");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,4 +69,20 @@ describe("sanitizeHtml", () => {
|
||||
expect(result).not.toContain("iframe");
|
||||
expect(result).toContain("safe");
|
||||
});
|
||||
|
||||
it("strips self-closing <br /> tag leaving no tag remnants", () => {
|
||||
const result = sanitizeHtml("line1<br />line2");
|
||||
expect(result).not.toContain("<br");
|
||||
expect(result).not.toContain("/>");
|
||||
expect(result).toContain("line1");
|
||||
expect(result).toContain("line2");
|
||||
});
|
||||
|
||||
it("strips self-closing <img /> tag with attributes leaving no tag remnants", () => {
|
||||
const result = sanitizeHtml('before<img src="x" alt="y" />after');
|
||||
expect(result).not.toContain("<img");
|
||||
expect(result).not.toContain("src=");
|
||||
expect(result).toContain("before");
|
||||
expect(result).toContain("after");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user