feat: 项目结构重构优化
This commit is contained in:
157
tests/unit/utils/auth.test.ts
Normal file
157
tests/unit/utils/auth.test.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { AuthStorage, hasPerm } from "@/utils/auth";
|
||||
import { Storage } from "@/utils/storage";
|
||||
import { STORAGE_KEYS, ROLE_ROOT } from "@/constants";
|
||||
|
||||
// Mock Storage
|
||||
vi.mock("@/utils/storage", () => ({
|
||||
Storage: {
|
||||
get: vi.fn(),
|
||||
set: vi.fn(),
|
||||
remove: vi.fn(),
|
||||
sessionGet: vi.fn(),
|
||||
sessionSet: vi.fn(),
|
||||
sessionRemove: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
// Mock useUserStoreHook
|
||||
vi.mock("@/store/modules/user", () => ({
|
||||
useUserStoreHook: vi.fn(() => ({
|
||||
userInfo: {
|
||||
roles: ["admin"],
|
||||
perms: ["sys:user:create", "sys:user:update"],
|
||||
},
|
||||
})),
|
||||
}));
|
||||
|
||||
describe("Auth 工具函数", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("AuthStorage", () => {
|
||||
describe("getAccessToken()", () => {
|
||||
it("记住我为 true 时应该从 localStorage 获取", () => {
|
||||
vi.mocked(Storage.get).mockReturnValueOnce(true); // rememberMe
|
||||
vi.mocked(Storage.get).mockReturnValueOnce("token123"); // accessToken
|
||||
|
||||
const token = AuthStorage.getAccessToken();
|
||||
|
||||
expect(Storage.get).toHaveBeenCalledWith(STORAGE_KEYS.REMEMBER_ME, false);
|
||||
expect(Storage.get).toHaveBeenCalledWith(STORAGE_KEYS.ACCESS_TOKEN, "");
|
||||
expect(token).toBe("token123");
|
||||
});
|
||||
|
||||
it("记住我为 false 时应该从 sessionStorage 获取", () => {
|
||||
vi.mocked(Storage.get).mockReturnValueOnce(false); // rememberMe
|
||||
vi.mocked(Storage.sessionGet).mockReturnValueOnce("session-token");
|
||||
|
||||
const token = AuthStorage.getAccessToken();
|
||||
|
||||
expect(Storage.sessionGet).toHaveBeenCalledWith(STORAGE_KEYS.ACCESS_TOKEN, "");
|
||||
expect(token).toBe("session-token");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRefreshToken()", () => {
|
||||
it("记住我为 true 时应该从 localStorage 获取", () => {
|
||||
vi.mocked(Storage.get).mockReturnValueOnce(true);
|
||||
vi.mocked(Storage.get).mockReturnValueOnce("refresh123");
|
||||
|
||||
const token = AuthStorage.getRefreshToken();
|
||||
|
||||
expect(token).toBe("refresh123");
|
||||
});
|
||||
|
||||
it("记住我为 false 时应该从 sessionStorage 获取", () => {
|
||||
vi.mocked(Storage.get).mockReturnValueOnce(false);
|
||||
vi.mocked(Storage.sessionGet).mockReturnValueOnce("session-refresh");
|
||||
|
||||
const token = AuthStorage.getRefreshToken();
|
||||
|
||||
expect(token).toBe("session-refresh");
|
||||
});
|
||||
});
|
||||
|
||||
describe("setTokens()", () => {
|
||||
it("记住我为 true 时应该存储到 localStorage", () => {
|
||||
AuthStorage.setTokens("access123", "refresh123", true);
|
||||
|
||||
expect(Storage.set).toHaveBeenCalledWith(STORAGE_KEYS.REMEMBER_ME, true);
|
||||
expect(Storage.set).toHaveBeenCalledWith(STORAGE_KEYS.ACCESS_TOKEN, "access123");
|
||||
expect(Storage.set).toHaveBeenCalledWith(STORAGE_KEYS.REFRESH_TOKEN, "refresh123");
|
||||
});
|
||||
|
||||
it("记住我为 false 时应该存储到 sessionStorage", () => {
|
||||
AuthStorage.setTokens("access123", "refresh123", false);
|
||||
|
||||
expect(Storage.set).toHaveBeenCalledWith(STORAGE_KEYS.REMEMBER_ME, false);
|
||||
expect(Storage.sessionSet).toHaveBeenCalledWith(STORAGE_KEYS.ACCESS_TOKEN, "access123");
|
||||
expect(Storage.sessionSet).toHaveBeenCalledWith(STORAGE_KEYS.REFRESH_TOKEN, "refresh123");
|
||||
expect(Storage.remove).toHaveBeenCalledWith(STORAGE_KEYS.ACCESS_TOKEN);
|
||||
expect(Storage.remove).toHaveBeenCalledWith(STORAGE_KEYS.REFRESH_TOKEN);
|
||||
});
|
||||
});
|
||||
|
||||
describe("clearAuth()", () => {
|
||||
it("应该清理所有认证信息", () => {
|
||||
AuthStorage.clearAuth();
|
||||
|
||||
expect(Storage.remove).toHaveBeenCalledWith(STORAGE_KEYS.ACCESS_TOKEN);
|
||||
expect(Storage.remove).toHaveBeenCalledWith(STORAGE_KEYS.REFRESH_TOKEN);
|
||||
expect(Storage.sessionRemove).toHaveBeenCalledWith(STORAGE_KEYS.ACCESS_TOKEN);
|
||||
expect(Storage.sessionRemove).toHaveBeenCalledWith(STORAGE_KEYS.REFRESH_TOKEN);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRememberMe()", () => {
|
||||
it("应该获取记住我状态", () => {
|
||||
vi.mocked(Storage.get).mockReturnValueOnce(true);
|
||||
|
||||
const rememberMe = AuthStorage.getRememberMe();
|
||||
|
||||
expect(Storage.get).toHaveBeenCalledWith(STORAGE_KEYS.REMEMBER_ME, false);
|
||||
expect(rememberMe).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasPerm()", () => {
|
||||
it("超级管理员应该拥有所有按钮权限", () => {
|
||||
const { useUserStoreHook } = await import("@/store/modules/user");
|
||||
vi.mocked(useUserStoreHook).mockReturnValueOnce({
|
||||
userInfo: {
|
||||
roles: [ROLE_ROOT],
|
||||
perms: [],
|
||||
},
|
||||
} as any);
|
||||
|
||||
expect(hasPerm("any:permission", "button")).toBe(true);
|
||||
});
|
||||
|
||||
it("应该验证单个按钮权限", () => {
|
||||
expect(hasPerm("sys:user:create", "button")).toBe(true);
|
||||
expect(hasPerm("sys:user:delete", "button")).toBe(false);
|
||||
});
|
||||
|
||||
it("应该验证多个按钮权限(或关系)", () => {
|
||||
expect(hasPerm(["sys:user:create", "sys:user:delete"], "button")).toBe(true);
|
||||
expect(hasPerm(["sys:user:delete", "sys:user:export"], "button")).toBe(false);
|
||||
});
|
||||
|
||||
it("应该验证角色权限", () => {
|
||||
expect(hasPerm("admin", "role")).toBe(true);
|
||||
expect(hasPerm("user", "role")).toBe(false);
|
||||
});
|
||||
|
||||
it("用户信息不完整时应该返回 false", () => {
|
||||
const { useUserStoreHook } = await import("@/store/modules/user");
|
||||
vi.mocked(useUserStoreHook).mockReturnValueOnce({
|
||||
userInfo: {},
|
||||
} as any);
|
||||
|
||||
expect(hasPerm("any:permission")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
136
tests/unit/utils/format.test.ts
Normal file
136
tests/unit/utils/format.test.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { formatGrowthRate, formatFileSize, formatNumber, formatCurrency } from "@/utils/format";
|
||||
|
||||
describe("Format 工具函数", () => {
|
||||
describe("formatGrowthRate()", () => {
|
||||
it("应该格式化正增长率", () => {
|
||||
expect(formatGrowthRate(0.25)).toBe("+25.00%");
|
||||
expect(formatGrowthRate(0.5)).toBe("+50.00%");
|
||||
expect(formatGrowthRate(1.5)).toBe("+150.00%");
|
||||
});
|
||||
|
||||
it("应该格式化负增长率", () => {
|
||||
expect(formatGrowthRate(-0.25)).toBe("-25.00%");
|
||||
expect(formatGrowthRate(-0.5)).toBe("-50.00%");
|
||||
});
|
||||
|
||||
it("应该格式化零增长率", () => {
|
||||
expect(formatGrowthRate(0)).toBe("0.00%");
|
||||
});
|
||||
|
||||
it("应该处理小数精度", () => {
|
||||
expect(formatGrowthRate(0.12345)).toBe("+12.35%");
|
||||
expect(formatGrowthRate(0.12344)).toBe("+12.34%");
|
||||
});
|
||||
|
||||
it("应该处理 null 和 undefined", () => {
|
||||
expect(formatGrowthRate(null as any)).toBe("0.00%");
|
||||
expect(formatGrowthRate(undefined as any)).toBe("0.00%");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatFileSize()", () => {
|
||||
it("应该格式化字节", () => {
|
||||
expect(formatFileSize(0)).toBe("0 B");
|
||||
expect(formatFileSize(100)).toBe("100 B");
|
||||
expect(formatFileSize(1023)).toBe("1023 B");
|
||||
});
|
||||
|
||||
it("应该格式化 KB", () => {
|
||||
expect(formatFileSize(1024)).toBe("1.00 KB");
|
||||
expect(formatFileSize(1536)).toBe("1.50 KB");
|
||||
expect(formatFileSize(10240)).toBe("10.00 KB");
|
||||
});
|
||||
|
||||
it("应该格式化 MB", () => {
|
||||
expect(formatFileSize(1048576)).toBe("1.00 MB");
|
||||
expect(formatFileSize(5242880)).toBe("5.00 MB");
|
||||
});
|
||||
|
||||
it("应该格式化 GB", () => {
|
||||
expect(formatFileSize(1073741824)).toBe("1.00 GB");
|
||||
expect(formatFileSize(5368709120)).toBe("5.00 GB");
|
||||
});
|
||||
|
||||
it("应该格式化 TB", () => {
|
||||
expect(formatFileSize(1099511627776)).toBe("1.00 TB");
|
||||
});
|
||||
|
||||
it("应该处理负数", () => {
|
||||
expect(formatFileSize(-1024)).toBe("0 B");
|
||||
});
|
||||
|
||||
it("应该处理 null 和 undefined", () => {
|
||||
expect(formatFileSize(null as any)).toBe("0 B");
|
||||
expect(formatFileSize(undefined as any)).toBe("0 B");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatNumber()", () => {
|
||||
it("应该格式化整数", () => {
|
||||
expect(formatNumber(1000)).toBe("1,000");
|
||||
expect(formatNumber(1000000)).toBe("1,000,000");
|
||||
expect(formatNumber(123456789)).toBe("123,456,789");
|
||||
});
|
||||
|
||||
it("应该格式化小数", () => {
|
||||
expect(formatNumber(1000.5)).toBe("1,000.5");
|
||||
expect(formatNumber(1234.56)).toBe("1,234.56");
|
||||
});
|
||||
|
||||
it("应该处理小数位数", () => {
|
||||
expect(formatNumber(1234.5678, 2)).toBe("1,234.57");
|
||||
expect(formatNumber(1234.5, 2)).toBe("1,234.50");
|
||||
expect(formatNumber(1234, 2)).toBe("1,234.00");
|
||||
});
|
||||
|
||||
it("应该处理零", () => {
|
||||
expect(formatNumber(0)).toBe("0");
|
||||
expect(formatNumber(0, 2)).toBe("0.00");
|
||||
});
|
||||
|
||||
it("应该处理负数", () => {
|
||||
expect(formatNumber(-1000)).toBe("-1,000");
|
||||
expect(formatNumber(-1234.56, 2)).toBe("-1,234.56");
|
||||
});
|
||||
|
||||
it("应该处理 null 和 undefined", () => {
|
||||
expect(formatNumber(null as any)).toBe("0");
|
||||
expect(formatNumber(undefined as any)).toBe("0");
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatCurrency()", () => {
|
||||
it("应该格式化货币(默认人民币)", () => {
|
||||
expect(formatCurrency(1000)).toBe("¥1,000.00");
|
||||
expect(formatCurrency(1234.56)).toBe("¥1,234.56");
|
||||
expect(formatCurrency(1000000)).toBe("¥1,000,000.00");
|
||||
});
|
||||
|
||||
it("应该格式化美元", () => {
|
||||
expect(formatCurrency(1000, "USD")).toBe("$1,000.00");
|
||||
expect(formatCurrency(1234.56, "USD")).toBe("$1,234.56");
|
||||
});
|
||||
|
||||
it("应该格式化欧元", () => {
|
||||
expect(formatCurrency(1000, "EUR")).toBe("€1,000.00");
|
||||
});
|
||||
|
||||
it("应该格式化日元", () => {
|
||||
expect(formatCurrency(1000, "JPY")).toBe("¥1,000");
|
||||
});
|
||||
|
||||
it("应该处理零", () => {
|
||||
expect(formatCurrency(0)).toBe("¥0.00");
|
||||
});
|
||||
|
||||
it("应该处理负数", () => {
|
||||
expect(formatCurrency(-1000)).toBe("-¥1,000.00");
|
||||
});
|
||||
|
||||
it("应该处理 null 和 undefined", () => {
|
||||
expect(formatCurrency(null as any)).toBe("¥0.00");
|
||||
expect(formatCurrency(undefined as any)).toBe("¥0.00");
|
||||
});
|
||||
});
|
||||
});
|
||||
150
tests/unit/utils/storage.test.ts
Normal file
150
tests/unit/utils/storage.test.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import { Storage } from "@/utils/storage";
|
||||
import { STORAGE_KEYS, APP_PREFIX } from "@/constants";
|
||||
|
||||
describe("Storage 工具类", () => {
|
||||
// 每个测试前清理存储
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
});
|
||||
|
||||
// 每个测试后清理存储
|
||||
afterEach(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
});
|
||||
|
||||
describe("localStorage 操作", () => {
|
||||
it("应该能够存储和获取字符串", () => {
|
||||
Storage.set("test-key", "test-value");
|
||||
expect(Storage.get("test-key")).toBe("test-value");
|
||||
});
|
||||
|
||||
it("应该能够存储和获取对象", () => {
|
||||
const testObj = { name: "张三", age: 25 };
|
||||
Storage.set("test-obj", testObj);
|
||||
expect(Storage.get("test-obj")).toEqual(testObj);
|
||||
});
|
||||
|
||||
it("应该能够存储和获取数组", () => {
|
||||
const testArr = [1, 2, 3, 4, 5];
|
||||
Storage.set("test-arr", testArr);
|
||||
expect(Storage.get("test-arr")).toEqual(testArr);
|
||||
});
|
||||
|
||||
it("应该能够存储和获取布尔值", () => {
|
||||
Storage.set("test-bool", true);
|
||||
expect(Storage.get("test-bool")).toBe(true);
|
||||
});
|
||||
|
||||
it("获取不存在的键应该返回 undefined", () => {
|
||||
expect(Storage.get("non-existent")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("获取不存在的键应该返回默认值", () => {
|
||||
expect(Storage.get("non-existent", "default")).toBe("default");
|
||||
});
|
||||
|
||||
it("应该能够删除存储项", () => {
|
||||
Storage.set("test-key", "test-value");
|
||||
Storage.remove("test-key");
|
||||
expect(Storage.get("test-key")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("sessionStorage 操作", () => {
|
||||
it("应该能够存储和获取字符串", () => {
|
||||
Storage.sessionSet("test-key", "test-value");
|
||||
expect(Storage.sessionGet("test-key")).toBe("test-value");
|
||||
});
|
||||
|
||||
it("应该能够存储和获取对象", () => {
|
||||
const testObj = { name: "李四", age: 30 };
|
||||
Storage.sessionSet("test-obj", testObj);
|
||||
expect(Storage.sessionGet("test-obj")).toEqual(testObj);
|
||||
});
|
||||
|
||||
it("获取不存在的键应该返回默认值", () => {
|
||||
expect(Storage.sessionGet("non-existent", "default")).toBe("default");
|
||||
});
|
||||
|
||||
it("应该能够删除存储项", () => {
|
||||
Storage.sessionSet("test-key", "test-value");
|
||||
Storage.sessionRemove("test-key");
|
||||
expect(Storage.sessionGet("test-key")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("批量清理操作", () => {
|
||||
it("clear() 应该同时清理 localStorage 和 sessionStorage", () => {
|
||||
Storage.set("test-key", "local-value");
|
||||
Storage.sessionSet("test-key", "session-value");
|
||||
|
||||
Storage.clear("test-key");
|
||||
|
||||
expect(Storage.get("test-key")).toBeUndefined();
|
||||
expect(Storage.sessionGet("test-key")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("clearMultiple() 应该批量清理多个键", () => {
|
||||
Storage.set("key1", "value1");
|
||||
Storage.set("key2", "value2");
|
||||
Storage.sessionSet("key1", "session1");
|
||||
|
||||
Storage.clearMultiple(["key1", "key2"]);
|
||||
|
||||
expect(Storage.get("key1")).toBeUndefined();
|
||||
expect(Storage.get("key2")).toBeUndefined();
|
||||
expect(Storage.sessionGet("key1")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("clearByPrefix() 应该清理指定前缀的所有存储项", () => {
|
||||
Storage.set(`${APP_PREFIX}:auth:token`, "token123");
|
||||
Storage.set(`${APP_PREFIX}:auth:user`, "user123");
|
||||
Storage.set(`${APP_PREFIX}:ui:theme`, "dark");
|
||||
Storage.set("other:key", "other-value");
|
||||
|
||||
Storage.clearByPrefix(`${APP_PREFIX}:auth:`);
|
||||
|
||||
expect(Storage.get(`${APP_PREFIX}:auth:token`)).toBeUndefined();
|
||||
expect(Storage.get(`${APP_PREFIX}:auth:user`)).toBeUndefined();
|
||||
expect(Storage.get(`${APP_PREFIX}:ui:theme`)).toBe("dark");
|
||||
expect(Storage.get("other:key")).toBe("other-value");
|
||||
});
|
||||
|
||||
it("clearAllProject() 应该清理所有项目相关的存储", () => {
|
||||
Storage.set(STORAGE_KEYS.ACCESS_TOKEN, "token123");
|
||||
Storage.set(STORAGE_KEYS.THEME, "dark");
|
||||
Storage.set("other:key", "other-value");
|
||||
|
||||
Storage.clearAllProject();
|
||||
|
||||
expect(Storage.get(STORAGE_KEYS.ACCESS_TOKEN)).toBeUndefined();
|
||||
expect(Storage.get(STORAGE_KEYS.THEME)).toBeUndefined();
|
||||
expect(Storage.get("other:key")).toBe("other-value");
|
||||
});
|
||||
});
|
||||
|
||||
describe("边界情况", () => {
|
||||
it("应该能够处理 null 值", () => {
|
||||
Storage.set("test-null", null);
|
||||
expect(Storage.get("test-null")).toBeNull();
|
||||
});
|
||||
|
||||
it("应该能够处理空字符串", () => {
|
||||
Storage.set("test-empty", "");
|
||||
expect(Storage.get("test-empty")).toBe("");
|
||||
});
|
||||
|
||||
it("应该能够处理数字 0", () => {
|
||||
Storage.set("test-zero", 0);
|
||||
expect(Storage.get("test-zero")).toBe(0);
|
||||
});
|
||||
|
||||
it("应该能够处理 false", () => {
|
||||
Storage.set("test-false", false);
|
||||
expect(Storage.get("test-false")).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
177
tests/unit/utils/validate.test.ts
Normal file
177
tests/unit/utils/validate.test.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { isExternal, isValidURL, isEmail, isMobile, VALIDATORS } from "@/utils/validate";
|
||||
|
||||
describe("Validate 工具函数", () => {
|
||||
describe("isExternal()", () => {
|
||||
it("应该识别外部链接", () => {
|
||||
expect(isExternal("https://www.example.com")).toBe(true);
|
||||
expect(isExternal("http://example.com")).toBe(true);
|
||||
expect(isExternal("//example.com")).toBe(true);
|
||||
expect(isExternal("mailto:test@example.com")).toBe(true);
|
||||
expect(isExternal("tel:1234567890")).toBe(true);
|
||||
});
|
||||
|
||||
it("应该识别内部链接", () => {
|
||||
expect(isExternal("/dashboard")).toBe(false);
|
||||
expect(isExternal("dashboard")).toBe(false);
|
||||
expect(isExternal("./dashboard")).toBe(false);
|
||||
expect(isExternal("../dashboard")).toBe(false);
|
||||
});
|
||||
|
||||
it("应该处理空字符串", () => {
|
||||
expect(isExternal("")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isValidURL()", () => {
|
||||
it("应该验证有效的 URL", () => {
|
||||
expect(isValidURL("https://www.example.com")).toBe(true);
|
||||
expect(isValidURL("http://example.com")).toBe(true);
|
||||
expect(isValidURL("https://example.com/path?query=1")).toBe(true);
|
||||
expect(isValidURL("http://localhost:3000")).toBe(true);
|
||||
});
|
||||
|
||||
it("应该拒绝无效的 URL", () => {
|
||||
expect(isValidURL("not-a-url")).toBe(false);
|
||||
expect(isValidURL("//example.com")).toBe(false);
|
||||
expect(isValidURL("/path")).toBe(false);
|
||||
expect(isValidURL("")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isEmail()", () => {
|
||||
it("应该验证有效的邮箱", () => {
|
||||
expect(isEmail("test@example.com")).toBe(true);
|
||||
expect(isEmail("user.name@example.com")).toBe(true);
|
||||
expect(isEmail("user+tag@example.co.uk")).toBe(true);
|
||||
expect(isEmail("123@example.com")).toBe(true);
|
||||
});
|
||||
|
||||
it("应该拒绝无效的邮箱", () => {
|
||||
expect(isEmail("invalid")).toBe(false);
|
||||
expect(isEmail("@example.com")).toBe(false);
|
||||
expect(isEmail("user@")).toBe(false);
|
||||
expect(isEmail("user @example.com")).toBe(false);
|
||||
expect(isEmail("")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isMobile()", () => {
|
||||
it("应该验证有效的手机号", () => {
|
||||
expect(isMobile("13800138000")).toBe(true);
|
||||
expect(isMobile("15912345678")).toBe(true);
|
||||
expect(isMobile("18612345678")).toBe(true);
|
||||
expect(isMobile("19912345678")).toBe(true);
|
||||
});
|
||||
|
||||
it("应该拒绝无效的手机号", () => {
|
||||
expect(isMobile("12345678901")).toBe(false); // 不是 1 开头的有效号段
|
||||
expect(isMobile("1381234567")).toBe(false); // 少于 11 位
|
||||
expect(isMobile("138123456789")).toBe(false); // 多于 11 位
|
||||
expect(isMobile("abcdefghijk")).toBe(false); // 非数字
|
||||
expect(isMobile("")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("VALIDATORS 对象", () => {
|
||||
describe("required 验证器", () => {
|
||||
it("应该验证必填项", () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
VALIDATORS.required({}, "test", callback);
|
||||
expect(callback).toHaveBeenCalledWith(new Error("此项为必填项"));
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.required({}, "", callback);
|
||||
expect(callback).toHaveBeenCalledWith(new Error("此项为必填项"));
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.required({}, null, callback);
|
||||
expect(callback).toHaveBeenCalledWith(new Error("此项为必填项"));
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.required({}, undefined, callback);
|
||||
expect(callback).toHaveBeenCalledWith(new Error("此项为必填项"));
|
||||
});
|
||||
|
||||
it("应该通过有效值", () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
VALIDATORS.required({}, "value", callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.required({}, 0, callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.required({}, false, callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe("email 验证器", () => {
|
||||
it("应该验证邮箱格式", () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
VALIDATORS.email({}, "invalid", callback);
|
||||
expect(callback).toHaveBeenCalledWith(new Error("请输入正确的邮箱地址"));
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.email({}, "test@example.com", callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it("应该允许空值", () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
VALIDATORS.email({}, "", callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.email({}, null, callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe("mobile 验证器", () => {
|
||||
it("应该验证手机号格式", () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
VALIDATORS.mobile({}, "12345678901", callback);
|
||||
expect(callback).toHaveBeenCalledWith(new Error("请输入正确的手机号码"));
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.mobile({}, "13800138000", callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it("应该允许空值", () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
VALIDATORS.mobile({}, "", callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
|
||||
describe("url 验证器", () => {
|
||||
it("应该验证 URL 格式", () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
VALIDATORS.url({}, "not-a-url", callback);
|
||||
expect(callback).toHaveBeenCalledWith(new Error("请输入正确的 URL 地址"));
|
||||
|
||||
callback.mockClear();
|
||||
VALIDATORS.url({}, "https://example.com", callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it("应该允许空值", () => {
|
||||
const callback = vi.fn();
|
||||
|
||||
VALIDATORS.url({}, "", callback);
|
||||
expect(callback).toHaveBeenCalledWith();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user