feat: 项目结构重构优化
This commit is contained in:
141
tests/unit/store/app.test.ts
Normal file
141
tests/unit/store/app.test.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { useAppStore } from "@/store/modules/app";
|
||||
import { DeviceEnum, SidebarStatus } from "@/enums";
|
||||
|
||||
describe("useAppStore", () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
describe("侧边栏状态", () => {
|
||||
it("应该切换侧边栏状态", () => {
|
||||
const store = useAppStore();
|
||||
const initialState = store.sidebar.opened;
|
||||
|
||||
store.toggleSidebar();
|
||||
expect(store.sidebar.opened).toBe(!initialState);
|
||||
|
||||
store.toggleSidebar();
|
||||
expect(store.sidebar.opened).toBe(initialState);
|
||||
});
|
||||
|
||||
it("应该关闭侧边栏", () => {
|
||||
const store = useAppStore();
|
||||
store.openSideBar();
|
||||
expect(store.sidebar.opened).toBe(true);
|
||||
|
||||
store.closeSideBar();
|
||||
expect(store.sidebar.opened).toBe(false);
|
||||
});
|
||||
|
||||
it("应该打开侧边栏", () => {
|
||||
const store = useAppStore();
|
||||
store.closeSideBar();
|
||||
expect(store.sidebar.opened).toBe(false);
|
||||
|
||||
store.openSideBar();
|
||||
expect(store.sidebar.opened).toBe(true);
|
||||
});
|
||||
|
||||
it("应该持久化侧边栏状态", () => {
|
||||
const store = useAppStore();
|
||||
store.openSideBar();
|
||||
|
||||
// 创建新的 store 实例模拟页面刷新
|
||||
const newStore = useAppStore();
|
||||
expect(newStore.sidebar.opened).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("设备类型", () => {
|
||||
it("应该切换设备类型", () => {
|
||||
const store = useAppStore();
|
||||
|
||||
store.toggleDevice(DeviceEnum.MOBILE);
|
||||
expect(store.device).toBe(DeviceEnum.MOBILE);
|
||||
|
||||
store.toggleDevice(DeviceEnum.DESKTOP);
|
||||
expect(store.device).toBe(DeviceEnum.DESKTOP);
|
||||
});
|
||||
|
||||
it("应该持久化设备类型", () => {
|
||||
const store = useAppStore();
|
||||
store.toggleDevice(DeviceEnum.MOBILE);
|
||||
|
||||
const newStore = useAppStore();
|
||||
expect(newStore.device).toBe(DeviceEnum.MOBILE);
|
||||
});
|
||||
});
|
||||
|
||||
describe("组件尺寸", () => {
|
||||
it("应该修改组件尺寸", () => {
|
||||
const store = useAppStore();
|
||||
|
||||
store.changeSize("large");
|
||||
expect(store.size).toBe("large");
|
||||
|
||||
store.changeSize("small");
|
||||
expect(store.size).toBe("small");
|
||||
});
|
||||
|
||||
it("应该持久化组件尺寸", () => {
|
||||
const store = useAppStore();
|
||||
store.changeSize("large");
|
||||
|
||||
const newStore = useAppStore();
|
||||
expect(newStore.size).toBe("large");
|
||||
});
|
||||
});
|
||||
|
||||
describe("语言设置", () => {
|
||||
it("应该修改语言", () => {
|
||||
const store = useAppStore();
|
||||
|
||||
store.changeLanguage("en");
|
||||
expect(store.language).toBe("en");
|
||||
|
||||
store.changeLanguage("zh-cn");
|
||||
expect(store.language).toBe("zh-cn");
|
||||
});
|
||||
|
||||
it("应该根据语言返回正确的 locale", () => {
|
||||
const store = useAppStore();
|
||||
|
||||
store.changeLanguage("en");
|
||||
expect(store.locale).toBeDefined();
|
||||
|
||||
store.changeLanguage("zh-cn");
|
||||
expect(store.locale).toBeDefined();
|
||||
});
|
||||
|
||||
it("应该持久化语言设置", () => {
|
||||
const store = useAppStore();
|
||||
store.changeLanguage("en");
|
||||
|
||||
const newStore = useAppStore();
|
||||
expect(newStore.language).toBe("en");
|
||||
});
|
||||
});
|
||||
|
||||
describe("顶部菜单", () => {
|
||||
it("应该激活顶部菜单", () => {
|
||||
const store = useAppStore();
|
||||
|
||||
store.activeTopMenu("/dashboard");
|
||||
expect(store.activeTopMenuPath).toBe("/dashboard");
|
||||
|
||||
store.activeTopMenu("/system");
|
||||
expect(store.activeTopMenuPath).toBe("/system");
|
||||
});
|
||||
|
||||
it("应该持久化顶部菜单路径", () => {
|
||||
const store = useAppStore();
|
||||
store.activeTopMenu("/dashboard");
|
||||
|
||||
const newStore = useAppStore();
|
||||
expect(newStore.activeTopMenuPath).toBe("/dashboard");
|
||||
});
|
||||
});
|
||||
});
|
||||
134
tests/unit/store/dict.test.ts
Normal file
134
tests/unit/store/dict.test.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { useDictStore } from "@/store/modules/dict";
|
||||
import DictAPI from "@/api/system/dict";
|
||||
import type { DictItemOption } from "@/types/api";
|
||||
|
||||
// Mock DictAPI
|
||||
vi.mock("@/api/system/dict", () => ({
|
||||
default: {
|
||||
getDictItems: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("useDictStore", () => {
|
||||
beforeEach(() => {
|
||||
// 创建新的 Pinia 实例
|
||||
setActivePinia(createPinia());
|
||||
// 清理 localStorage
|
||||
localStorage.clear();
|
||||
// 重置所有 mock
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("字典缓存", () => {
|
||||
it("应该缓存字典数据", async () => {
|
||||
const store = useDictStore();
|
||||
const mockData: DictItemOption[] = [
|
||||
{ value: "1", label: "选项1" },
|
||||
{ value: "2", label: "选项2" },
|
||||
];
|
||||
|
||||
vi.mocked(DictAPI.getDictItems).mockResolvedValue(mockData);
|
||||
|
||||
await store.loadDictItems("test_dict");
|
||||
|
||||
expect(store.getDictItems("test_dict")).toEqual(mockData);
|
||||
expect(DictAPI.getDictItems).toHaveBeenCalledWith("test_dict");
|
||||
expect(DictAPI.getDictItems).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("应该从缓存中获取字典数据,不重复请求", async () => {
|
||||
const store = useDictStore();
|
||||
const mockData: DictItemOption[] = [{ value: "1", label: "选项1" }];
|
||||
|
||||
vi.mocked(DictAPI.getDictItems).mockResolvedValue(mockData);
|
||||
|
||||
// 第一次加载
|
||||
await store.loadDictItems("test_dict");
|
||||
// 第二次加载(应该从缓存获取)
|
||||
await store.loadDictItems("test_dict");
|
||||
|
||||
expect(DictAPI.getDictItems).toHaveBeenCalledTimes(1);
|
||||
expect(store.getDictItems("test_dict")).toEqual(mockData);
|
||||
});
|
||||
|
||||
it("应该防止并发重复请求", async () => {
|
||||
const store = useDictStore();
|
||||
const mockData: DictItemOption[] = [{ value: "1", label: "选项1" }];
|
||||
|
||||
vi.mocked(DictAPI.getDictItems).mockResolvedValue(mockData);
|
||||
|
||||
// 同时发起多个请求
|
||||
const promises = [
|
||||
store.loadDictItems("test_dict"),
|
||||
store.loadDictItems("test_dict"),
|
||||
store.loadDictItems("test_dict"),
|
||||
];
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
// 应该只请求一次
|
||||
expect(DictAPI.getDictItems).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("字典操作", () => {
|
||||
it("应该返回空数组当字典不存在时", () => {
|
||||
const store = useDictStore();
|
||||
expect(store.getDictItems("non_exist")).toEqual([]);
|
||||
});
|
||||
|
||||
it("应该移除指定字典项", async () => {
|
||||
const store = useDictStore();
|
||||
const mockData: DictItemOption[] = [{ value: "1", label: "选项1" }];
|
||||
|
||||
vi.mocked(DictAPI.getDictItems).mockResolvedValue(mockData);
|
||||
|
||||
await store.loadDictItems("test_dict");
|
||||
expect(store.getDictItems("test_dict")).toEqual(mockData);
|
||||
|
||||
store.removeDictItem("test_dict");
|
||||
expect(store.getDictItems("test_dict")).toEqual([]);
|
||||
});
|
||||
|
||||
it("应该清空所有字典缓存", async () => {
|
||||
const store = useDictStore();
|
||||
const mockData1: DictItemOption[] = [{ value: "1", label: "选项1" }];
|
||||
const mockData2: DictItemOption[] = [{ value: "2", label: "选项2" }];
|
||||
|
||||
vi.mocked(DictAPI.getDictItems)
|
||||
.mockResolvedValueOnce(mockData1)
|
||||
.mockResolvedValueOnce(mockData2);
|
||||
|
||||
await store.loadDictItems("dict1");
|
||||
await store.loadDictItems("dict2");
|
||||
|
||||
expect(store.getDictItems("dict1")).toEqual(mockData1);
|
||||
expect(store.getDictItems("dict2")).toEqual(mockData2);
|
||||
|
||||
store.clearDictCache();
|
||||
|
||||
expect(store.getDictItems("dict1")).toEqual([]);
|
||||
expect(store.getDictItems("dict2")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("错误处理", () => {
|
||||
it("应该处理请求失败的情况", async () => {
|
||||
const store = useDictStore();
|
||||
const error = new Error("Network error");
|
||||
|
||||
vi.mocked(DictAPI.getDictItems).mockRejectedValue(error);
|
||||
|
||||
await expect(store.loadDictItems("test_dict")).rejects.toThrow("Network error");
|
||||
|
||||
// 失败后应该允许重试
|
||||
const mockData: DictItemOption[] = [{ value: "1", label: "选项1" }];
|
||||
vi.mocked(DictAPI.getDictItems).mockResolvedValue(mockData);
|
||||
|
||||
await store.loadDictItems("test_dict");
|
||||
expect(store.getDictItems("test_dict")).toEqual(mockData);
|
||||
});
|
||||
});
|
||||
});
|
||||
186
tests/unit/store/settings.test.ts
Normal file
186
tests/unit/store/settings.test.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { useSettingsStore } from "@/store/modules/settings";
|
||||
import { ThemeMode, SidebarColor } from "@/enums";
|
||||
|
||||
// Mock theme utils
|
||||
vi.mock("@/utils/theme", () => ({
|
||||
applyTheme: vi.fn(),
|
||||
generateThemeColors: vi.fn(() => ({})),
|
||||
toggleDarkMode: vi.fn(),
|
||||
toggleSidebarColor: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("useSettingsStore", () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
localStorage.clear();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("界面显示设置", () => {
|
||||
it("应该切换标签页显示", () => {
|
||||
const store = useSettingsStore();
|
||||
const initial = store.showTagsView;
|
||||
|
||||
store.showTagsView = !initial;
|
||||
expect(store.showTagsView).toBe(!initial);
|
||||
});
|
||||
|
||||
it("应该切换 Logo 显示", () => {
|
||||
const store = useSettingsStore();
|
||||
const initial = store.showAppLogo;
|
||||
|
||||
store.showAppLogo = !initial;
|
||||
expect(store.showAppLogo).toBe(!initial);
|
||||
});
|
||||
|
||||
it("应该切换水印显示", () => {
|
||||
const store = useSettingsStore();
|
||||
const initial = store.showWatermark;
|
||||
|
||||
store.showWatermark = !initial;
|
||||
expect(store.showWatermark).toBe(!initial);
|
||||
});
|
||||
|
||||
it("应该持久化界面显示设置", () => {
|
||||
const store = useSettingsStore();
|
||||
store.showTagsView = false;
|
||||
store.showAppLogo = false;
|
||||
store.showWatermark = true;
|
||||
|
||||
const newStore = useSettingsStore();
|
||||
expect(newStore.showTagsView).toBe(false);
|
||||
expect(newStore.showAppLogo).toBe(false);
|
||||
expect(newStore.showWatermark).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("主题设置", () => {
|
||||
it("应该切换主题模式", () => {
|
||||
const store = useSettingsStore();
|
||||
|
||||
store.theme = ThemeMode.DARK;
|
||||
expect(store.theme).toBe(ThemeMode.DARK);
|
||||
|
||||
store.theme = ThemeMode.LIGHT;
|
||||
expect(store.theme).toBe(ThemeMode.LIGHT);
|
||||
});
|
||||
|
||||
it("应该修改主题颜色", () => {
|
||||
const store = useSettingsStore();
|
||||
|
||||
store.themeColor = "#409EFF";
|
||||
expect(store.themeColor).toBe("#409EFF");
|
||||
|
||||
store.themeColor = "#67C23A";
|
||||
expect(store.themeColor).toBe("#67C23A");
|
||||
});
|
||||
|
||||
it("应该持久化主题设置", () => {
|
||||
const store = useSettingsStore();
|
||||
store.theme = ThemeMode.DARK;
|
||||
store.themeColor = "#409EFF";
|
||||
|
||||
const newStore = useSettingsStore();
|
||||
expect(newStore.theme).toBe(ThemeMode.DARK);
|
||||
expect(newStore.themeColor).toBe("#409EFF");
|
||||
});
|
||||
});
|
||||
|
||||
describe("侧边栏配色", () => {
|
||||
it("应该切换侧边栏配色方案", () => {
|
||||
const store = useSettingsStore();
|
||||
|
||||
store.sidebarColorScheme = SidebarColor.CLASSIC_BLUE;
|
||||
expect(store.sidebarColorScheme).toBe(SidebarColor.CLASSIC_BLUE);
|
||||
|
||||
store.sidebarColorScheme = SidebarColor.THEME_COLOR;
|
||||
expect(store.sidebarColorScheme).toBe(SidebarColor.THEME_COLOR);
|
||||
});
|
||||
|
||||
it("应该持久化侧边栏配色", () => {
|
||||
const store = useSettingsStore();
|
||||
store.sidebarColorScheme = SidebarColor.CLASSIC_BLUE;
|
||||
|
||||
const newStore = useSettingsStore();
|
||||
expect(newStore.sidebarColorScheme).toBe(SidebarColor.CLASSIC_BLUE);
|
||||
});
|
||||
});
|
||||
|
||||
describe("特殊模式", () => {
|
||||
it("应该切换灰色模式", () => {
|
||||
const store = useSettingsStore();
|
||||
|
||||
store.grayMode = true;
|
||||
expect(store.grayMode).toBe(true);
|
||||
|
||||
store.grayMode = false;
|
||||
expect(store.grayMode).toBe(false);
|
||||
});
|
||||
|
||||
it("应该切换色弱模式", () => {
|
||||
const store = useSettingsStore();
|
||||
|
||||
store.colorWeak = true;
|
||||
expect(store.colorWeak).toBe(true);
|
||||
|
||||
store.colorWeak = false;
|
||||
expect(store.colorWeak).toBe(false);
|
||||
});
|
||||
|
||||
it("应该持久化特殊模式", () => {
|
||||
const store = useSettingsStore();
|
||||
store.grayMode = true;
|
||||
store.colorWeak = true;
|
||||
|
||||
const newStore = useSettingsStore();
|
||||
expect(newStore.grayMode).toBe(true);
|
||||
expect(newStore.colorWeak).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("AI 助手", () => {
|
||||
it("应该切换 AI 助手", () => {
|
||||
const store = useSettingsStore();
|
||||
|
||||
store.userEnableAi = true;
|
||||
expect(store.userEnableAi).toBe(true);
|
||||
|
||||
store.userEnableAi = false;
|
||||
expect(store.userEnableAi).toBe(false);
|
||||
});
|
||||
|
||||
it("应该持久化 AI 助手设置", () => {
|
||||
const store = useSettingsStore();
|
||||
store.userEnableAi = true;
|
||||
|
||||
const newStore = useSettingsStore();
|
||||
expect(newStore.userEnableAi).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("重置设置", () => {
|
||||
it("应该重置所有设置为默认值", () => {
|
||||
const store = useSettingsStore();
|
||||
|
||||
// 修改所有设置
|
||||
store.showTagsView = false;
|
||||
store.showAppLogo = false;
|
||||
store.showWatermark = false;
|
||||
store.userEnableAi = true;
|
||||
store.grayMode = true;
|
||||
store.colorWeak = true;
|
||||
store.theme = ThemeMode.DARK;
|
||||
store.themeColor = "#409EFF";
|
||||
|
||||
// 重置
|
||||
store.resetSettings();
|
||||
|
||||
// 验证已重置(具体值取决于 defaults)
|
||||
expect(store.userEnableAi).toBe(false);
|
||||
expect(store.grayMode).toBe(false);
|
||||
expect(store.colorWeak).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user