preferences.test.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. import { beforeEach, describe, expect, it, vi } from 'vitest';
  2. import { defaultPreferences } from './config';
  3. import { PreferenceManager, isDarkTheme } from './preferences';
  4. describe('preferences', () => {
  5. let preferenceManager: PreferenceManager;
  6. vi.mock('@vben-core/cache', () => {
  7. return {
  8. StorageManager: vi.fn().mockImplementation(() => {
  9. return {
  10. getItem: vi.fn(),
  11. removeItem: vi.fn(),
  12. setItem: vi.fn(),
  13. };
  14. }),
  15. };
  16. });
  17. // 模拟 window.matchMedia 方法
  18. vi.stubGlobal(
  19. 'matchMedia',
  20. vi.fn().mockImplementation((query) => ({
  21. addEventListener: vi.fn(),
  22. addListener: vi.fn(), // Deprecated
  23. dispatchEvent: vi.fn(),
  24. matches: query === '(prefers-color-scheme: dark)',
  25. media: query,
  26. onchange: null,
  27. removeEventListener: vi.fn(),
  28. removeListener: vi.fn(), // Deprecated
  29. })),
  30. );
  31. beforeEach(() => {
  32. preferenceManager = new PreferenceManager();
  33. });
  34. it('initPreferences should initialize preferences with overrides and namespace', async () => {
  35. const overrides = { theme: { colorPrimary: 'hsl(211 91% 39%)' } };
  36. const namespace = 'testNamespace';
  37. await preferenceManager.initPreferences({ namespace, overrides });
  38. expect(preferenceManager.getPreferences().theme.colorPrimary).toBe(
  39. overrides.theme.colorPrimary,
  40. );
  41. });
  42. it('loads default preferences if no saved preferences found', () => {
  43. const preferences = preferenceManager.getPreferences();
  44. expect(preferences).toEqual(defaultPreferences);
  45. });
  46. it('initializes preferences with overrides', async () => {
  47. const overrides: any = {
  48. app: {
  49. locale: 'en-US',
  50. },
  51. };
  52. await preferenceManager.initPreferences({
  53. namespace: 'testNamespace',
  54. overrides,
  55. });
  56. // 等待防抖动操作完成
  57. // await new Promise((resolve) => setTimeout(resolve, 300)); // 等待100毫秒
  58. const expected = {
  59. ...defaultPreferences,
  60. app: {
  61. ...defaultPreferences.app,
  62. ...overrides.app,
  63. },
  64. };
  65. expect(preferenceManager.getPreferences()).toEqual(expected);
  66. });
  67. it('updates theme mode correctly', () => {
  68. preferenceManager.updatePreferences({
  69. theme: {
  70. mode: 'light',
  71. },
  72. });
  73. expect(preferenceManager.getPreferences().theme.mode).toBe('light');
  74. });
  75. it('updates color modes correctly', () => {
  76. preferenceManager.updatePreferences({
  77. app: { colorGrayMode: true, colorWeakMode: true },
  78. });
  79. expect(preferenceManager.getPreferences().app.colorGrayMode).toBe(true);
  80. expect(preferenceManager.getPreferences().app.colorWeakMode).toBe(true);
  81. });
  82. it('resets preferences to default', () => {
  83. // 先更新一些偏好设置
  84. preferenceManager.updatePreferences({
  85. theme: {
  86. mode: 'light',
  87. },
  88. });
  89. // 然后重置偏好设置
  90. preferenceManager.resetPreferences();
  91. expect(preferenceManager.getPreferences()).toEqual(defaultPreferences);
  92. });
  93. it('updates isMobile correctly', () => {
  94. // 模拟移动端状态
  95. vi.stubGlobal(
  96. 'matchMedia',
  97. vi.fn().mockImplementation((query) => ({
  98. addEventListener: vi.fn(),
  99. addListener: vi.fn(),
  100. dispatchEvent: vi.fn(),
  101. matches: query === '(max-width: 768px)',
  102. media: query,
  103. onchange: null,
  104. removeEventListener: vi.fn(),
  105. removeListener: vi.fn(),
  106. })),
  107. );
  108. preferenceManager.updatePreferences({
  109. app: { isMobile: true },
  110. });
  111. expect(preferenceManager.getPreferences().app.isMobile).toBe(true);
  112. });
  113. it('updates the locale preference correctly', () => {
  114. preferenceManager.updatePreferences({
  115. app: { locale: 'en-US' },
  116. });
  117. expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
  118. });
  119. it('updates the sidebar width correctly', () => {
  120. preferenceManager.updatePreferences({
  121. sidebar: { width: 200 },
  122. });
  123. expect(preferenceManager.getPreferences().sidebar.width).toBe(200);
  124. });
  125. it('updates the sidebar collapse state correctly', () => {
  126. preferenceManager.updatePreferences({
  127. sidebar: { collapsed: true },
  128. });
  129. expect(preferenceManager.getPreferences().sidebar.collapsed).toBe(true);
  130. });
  131. it('updates the navigation style type correctly', () => {
  132. preferenceManager.updatePreferences({
  133. navigation: { styleType: 'flat' },
  134. } as any);
  135. expect(preferenceManager.getPreferences().navigation.styleType).toBe(
  136. 'flat',
  137. );
  138. });
  139. it('resets preferences to default correctly', () => {
  140. // 先更新一些偏好设置
  141. preferenceManager.updatePreferences({
  142. app: { locale: 'en-US' },
  143. sidebar: { collapsed: true, width: 200 },
  144. theme: {
  145. mode: 'light',
  146. },
  147. });
  148. // 然后重置偏好设置
  149. preferenceManager.resetPreferences();
  150. expect(preferenceManager.getPreferences()).toEqual(defaultPreferences);
  151. });
  152. it('does not update undefined preferences', () => {
  153. const originalPreferences = preferenceManager.getPreferences();
  154. preferenceManager.updatePreferences({
  155. app: { nonexistentField: 'value' },
  156. } as any);
  157. expect(preferenceManager.getPreferences()).toEqual(originalPreferences);
  158. });
  159. it('reverts to default when a preference field is deleted', () => {
  160. preferenceManager.updatePreferences({
  161. app: { locale: 'en-US' },
  162. });
  163. preferenceManager.updatePreferences({
  164. app: { locale: undefined },
  165. });
  166. expect(preferenceManager.getPreferences().app.locale).toBe('en-US');
  167. });
  168. it('ignores updates with invalid preference value types', () => {
  169. const originalPreferences = preferenceManager.getPreferences();
  170. preferenceManager.updatePreferences({
  171. app: { isMobile: 'true' as unknown as boolean }, // 错误类型
  172. });
  173. expect(preferenceManager.getPreferences()).toEqual(originalPreferences);
  174. });
  175. it('merges nested preference objects correctly', () => {
  176. preferenceManager.updatePreferences({
  177. app: { name: 'New App Name' },
  178. });
  179. const expected = {
  180. ...defaultPreferences,
  181. app: {
  182. ...defaultPreferences.app,
  183. name: 'New App Name',
  184. },
  185. };
  186. expect(preferenceManager.getPreferences()).toEqual(expected);
  187. });
  188. it('applies updates immediately after initialization', async () => {
  189. const overrides: any = {
  190. app: {
  191. locale: 'en-US',
  192. },
  193. };
  194. await preferenceManager.initPreferences(overrides);
  195. preferenceManager.updatePreferences({
  196. theme: { mode: 'light' },
  197. });
  198. expect(preferenceManager.getPreferences().theme.mode).toBe('light');
  199. });
  200. });
  201. describe('isDarkTheme', () => {
  202. it('should return true for dark theme', () => {
  203. expect(isDarkTheme('dark')).toBe(true);
  204. });
  205. it('should return false for light theme', () => {
  206. expect(isDarkTheme('light')).toBe(false);
  207. });
  208. it('should return system preference for auto theme', () => {
  209. vi.spyOn(window, 'matchMedia').mockImplementation((query) => ({
  210. addEventListener: vi.fn(),
  211. addListener: vi.fn(), // Deprecated
  212. dispatchEvent: vi.fn(),
  213. matches: query === '(prefers-color-scheme: dark)',
  214. media: query,
  215. onchange: null,
  216. removeEventListener: vi.fn(),
  217. removeListener: vi.fn(), // Deprecated
  218. }));
  219. expect(isDarkTheme('auto')).toBe(true);
  220. expect(window.matchMedia).toHaveBeenCalledWith(
  221. '(prefers-color-scheme: dark)',
  222. );
  223. });
  224. });