@digitaldefiance/express-suite-react-components 2.9.38 → 2.9.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/package.json +5 -4
  2. package/src/auth/Private.d.ts +6 -0
  3. package/src/auth/Private.d.ts.map +1 -0
  4. package/src/auth/Private.js +14 -0
  5. package/src/auth/PrivateRoute.d.ts +8 -0
  6. package/src/auth/PrivateRoute.d.ts.map +1 -0
  7. package/src/auth/PrivateRoute.js +23 -0
  8. package/src/auth/UnAuth.d.ts +6 -0
  9. package/src/auth/UnAuth.d.ts.map +1 -0
  10. package/src/auth/UnAuth.js +14 -0
  11. package/src/auth/UnAuthRoute.d.ts +8 -0
  12. package/src/auth/UnAuthRoute.d.ts.map +1 -0
  13. package/src/auth/UnAuthRoute.js +22 -0
  14. package/src/auth/{index.ts → index.d.ts} +2 -1
  15. package/src/auth/index.d.ts.map +1 -0
  16. package/src/auth/index.js +10 -0
  17. package/src/components/ApiAccess.d.ts +16 -0
  18. package/src/components/ApiAccess.d.ts.map +1 -0
  19. package/src/components/ApiAccess.js +77 -0
  20. package/src/components/BackupCodeLoginForm.d.ts +43 -0
  21. package/src/components/BackupCodeLoginForm.d.ts.map +1 -0
  22. package/src/components/BackupCodeLoginForm.js +139 -0
  23. package/src/components/BackupCodesForm.d.ts +26 -0
  24. package/src/components/BackupCodesForm.d.ts.map +1 -0
  25. package/src/components/BackupCodesForm.js +120 -0
  26. package/src/components/ChangePasswordForm.d.ts +26 -0
  27. package/src/components/ChangePasswordForm.d.ts.map +1 -0
  28. package/src/components/ChangePasswordForm.js +78 -0
  29. package/src/components/ConfirmationDialog.d.ts +13 -0
  30. package/src/components/ConfirmationDialog.d.ts.map +1 -0
  31. package/src/components/ConfirmationDialog.js +10 -0
  32. package/src/components/CurrencyCodeSelector.d.ts +9 -0
  33. package/src/components/CurrencyCodeSelector.d.ts.map +1 -0
  34. package/src/components/CurrencyCodeSelector.js +31 -0
  35. package/src/components/CurrencyInput.d.ts +13 -0
  36. package/src/components/CurrencyInput.d.ts.map +1 -0
  37. package/src/components/CurrencyInput.js +22 -0
  38. package/src/components/DashboardPage.d.ts +8 -0
  39. package/src/components/DashboardPage.d.ts.map +1 -0
  40. package/src/components/DashboardPage.js +10 -0
  41. package/src/components/DropdownMenu.d.ts +9 -0
  42. package/src/components/DropdownMenu.d.ts.map +1 -0
  43. package/src/components/DropdownMenu.js +56 -0
  44. package/src/components/ExpirationSecondsSelector.d.ts +13 -0
  45. package/src/components/ExpirationSecondsSelector.d.ts.map +1 -0
  46. package/src/components/ExpirationSecondsSelector.js +32 -0
  47. package/src/components/Flag.d.ts +20 -0
  48. package/src/components/Flag.d.ts.map +1 -0
  49. package/src/components/Flag.js +43 -0
  50. package/src/components/ForgotPasswordForm.d.ts +18 -0
  51. package/src/components/ForgotPasswordForm.d.ts.map +1 -0
  52. package/src/components/ForgotPasswordForm.js +61 -0
  53. package/src/components/LoginForm.d.ts +44 -0
  54. package/src/components/LoginForm.d.ts.map +1 -0
  55. package/src/components/LoginForm.js +122 -0
  56. package/src/components/LogoutPage.d.ts +8 -0
  57. package/src/components/LogoutPage.d.ts.map +1 -0
  58. package/src/components/LogoutPage.js +16 -0
  59. package/src/components/RegisterForm.d.ts +56 -0
  60. package/src/components/RegisterForm.d.ts.map +1 -0
  61. package/src/components/RegisterForm.js +140 -0
  62. package/src/components/ResetPasswordForm.d.ts +23 -0
  63. package/src/components/ResetPasswordForm.d.ts.map +1 -0
  64. package/src/components/ResetPasswordForm.js +78 -0
  65. package/src/components/SideMenu.d.ts +8 -0
  66. package/src/components/SideMenu.d.ts.map +1 -0
  67. package/src/components/SideMenu.js +25 -0
  68. package/src/components/SideMenuListItem.d.ts +13 -0
  69. package/src/components/SideMenuListItem.d.ts.map +1 -0
  70. package/src/components/SideMenuListItem.js +44 -0
  71. package/src/components/TopMenu.d.ts +24 -0
  72. package/src/components/TopMenu.d.ts.map +1 -0
  73. package/src/components/TopMenu.js +35 -0
  74. package/src/components/TranslatedTitle.d.ts +7 -0
  75. package/src/components/TranslatedTitle.d.ts.map +1 -0
  76. package/src/components/TranslatedTitle.js +15 -0
  77. package/src/components/UserLanguageSelector.d.ts +4 -0
  78. package/src/components/UserLanguageSelector.d.ts.map +1 -0
  79. package/src/components/UserLanguageSelector.js +31 -0
  80. package/src/components/UserMenu.d.ts +4 -0
  81. package/src/components/UserMenu.d.ts.map +1 -0
  82. package/src/components/UserMenu.js +12 -0
  83. package/src/components/UserSettingsForm.d.ts +57 -0
  84. package/src/components/UserSettingsForm.d.ts.map +1 -0
  85. package/src/components/UserSettingsForm.js +126 -0
  86. package/src/components/VerifyEmailPage.d.ts +23 -0
  87. package/src/components/VerifyEmailPage.d.ts.map +1 -0
  88. package/src/components/VerifyEmailPage.js +70 -0
  89. package/src/components/{index.ts → index.d.ts} +1 -1
  90. package/src/components/index.d.ts.map +1 -0
  91. package/src/components/index.js +28 -0
  92. package/src/contexts/AuthProvider.d.ts +152 -0
  93. package/src/contexts/AuthProvider.d.ts.map +1 -0
  94. package/src/contexts/AuthProvider.js +502 -0
  95. package/src/contexts/I18nProvider.d.ts +16 -0
  96. package/src/contexts/I18nProvider.d.ts.map +1 -0
  97. package/src/contexts/I18nProvider.js +46 -0
  98. package/src/contexts/MenuContext.d.ts +20 -0
  99. package/src/contexts/MenuContext.d.ts.map +1 -0
  100. package/src/contexts/MenuContext.js +273 -0
  101. package/src/contexts/SuiteConfigProvider.d.ts +44 -0
  102. package/src/contexts/SuiteConfigProvider.d.ts.map +1 -0
  103. package/src/contexts/SuiteConfigProvider.js +43 -0
  104. package/src/contexts/ThemeProvider.d.ts +15 -0
  105. package/src/contexts/ThemeProvider.d.ts.map +1 -0
  106. package/src/contexts/ThemeProvider.js +36 -0
  107. package/src/contexts/{index.ts → index.d.ts} +1 -0
  108. package/src/contexts/index.d.ts.map +1 -0
  109. package/src/contexts/index.js +8 -0
  110. package/src/hooks/{index.ts → index.d.ts} +1 -0
  111. package/src/hooks/index.d.ts.map +1 -0
  112. package/src/hooks/index.js +8 -0
  113. package/src/hooks/useBackupCodes.d.ts +15 -0
  114. package/src/hooks/useBackupCodes.d.ts.map +1 -0
  115. package/src/hooks/useBackupCodes.js +74 -0
  116. package/src/hooks/useEmailVerification.d.ts +10 -0
  117. package/src/hooks/useEmailVerification.d.ts.map +1 -0
  118. package/src/hooks/useEmailVerification.js +40 -0
  119. package/src/hooks/useExpiringValue.d.ts +14 -0
  120. package/src/hooks/useExpiringValue.d.ts.map +1 -0
  121. package/src/hooks/useExpiringValue.js +53 -0
  122. package/src/hooks/useLocalStorage.d.ts +2 -0
  123. package/src/hooks/useLocalStorage.d.ts.map +1 -0
  124. package/src/hooks/useLocalStorage.js +15 -0
  125. package/src/hooks/useUserSettings.d.ts +48 -0
  126. package/src/hooks/useUserSettings.d.ts.map +1 -0
  127. package/src/hooks/useUserSettings.js +169 -0
  128. package/src/{index.ts → index.d.ts} +1 -1
  129. package/src/index.d.ts.map +1 -0
  130. package/src/index.js +12 -0
  131. package/src/interfaces/IAppConfig.d.ts +6 -0
  132. package/src/interfaces/IAppConfig.d.ts.map +1 -0
  133. package/src/interfaces/IAppConfig.js +2 -0
  134. package/src/interfaces/IMenuConfig.d.ts +11 -0
  135. package/src/interfaces/IMenuConfig.d.ts.map +1 -0
  136. package/src/interfaces/IMenuConfig.js +2 -0
  137. package/src/interfaces/IMenuOption.d.ts +58 -0
  138. package/src/interfaces/IMenuOption.d.ts.map +1 -0
  139. package/src/interfaces/IMenuOption.js +2 -0
  140. package/src/interfaces/index.d.ts +4 -0
  141. package/src/interfaces/index.d.ts.map +1 -0
  142. package/src/interfaces/index.js +6 -0
  143. package/src/services/__mocks__/authService.d.ts +21 -0
  144. package/src/services/__mocks__/authService.d.ts.map +1 -0
  145. package/src/services/__mocks__/authService.js +15 -0
  146. package/src/services/api.d.ts +3 -0
  147. package/src/services/api.d.ts.map +1 -0
  148. package/src/services/api.js +14 -0
  149. package/src/services/authService.d.ts +72 -0
  150. package/src/services/authService.d.ts.map +1 -0
  151. package/src/services/authService.js +335 -0
  152. package/src/services/authenticatedApi.d.ts +3 -0
  153. package/src/services/authenticatedApi.d.ts.map +1 -0
  154. package/src/services/authenticatedApi.js +18 -0
  155. package/src/services/index.d.ts +4 -0
  156. package/src/services/index.d.ts.map +1 -0
  157. package/src/services/index.js +6 -0
  158. package/src/types/MenuType.d.ts +11 -0
  159. package/src/types/MenuType.d.ts.map +1 -0
  160. package/src/types/MenuType.js +12 -0
  161. package/src/types/expirationSeconds.d.ts +3 -0
  162. package/src/types/expirationSeconds.d.ts.map +1 -0
  163. package/src/types/expirationSeconds.js +17 -0
  164. package/src/types/index.d.ts +2 -0
  165. package/src/types/index.d.ts.map +1 -0
  166. package/src/types/index.js +4 -0
  167. package/src/types/translation.d.ts +10 -0
  168. package/src/types/translation.d.ts.map +1 -0
  169. package/src/types/translation.js +9 -0
  170. package/src/wrappers/BackupCodeLoginWrapper.d.ts +8 -0
  171. package/src/wrappers/BackupCodeLoginWrapper.d.ts.map +1 -0
  172. package/src/wrappers/BackupCodeLoginWrapper.js +20 -0
  173. package/src/wrappers/BackupCodesWrapper.d.ts +7 -0
  174. package/src/wrappers/BackupCodesWrapper.d.ts.map +1 -0
  175. package/src/wrappers/BackupCodesWrapper.js +17 -0
  176. package/src/wrappers/ChangePasswordFormWrapper.d.ts +8 -0
  177. package/src/wrappers/ChangePasswordFormWrapper.d.ts.map +1 -0
  178. package/src/wrappers/ChangePasswordFormWrapper.js +21 -0
  179. package/src/wrappers/LoginFormWrapper.d.ts +9 -0
  180. package/src/wrappers/LoginFormWrapper.d.ts.map +1 -0
  181. package/src/wrappers/LoginFormWrapper.js +43 -0
  182. package/src/wrappers/LogoutPageWrapper.d.ts +9 -0
  183. package/src/wrappers/LogoutPageWrapper.d.ts.map +1 -0
  184. package/src/wrappers/LogoutPageWrapper.js +21 -0
  185. package/src/wrappers/RegisterFormWrapper.d.ts +9 -0
  186. package/src/wrappers/RegisterFormWrapper.d.ts.map +1 -0
  187. package/src/wrappers/RegisterFormWrapper.js +31 -0
  188. package/src/wrappers/UserSettingsFormWrapper.d.ts +8 -0
  189. package/src/wrappers/UserSettingsFormWrapper.d.ts.map +1 -0
  190. package/src/wrappers/UserSettingsFormWrapper.js +24 -0
  191. package/src/wrappers/VerifyEmailPageWrapper.d.ts +8 -0
  192. package/src/wrappers/VerifyEmailPageWrapper.d.ts.map +1 -0
  193. package/src/wrappers/VerifyEmailPageWrapper.js +20 -0
  194. package/src/wrappers/{index.tsx → index.d.ts} +1 -8
  195. package/src/wrappers/index.d.ts.map +1 -0
  196. package/src/wrappers/index.js +20 -0
  197. package/LICENSE +0 -21
  198. package/src/auth/Private.tsx +0 -17
  199. package/src/auth/PrivateRoute.tsx +0 -28
  200. package/src/auth/UnAuth.tsx +0 -16
  201. package/src/auth/UnAuthRoute.tsx +0 -30
  202. package/src/components/ApiAccess.tsx +0 -174
  203. package/src/components/BackupCodeLoginForm.tsx +0 -488
  204. package/src/components/BackupCodesForm.tsx +0 -286
  205. package/src/components/ChangePasswordForm.tsx +0 -272
  206. package/src/components/ConfirmationDialog.tsx +0 -48
  207. package/src/components/CurrencyCodeSelector.tsx +0 -60
  208. package/src/components/CurrencyInput.tsx +0 -80
  209. package/src/components/DashboardPage.tsx +0 -24
  210. package/src/components/DropdownMenu.tsx +0 -92
  211. package/src/components/ExpirationSecondsSelector.tsx +0 -60
  212. package/src/components/Flag.tsx +0 -52
  213. package/src/components/ForgotPasswordForm.tsx +0 -173
  214. package/src/components/LoginForm.tsx +0 -455
  215. package/src/components/LogoutPage.tsx +0 -21
  216. package/src/components/RegisterForm.tsx +0 -602
  217. package/src/components/ResetPasswordForm.tsx +0 -246
  218. package/src/components/SideMenu.tsx +0 -46
  219. package/src/components/SideMenuListItem.tsx +0 -74
  220. package/src/components/TopMenu.tsx +0 -145
  221. package/src/components/TranslatedTitle.tsx +0 -29
  222. package/src/components/UserLanguageSelector.tsx +0 -45
  223. package/src/components/UserMenu.tsx +0 -15
  224. package/src/components/UserSettingsForm.tsx +0 -505
  225. package/src/components/VerifyEmailPage.tsx +0 -184
  226. package/src/contexts/AuthProvider.spec.tsx +0 -1195
  227. package/src/contexts/AuthProvider.tsx +0 -924
  228. package/src/contexts/I18nProvider.tsx +0 -114
  229. package/src/contexts/MenuContext.tsx +0 -398
  230. package/src/contexts/SuiteConfigProvider.tsx +0 -93
  231. package/src/contexts/ThemeProvider.tsx +0 -67
  232. package/src/hooks/useBackupCodes.ts +0 -105
  233. package/src/hooks/useEmailVerification.ts +0 -49
  234. package/src/hooks/useExpiringValue.ts +0 -78
  235. package/src/hooks/useLocalStorage.ts +0 -18
  236. package/src/hooks/useUserSettings.ts +0 -269
  237. package/src/interfaces/IAppConfig.ts +0 -5
  238. package/src/interfaces/IMenuConfig.ts +0 -11
  239. package/src/interfaces/IMenuOption.ts +0 -55
  240. package/src/interfaces/index.ts +0 -3
  241. package/src/services/__mocks__/authService.ts +0 -14
  242. package/src/services/api.ts +0 -13
  243. package/src/services/authService.ts +0 -500
  244. package/src/services/authenticatedApi.ts +0 -17
  245. package/src/services/index.ts +0 -3
  246. package/src/types/MenuType.ts +0 -15
  247. package/src/types/expirationSeconds.ts +0 -18
  248. package/src/types/index.ts +0 -1
  249. package/src/types/translation.ts +0 -20
  250. package/src/wrappers/BackupCodeLoginWrapper.tsx +0 -34
  251. package/src/wrappers/BackupCodesWrapper.tsx +0 -28
  252. package/src/wrappers/ChangePasswordFormWrapper.tsx +0 -34
  253. package/src/wrappers/LoginFormWrapper.tsx +0 -59
  254. package/src/wrappers/LogoutPageWrapper.tsx +0 -30
  255. package/src/wrappers/RegisterFormWrapper.tsx +0 -61
  256. package/src/wrappers/UserSettingsFormWrapper.tsx +0 -39
  257. package/src/wrappers/VerifyEmailPageWrapper.tsx +0 -27
