fix: improve the scroll bar flashing when the modal box is opened (#4438)
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`defaultPreferences immutability test > should not modify the config object 1`] = `
|
||||
{
|
||||
"app": {
|
||||
"accessMode": "frontend",
|
||||
"authPageLayout": "panel-right",
|
||||
"checkUpdatesInterval": 1,
|
||||
"colorGrayMode": false,
|
||||
"colorWeakMode": false,
|
||||
"compact": false,
|
||||
"contentCompact": "wide",
|
||||
"defaultAvatar": "https://unpkg.com/@vbenjs/static-source@0.1.6/source/avatar-v1.webp",
|
||||
"dynamicTitle": true,
|
||||
"enableCheckUpdates": true,
|
||||
"enablePreferences": true,
|
||||
"enableRefreshToken": false,
|
||||
"isMobile": false,
|
||||
"layout": "sidebar-nav",
|
||||
"locale": "zh-CN",
|
||||
"loginExpiredMode": "page",
|
||||
"name": "Vben Admin",
|
||||
"preferencesButtonPosition": "auto",
|
||||
"watermark": false,
|
||||
},
|
||||
"breadcrumb": {
|
||||
"enable": true,
|
||||
"hideOnlyOne": false,
|
||||
"showHome": false,
|
||||
"showIcon": true,
|
||||
"styleType": "normal",
|
||||
},
|
||||
"copyright": {
|
||||
"companyName": "Vben",
|
||||
"companySiteLink": "https://www.vben.pro",
|
||||
"date": "2024",
|
||||
"enable": true,
|
||||
"icp": "",
|
||||
"icpLink": "",
|
||||
},
|
||||
"footer": {
|
||||
"enable": true,
|
||||
"fixed": false,
|
||||
},
|
||||
"header": {
|
||||
"enable": true,
|
||||
"hidden": false,
|
||||
"mode": "fixed",
|
||||
},
|
||||
"logo": {
|
||||
"enable": true,
|
||||
"source": "https://unpkg.com/@vbenjs/static-source@0.1.6/source/logo-v1.webp",
|
||||
},
|
||||
"navigation": {
|
||||
"accordion": true,
|
||||
"split": true,
|
||||
"styleType": "rounded",
|
||||
},
|
||||
"shortcutKeys": {
|
||||
"enable": true,
|
||||
"globalLockScreen": true,
|
||||
"globalLogout": true,
|
||||
"globalPreferences": true,
|
||||
"globalSearch": true,
|
||||
},
|
||||
"sidebar": {
|
||||
"collapsed": false,
|
||||
"collapsedShowTitle": false,
|
||||
"enable": true,
|
||||
"expandOnHover": true,
|
||||
"extraCollapse": true,
|
||||
"hidden": false,
|
||||
"width": 224,
|
||||
},
|
||||
"tabbar": {
|
||||
"dragable": true,
|
||||
"enable": true,
|
||||
"height": 38,
|
||||
"keepAlive": true,
|
||||
"persist": true,
|
||||
"showIcon": true,
|
||||
"showMaximize": true,
|
||||
"showMore": true,
|
||||
"showRefresh": true,
|
||||
"styleType": "chrome",
|
||||
},
|
||||
"theme": {
|
||||
"builtinType": "default",
|
||||
"colorDestructive": "hsl(348 100% 61%)",
|
||||
"colorPrimary": "hsl(212 100% 45%)",
|
||||
"colorSuccess": "hsl(144 57% 58%)",
|
||||
"colorWarning": "hsl(42 84% 61%)",
|
||||
"mode": "dark",
|
||||
"radius": "0.5",
|
||||
"semiDarkHeader": false,
|
||||
"semiDarkSidebar": true,
|
||||
},
|
||||
"transition": {
|
||||
"enable": true,
|
||||
"loading": true,
|
||||
"name": "fade-slide",
|
||||
"progress": true,
|
||||
},
|
||||
"widget": {
|
||||
"fullscreen": true,
|
||||
"globalSearch": true,
|
||||
"languageToggle": true,
|
||||
"lockScreen": true,
|
||||
"notification": true,
|
||||
"sidebarToggle": true,
|
||||
"themeToggle": true,
|
||||
},
|
||||
}
|
||||
`;
|
10
packages/@core/preferences/__tests__/config.test.ts
Normal file
10
packages/@core/preferences/__tests__/config.test.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { defaultPreferences } from '../src/config';
|
||||
|
||||
describe('defaultPreferences immutability test', () => {
|
||||
// 创建快照,确保默认配置对象不被修改
|
||||
it('should not modify the config object', () => {
|
||||
expect(defaultPreferences).toMatchSnapshot();
|
||||
});
|
||||
});
|
253
packages/@core/preferences/__tests__/preferences.test.ts
Normal file
253
packages/@core/preferences/__tests__/preferences.test.ts
Normal file
@@ -0,0 +1,253 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { defaultPreferences } from '../src/config';
|
||||
import { PreferenceManager } from '../src/preferences';
|
||||
import { isDarkTheme } from '../src/update-css-variables';
|
||||
|
||||
describe('preferences', () => {
|
||||
let preferenceManager: PreferenceManager;
|
||||
|
||||
// 模拟 window.matchMedia 方法
|
||||
vi.stubGlobal(
|
||||
'matchMedia',
|
||||
vi.fn().mockImplementation((query) => ({
|
||||
addEventListener: vi.fn(),
|
||||
addListener: vi.fn(), // Deprecated
|
||||
dispatchEvent: vi.fn(),
|
||||
matches: query === '(prefers-color-scheme: dark)',
|
||||
media: query,
|
||||
onchange: null,
|
||||
removeEventListener: vi.fn(),
|
||||
removeListener: vi.fn(), // Deprecated
|
||||
})),
|
||||
);
|
||||
beforeEach(() => {
|
||||
preferenceManager = new PreferenceManager();
|
||||
});
|
||||
|
||||
it('loads default preferences if no saved preferences found', () => {
|
||||
const preferences = preferenceManager.getPreferences();
|
||||
expect(preferences).toEqual(defaultPreferences);
|
||||
});
|
||||
|
||||
it('initializes preferences with overrides', async () => {
|
||||
const overrides: any = {
|
||||
app: {
|
||||
locale: 'en-US',
|
||||
},
|
||||
};
|
||||
await preferenceManager.initPreferences({
|
||||
namespace: 'testNamespace',
|
||||
overrides,
|
||||
});
|
||||
|
||||
// 等待防抖动操作完成
|
||||
// await new Promise((resolve) => setTimeout(resolve, 300)); // 等待100毫秒
|
||||
|
||||
const expected = {
|
||||
...defaultPreferences,
|
||||
app: {
|
||||
...defaultPreferences.app,
|
||||
...overrides.app,
|
||||
},
|
||||
};
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(expected);
|
||||
});
|
||||
|
||||
it('updates theme mode correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().theme.mode).toBe('light');
|
||||
});
|
||||
|
||||
it('updates color modes correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { colorGrayMode: true, colorWeakMode: true },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.colorGrayMode).toBe(true);
|
||||
expect(preferenceManager.getPreferences().app.colorWeakMode).toBe(true);
|
||||
});
|
||||
|
||||
it('resets preferences to default', () => {
|
||||
// 先更新一些偏好设置
|
||||
preferenceManager.updatePreferences({
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 然后重置偏好设置
|
||||
preferenceManager.resetPreferences();
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(defaultPreferences);
|
||||
});
|
||||
|
||||
it('updates isMobile correctly', () => {
|
||||
// 模拟移动端状态
|
||||
vi.stubGlobal(
|
||||
'matchMedia',
|
||||
vi.fn().mockImplementation((query) => ({
|
||||
addEventListener: vi.fn(),
|
||||
addListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
matches: query === '(max-width: 768px)',
|
||||
media: query,
|
||||
onchange: null,
|
||||
removeEventListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
})),
|
||||
);
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { isMobile: true },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.isMobile).toBe(true);
|
||||
});
|
||||
|
||||
it('updates the locale preference correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: 'en-US' },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
|
||||
});
|
||||
|
||||
it('updates the sidebar width correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
sidebar: { width: 200 },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().sidebar.width).toBe(200);
|
||||
});
|
||||
it('updates the sidebar collapse state correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
sidebar: { collapsed: true },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().sidebar.collapsed).toBe(true);
|
||||
});
|
||||
it('updates the navigation style type correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
navigation: { styleType: 'flat' },
|
||||
} as any);
|
||||
|
||||
expect(preferenceManager.getPreferences().navigation.styleType).toBe(
|
||||
'flat',
|
||||
);
|
||||
});
|
||||
|
||||
it('resets preferences to default correctly', () => {
|
||||
// 先更新一些偏好设置
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: 'en-US' },
|
||||
sidebar: { collapsed: true, width: 200 },
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 然后重置偏好设置
|
||||
preferenceManager.resetPreferences();
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(defaultPreferences);
|
||||
});
|
||||
|
||||
it('does not update undefined preferences', () => {
|
||||
const originalPreferences = preferenceManager.getPreferences();
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { nonexistentField: 'value' },
|
||||
} as any);
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(originalPreferences);
|
||||
});
|
||||
|
||||
it('reverts to default when a preference field is deleted', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: 'en-US' },
|
||||
});
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: undefined },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
|
||||
});
|
||||
|
||||
it('ignores updates with invalid preference value types', () => {
|
||||
const originalPreferences = preferenceManager.getPreferences();
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { isMobile: 'true' as unknown as boolean }, // 错误类型
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(originalPreferences);
|
||||
});
|
||||
|
||||
it('merges nested preference objects correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { name: 'New App Name' },
|
||||
});
|
||||
|
||||
const expected = {
|
||||
...defaultPreferences,
|
||||
app: {
|
||||
...defaultPreferences.app,
|
||||
name: 'New App Name',
|
||||
},
|
||||
};
|
||||
|
||||
expect(preferenceManager.getPreferences()).toEqual(expected);
|
||||
});
|
||||
|
||||
it('applies updates immediately after initialization', async () => {
|
||||
const overrides: any = {
|
||||
app: {
|
||||
locale: 'en-US',
|
||||
},
|
||||
};
|
||||
|
||||
await preferenceManager.initPreferences(overrides);
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
theme: { mode: 'light' },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().theme.mode).toBe('light');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDarkTheme', () => {
|
||||
it('should return true for dark theme', () => {
|
||||
expect(isDarkTheme('dark')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for light theme', () => {
|
||||
expect(isDarkTheme('light')).toBe(false);
|
||||
});
|
||||
|
||||
it('should return system preference for auto theme', () => {
|
||||
vi.spyOn(window, 'matchMedia').mockImplementation((query) => ({
|
||||
addEventListener: vi.fn(),
|
||||
addListener: vi.fn(), // Deprecated
|
||||
dispatchEvent: vi.fn(),
|
||||
matches: query === '(prefers-color-scheme: dark)',
|
||||
media: query,
|
||||
onchange: null,
|
||||
removeEventListener: vi.fn(),
|
||||
removeListener: vi.fn(), // Deprecated
|
||||
}));
|
||||
|
||||
expect(isDarkTheme('auto')).toBe(true);
|
||||
expect(window.matchMedia).toHaveBeenCalledWith(
|
||||
'(prefers-color-scheme: dark)',
|
||||
);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user