mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-14 01:45:12 +00:00
chore(login): add unit tests for core business logic (#11083)
# Which Problems Are Solved - Adds basic unit tests for core business logic - Adds playwright tests for core flows # How the Problems Are Solved - Extends unit tests for session - Adds unit tests for other core business logic (oidc, idp, auth etc) # Additional Changes - N/A --------- Co-authored-by: Max Peintner <[email protected]>
This commit is contained in:
co-authored by
Max Peintner
parent
207095f7c1
commit
86cce629c7
@@ -0,0 +1,315 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { coerceToBase64Url, coerceToArrayBuffer } from "./base64";
|
||||
|
||||
const originalBtoa = global.btoa;
|
||||
const originalAtob = global.atob;
|
||||
|
||||
beforeEach(() => {
|
||||
global.btoa = (str: string) => Buffer.from(str, "binary").toString("base64");
|
||||
global.atob = (b64: string) => Buffer.from(b64, "base64").toString("binary");
|
||||
global.window = global.window || ({} as any);
|
||||
(global.window as any).btoa = global.btoa;
|
||||
(global.window as any).atob = global.atob;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
global.btoa = originalBtoa;
|
||||
global.atob = originalAtob;
|
||||
});
|
||||
|
||||
describe("base64 utilities", () => {
|
||||
describe("coerceToBase64Url", () => {
|
||||
it("should convert Uint8Array to base64url", () => {
|
||||
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);
|
||||
const result = coerceToBase64Url(uint8Array, "test");
|
||||
|
||||
expect(result).not.toContain("+");
|
||||
expect(result).not.toContain("/");
|
||||
expect(result).not.toContain("=");
|
||||
expect(typeof result).toBe("string");
|
||||
});
|
||||
|
||||
it("should convert Array to base64url", () => {
|
||||
const array = [72, 101, 108, 108, 111];
|
||||
const result = coerceToBase64Url(array, "test");
|
||||
|
||||
expect(result).not.toContain("+");
|
||||
expect(result).not.toContain("/");
|
||||
expect(result).not.toContain("=");
|
||||
expect(typeof result).toBe("string");
|
||||
});
|
||||
|
||||
it("should convert ArrayBuffer to base64url", () => {
|
||||
const buffer = new Uint8Array([72, 101, 108, 108, 111]).buffer;
|
||||
const result = coerceToBase64Url(buffer, "test");
|
||||
|
||||
expect(result).not.toContain("+");
|
||||
expect(result).not.toContain("/");
|
||||
expect(result).not.toContain("=");
|
||||
expect(typeof result).toBe("string");
|
||||
});
|
||||
|
||||
it("should convert standard base64 string to base64url", () => {
|
||||
const base64 = "Hello+World/Test=";
|
||||
const result = coerceToBase64Url(base64, "test");
|
||||
|
||||
expect(result).toBe("Hello-World_Test");
|
||||
expect(result).not.toContain("+");
|
||||
expect(result).not.toContain("/");
|
||||
expect(result).not.toContain("=");
|
||||
});
|
||||
|
||||
it("should handle already URL-safe base64 strings", () => {
|
||||
const base64url = "Hello-World_Test";
|
||||
const result = coerceToBase64Url(base64url, "test");
|
||||
|
||||
expect(result).toBe("Hello-World_Test");
|
||||
});
|
||||
|
||||
it("should remove padding from base64 strings", () => {
|
||||
const base64WithPadding = "Test==";
|
||||
const result = coerceToBase64Url(base64WithPadding, "test");
|
||||
|
||||
expect(result).toBe("Test");
|
||||
expect(result).not.toContain("=");
|
||||
});
|
||||
|
||||
it("should handle empty Uint8Array", () => {
|
||||
const uint8Array = new Uint8Array([]);
|
||||
const result = coerceToBase64Url(uint8Array, "test");
|
||||
|
||||
expect(typeof result).toBe("string");
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
it("should handle empty string", () => {
|
||||
const result = coerceToBase64Url("", "test");
|
||||
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
it("should throw error for non-coercible types", () => {
|
||||
expect(() => coerceToBase64Url(123, "number")).toThrow("could not coerce 'number' to string");
|
||||
|
||||
expect(() => coerceToBase64Url(null, "null")).toThrow("could not coerce 'null' to string");
|
||||
|
||||
expect(() => coerceToBase64Url(undefined, "undefined")).toThrow("could not coerce 'undefined' to string");
|
||||
|
||||
expect(() => coerceToBase64Url({}, "object")).toThrow("could not coerce 'object' to string");
|
||||
});
|
||||
|
||||
it("should handle binary data correctly", () => {
|
||||
const binaryData = new Uint8Array([0, 1, 2, 255, 254, 253]);
|
||||
const result = coerceToBase64Url(binaryData, "binary");
|
||||
|
||||
expect(typeof result).toBe("string");
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should handle large Uint8Arrays", () => {
|
||||
const largeArray = new Uint8Array(1000);
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
largeArray[i] = i % 256;
|
||||
}
|
||||
|
||||
const result = coerceToBase64Url(largeArray, "large");
|
||||
|
||||
expect(typeof result).toBe("string");
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should produce consistent results for same input", () => {
|
||||
const input = new Uint8Array([1, 2, 3, 4, 5]);
|
||||
const result1 = coerceToBase64Url(input, "test");
|
||||
const result2 = coerceToBase64Url(input, "test");
|
||||
|
||||
expect(result1).toBe(result2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("coerceToArrayBuffer", () => {
|
||||
it("should convert base64url string to ArrayBuffer", () => {
|
||||
const base64url = "SGVsbG8";
|
||||
const result = coerceToArrayBuffer(base64url, "test");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
expect(result.byteLength).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should convert base64 string with URL-safe characters to ArrayBuffer", () => {
|
||||
const base64url = "Hello-World_Test";
|
||||
const result = coerceToArrayBuffer(base64url, "test");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
});
|
||||
|
||||
it("should convert Array to ArrayBuffer", () => {
|
||||
const array = [72, 101, 108, 108, 111];
|
||||
const result = coerceToArrayBuffer(array, "test");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
|
||||
const view = new Uint8Array(result);
|
||||
expect(Array.from(view)).toEqual(array);
|
||||
});
|
||||
|
||||
it("should convert Uint8Array to ArrayBuffer", () => {
|
||||
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]);
|
||||
const result = coerceToArrayBuffer(uint8Array, "test");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
expect(result).toBe(uint8Array.buffer);
|
||||
});
|
||||
|
||||
it("should handle ArrayBuffer input (passthrough)", () => {
|
||||
const buffer = new Uint8Array([1, 2, 3]).buffer;
|
||||
const result = coerceToArrayBuffer(buffer, "test");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
expect(result).toBe(buffer);
|
||||
});
|
||||
|
||||
it("should handle empty string", () => {
|
||||
const result = coerceToArrayBuffer("", "test");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
expect(result.byteLength).toBe(0);
|
||||
});
|
||||
|
||||
it("should handle empty array", () => {
|
||||
const result = coerceToArrayBuffer([], "test");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
expect(result.byteLength).toBe(0);
|
||||
});
|
||||
|
||||
it("should throw TypeError for non-coercible types", () => {
|
||||
expect(() => coerceToArrayBuffer(123, "number")).toThrow(TypeError);
|
||||
expect(() => coerceToArrayBuffer(123, "number")).toThrow("could not coerce 'number' to ArrayBuffer");
|
||||
|
||||
expect(() => coerceToArrayBuffer(null, "null")).toThrow(TypeError);
|
||||
expect(() => coerceToArrayBuffer(undefined, "undefined")).toThrow(TypeError);
|
||||
expect(() => coerceToArrayBuffer({}, "object")).toThrow(TypeError);
|
||||
});
|
||||
|
||||
it("should handle binary data correctly", () => {
|
||||
const binaryArray = [0, 1, 2, 255, 254, 253];
|
||||
const result = coerceToArrayBuffer(binaryArray, "binary");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
|
||||
const view = new Uint8Array(result);
|
||||
expect(Array.from(view)).toEqual(binaryArray);
|
||||
});
|
||||
|
||||
it("should handle large arrays", () => {
|
||||
const largeArray = new Array(1000).fill(0).map((_, i) => i % 256);
|
||||
const result = coerceToArrayBuffer(largeArray, "large");
|
||||
|
||||
expect(result).toBeInstanceOf(ArrayBuffer);
|
||||
expect(result.byteLength).toBe(1000);
|
||||
});
|
||||
|
||||
it("should produce consistent results for same input", () => {
|
||||
const input = [1, 2, 3, 4, 5];
|
||||
const result1 = coerceToArrayBuffer(input, "test");
|
||||
const result2 = coerceToArrayBuffer(input, "test");
|
||||
|
||||
const view1 = new Uint8Array(result1);
|
||||
const view2 = new Uint8Array(result2);
|
||||
|
||||
expect(Array.from(view1)).toEqual(Array.from(view2));
|
||||
});
|
||||
});
|
||||
|
||||
describe("round-trip conversions", () => {
|
||||
it("should successfully round-trip Uint8Array -> base64url -> ArrayBuffer", () => {
|
||||
const original = new Uint8Array([1, 2, 3, 4, 5, 255, 254, 253]);
|
||||
|
||||
const base64url = coerceToBase64Url(original, "test");
|
||||
const recovered = coerceToArrayBuffer(base64url, "test");
|
||||
|
||||
const recoveredArray = new Uint8Array(recovered);
|
||||
expect(Array.from(recoveredArray)).toEqual(Array.from(original));
|
||||
});
|
||||
|
||||
it("should successfully round-trip Array -> base64url -> ArrayBuffer -> Array", () => {
|
||||
const original = [10, 20, 30, 40, 50];
|
||||
|
||||
const base64url = coerceToBase64Url(original, "test");
|
||||
const buffer = coerceToArrayBuffer(base64url, "test");
|
||||
const recovered = Array.from(new Uint8Array(buffer));
|
||||
|
||||
expect(recovered).toEqual(original);
|
||||
});
|
||||
|
||||
it("should handle empty data in round-trip", () => {
|
||||
const original = new Uint8Array([]);
|
||||
|
||||
const base64url = coerceToBase64Url(original, "test");
|
||||
const recovered = coerceToArrayBuffer(base64url, "test");
|
||||
|
||||
expect(recovered.byteLength).toBe(0);
|
||||
});
|
||||
|
||||
it("should handle special characters in round-trip", () => {
|
||||
const original = new Uint8Array([0xff, 0xff, 0xff, 0xfe, 0x00, 0x01]);
|
||||
|
||||
const base64url = coerceToBase64Url(original, "test");
|
||||
|
||||
expect(base64url).not.toContain("+");
|
||||
expect(base64url).not.toContain("/");
|
||||
expect(base64url).not.toContain("=");
|
||||
|
||||
const recovered = coerceToArrayBuffer(base64url, "test");
|
||||
const recoveredArray = new Uint8Array(recovered);
|
||||
|
||||
expect(Array.from(recoveredArray)).toEqual(Array.from(original));
|
||||
});
|
||||
|
||||
it("should handle large data in round-trip", () => {
|
||||
const original = new Uint8Array(1024);
|
||||
for (let i = 0; i < 1024; i++) {
|
||||
original[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
|
||||
const base64url = coerceToBase64Url(original, "test");
|
||||
const recovered = coerceToArrayBuffer(base64url, "test");
|
||||
const recoveredArray = new Uint8Array(recovered);
|
||||
|
||||
expect(Array.from(recoveredArray)).toEqual(Array.from(original));
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", () => {
|
||||
it("should handle all zero bytes", () => {
|
||||
const zeros = new Uint8Array([0, 0, 0, 0]);
|
||||
const base64url = coerceToBase64Url(zeros, "test");
|
||||
const recovered = coerceToArrayBuffer(base64url, "test");
|
||||
|
||||
expect(Array.from(new Uint8Array(recovered))).toEqual([0, 0, 0, 0]);
|
||||
});
|
||||
|
||||
it("should handle all max bytes", () => {
|
||||
const maxBytes = new Uint8Array([255, 255, 255, 255]);
|
||||
const base64url = coerceToBase64Url(maxBytes, "test");
|
||||
const recovered = coerceToArrayBuffer(base64url, "test");
|
||||
|
||||
expect(Array.from(new Uint8Array(recovered))).toEqual([255, 255, 255, 255]);
|
||||
});
|
||||
|
||||
it("should handle single byte", () => {
|
||||
const singleByte = new Uint8Array([42]);
|
||||
const base64url = coerceToBase64Url(singleByte, "test");
|
||||
const recovered = coerceToArrayBuffer(base64url, "test");
|
||||
|
||||
expect(Array.from(new Uint8Array(recovered))).toEqual([42]);
|
||||
});
|
||||
|
||||
it("should provide meaningful error messages with parameter names", () => {
|
||||
expect(() => coerceToBase64Url(123, "myParameter")).toThrow("could not coerce 'myParameter' to string");
|
||||
|
||||
expect(() => coerceToArrayBuffer(123, "anotherParam")).toThrow("could not coerce 'anotherParam' to ArrayBuffer");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,622 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import {
|
||||
computeMap,
|
||||
setTheme,
|
||||
DARK_PRIMARY,
|
||||
PRIMARY,
|
||||
DARK_WARN,
|
||||
WARN,
|
||||
DARK_BACKGROUND,
|
||||
BACKGROUND,
|
||||
DARK_TEXT,
|
||||
TEXT,
|
||||
} from "./colors";
|
||||
import { BrandingSettings } from "@zitadel/proto/zitadel/settings/v2/branding_settings_pb";
|
||||
|
||||
describe("color utilities", () => {
|
||||
describe("computeMap", () => {
|
||||
it("should compute light theme color maps with default colors", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: PRIMARY,
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
|
||||
expect(result).toHaveProperty("background");
|
||||
expect(result).toHaveProperty("primary");
|
||||
expect(result).toHaveProperty("warn");
|
||||
expect(result).toHaveProperty("text");
|
||||
expect(result).toHaveProperty("link");
|
||||
|
||||
expect(result.background).toHaveLength(14);
|
||||
expect(result.primary).toHaveLength(14);
|
||||
expect(result.warn).toHaveLength(14);
|
||||
expect(result.text).toHaveLength(14);
|
||||
expect(result.link).toHaveLength(14);
|
||||
});
|
||||
|
||||
it("should compute dark theme color maps", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: PRIMARY,
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, true);
|
||||
|
||||
expect(result.background).toHaveLength(14);
|
||||
expect(result.primary).toHaveLength(14);
|
||||
expect(result.warn).toHaveLength(14);
|
||||
expect(result.text).toHaveLength(14);
|
||||
expect(result.link).toHaveLength(14);
|
||||
});
|
||||
|
||||
it("should generate color objects with correct properties", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: "#ffffff",
|
||||
fontColor: "#000000",
|
||||
primaryColor: "#5469d4",
|
||||
warnColor: "#cd3d56",
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
|
||||
result.primary.forEach((color) => {
|
||||
expect(color).toHaveProperty("name");
|
||||
expect(color).toHaveProperty("hex");
|
||||
expect(color).toHaveProperty("rgb");
|
||||
expect(color).toHaveProperty("contrastColor");
|
||||
|
||||
expect(color.hex).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(color.rgb).toMatch(/^rgb\(/);
|
||||
expect(["#ffffff", "hsla(0, 0%, 0%, 0.87)"]).toContain(color.contrastColor);
|
||||
});
|
||||
});
|
||||
|
||||
it("should generate correct shade names", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: PRIMARY,
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
|
||||
const expectedNames = [
|
||||
"50",
|
||||
"100",
|
||||
"200",
|
||||
"300",
|
||||
"400",
|
||||
"500",
|
||||
"600",
|
||||
"700",
|
||||
"800",
|
||||
"900",
|
||||
"A100",
|
||||
"A200",
|
||||
"A400",
|
||||
"A700",
|
||||
];
|
||||
|
||||
expect(result.primary.map((c) => c.name)).toEqual(expectedNames);
|
||||
});
|
||||
|
||||
it("should compute different colors for light vs dark theme", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: "#ffffff",
|
||||
fontColor: "#000000",
|
||||
primaryColor: "#5469d4",
|
||||
warnColor: "#cd3d56",
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: "#111827",
|
||||
fontColor: "#ffffff",
|
||||
primaryColor: "#2073c4",
|
||||
warnColor: "#ff3b5b",
|
||||
},
|
||||
};
|
||||
|
||||
const light = computeMap(branding, false);
|
||||
const dark = computeMap(branding, true);
|
||||
|
||||
// Primary colors should be different
|
||||
expect(light.primary[5].hex).not.toBe(dark.primary[5].hex);
|
||||
|
||||
// Background colors should be different
|
||||
expect(light.background[5].hex).not.toBe(dark.background[5].hex);
|
||||
|
||||
// Text colors should be different
|
||||
expect(light.text[5].hex).not.toBe(dark.text[5].hex);
|
||||
});
|
||||
|
||||
it("should handle custom brand colors", () => {
|
||||
const customBranding = {
|
||||
lightTheme: {
|
||||
backgroundColor: "#f0f0f0",
|
||||
fontColor: "#333333",
|
||||
primaryColor: "#ff6b6b",
|
||||
warnColor: "#feca57",
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: "#1e1e1e",
|
||||
fontColor: "#e0e0e0",
|
||||
primaryColor: "#ff8b8b",
|
||||
warnColor: "#fed57",
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(customBranding, false);
|
||||
|
||||
// Should generate valid hex colors
|
||||
result.primary.forEach((color) => {
|
||||
expect(color.hex).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
});
|
||||
});
|
||||
|
||||
it("should generate lighter shades for lower values", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: "#5469d4",
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
|
||||
// 50 should be lighter than 500
|
||||
const shade50 = result.primary.find((c) => c.name === "50");
|
||||
const shade500 = result.primary.find((c) => c.name === "500");
|
||||
|
||||
expect(shade50).toBeDefined();
|
||||
expect(shade500).toBeDefined();
|
||||
|
||||
// Lighter colors have higher RGB values
|
||||
const rgb50 = shade50!.rgb;
|
||||
const rgb500 = shade500!.rgb;
|
||||
|
||||
expect(rgb50).toBeDefined();
|
||||
expect(rgb500).toBeDefined();
|
||||
});
|
||||
|
||||
it("should generate darker shades for higher values", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: "#5469d4",
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
|
||||
// 900 should be darker than 500
|
||||
const shade500 = result.primary.find((c) => c.name === "500");
|
||||
const shade900 = result.primary.find((c) => c.name === "900");
|
||||
|
||||
expect(shade500).toBeDefined();
|
||||
expect(shade900).toBeDefined();
|
||||
});
|
||||
|
||||
it("should compute link colors using primary color", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: "#5469d4",
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
|
||||
// Link should be computed based on primary color
|
||||
expect(result.link).toHaveLength(14);
|
||||
result.link.forEach((color) => {
|
||||
expect(color.hex).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle grayscale colors", () => {
|
||||
const grayscaleBranding = {
|
||||
lightTheme: {
|
||||
backgroundColor: "#ffffff",
|
||||
fontColor: "#000000",
|
||||
primaryColor: "#808080",
|
||||
warnColor: "#666666",
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: "#000000",
|
||||
fontColor: "#ffffff",
|
||||
primaryColor: "#a0a0a0",
|
||||
warnColor: "#999999",
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(grayscaleBranding, false);
|
||||
|
||||
// Should handle grayscale without errors
|
||||
result.primary.forEach((color) => {
|
||||
expect(color.hex).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(color.contrastColor).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("should assign correct contrast colors for light backgrounds", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: "#ffffff",
|
||||
fontColor: TEXT,
|
||||
primaryColor: "#ffeb3b", // Light yellow
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
|
||||
// Light colors should have dark contrast
|
||||
const lightShades = result.primary.filter((c) => ["50", "100", "200", "A100"].includes(c.name));
|
||||
|
||||
lightShades.forEach((color) => {
|
||||
expect(color.contrastColor).toBe("hsla(0, 0%, 0%, 0.87)");
|
||||
});
|
||||
});
|
||||
|
||||
it("should assign correct contrast colors for dark backgrounds", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: "#1a237e", // Dark blue
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
|
||||
// Dark colors should have light contrast
|
||||
const darkShades = result.primary.filter((c) => ["800", "900"].includes(c.name));
|
||||
|
||||
darkShades.forEach((color) => {
|
||||
expect(color.contrastColor).toBe("#ffffff");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("setTheme", () => {
|
||||
let mockDocument: any;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDocument = {
|
||||
documentElement: {
|
||||
style: {
|
||||
setProperty: vi.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it("should set theme with default colors when no policy provided", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
expect(mockDocument.documentElement.style.setProperty).toHaveBeenCalled();
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
const propertyNames = calls.map((call: any[]) => call[0]);
|
||||
|
||||
const hasLightBackground = propertyNames.some((name: string) => name.includes("--theme-light-background-"));
|
||||
const hasDarkBackground = propertyNames.some((name: string) => name.includes("--theme-dark-background-"));
|
||||
const hasLightPrimary = propertyNames.some((name: string) => name.includes("--theme-light-primary-"));
|
||||
const hasDarkPrimary = propertyNames.some((name: string) => name.includes("--theme-dark-primary-"));
|
||||
|
||||
expect(hasLightBackground).toBe(true);
|
||||
expect(hasDarkBackground).toBe(true);
|
||||
expect(hasLightPrimary).toBe(true);
|
||||
expect(hasDarkPrimary).toBe(true);
|
||||
});
|
||||
|
||||
it("should set theme with custom policy colors", () => {
|
||||
const customPolicy = {
|
||||
lightTheme: {
|
||||
backgroundColor: "#f5f5f5",
|
||||
fontColor: "#212121",
|
||||
primaryColor: "#ff6b6b",
|
||||
warnColor: "#feca57",
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: "#1a1a1a",
|
||||
fontColor: "#fafafa",
|
||||
primaryColor: "#ff8b8b",
|
||||
warnColor: "#fed57",
|
||||
},
|
||||
} as BrandingSettings;
|
||||
|
||||
setTheme(mockDocument, customPolicy);
|
||||
|
||||
expect(mockDocument.documentElement.style.setProperty).toHaveBeenCalled();
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should set CSS custom properties for all color shades", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
const propertyNames = calls.map((call: any[]) => call[0]);
|
||||
|
||||
["50", "100", "200", "300", "400", "500", "600", "700", "800", "900"].forEach((shade) => {
|
||||
const hasShade = propertyNames.some((name: string) => name.includes(`-${shade}`));
|
||||
expect(hasShade).toBe(true);
|
||||
});
|
||||
|
||||
["A100", "A200", "A400", "A700"].forEach((shade) => {
|
||||
const hasShade = propertyNames.some((name: string) => name.includes(`-${shade}`));
|
||||
expect(hasShade).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should set contrast colors for each shade", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
const propertyNames = calls.map((call: any[]) => call[0]);
|
||||
|
||||
const contrastProperties = propertyNames.filter((name: string) => name.includes("-contrast-"));
|
||||
|
||||
expect(contrastProperties.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should set secondary alpha colors for text and link", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
const propertyNames = calls.map((call: any[]) => call[0]);
|
||||
|
||||
const secondaryProperties = propertyNames.filter((name: string) => name.includes("-secondary-"));
|
||||
|
||||
expect(secondaryProperties.length).toBeGreaterThan(0);
|
||||
|
||||
secondaryProperties.forEach((prop: string) => {
|
||||
expect(prop.includes("-text-") || prop.includes("-link-")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should set properties for both light and dark themes", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
const propertyNames = calls.map((call: any[]) => call[0]);
|
||||
|
||||
const lightProperties = propertyNames.filter((name: string) => name.includes("--theme-light-"));
|
||||
const darkProperties = propertyNames.filter((name: string) => name.includes("--theme-dark-"));
|
||||
|
||||
expect(lightProperties.length).toBeGreaterThan(0);
|
||||
expect(darkProperties.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should set properties for all color types", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
const propertyNames = calls.map((call: any[]) => call[0]);
|
||||
|
||||
const types = ["background", "primary", "warn", "text", "link"];
|
||||
|
||||
types.forEach((type) => {
|
||||
const typeProperties = propertyNames.filter((name: string) => name.includes(`-${type}-`));
|
||||
expect(typeProperties.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("should use fallback colors when policy has partial values", () => {
|
||||
const partialPolicy = {
|
||||
lightTheme: {
|
||||
primaryColor: "#ff0000",
|
||||
},
|
||||
} as BrandingSettings;
|
||||
|
||||
setTheme(mockDocument, partialPolicy);
|
||||
|
||||
expect(mockDocument.documentElement.style.setProperty).toHaveBeenCalled();
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should handle undefined policy gracefully", () => {
|
||||
expect(() => setTheme(mockDocument, undefined)).not.toThrow();
|
||||
expect(mockDocument.documentElement.style.setProperty).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should set valid hex color values", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
|
||||
calls.forEach((call: any[]) => {
|
||||
const [propertyName, propertyValue] = call;
|
||||
|
||||
if (!propertyName.includes("-contrast-") && !propertyName.includes("-secondary-")) {
|
||||
expect(propertyValue).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should set alpha channel for secondary colors", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
|
||||
const secondaryCalls = calls.filter((call: any[]) => call[0].includes("-secondary-"));
|
||||
|
||||
secondaryCalls.forEach((call: any[]) => {
|
||||
const [, propertyValue] = call;
|
||||
expect(propertyValue).toMatch(/c7$/i);
|
||||
});
|
||||
});
|
||||
|
||||
it("should call setProperty exactly once per CSS variable", () => {
|
||||
setTheme(mockDocument);
|
||||
|
||||
const calls = mockDocument.documentElement.style.setProperty.mock.calls;
|
||||
const propertyNames = calls.map((call: any[]) => call[0]);
|
||||
|
||||
// Check for duplicates
|
||||
const uniqueProperties = new Set(propertyNames);
|
||||
expect(uniqueProperties.size).toBe(propertyNames.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("default color constants", () => {
|
||||
it("should have valid default color values", () => {
|
||||
expect(PRIMARY).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(DARK_PRIMARY).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(WARN).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(DARK_WARN).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(BACKGROUND).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(DARK_BACKGROUND).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(TEXT).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
expect(DARK_TEXT).toMatch(/^#[0-9a-f]{6}$/i);
|
||||
});
|
||||
|
||||
it("should have distinct colors for light and dark themes", () => {
|
||||
expect(PRIMARY).not.toBe(DARK_PRIMARY);
|
||||
expect(WARN).not.toBe(DARK_WARN);
|
||||
expect(BACKGROUND).not.toBe(DARK_BACKGROUND);
|
||||
expect(TEXT).not.toBe(DARK_TEXT);
|
||||
});
|
||||
});
|
||||
|
||||
describe("edge cases", () => {
|
||||
it("should handle extreme light colors", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: "#ffffff",
|
||||
fontColor: "#fefefe",
|
||||
primaryColor: "#fffacd",
|
||||
warnColor: "#fff8dc",
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: DARK_PRIMARY,
|
||||
warnColor: DARK_WARN,
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => computeMap(branding, false)).not.toThrow();
|
||||
});
|
||||
|
||||
it("should handle extreme dark colors", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: PRIMARY,
|
||||
warnColor: WARN,
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: "#000000",
|
||||
fontColor: "#010101",
|
||||
primaryColor: "#0a0a0a",
|
||||
warnColor: "#141414",
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => computeMap(branding, true)).not.toThrow();
|
||||
});
|
||||
|
||||
it("should handle highly saturated colors", () => {
|
||||
const branding = {
|
||||
lightTheme: {
|
||||
backgroundColor: BACKGROUND,
|
||||
fontColor: TEXT,
|
||||
primaryColor: "#ff0000",
|
||||
warnColor: "#00ff00",
|
||||
},
|
||||
darkTheme: {
|
||||
backgroundColor: DARK_BACKGROUND,
|
||||
fontColor: DARK_TEXT,
|
||||
primaryColor: "#0000ff",
|
||||
warnColor: "#ffff00",
|
||||
},
|
||||
};
|
||||
|
||||
const result = computeMap(branding, false);
|
||||
expect(result.primary).toHaveLength(14);
|
||||
expect(result.warn).toHaveLength(14);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,317 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { symbolValidator, numberValidator, upperCaseValidator, lowerCaseValidator } from "./validators";
|
||||
|
||||
describe("validators", () => {
|
||||
describe("symbolValidator", () => {
|
||||
it("should return true for strings with symbols", () => {
|
||||
expect(symbolValidator("!")).toBe(true);
|
||||
expect(symbolValidator("@")).toBe(true);
|
||||
expect(symbolValidator("#")).toBe(true);
|
||||
expect(symbolValidator("$")).toBe(true);
|
||||
expect(symbolValidator("%")).toBe(true);
|
||||
expect(symbolValidator("^")).toBe(true);
|
||||
expect(symbolValidator("&")).toBe(true);
|
||||
expect(symbolValidator("*")).toBe(true);
|
||||
expect(symbolValidator("(")).toBe(true);
|
||||
expect(symbolValidator(")")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true for strings with punctuation", () => {
|
||||
expect(symbolValidator(".")).toBe(true);
|
||||
expect(symbolValidator(",")).toBe(true);
|
||||
expect(symbolValidator(";")).toBe(true);
|
||||
expect(symbolValidator(":")).toBe(true);
|
||||
expect(symbolValidator("?")).toBe(true);
|
||||
expect(symbolValidator("'")).toBe(true);
|
||||
expect(symbolValidator('"')).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true for strings with special characters", () => {
|
||||
expect(symbolValidator("-")).toBe(true);
|
||||
expect(symbolValidator("_")).toBe(true);
|
||||
expect(symbolValidator("=")).toBe(true);
|
||||
expect(symbolValidator("+")).toBe(true);
|
||||
expect(symbolValidator("[")).toBe(true);
|
||||
expect(symbolValidator("]")).toBe(true);
|
||||
expect(symbolValidator("{")).toBe(true);
|
||||
expect(symbolValidator("}")).toBe(true);
|
||||
expect(symbolValidator("|")).toBe(true);
|
||||
expect(symbolValidator("\\")).toBe(true);
|
||||
expect(symbolValidator("/")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true for strings with whitespace", () => {
|
||||
expect(symbolValidator(" ")).toBe(true);
|
||||
expect(symbolValidator("\t")).toBe(true);
|
||||
expect(symbolValidator("\n")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for strings with only letters", () => {
|
||||
expect(symbolValidator("abc")).toBe(false);
|
||||
expect(symbolValidator("ABC")).toBe(false);
|
||||
expect(symbolValidator("aBc")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false for strings with only numbers", () => {
|
||||
expect(symbolValidator("123")).toBe(false);
|
||||
expect(symbolValidator("0")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false for alphanumeric strings", () => {
|
||||
expect(symbolValidator("abc123")).toBe(false);
|
||||
expect(symbolValidator("ABC123")).toBe(false);
|
||||
expect(symbolValidator("Test123")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true for mixed strings with symbols", () => {
|
||||
expect(symbolValidator("Password!")).toBe(true);
|
||||
expect(symbolValidator("Test@123")).toBe(true);
|
||||
expect(symbolValidator("hello-world")).toBe(true);
|
||||
expect(symbolValidator("user_name")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for empty strings", () => {
|
||||
expect(symbolValidator("")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true for Unicode symbols", () => {
|
||||
expect(symbolValidator("€")).toBe(true);
|
||||
expect(symbolValidator("©")).toBe(true);
|
||||
expect(symbolValidator("™")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("numberValidator", () => {
|
||||
it("should return true for strings with single digits", () => {
|
||||
expect(numberValidator("0")).toBe(true);
|
||||
expect(numberValidator("1")).toBe(true);
|
||||
expect(numberValidator("5")).toBe(true);
|
||||
expect(numberValidator("9")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true for strings with multiple digits", () => {
|
||||
expect(numberValidator("123")).toBe(true);
|
||||
expect(numberValidator("000")).toBe(true);
|
||||
expect(numberValidator("999")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for strings without numbers", () => {
|
||||
expect(numberValidator("abc")).toBe(false);
|
||||
expect(numberValidator("ABC")).toBe(false);
|
||||
expect(numberValidator("test")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true for alphanumeric strings", () => {
|
||||
expect(numberValidator("abc123")).toBe(true);
|
||||
expect(numberValidator("Test1")).toBe(true);
|
||||
expect(numberValidator("1test")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for strings with only symbols", () => {
|
||||
expect(numberValidator("!@#$%")).toBe(false);
|
||||
expect(numberValidator("***")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true for mixed content with numbers", () => {
|
||||
expect(numberValidator("Password1")).toBe(true);
|
||||
expect(numberValidator("Test@123")).toBe(true);
|
||||
expect(numberValidator("hello-2-world")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for empty strings", () => {
|
||||
expect(numberValidator("")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false for whitespace only", () => {
|
||||
expect(numberValidator(" ")).toBe(false);
|
||||
expect(numberValidator("\t\n")).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle numbers anywhere in the string", () => {
|
||||
expect(numberValidator("a1")).toBe(true);
|
||||
expect(numberValidator("1a")).toBe(true);
|
||||
expect(numberValidator("abc1def")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("upperCaseValidator", () => {
|
||||
it("should return true for strings with single uppercase letters", () => {
|
||||
expect(upperCaseValidator("A")).toBe(true);
|
||||
expect(upperCaseValidator("Z")).toBe(true);
|
||||
expect(upperCaseValidator("M")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true for strings with multiple uppercase letters", () => {
|
||||
expect(upperCaseValidator("ABC")).toBe(true);
|
||||
expect(upperCaseValidator("TEST")).toBe(true);
|
||||
expect(upperCaseValidator("HELLO")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for strings with only lowercase letters", () => {
|
||||
expect(upperCaseValidator("abc")).toBe(false);
|
||||
expect(upperCaseValidator("test")).toBe(false);
|
||||
expect(upperCaseValidator("hello")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true for mixed case strings", () => {
|
||||
expect(upperCaseValidator("Test")).toBe(true);
|
||||
expect(upperCaseValidator("heLLo")).toBe(true);
|
||||
expect(upperCaseValidator("aBcDeF")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for strings with only numbers", () => {
|
||||
expect(upperCaseValidator("123")).toBe(false);
|
||||
expect(upperCaseValidator("000")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false for strings with only symbols", () => {
|
||||
expect(upperCaseValidator("!@#$%")).toBe(false);
|
||||
expect(upperCaseValidator("***")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true for complex passwords with uppercase", () => {
|
||||
expect(upperCaseValidator("Password1!")).toBe(true);
|
||||
expect(upperCaseValidator("test@Test123")).toBe(true);
|
||||
expect(upperCaseValidator("hello-World")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for empty strings", () => {
|
||||
expect(upperCaseValidator("")).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle uppercase anywhere in the string", () => {
|
||||
expect(upperCaseValidator("Aaaa")).toBe(true);
|
||||
expect(upperCaseValidator("aaaA")).toBe(true);
|
||||
expect(upperCaseValidator("aaAaa")).toBe(true);
|
||||
});
|
||||
|
||||
it("should work with numbers and symbols present", () => {
|
||||
expect(upperCaseValidator("Test123!@#")).toBe(true);
|
||||
expect(upperCaseValidator("123A456")).toBe(true);
|
||||
expect(upperCaseValidator("!@#A$%^")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("lowerCaseValidator", () => {
|
||||
it("should return true for strings with single lowercase letters", () => {
|
||||
expect(lowerCaseValidator("a")).toBe(true);
|
||||
expect(lowerCaseValidator("z")).toBe(true);
|
||||
expect(lowerCaseValidator("m")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true for strings with multiple lowercase letters", () => {
|
||||
expect(lowerCaseValidator("abc")).toBe(true);
|
||||
expect(lowerCaseValidator("test")).toBe(true);
|
||||
expect(lowerCaseValidator("hello")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for strings with only uppercase letters", () => {
|
||||
expect(lowerCaseValidator("ABC")).toBe(false);
|
||||
expect(lowerCaseValidator("TEST")).toBe(false);
|
||||
expect(lowerCaseValidator("HELLO")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true for mixed case strings", () => {
|
||||
expect(lowerCaseValidator("Test")).toBe(true);
|
||||
expect(lowerCaseValidator("heLLo")).toBe(true);
|
||||
expect(lowerCaseValidator("aBcDeF")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for strings with only numbers", () => {
|
||||
expect(lowerCaseValidator("123")).toBe(false);
|
||||
expect(lowerCaseValidator("000")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false for strings with only symbols", () => {
|
||||
expect(lowerCaseValidator("!@#$%")).toBe(false);
|
||||
expect(lowerCaseValidator("***")).toBe(false);
|
||||
});
|
||||
|
||||
it("should return true for complex passwords with lowercase", () => {
|
||||
expect(lowerCaseValidator("Password1!")).toBe(true);
|
||||
expect(lowerCaseValidator("TEST@test123")).toBe(true);
|
||||
expect(lowerCaseValidator("HELLO-world")).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false for empty strings", () => {
|
||||
expect(lowerCaseValidator("")).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle lowercase anywhere in the string", () => {
|
||||
expect(lowerCaseValidator("aAAA")).toBe(true);
|
||||
expect(lowerCaseValidator("AAAa")).toBe(true);
|
||||
expect(lowerCaseValidator("AAaAA")).toBe(true);
|
||||
});
|
||||
|
||||
it("should work with numbers and symbols present", () => {
|
||||
expect(lowerCaseValidator("test123!@#")).toBe(true);
|
||||
expect(lowerCaseValidator("123a456")).toBe(true);
|
||||
expect(lowerCaseValidator("!@#a$%^")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("password complexity validation scenarios", () => {
|
||||
it("should validate a strong password with all requirements", () => {
|
||||
const password = "MyP@ssw0rd!";
|
||||
|
||||
expect(upperCaseValidator(password)).toBe(true);
|
||||
expect(lowerCaseValidator(password)).toBe(true);
|
||||
expect(numberValidator(password)).toBe(true);
|
||||
expect(symbolValidator(password)).toBe(true);
|
||||
});
|
||||
|
||||
it("should fail weak password without symbols", () => {
|
||||
const password = "MyPassword123";
|
||||
|
||||
expect(upperCaseValidator(password)).toBe(true);
|
||||
expect(lowerCaseValidator(password)).toBe(true);
|
||||
expect(numberValidator(password)).toBe(true);
|
||||
expect(symbolValidator(password)).toBe(false);
|
||||
});
|
||||
|
||||
it("should fail password without uppercase", () => {
|
||||
const password = "myp@ssw0rd!";
|
||||
|
||||
expect(upperCaseValidator(password)).toBe(false);
|
||||
expect(lowerCaseValidator(password)).toBe(true);
|
||||
expect(numberValidator(password)).toBe(true);
|
||||
expect(symbolValidator(password)).toBe(true);
|
||||
});
|
||||
|
||||
it("should fail password without lowercase", () => {
|
||||
const password = "MYP@SSW0RD!";
|
||||
|
||||
expect(upperCaseValidator(password)).toBe(true);
|
||||
expect(lowerCaseValidator(password)).toBe(false);
|
||||
expect(numberValidator(password)).toBe(true);
|
||||
expect(symbolValidator(password)).toBe(true);
|
||||
});
|
||||
|
||||
it("should fail password without numbers", () => {
|
||||
const password = "MyP@ssword!";
|
||||
|
||||
expect(upperCaseValidator(password)).toBe(true);
|
||||
expect(lowerCaseValidator(password)).toBe(true);
|
||||
expect(numberValidator(password)).toBe(false);
|
||||
expect(symbolValidator(password)).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate very long complex password", () => {
|
||||
const password = "ThisIsAVeryLongP@ssw0rd!WithManyCharacters123";
|
||||
|
||||
expect(upperCaseValidator(password)).toBe(true);
|
||||
expect(lowerCaseValidator(password)).toBe(true);
|
||||
expect(numberValidator(password)).toBe(true);
|
||||
expect(symbolValidator(password)).toBe(true);
|
||||
});
|
||||
|
||||
it("should validate password with multiple symbols", () => {
|
||||
const password = "P@$$w0rd!#%";
|
||||
|
||||
expect(upperCaseValidator(password)).toBe(true);
|
||||
expect(lowerCaseValidator(password)).toBe(true);
|
||||
expect(numberValidator(password)).toBe(true);
|
||||
expect(symbolValidator(password)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user