import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { isDebugMode, setDebugMode, validateTaxId, validateVatId, validateIban, validateBic, validateWebsite, validateCompanyForm, getFieldError, hasFieldError, type ValidationError, type CompanyFormData, } from "@/lib/client-validation"; describe("client-validation.ts", () => { // Mock localStorage const localStorageMock = (() => { let store: Record = {}; return { getItem: (key: string) => store[key] || null, setItem: (key: string, value: string) => { store[key] = value; }, removeItem: (key: string) => { delete store[key]; }, clear: () => { store = {}; }, }; })(); beforeEach(() => { // Reset localStorage mock localStorageMock.clear(); (global as any).localStorage = localStorageMock; (global as any).window = { localStorage: localStorageMock }; }); afterEach(() => { (global as any).window = undefined; }); describe("Debug Mode", () => { it("should be disabled by default (no window)", () => { (global as any).window = undefined; expect(isDebugMode()).toBe(false); }); it("should detect debug mode from localStorage", () => { setDebugMode(true); expect(isDebugMode()).toBe(true); }); it("should disable debug mode", () => { setDebugMode(true); expect(isDebugMode()).toBe(true); setDebugMode(false); expect(isDebugMode()).toBe(false); }); }); describe("validateTaxId", () => { it("should return null for empty/null values", () => { expect(validateTaxId("")).toBeNull(); expect(validateTaxId(null)).toBeNull(); expect(validateTaxId(undefined)).toBeNull(); }); it("should validate correct 10-digit tax ID", () => { expect(validateTaxId("1234567890")).toBeNull(); }); it("should reject invalid tax IDs", () => { const result = validateTaxId("123"); // Too short expect(result).not.toBeNull(); expect(result?.field).toBe("taxId"); expect(result?.message).toContain("10 Ziffern"); }); it("should reject tax ID with letters", () => { const result = validateTaxId("abc4567890"); expect(result).not.toBeNull(); }); }); describe("validateVatId", () => { it("should return null for empty/null values", () => { expect(validateVatId("")).toBeNull(); expect(validateVatId(null)).toBeNull(); }); it("should validate correct DE VAT ID", () => { expect(validateVatId("DE123456789")).toBeNull(); }); it("should reject VAT ID without DE prefix", () => { const result = validateVatId("1234567890"); expect(result).not.toBeNull(); expect(result?.field).toBe("vatId"); expect(result?.message).toContain("DE + 9 Ziffern"); }); it("should reject VAT ID with wrong length", () => { const result = validateVatId("DE12345"); // Too short expect(result).not.toBeNull(); }); }); describe("validateIban", () => { it("should return null for empty/null values", () => { expect(validateIban("")).toBeNull(); expect(validateIban(null)).toBeNull(); }); it("should validate correct IBAN", () => { expect(validateIban("DE89370400440532013000")).toBeNull(); }); it("should reject invalid IBAN", () => { const result = validateIban("INVALID"); expect(result).not.toBeNull(); expect(result?.field).toBe("bankIban"); expect(result?.message).toContain("IBAN"); }); }); describe("validateBic", () => { it("should return null for empty/null values", () => { expect(validateBic("")).toBeNull(); expect(validateBic(null)).toBeNull(); }); it("should validate correct BIC", () => { expect(validateBic("DEUTDEFF")).toBeNull(); }); it("should reject invalid BIC", () => { const result = validateBic("123"); // Too short expect(result).not.toBeNull(); expect(result?.field).toBe("bankBic"); }); }); describe("validateWebsite", () => { it("should return null for empty/null values", () => { expect(validateWebsite("")).toBeNull(); expect(validateWebsite(null)).toBeNull(); }); it("should validate correct website URLs", () => { expect(validateWebsite("https://example.com")).toBeNull(); expect(validateWebsite("http://example.com")).toBeNull(); }); it("should reject website without protocol", () => { const result = validateWebsite("example.com"); expect(result).not.toBeNull(); expect(result?.field).toBe("website"); expect(result?.message).toContain("http:// oder https://"); }); it("should reject too long website URLs", () => { const longUrl = "https://" + "a".repeat(250); const result = validateWebsite(longUrl); expect(result).not.toBeNull(); expect(result?.message).toContain("255"); }); }); describe("validateCompanyForm", () => { const validFormData: CompanyFormData = { name: "Test GmbH", address: "Hauptstraße 1", zip: "12345", city: "Berlin", }; it("should pass validation for valid data", () => { const errors = validateCompanyForm(validFormData); expect(errors).toHaveLength(0); }); it("should require name", () => { const data = { ...validFormData, name: "" }; const errors = validateCompanyForm(data); expect(errors.length).toBeGreaterThan(0); expect(errors[0].field).toBe("name"); }); it("should require address", () => { const data = { ...validFormData, address: "" }; const errors = validateCompanyForm(data); const addressError = errors.find(e => e.field === "address"); expect(addressError).toBeDefined(); }); it("should require zip", () => { const data = { ...validFormData, zip: "" }; const errors = validateCompanyForm(data); const zipError = errors.find(e => e.field === "zip"); expect(zipError).toBeDefined(); }); it("should require city", () => { const data = { ...validFormData, city: "" }; const errors = validateCompanyForm(data); const cityError = errors.find(e => e.field === "city"); expect(cityError).toBeDefined(); }); it("should validate zip format (digits only)", () => { const data = { ...validFormData, zip: "abc" }; const errors = validateCompanyForm(data); const zipError = errors.find(e => e.field === "zip"); expect(zipError).toBeDefined(); expect(zipError?.message).toContain("Zahlen"); }); it("should validate optional fields when provided", () => { const data = { ...validFormData, taxId: "123", // Invalid vatId: "INVALID", // Invalid website: "example.com", // Invalid (no protocol) }; const errors = validateCompanyForm(data); expect(errors.length).toBeGreaterThanOrEqual(3); }); it("should accept valid optional fields", () => { const data = { ...validFormData, taxId: "1234567890", vatId: "DE123456789", website: "https://example.com", }; const errors = validateCompanyForm(data); const hasTaxIdError = errors.some(e => e.field === "taxId"); const hasVatIdError = errors.some(e => e.field === "vatId"); const hasWebsiteError = errors.some(e => e.field === "website"); expect(hasTaxIdError).toBe(false); expect(hasVatIdError).toBe(false); expect(hasWebsiteError).toBe(false); }); it("should validate email format", () => { const data = { ...validFormData, email: "invalid-email" }; const errors = validateCompanyForm(data); const emailError = errors.find(e => e.field === "email"); expect(emailError).toBeDefined(); }); it("should accept valid email", () => { const data = { ...validFormData, email: "test@example.com" }; const errors = validateCompanyForm(data); const emailError = errors.find(e => e.field === "email"); expect(emailError).toBeUndefined(); }); it("should validate field length limits", () => { const data = { ...validFormData, name: "a".repeat(300), // Too long phone: "a".repeat(30), // Too long }; const errors = validateCompanyForm(data); expect(errors.length).toBeGreaterThan(0); }); }); describe("getFieldError", () => { it("should return error message for field", () => { const errors: ValidationError[] = [ { field: "name", message: "Name required" }, { field: "email", message: "Invalid email" }, ]; expect(getFieldError("name", errors)).toBe("Name required"); expect(getFieldError("email", errors)).toBe("Invalid email"); }); it("should return null for field without error", () => { const errors: ValidationError[] = []; expect(getFieldError("name", errors)).toBeNull(); }); }); describe("hasFieldError", () => { it("should return true if field has error", () => { const errors: ValidationError[] = [ { field: "name", message: "Name required" }, ]; expect(hasFieldError("name", errors)).toBe(true); expect(hasFieldError("email", errors)).toBe(false); }); it("should return false for empty errors", () => { expect(hasFieldError("name", [])).toBe(false); }); }); });