@@ -1,1195 +0,0 @@
1
- import {
2
- afterEach,
3
- beforeEach,
4
- describe,
5
- expect,
6
- it,
7
- jest,
8
- } from '@jest/globals';
9
- import { ReactNode } from 'react';
10
-
11
- // Mock PasswordLoginService
12
- const mockPasswordLoginService = {
13
- getWalletAndMnemonicFromLocalStorageBundle: jest.fn() as jest.Mock,
14
- setupPasswordLoginLocalStorageBundle: jest.fn() as jest.Mock,
15
- };
16
-
17
- const mockNavigate = jest.fn();
18
-
19
- // Create shared mock instance
20
- const mockAuthService = {
21
- verifyToken: jest.fn() as jest.Mock,
22
- directLogin: jest.fn() as jest.Mock,
23
- emailChallengeLogin: jest.fn() as jest.Mock,
24
- refreshToken: jest.fn() as jest.Mock,
25
- register: jest.fn() as jest.Mock,
26
- requestEmailLogin: jest.fn() as jest.Mock,
27
- backupCodeLogin: jest.fn() as jest.Mock,
28
- changePassword: jest.fn() as jest.Mock,
29
- };
30
-
31
- // Mock dependencies - must be before imports
32
- jest.mock('../services/authService', () => ({
33
- mockAuthService,
34
- createAuthService: jest.fn(() => mockAuthService),
35
- }));
36
- jest.mock('../services/authenticatedApi', () => ({
37
- createAuthenticatedApiClient: jest.fn(() => ({
38
- post: jest.fn(),
39
- get: jest.fn(),
40
- })),
41
- }));
42
-
43
- jest.mock('@digitaldefiance/i18n-lib', () => {
44
- const actual = jest.requireActual('@digitaldefiance/i18n-lib');
45
- const mockI18nEngineInstance = {
46
- t: jest.fn((key: string) => key),
47
- translate: jest.fn((componentId: string, key: string) => key),
48
- setLanguage: jest.fn(),
49
- };
50
- return {
51
- ...actual,
52
- DefaultCurrencyCode: 'USD',
53
- CurrencyCode: class MockCurrencyCode {
54
- constructor(public value: string = 'USD') {}
55
- },
56
- I18nEngine: {
57
- getInstance: () => mockI18nEngineInstance,
58
- },
59
- ECIESService: jest.fn(),
60
- t: jest.fn((key) => key),
61
- };
62
- });
63
-
64
- jest.mock('@digitaldefiance/ecies-lib', () => {
65
- const actual = jest.requireActual('@digitaldefiance/ecies-lib');
66
-
67
- // Mock ECIESService to avoid ID provider validation issues in tests
68
- const mockECIESService = jest.fn().mockImplementation(() => ({
69
- generateNewMnemonic: jest.fn(),
70
- mnemonicToSimpleKeyPair: jest.fn(),
71
- getPublicKey: jest.fn(),
72
- encryptSimpleOrSingle: jest.fn(),
73
- decryptSimpleOrSingleWithHeader: jest.fn(),
74
- signMessage: jest.fn(),
75
- verifyMessage: jest.fn(),
76
- config: {},
77
- constants: actual.Constants,
78
- idProvider: actual.Constants.idProvider,
79
- }));
80
-
81
- return {
82
- ...actual,
83
- ECIESService: mockECIESService,
84
- PasswordLoginService: jest
85
- .fn()
86
- .mockImplementation(() => mockPasswordLoginService),
87
- };
88
- });
89
-
90
- jest.mock('./I18nProvider', () => {
91
- const React = require('react');
92
- const I18nProvider = ({ children }: { children: any }) => {
93
- return React.createElement(React.Fragment, null, children);
94
- };
95
- I18nProvider.displayName = 'MockI18nProvider';
96
-
97
- return {
98
- I18nProvider,
99
- useI18n: () => ({
100
- t: (key: string) => key,
101
- tComponent: (componentId: string, key: string) => key,
102
- changeLanguage: jest.fn(),
103
- currentLanguage: 'en-US',
104
- }),
105
- };
106
- });
107
-
108
- jest.mock('@ethereumjs/wallet', () => ({
109
- Wallet: {
110
- generate: jest.fn(() => ({
111
- getPrivateKey: jest.fn(() => Buffer.from('test')),
112
- getPublicKey: jest.fn(() => Buffer.from('test')),
113
- getAddress: jest.fn(() => Buffer.from('test')),
114
- })),
115
- },
116
- }));
117
-
118
- // Now import after mocks
119
- import {
120
- ECIES as ECIESConstants,
121
- SecureString,
122
- } from '@digitaldefiance/ecies-lib';
123
- import { Constants } from '@digitaldefiance/suite-core-lib';
124
- import { Wallet } from '@ethereumjs/wallet';
125
- import { act, renderHook, waitFor } from '@testing-library/react';
126
- import { localStorageMock } from '../../tests/setup';
127
- import { AuthProvider, useAuth } from './AuthProvider';
128
- import { I18nProvider } from './I18nProvider';
129
- import { AppThemeProvider } from './ThemeProvider';
130
-
131
- // Mock localStorage is imported from test-setup
132
-
133
- // Mock console methods
134
- const consoleMock = {
135
- error: jest.fn(),
136
- log: jest.fn(),
137
- };
138
- Object.defineProperty(console, 'error', { value: consoleMock.error });
139
-
140
- // Test wrapper component
141
- const TestWrapper = ({ children }: { children: ReactNode }) => (
142
- <I18nProvider i18nEngine={null as any} onLanguageChange={async () => {}}>
143
- <AppThemeProvider>
144
- <AuthProvider
145
- baseUrl="http://localhost:3000"
146
- constants={Constants}
147
- eciesConfig={ECIESConstants}
148
- onLogout={mockNavigate}
149
- >
150
- {children}
151
- </AuthProvider>
152
- </AppThemeProvider>
153
- </I18nProvider>
154
- );
155
-
156
- // Helper to create mock user
157
- const createMockUser = (admin = false) => ({
158
- id: '1',
159
- username: admin ? 'admin' : 'testuser',
160
- email: admin ? 'admin@example.com' : 'test@example.com',
161
- roles: [
162
- {
163
- id: 'role1',
164
- _id: 'role1',
165
- name: admin ? 'admin' : 'user',
166
- member: true,
167
- child: false,
168
- system: false,
169
- admin,
170
- createdAt: '2023-01-01T00:00:00.000Z',
171
- updatedAt: '2023-01-01T00:00:00.000Z',
172
- deletedAt: undefined,
173
- createdBy: 'system',
174
- updatedBy: 'system',
175
- },
176
- ],
177
- siteLanguage: 'en-US',
178
- timezone: 'UTC',
179
- currency: 'USD',
180
- darkMode: false,
181
- directChallenge: false,
182
- emailVerified: true,
183
- });
184
-
185
- describe('AuthProvider', () => {
186
- beforeEach(() => {
187
- localStorageMock.getItem.mockReturnValue(null);
188
- localStorageMock.setItem.mockClear();
189
- localStorageMock.removeItem.mockClear();
190
- mockPasswordLoginService.getWalletAndMnemonicFromLocalStorageBundle.mockClear();
191
- mockPasswordLoginService.setupPasswordLoginLocalStorageBundle.mockClear();
192
- mockAuthService.verifyToken.mockClear();
193
- mockAuthService.directLogin.mockClear();
194
- mockAuthService.emailChallengeLogin.mockClear();
195
- mockAuthService.refreshToken.mockClear();
196
- mockAuthService.register.mockClear();
197
- mockAuthService.requestEmailLogin.mockClear();
198
- mockAuthService.backupCodeLogin.mockClear();
199
- });
200
-
201
- afterEach(() => {
202
- jest.clearAllTimers();
203
- });
204
-
205
- describe('Initial State', () => {
206
- it('should initialize with default values', async () => {
207
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
208
-
209
- await waitFor(() => {
210
- expect(result.current.isAuthenticated).toBe(false);
211
- expect(result.current.user).toBe(null);
212
- expect(result.current.userData).toBe(null);
213
- expect(result.current.token).toBe(null);
214
- expect(result.current.mnemonic).toBeUndefined();
215
- expect(result.current.wallet).toBeUndefined();
216
- expect(result.current.admin).toBe(false);
217
- });
218
- });
219
-
220
- it('should initialize expiration seconds from localStorage', async () => {
221
- localStorageMock.getItem.mockImplementation((key) => {
222
- if (key === 'mnemonicExpirationSeconds') return '300';
223
- if (key === 'walletExpirationSeconds') return '600';
224
- return null;
225
- });
226
-
227
- renderHook(() => useAuth(), { wrapper: TestWrapper });
228
-
229
- await waitFor(() => {
230
- expect(localStorageMock.getItem).toHaveBeenCalledWith(
231
- 'mnemonicExpirationSeconds'
232
- );
233
- expect(localStorageMock.getItem).toHaveBeenCalledWith(
234
- 'walletExpirationSeconds'
235
- );
236
- });
237
- });
238
- });
239
-
240
- describe('checkAuth', () => {
241
- it('should clear auth state when no token exists', async () => {
242
- localStorageMock.getItem.mockReturnValue(null);
243
-
244
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
245
-
246
- await waitFor(() => {
247
- expect(result.current.loading).toBe(false);
248
- expect(result.current.isCheckingAuth).toBe(false);
249
- expect(result.current.isAuthenticated).toBe(false);
250
- });
251
- });
252
-
253
- it('should verify token and set user when token exists', async () => {
254
- const mockUser = createMockUser();
255
- mockAuthService.verifyToken.mockResolvedValue(mockUser);
256
-
257
- localStorageMock.getItem.mockImplementation((key) =>
258
- key === 'authToken' ? 'valid-token' : null
259
- );
260
-
261
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
262
-
263
- await waitFor(
264
- () => {
265
- expect(result.current.loading).toBe(false);
266
- expect(result.current.isCheckingAuth).toBe(false);
267
- expect(result.current.isAuthenticated).toBe(true);
268
- },
269
- { timeout: 5000 }
270
- );
271
-
272
- expect(result.current.userData).toEqual(mockUser);
273
- expect(result.current.token).toBe('valid-token');
274
- });
275
-
276
- it('should handle token verification failure', async () => {
277
- localStorageMock.getItem.mockImplementation((key) => {
278
- if (key === 'authToken') return 'invalid-token';
279
- return null;
280
- });
281
- mockAuthService.verifyToken.mockRejectedValue(new Error('Token invalid'));
282
-
283
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
284
-
285
- await waitFor(() => {
286
- expect(result.current.isAuthenticated).toBe(false);
287
- expect(result.current.userData).toBe(null);
288
- expect(result.current.loading).toBe(false);
289
- expect(result.current.isCheckingAuth).toBe(false);
290
- expect(localStorageMock.removeItem).toHaveBeenCalledWith('authToken');
291
- });
292
- });
293
-
294
- it('should set admin flag for admin users', async () => {
295
- const mockAdminUser = createMockUser(true);
296
- mockAuthService.verifyToken.mockResolvedValue(mockAdminUser);
297
-
298
- localStorageMock.getItem.mockImplementation((key) =>
299
- key === 'authToken' ? 'admin-token' : null
300
- );
301
-
302
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
303
-
304
- await waitFor(
305
- () => {
306
- expect(result.current.loading).toBe(false);
307
- expect(result.current.admin).toBe(true);
308
- },
309
- { timeout: 5000 }
310
- );
311
- });
312
- });
313
-
314
- describe('directLogin', () => {
315
- it('should perform successful direct login', async () => {
316
- const mockMnemonic = new SecureString('test mnemonic');
317
- const mockWallet = Wallet.generate();
318
- const mockUser = createMockUser();
319
- const mockLoginResult = {
320
- token: 'new-token',
321
- user: mockUser,
322
- wallet: mockWallet,
323
- message: 'Login successful',
324
- };
325
-
326
- mockAuthService.directLogin.mockResolvedValue(mockLoginResult);
327
-
328
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
329
-
330
- await waitFor(() => {
331
- expect(result.current.directLogin).toBeDefined();
332
- });
333
-
334
- let loginResult;
335
- await act(async () => {
336
- loginResult = await result.current.directLogin(
337
- mockMnemonic,
338
- 'testuser'
339
- );
340
- });
341
-
342
- expect(mockAuthService.directLogin).toHaveBeenCalled();
343
- expect(loginResult).toEqual(mockLoginResult);
344
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
345
- 'authToken',
346
- 'new-token'
347
- );
348
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
349
- 'user',
350
- JSON.stringify(mockUser)
351
- );
352
- });
353
-
354
- it('should perform successful direct login with custom expiration seconds', async () => {
355
- const mockMnemonic = new SecureString('test mnemonic');
356
- const mockWallet = Wallet.generate();
357
- const mockUser = createMockUser();
358
- const mockLoginResult = {
359
- token: 'new-token',
360
- user: mockUser,
361
- wallet: mockWallet,
362
- message: 'Login successful',
363
- };
364
-
365
- mockAuthService.directLogin.mockResolvedValue(mockLoginResult);
366
-
367
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
368
-
369
- let loginResult;
370
- await act(async () => {
371
- loginResult = await result.current.directLogin(
372
- mockMnemonic,
373
- 'testuser',
374
- undefined,
375
- 600, // mnemonicExpirationSeconds
376
- 1200 // walletExpirationSeconds
377
- );
378
- });
379
-
380
- expect(loginResult).toEqual(mockLoginResult);
381
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
382
- 'authToken',
383
- 'new-token'
384
- );
385
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
386
- 'user',
387
- JSON.stringify(mockUser)
388
- );
389
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
390
- 'mnemonicExpirationSeconds',
391
- '600'
392
- );
393
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
394
- 'walletExpirationSeconds',
395
- '1200'
396
- );
397
- });
398
-
399
- it('should handle direct login failure', async () => {
400
- const mockMnemonic = new SecureString('test mnemonic');
401
- const mockError = { error: 'Login failed' };
402
-
403
- mockAuthService.directLogin.mockResolvedValue(mockError);
404
-
405
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
406
-
407
- await act(async () => {
408
- await result.current.directLogin(mockMnemonic, 'testuser');
409
- });
410
- expect(result.current.isAuthenticated).toBe(false);
411
- });
412
- });
413
-
414
- describe('emailChallengeLogin', () => {
415
- it('should perform successful email challenge login', async () => {
416
- const mockMnemonic = new SecureString('test mnemonic');
417
- const mockWallet = Wallet.generate();
418
- const mockUser = createMockUser();
419
- const mockLoginResult = {
420
- token: 'new-token',
421
- user: mockUser,
422
- wallet: mockWallet,
423
- message: 'Login successful',
424
- };
425
-
426
- mockAuthService.emailChallengeLogin.mockResolvedValue(mockLoginResult);
427
-
428
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
429
-
430
- let loginResult;
431
- await act(async () => {
432
- loginResult = await result.current.emailChallengeLogin(
433
- mockMnemonic,
434
- 'challenge-token',
435
- 'testuser'
436
- );
437
- });
438
-
439
- expect(loginResult).toEqual(mockLoginResult);
440
- });
441
-
442
- it('should perform successful email challenge login with custom expiration seconds', async () => {
443
- const mockMnemonic = new SecureString('test mnemonic');
444
- const mockWallet = Wallet.generate();
445
- const mockUser = createMockUser();
446
- const mockLoginResult = {
447
- token: 'new-token',
448
- user: mockUser,
449
- wallet: mockWallet,
450
- message: 'Login successful',
451
- };
452
-
453
- mockAuthService.emailChallengeLogin.mockResolvedValue(mockLoginResult);
454
-
455
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
456
-
457
- let loginResult;
458
- await act(async () => {
459
- loginResult = await result.current.emailChallengeLogin(
460
- mockMnemonic,
461
- 'challenge-token',
462
- 'testuser',
463
- undefined,
464
- 300, // mnemonicExpirationSeconds
465
- 900 // walletExpirationSeconds
466
- );
467
- });
468
-
469
- expect(loginResult).toEqual(mockLoginResult);
470
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
471
- 'mnemonicExpirationSeconds',
472
- '300'
473
- );
474
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
475
- 'walletExpirationSeconds',
476
- '900'
477
- );
478
- });
479
- });
480
-
481
- describe('refreshToken', () => {
482
- it('should refresh token successfully', async () => {
483
- const mockUser = createMockUser();
484
- const mockRefreshResult = {
485
- token: 'refreshed-token',
486
- user: mockUser,
487
- };
488
-
489
- mockAuthService.refreshToken.mockResolvedValue(mockRefreshResult);
490
-
491
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
492
-
493
- await waitFor(() => {
494
- expect(result.current.refreshToken).toBeDefined();
495
- });
496
-
497
- let refreshResult;
498
- await act(async () => {
499
- refreshResult = await result.current.refreshToken();
500
- });
501
-
502
- expect(refreshResult).toEqual(mockRefreshResult);
503
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
504
- 'authToken',
505
- 'refreshed-token'
506
- );
507
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
508
- 'user',
509
- JSON.stringify(mockUser)
510
- );
511
- });
512
-
513
- it('should handle refresh token failure', async () => {
514
- mockAuthService.refreshToken.mockRejectedValue(
515
- new Error('Refresh failed')
516
- );
517
-
518
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
519
-
520
- await waitFor(() => {
521
- expect(result.current.refreshToken).toBeDefined();
522
- });
523
-
524
- await expect(
525
- act(async () => {
526
- await result.current.refreshToken();
527
- })
528
- ).rejects.toThrow('Refresh failed');
529
-
530
- expect(localStorageMock.removeItem).toHaveBeenCalledWith('authToken');
531
- expect(localStorageMock.removeItem).toHaveBeenCalledWith('user');
532
- });
533
- });
534
-
535
- describe('register', () => {
536
- it('should register user successfully', async () => {
537
- const mockRegisterResult = {
538
- success: true,
539
- message: 'Registration successful',
540
- mnemonic: 'test mnemonic',
541
- };
542
-
543
- mockAuthService.register.mockResolvedValue(mockRegisterResult);
544
-
545
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
546
-
547
- await act(async () => {
548
- await result.current.register(
549
- 'testuser',
550
- 'test@example.com',
551
- 'UTC',
552
- 'password'
553
- );
554
- });
555
- });
556
- });
557
-
558
- describe('requestEmailLogin', () => {
559
- it('should request email login successfully', async () => {
560
- const mockMessage = 'Email sent successfully';
561
- mockAuthService.requestEmailLogin.mockResolvedValue(mockMessage);
562
-
563
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
564
-
565
- await act(async () => {
566
- await result.current.requestEmailLogin('testuser');
567
- });
568
- });
569
- });
570
-
571
- describe('backupCodeLogin', () => {
572
- it('should perform backup code login successfully', async () => {
573
- const mockUser = createMockUser();
574
- const mockLoginResult = {
575
- token: 'backup-token',
576
- user: mockUser,
577
- codeCount: 5,
578
- mnemonic: 'recovered mnemonic',
579
- message: 'Login successful',
580
- };
581
-
582
- mockAuthService.backupCodeLogin.mockResolvedValue(mockLoginResult);
583
-
584
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
585
-
586
- let loginResult;
587
- await act(async () => {
588
- loginResult = await result.current.backupCodeLogin(
589
- 'testuser',
590
- 'backup-code',
591
- false,
592
- true
593
- );
594
- });
595
-
596
- expect(loginResult).toEqual({
597
- token: 'backup-token',
598
- codeCount: 5,
599
- mnemonic: 'recovered mnemonic',
600
- message: 'Login successful',
601
- });
602
- });
603
- });
604
-
605
- describe('logout', () => {
606
- it('should logout user and clear all data', async () => {
607
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
608
-
609
- await act(async () => {
610
- await result.current.logout();
611
- });
612
-
613
- expect(localStorageMock.removeItem).toHaveBeenCalledWith('user');
614
- expect(localStorageMock.removeItem).toHaveBeenCalledWith('authToken');
615
- expect(result.current.isAuthenticated).toBe(false);
616
- expect(result.current.userData).toBe(null);
617
- expect(result.current.mnemonic).toBeUndefined();
618
- expect(result.current.wallet).toBeUndefined();
619
- expect(mockNavigate).toHaveBeenCalled();
620
- });
621
- });
622
-
623
- describe('verifyToken', () => {
624
- it('should verify token successfully', async () => {
625
- const mockUser = createMockUser();
626
- mockAuthService.verifyToken.mockResolvedValue(mockUser);
627
-
628
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
629
-
630
- let verifyResult;
631
- await act(async () => {
632
- verifyResult = await result.current.verifyToken('test-token');
633
- });
634
-
635
- expect(verifyResult).toBe(true);
636
- expect(mockAuthService.verifyToken).toHaveBeenCalledWith('test-token');
637
- });
638
-
639
- it('should handle token verification failure', async () => {
640
- mockAuthService.verifyToken.mockResolvedValue({ error: 'Invalid token' });
641
-
642
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
643
-
644
- let verifyResult;
645
- await act(async () => {
646
- verifyResult = await result.current.verifyToken('invalid-token');
647
- });
648
-
649
- expect(verifyResult).toBe(false);
650
- expect(mockAuthService.verifyToken).toHaveBeenCalledWith('invalid-token');
651
- });
652
- });
653
-
654
- describe('changePassword', () => {
655
- it('should change password successfully', async () => {
656
- // Mock localStorage to have encrypted password (password login available)
657
- localStorageMock.getItem.mockImplementation((key) => {
658
- if (key === 'encryptedPassword') return 'mock-encrypted-password';
659
- return null;
660
- });
661
-
662
- // Mock successful password login service operations
663
- const mockMnemonic = new SecureString('test mnemonic');
664
- const mockWallet = Wallet.generate();
665
- mockPasswordLoginService.getWalletAndMnemonicFromLocalStorageBundle.mockResolvedValue(
666
- {
667
- mnemonic: mockMnemonic,
668
- wallet: mockWallet,
669
- }
670
- );
671
- mockPasswordLoginService.setupPasswordLoginLocalStorageBundle.mockResolvedValue(
672
- mockWallet
673
- );
674
-
675
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
676
-
677
- await act(async () => {
678
- await result.current.changePassword('oldpass', 'newpass');
679
- });
680
- });
681
-
682
- it('should handle password change failure', async () => {
683
- // Mock localStorage to not have encrypted password (password login not available)
684
- localStorageMock.getItem.mockReturnValue(null);
685
-
686
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
687
-
688
- let changeResult;
689
- await act(async () => {
690
- changeResult = await result.current.changePassword(
691
- 'oldpass',
692
- 'newpass'
693
- );
694
- });
695
-
696
- expect(changeResult).toEqual({
697
- error: 'error_login_passwordLoginNotSetup',
698
- errorType: 'PasswordLoginNotSetup',
699
- });
700
- });
701
- });
702
-
703
- describe('Mnemonic Management', () => {
704
- it('should set and clear mnemonic', async () => {
705
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
706
- const mockMnemonic = new SecureString('test mnemonic');
707
-
708
- await waitFor(() => {
709
- expect(result.current.setMnemonic).toBeDefined();
710
- });
711
-
712
- act(() => {
713
- result.current.setMnemonic(mockMnemonic, 10);
714
- });
715
-
716
- expect(result.current.mnemonic).toEqual(mockMnemonic);
717
-
718
- act(() => {
719
- result.current.clearMnemonic();
720
- });
721
-
722
- expect(result.current.mnemonic).toBeUndefined();
723
- });
724
-
725
- it('should store mnemonic expiration seconds to localStorage when provided', async () => {
726
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
727
- const mockMnemonic = new SecureString('test mnemonic');
728
-
729
- await waitFor(() => {
730
- expect(result.current.setMnemonic).toBeDefined();
731
- });
732
-
733
- act(() => {
734
- result.current.setMnemonic(mockMnemonic, 300);
735
- });
736
-
737
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
738
- 'mnemonicExpirationSeconds',
739
- '300'
740
- );
741
- expect(result.current.mnemonic).toEqual(mockMnemonic);
742
- });
743
-
744
- it('should use stored expiration seconds when duration not provided', async () => {
745
- localStorageMock.getItem.mockImplementation((key) =>
746
- key === 'mnemonicExpirationSeconds' ? '600' : null
747
- );
748
-
749
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
750
- const mockMnemonic = new SecureString('test mnemonic');
751
-
752
- await waitFor(() => {
753
- expect(result.current.setMnemonic).toBeDefined();
754
- });
755
-
756
- act(() => {
757
- result.current.setMnemonic(mockMnemonic);
758
- });
759
-
760
- expect(result.current.mnemonic).toEqual(mockMnemonic);
761
- // Should not call setItem when no duration provided
762
- expect(localStorageMock.setItem).not.toHaveBeenCalledWith(
763
- 'mnemonicExpirationSeconds',
764
- expect.any(String)
765
- );
766
- });
767
- });
768
-
769
- describe('Wallet Management', () => {
770
- it('should set and clear wallet', async () => {
771
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
772
- const mockWallet = Wallet.generate();
773
-
774
- await waitFor(() => {
775
- expect(result.current.setWallet).toBeDefined();
776
- });
777
-
778
- act(() => {
779
- result.current.setWallet(mockWallet, 10);
780
- });
781
-
782
- expect(result.current.wallet).toEqual(mockWallet);
783
-
784
- act(() => {
785
- result.current.clearWallet();
786
- });
787
-
788
- expect(result.current.wallet).toBeUndefined();
789
- });
790
-
791
- it('should store wallet expiration seconds to localStorage when provided', async () => {
792
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
793
- const mockWallet = Wallet.generate();
794
-
795
- await waitFor(() => {
796
- expect(result.current.setWallet).toBeDefined();
797
- });
798
-
799
- act(() => {
800
- result.current.setWallet(mockWallet, 900);
801
- });
802
-
803
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
804
- 'walletExpirationSeconds',
805
- '900'
806
- );
807
- expect(result.current.wallet).toEqual(mockWallet);
808
- });
809
-
810
- it('should use stored expiration seconds when duration not provided', async () => {
811
- localStorageMock.getItem.mockImplementation((key) =>
812
- key === 'walletExpirationSeconds' ? '1200' : null
813
- );
814
-
815
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
816
- const mockWallet = Wallet.generate();
817
-
818
- await waitFor(() => {
819
- expect(result.current.setWallet).toBeDefined();
820
- });
821
-
822
- act(() => {
823
- result.current.setWallet(mockWallet);
824
- });
825
-
826
- expect(result.current.wallet).toEqual(mockWallet);
827
- // Should not call setItem when no duration provided
828
- expect(localStorageMock.setItem).not.toHaveBeenCalledWith(
829
- 'walletExpirationSeconds',
830
- expect.any(String)
831
- );
832
- });
833
- });
834
-
835
- describe('Expiration Settings Management', () => {
836
- it('should set mnemonic expiration seconds and update localStorage', async () => {
837
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
838
-
839
- await waitFor(() => {
840
- expect(result.current.setMnemonicExpirationSeconds).toBeDefined();
841
- });
842
-
843
- act(() => {
844
- result.current.setMnemonicExpirationSeconds(1800);
845
- });
846
-
847
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
848
- 'mnemonicExpirationSeconds',
849
- '1800'
850
- );
851
- });
852
-
853
- it('should set wallet expiration seconds and update localStorage', async () => {
854
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
855
-
856
- await waitFor(() => {
857
- expect(result.current.setWalletExpirationSeconds).toBeDefined();
858
- });
859
-
860
- act(() => {
861
- result.current.setWalletExpirationSeconds(3600);
862
- });
863
-
864
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
865
- 'walletExpirationSeconds',
866
- '3600'
867
- );
868
- });
869
-
870
- it('should use updated expiration seconds after setting them', async () => {
871
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
872
- const mockMnemonic = new SecureString('test mnemonic');
873
- const mockWallet = Wallet.generate();
874
-
875
- await waitFor(() => {
876
- expect(result.current.setMnemonicExpirationSeconds).toBeDefined();
877
- expect(result.current.setWalletExpirationSeconds).toBeDefined();
878
- });
879
-
880
- // Set new expiration times
881
- act(() => {
882
- result.current.setMnemonicExpirationSeconds(2400);
883
- result.current.setWalletExpirationSeconds(4800);
884
- });
885
-
886
- // Clear the setItem calls from setting expiration times
887
- localStorageMock.setItem.mockClear();
888
-
889
- // Now set mnemonic and wallet without duration - should use the stored values
890
- act(() => {
891
- result.current.setMnemonic(mockMnemonic);
892
- });
893
-
894
- act(() => {
895
- result.current.setWallet(mockWallet);
896
- });
897
-
898
- expect(result.current.mnemonic).toEqual(mockMnemonic);
899
- expect(result.current.wallet).toEqual(mockWallet);
900
- // Should not call setItem again since we're using stored defaults
901
- expect(localStorageMock.setItem).not.toHaveBeenCalledWith(
902
- 'mnemonicExpirationSeconds',
903
- expect.any(String)
904
- );
905
- expect(localStorageMock.setItem).not.toHaveBeenCalledWith(
906
- 'walletExpirationSeconds',
907
- expect.any(String)
908
- );
909
- });
910
- });
911
-
912
- describe('Timeout Management', () => {
913
- it('should set expiration times and use them for timeouts', async () => {
914
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
915
- const mockMnemonic = new SecureString('test mnemonic');
916
- const mockWallet = Wallet.generate();
917
-
918
- await waitFor(() => {
919
- expect(result.current.setMnemonicExpirationSeconds).toBeDefined();
920
- expect(result.current.setWalletExpirationSeconds).toBeDefined();
921
- });
922
-
923
- // Set custom expiration times
924
- act(() => {
925
- result.current.setMnemonicExpirationSeconds(120); // 2 minutes
926
- result.current.setWalletExpirationSeconds(180); // 3 minutes
927
- });
928
-
929
- // Verify localStorage was updated
930
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
931
- 'mnemonicExpirationSeconds',
932
- '120'
933
- );
934
- expect(localStorageMock.setItem).toHaveBeenCalledWith(
935
- 'walletExpirationSeconds',
936
- '180'
937
- );
938
-
939
- // Set mnemonic and wallet without explicit duration - should use the custom times
940
- act(() => {
941
- result.current.setMnemonic(mockMnemonic);
942
- result.current.setWallet(mockWallet);
943
- });
944
-
945
- expect(result.current.mnemonic).toEqual(mockMnemonic);
946
- expect(result.current.wallet).toEqual(mockWallet);
947
-
948
- // Verify timeout functions are set up (they should be active but we won't test timing)
949
- expect(result.current.mnemonic).toBeDefined();
950
- expect(result.current.wallet).toBeDefined();
951
- });
952
- });
953
-
954
- describe('passwordLogin', () => {
955
- it('should perform successful password login', async () => {
956
- const mockMnemonic = new SecureString('test mnemonic');
957
- const mockWallet = Wallet.generate();
958
- const mockUser = createMockUser();
959
- const mockLoginResult = {
960
- token: 'password-token',
961
- message: 'Login successful',
962
- user: mockUser,
963
- wallet: mockWallet,
964
- };
965
-
966
- localStorageMock.getItem.mockImplementation((key) => {
967
- if (key === 'encryptedPassword') return 'mock-encrypted-password';
968
- if (key === 'passwordLoginSalt') return '0'.repeat(64);
969
- if (key === 'encryptedPrivateKey') return '0'.repeat(128);
970
- if (key === 'encryptedMnemonic') return '0'.repeat(128);
971
- if (key === 'pbkdf2Profile') return 'BROWSER_PASSWORD';
972
- return null;
973
- });
974
-
975
- mockPasswordLoginService.getWalletAndMnemonicFromLocalStorageBundle.mockResolvedValue(
976
- {
977
- mnemonic: mockMnemonic,
978
- wallet: mockWallet,
979
- }
980
- );
981
- mockAuthService.directLogin.mockResolvedValue(mockLoginResult);
982
-
983
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
984
-
985
- let loginResult;
986
- await act(async () => {
987
- loginResult = await result.current.passwordLogin(
988
- new SecureString('password')
989
- );
990
- });
991
-
992
- expect(loginResult).toEqual(mockLoginResult);
993
- expect(
994
- mockPasswordLoginService.getWalletAndMnemonicFromLocalStorageBundle
995
- ).toHaveBeenCalled();
996
- });
997
-
998
- it('should handle password login when not available', async () => {
999
- localStorageMock.getItem.mockReturnValue(null);
1000
-
1001
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1002
-
1003
- let loginResult;
1004
- await act(async () => {
1005
- loginResult = await result.current.passwordLogin(
1006
- new SecureString('password')
1007
- );
1008
- });
1009
-
1010
- expect(loginResult).toEqual({
1011
- error: 'error_login_passwordLoginNotSetup',
1012
- errorType: 'PasswordLoginNotSetup',
1013
- });
1014
- });
1015
- });
1016
-
1017
- describe('setUpBrowserPasswordLogin', () => {
1018
- it('should set up password login successfully', async () => {
1019
- const mockMnemonic = new SecureString('test mnemonic');
1020
- const mockWallet = Wallet.generate();
1021
-
1022
- mockPasswordLoginService.setupPasswordLoginLocalStorageBundle.mockResolvedValue(
1023
- mockWallet
1024
- );
1025
-
1026
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1027
-
1028
- let setupResult;
1029
- await act(async () => {
1030
- setupResult = await result.current.setUpBrowserPasswordLogin(
1031
- mockMnemonic,
1032
- new SecureString('password')
1033
- );
1034
- });
1035
-
1036
- expect(setupResult).toEqual({
1037
- success: true,
1038
- message: expect.any(String),
1039
- });
1040
- expect(
1041
- mockPasswordLoginService.setupPasswordLoginLocalStorageBundle
1042
- ).toHaveBeenCalled();
1043
- });
1044
-
1045
- it('should handle password setup failure', async () => {
1046
- const mockMnemonic = new SecureString('test mnemonic');
1047
- mockPasswordLoginService.setupPasswordLoginLocalStorageBundle.mockRejectedValue(
1048
- new Error('Setup failed')
1049
- );
1050
-
1051
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1052
-
1053
- let setupResult;
1054
- await act(async () => {
1055
- setupResult = await result.current.setUpBrowserPasswordLogin(
1056
- mockMnemonic,
1057
- new SecureString('password')
1058
- );
1059
- });
1060
-
1061
- expect(setupResult).toEqual({
1062
- success: false,
1063
- message: 'passwordLogin_setup_failure',
1064
- });
1065
- });
1066
- });
1067
-
1068
- describe('isBrowserPasswordLoginAvailable', () => {
1069
- it('should return true when encrypted password exists', async () => {
1070
- localStorageMock.getItem.mockImplementation((key) =>
1071
- key === 'encryptedPassword' ? 'mock-encrypted-password' : null
1072
- );
1073
-
1074
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1075
-
1076
- await waitFor(() => {
1077
- expect(result.current.isBrowserPasswordLoginAvailable?.()).toBe(true);
1078
- });
1079
- });
1080
-
1081
- it('should return false when no encrypted password exists', async () => {
1082
- localStorageMock.getItem.mockReturnValue(null);
1083
-
1084
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1085
-
1086
- await waitFor(() => {
1087
- expect(result.current.isBrowserPasswordLoginAvailable?.()).toBe(false);
1088
- });
1089
- });
1090
- });
1091
-
1092
- describe('Timeout Setup', () => {
1093
- it('should set up timeouts when setting mnemonic and wallet', async () => {
1094
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1095
- const mockMnemonic = new SecureString('test mnemonic');
1096
- const mockWallet = Wallet.generate();
1097
-
1098
- await waitFor(() => {
1099
- expect(result.current.setMnemonic).toBeDefined();
1100
- expect(result.current.setWallet).toBeDefined();
1101
- });
1102
-
1103
- act(() => {
1104
- result.current.setMnemonic(mockMnemonic, 300);
1105
- result.current.setWallet(mockWallet, 600);
1106
- });
1107
-
1108
- expect(result.current.mnemonic).toEqual(mockMnemonic);
1109
- expect(result.current.wallet).toEqual(mockWallet);
1110
- });
1111
- });
1112
-
1113
- describe('State Exposure', () => {
1114
- it('should expose mnemonicExpirationSeconds and walletExpirationSeconds', async () => {
1115
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1116
-
1117
- await waitFor(() => {
1118
- expect(typeof result.current.mnemonicExpirationSeconds).toBe('number');
1119
- expect(typeof result.current.walletExpirationSeconds).toBe('number');
1120
- });
1121
- });
1122
- });
1123
-
1124
- describe('Edge Cases', () => {
1125
- it('should handle invalid localStorage values gracefully', async () => {
1126
- localStorageMock.getItem.mockImplementation((key) => {
1127
- if (key === 'mnemonicExpirationSeconds') return 'invalid';
1128
- if (key === 'walletExpirationSeconds') return 'invalid';
1129
- return null;
1130
- });
1131
-
1132
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1133
-
1134
- await waitFor(() => {
1135
- expect(typeof result.current.mnemonicExpirationSeconds).toBe('number');
1136
- expect(typeof result.current.walletExpirationSeconds).toBe('number');
1137
- });
1138
- });
1139
-
1140
- it('should handle checkAuth with error response from verifyToken', async () => {
1141
- localStorageMock.getItem.mockImplementation((key) => {
1142
- if (key === 'authToken') return 'token-with-error';
1143
- return null;
1144
- });
1145
- mockAuthService.verifyToken.mockResolvedValue({ error: 'Token expired' });
1146
-
1147
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1148
-
1149
- await waitFor(() => {
1150
- expect(result.current.isAuthenticated).toBe(false);
1151
- expect(result.current.token).toBe(null);
1152
- });
1153
- });
1154
-
1155
- it('should handle changePassword with invalid current password', async () => {
1156
- localStorageMock.getItem.mockImplementation((key) => {
1157
- if (key === 'encryptedPassword') return 'mock-encrypted-password';
1158
- return null;
1159
- });
1160
-
1161
- mockPasswordLoginService.getWalletAndMnemonicFromLocalStorageBundle.mockResolvedValue(
1162
- {
1163
- mnemonic: null, // Invalid mnemonic
1164
- wallet: Wallet.generate(),
1165
- }
1166
- );
1167
-
1168
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1169
-
1170
- await act(async () => {
1171
- await result.current.changePassword('wrongpass', 'newpass');
1172
- });
1173
- });
1174
- });
1175
-
1176
- describe('Error Handling', () => {
1177
- it('should handle console errors gracefully', async () => {
1178
- localStorageMock.getItem.mockImplementation((key) => {
1179
- if (key === 'authToken') return 'invalid-token';
1180
- return null;
1181
- });
1182
- mockAuthService.verifyToken.mockRejectedValue(new Error('Network error'));
1183
-
1184
- const { result } = renderHook(() => useAuth(), { wrapper: TestWrapper });
1185
-
1186
- await waitFor(() => {
1187
- expect(result.current.isAuthenticated).toBe(false);
1188
- expect(consoleMock.error).toHaveBeenCalledWith(
1189
- 'Token verification failed:',
1190
- expect.any(Error)
1191
- );
1192
- });
1193
- });
1194
- });
1195
- });