@umituz/react-native-auth 1.12.0 → 2.0.2

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 (136) hide show
  1. package/README.md +0 -0
  2. package/lib/__tests__/services/AuthCoreService.test.d.ts +4 -0
  3. package/lib/__tests__/services/AuthCoreService.test.js +198 -0
  4. package/lib/__tests__/services/AuthPackage.test.d.ts +4 -0
  5. package/lib/__tests__/services/AuthPackage.test.js +177 -0
  6. package/lib/__tests__/services/GuestModeService.test.d.ts +4 -0
  7. package/lib/__tests__/services/GuestModeService.test.js +141 -0
  8. package/lib/__tests__/utils/AuthValidation.test.d.ts +4 -0
  9. package/lib/__tests__/utils/AuthValidation.test.js +222 -0
  10. package/lib/application/ports/IAuthProvider.d.ts +42 -0
  11. package/lib/application/ports/IAuthProvider.js +5 -0
  12. package/lib/application/ports/IAuthService.d.ts +48 -0
  13. package/lib/application/ports/IAuthService.js +5 -0
  14. package/lib/domain/entities/AuthUser.d.ts +12 -0
  15. package/lib/domain/entities/AuthUser.js +5 -0
  16. package/lib/domain/errors/AuthError.d.ts +36 -0
  17. package/lib/domain/errors/AuthError.js +76 -0
  18. package/lib/domain/value-objects/AuthConfig.d.ts +16 -0
  19. package/lib/domain/value-objects/AuthConfig.js +14 -0
  20. package/lib/index.d.ts +45 -0
  21. package/lib/index.js +59 -0
  22. package/lib/infrastructure/adapters/StorageProviderAdapter.d.ts +16 -0
  23. package/lib/infrastructure/adapters/StorageProviderAdapter.js +72 -0
  24. package/lib/infrastructure/adapters/UIProviderAdapter.d.ts +18 -0
  25. package/lib/infrastructure/adapters/UIProviderAdapter.js +28 -0
  26. package/lib/infrastructure/providers/FirebaseAuthProvider.d.ts +19 -0
  27. package/lib/infrastructure/providers/FirebaseAuthProvider.js +94 -0
  28. package/lib/infrastructure/services/AuthCoreService.d.ts +22 -0
  29. package/lib/infrastructure/services/AuthCoreService.js +102 -0
  30. package/lib/infrastructure/services/AuthEventService.d.ts +28 -0
  31. package/lib/infrastructure/services/AuthEventService.js +88 -0
  32. package/lib/infrastructure/services/AuthPackage.d.ts +62 -0
  33. package/lib/infrastructure/services/AuthPackage.js +91 -0
  34. package/lib/infrastructure/services/AuthService.d.ts +42 -0
  35. package/lib/infrastructure/services/AuthService.js +123 -0
  36. package/lib/infrastructure/services/GuestModeService.d.ts +23 -0
  37. package/lib/infrastructure/services/GuestModeService.js +69 -0
  38. package/lib/infrastructure/storage/GuestModeStorage.d.ts +16 -0
  39. package/lib/infrastructure/storage/GuestModeStorage.js +73 -0
  40. package/lib/infrastructure/utils/AuthErrorMapper.d.ts +8 -0
  41. package/lib/infrastructure/utils/AuthErrorMapper.js +51 -0
  42. package/lib/infrastructure/utils/AuthEventEmitter.d.ts +12 -0
  43. package/lib/infrastructure/utils/AuthEventEmitter.js +25 -0
  44. package/lib/infrastructure/utils/AuthValidation.d.ts +49 -0
  45. package/lib/infrastructure/utils/AuthValidation.js +133 -0
  46. package/lib/infrastructure/utils/UserMapper.d.ts +15 -0
  47. package/lib/infrastructure/utils/UserMapper.js +16 -0
  48. package/lib/presentation/components/AuthContainer.d.ts +10 -0
  49. package/lib/presentation/components/AuthContainer.js +27 -0
  50. package/lib/presentation/components/AuthDivider.d.ts +6 -0
  51. package/lib/presentation/components/AuthDivider.js +36 -0
  52. package/lib/presentation/components/AuthErrorDisplay.d.ts +10 -0
  53. package/lib/presentation/components/AuthErrorDisplay.js +24 -0
  54. package/lib/presentation/components/AuthFormCard.d.ts +10 -0
  55. package/lib/presentation/components/AuthFormCard.js +19 -0
  56. package/lib/presentation/components/AuthGradientBackground.d.ts +6 -0
  57. package/lib/presentation/components/AuthGradientBackground.js +8 -0
  58. package/lib/presentation/components/AuthHeader.d.ts +11 -0
  59. package/lib/presentation/components/AuthHeader.js +38 -0
  60. package/lib/presentation/components/AuthLegalLinks.d.ts +28 -0
  61. package/lib/presentation/components/AuthLegalLinks.js +54 -0
  62. package/lib/presentation/components/AuthLink.d.ts +13 -0
  63. package/lib/presentation/components/AuthLink.js +27 -0
  64. package/lib/presentation/components/LoginForm.d.ts +10 -0
  65. package/lib/presentation/components/LoginForm.js +27 -0
  66. package/lib/presentation/components/PasswordMatchIndicator.d.ts +9 -0
  67. package/lib/presentation/components/PasswordMatchIndicator.js +30 -0
  68. package/lib/presentation/components/PasswordStrengthIndicator.d.ts +11 -0
  69. package/lib/presentation/components/PasswordStrengthIndicator.js +60 -0
  70. package/lib/presentation/components/RegisterForm.d.ts +14 -0
  71. package/lib/presentation/components/RegisterForm.js +30 -0
  72. package/lib/presentation/hooks/useAuth.d.ts +44 -0
  73. package/lib/presentation/hooks/useAuth.js +38 -0
  74. package/lib/presentation/hooks/useAuthActions.d.ts +15 -0
  75. package/lib/presentation/hooks/useAuthActions.js +162 -0
  76. package/lib/presentation/hooks/useAuthState.d.ts +19 -0
  77. package/lib/presentation/hooks/useAuthState.js +79 -0
  78. package/lib/presentation/hooks/useLoginForm.d.ts +21 -0
  79. package/lib/presentation/hooks/useLoginForm.js +131 -0
  80. package/lib/presentation/hooks/useRegisterForm.d.ts +31 -0
  81. package/lib/presentation/hooks/useRegisterForm.js +136 -0
  82. package/lib/presentation/navigation/AuthNavigator.d.ts +28 -0
  83. package/lib/presentation/navigation/AuthNavigator.js +37 -0
  84. package/lib/presentation/screens/LoginScreen.d.ts +6 -0
  85. package/lib/presentation/screens/LoginScreen.js +15 -0
  86. package/lib/presentation/screens/RegisterScreen.d.ts +12 -0
  87. package/lib/presentation/screens/RegisterScreen.js +15 -0
  88. package/lib/presentation/utils/getAuthErrorMessage.d.ts +8 -0
  89. package/lib/presentation/utils/getAuthErrorMessage.js +69 -0
  90. package/package.json +12 -4
  91. package/src/__tests__/services/AuthCoreService.test.ts +247 -0
  92. package/src/__tests__/services/AuthPackage.test.ts +226 -0
  93. package/src/__tests__/services/GuestModeService.test.ts +194 -0
  94. package/src/__tests__/utils/AuthValidation.test.ts +270 -0
  95. package/src/application/ports/IAuthProvider.ts +0 -0
  96. package/src/application/ports/IAuthService.ts +0 -0
  97. package/src/domain/entities/AuthUser.ts +0 -0
  98. package/src/domain/errors/AuthError.ts +0 -0
  99. package/src/domain/value-objects/AuthConfig.ts +0 -0
  100. package/src/index.ts +0 -0
  101. package/src/infrastructure/adapters/StorageProviderAdapter.ts +73 -0
  102. package/src/infrastructure/adapters/UIProviderAdapter.ts +39 -0
  103. package/src/infrastructure/providers/FirebaseAuthProvider.ts +10 -2
  104. package/src/infrastructure/services/AuthCoreService.ts +138 -0
  105. package/src/infrastructure/services/AuthEventService.ts +115 -0
  106. package/src/infrastructure/services/AuthPackage.ts +148 -0
  107. package/src/infrastructure/services/AuthService.ts +62 -128
  108. package/src/infrastructure/services/GuestModeService.ts +86 -0
  109. package/src/infrastructure/storage/GuestModeStorage.ts +40 -14
  110. package/src/infrastructure/utils/AuthErrorMapper.ts +7 -3
  111. package/src/infrastructure/utils/AuthEventEmitter.ts +0 -0
  112. package/src/infrastructure/utils/AuthValidation.ts +47 -17
  113. package/src/infrastructure/utils/UserMapper.ts +0 -0
  114. package/src/presentation/components/AuthContainer.tsx +0 -0
  115. package/src/presentation/components/AuthDivider.tsx +0 -0
  116. package/src/presentation/components/AuthErrorDisplay.tsx +0 -0
  117. package/src/presentation/components/AuthFormCard.tsx +0 -0
  118. package/src/presentation/components/AuthGradientBackground.tsx +0 -0
  119. package/src/presentation/components/AuthHeader.tsx +0 -0
  120. package/src/presentation/components/AuthLegalLinks.tsx +0 -0
  121. package/src/presentation/components/AuthLink.tsx +0 -0
  122. package/src/presentation/components/LoginForm.tsx +0 -0
  123. package/src/presentation/components/PasswordMatchIndicator.tsx +2 -2
  124. package/src/presentation/components/PasswordStrengthIndicator.tsx +0 -0
  125. package/src/presentation/components/RegisterForm.tsx +0 -0
  126. package/src/presentation/hooks/useAuth.ts +0 -0
  127. package/src/presentation/hooks/useAuthActions.ts +8 -11
  128. package/src/presentation/hooks/useAuthState.ts +10 -0
  129. package/src/presentation/hooks/useLoginForm.ts +0 -0
  130. package/src/presentation/hooks/useRegisterForm.ts +16 -17
  131. package/src/presentation/navigation/AuthNavigator.tsx +2 -2
  132. package/src/presentation/screens/LoginScreen.tsx +3 -6
  133. package/src/presentation/screens/RegisterScreen.tsx +3 -6
  134. package/src/presentation/utils/getAuthErrorMessage.ts +0 -0
  135. package/src/types/external.d.ts +68 -0
  136. package/LICENSE +0 -22
@@ -0,0 +1,194 @@
1
+ /**
2
+ * GuestModeService Tests
3
+ */
4
+
5
+ import { GuestModeService } from '../../../src/infrastructure/services/GuestModeService';
6
+ import type { IStorageProvider } from '../../../src/infrastructure/services/GuestModeService';
7
+
8
+ describe('GuestModeService', () => {
9
+ let guestModeService: GuestModeService;
10
+ let mockStorageProvider: jest.Mocked<IStorageProvider>;
11
+
12
+ beforeEach(() => {
13
+ mockStorageProvider = {
14
+ get: jest.fn(),
15
+ set: jest.fn(),
16
+ remove: jest.fn(),
17
+ };
18
+
19
+ guestModeService = new GuestModeService('@test_guest_mode');
20
+ });
21
+
22
+ describe('constructor', () => {
23
+ it('should use default storage key when none provided', () => {
24
+ const service = new GuestModeService();
25
+ expect(service.getIsGuestMode()).toBe(false);
26
+ });
27
+
28
+ it('should use custom storage key when provided', () => {
29
+ const service = new GuestModeService('@custom_key');
30
+ expect(service.getIsGuestMode()).toBe(false);
31
+ });
32
+ });
33
+
34
+ describe('load', () => {
35
+ it('should load false when storage returns null', async () => {
36
+ mockStorageProvider.get.mockResolvedValue(null);
37
+
38
+ const result = await guestModeService.load(mockStorageProvider);
39
+
40
+ expect(result).toBe(false);
41
+ expect(mockStorageProvider.get).toHaveBeenCalledWith('@test_guest_mode');
42
+ });
43
+
44
+ it('should load false when storage returns "false"', async () => {
45
+ mockStorageProvider.get.mockResolvedValue('false');
46
+
47
+ const result = await guestModeService.load(mockStorageProvider);
48
+
49
+ expect(result).toBe(false);
50
+ });
51
+
52
+ it('should load true when storage returns "true"', async () => {
53
+ mockStorageProvider.get.mockResolvedValue('true');
54
+
55
+ const result = await guestModeService.load(mockStorageProvider);
56
+
57
+ expect(result).toBe(true);
58
+ expect(guestModeService.getIsGuestMode()).toBe(true);
59
+ });
60
+
61
+ it('should handle storage errors gracefully', async () => {
62
+ mockStorageProvider.get.mockRejectedValue(new Error('Storage error'));
63
+
64
+ const result = await guestModeService.load(mockStorageProvider);
65
+
66
+ expect(result).toBe(false);
67
+ });
68
+ });
69
+
70
+ describe('save', () => {
71
+ it('should save true to storage when guest mode is enabled', async () => {
72
+ guestModeService.setGuestMode(true);
73
+
74
+ await guestModeService.save(mockStorageProvider);
75
+
76
+ expect(mockStorageProvider.set).toHaveBeenCalledWith('@test_guest_mode', 'true');
77
+ });
78
+
79
+ it('should remove from storage when guest mode is disabled', async () => {
80
+ guestModeService.setGuestMode(false);
81
+
82
+ await guestModeService.save(mockStorageProvider);
83
+
84
+ expect(mockStorageProvider.remove).toHaveBeenCalledWith('@test_guest_mode');
85
+ });
86
+
87
+ it('should handle storage errors gracefully', async () => {
88
+ guestModeService.setGuestMode(true);
89
+ mockStorageProvider.set.mockRejectedValue(new Error('Storage error'));
90
+
91
+ await expect(guestModeService.save(mockStorageProvider)).resolves.not.toThrow();
92
+ });
93
+ });
94
+
95
+ describe('clear', () => {
96
+ it('should clear guest mode and remove from storage', async () => {
97
+ guestModeService.setGuestMode(true);
98
+
99
+ await guestModeService.clear(mockStorageProvider);
100
+
101
+ expect(guestModeService.getIsGuestMode()).toBe(false);
102
+ expect(mockStorageProvider.remove).toHaveBeenCalledWith('@test_guest_mode');
103
+ });
104
+
105
+ it('should handle storage errors gracefully', async () => {
106
+ mockStorageProvider.remove.mockRejectedValue(new Error('Storage error'));
107
+
108
+ await expect(guestModeService.clear(mockStorageProvider)).resolves.not.toThrow();
109
+ expect(guestModeService.getIsGuestMode()).toBe(false);
110
+ });
111
+ });
112
+
113
+ describe('enable', () => {
114
+ let mockAuthProvider: any;
115
+
116
+ beforeEach(() => {
117
+ mockAuthProvider = {
118
+ getCurrentUser: jest.fn(),
119
+ signOut: jest.fn(),
120
+ };
121
+ });
122
+
123
+ it('should enable guest mode without provider', async () => {
124
+ await guestModeService.enable(mockStorageProvider);
125
+
126
+ expect(guestModeService.getIsGuestMode()).toBe(true);
127
+ expect(mockStorageProvider.set).toHaveBeenCalledWith('@test_guest_mode', 'true');
128
+ });
129
+
130
+ it('should sign out provider if user is logged in', async () => {
131
+ mockAuthProvider.getCurrentUser.mockReturnValue({ uid: 'test-user' });
132
+ mockAuthProvider.signOut.mockResolvedValue(undefined);
133
+
134
+ await guestModeService.enable(mockStorageProvider, mockAuthProvider);
135
+
136
+ expect(mockAuthProvider.signOut).toHaveBeenCalled();
137
+ expect(guestModeService.getIsGuestMode()).toBe(true);
138
+ });
139
+
140
+ it('should ignore sign out errors', async () => {
141
+ mockAuthProvider.getCurrentUser.mockReturnValue({ uid: 'test-user' });
142
+ mockAuthProvider.signOut.mockRejectedValue(new Error('Sign out error'));
143
+
144
+ await expect(guestModeService.enable(mockStorageProvider, mockAuthProvider)).resolves.not.toThrow();
145
+ expect(guestModeService.getIsGuestMode()).toBe(true);
146
+ });
147
+ });
148
+
149
+ describe('wrapAuthStateCallback', () => {
150
+ it('should call callback with user when not in guest mode', () => {
151
+ const callback = jest.fn();
152
+ const wrappedCallback = guestModeService.wrapAuthStateCallback(callback);
153
+ const mockUser = { uid: 'test-user' };
154
+
155
+ wrappedCallback(mockUser);
156
+
157
+ expect(callback).toHaveBeenCalledWith(mockUser);
158
+ });
159
+
160
+ it('should call callback with null when in guest mode', () => {
161
+ guestModeService.setGuestMode(true);
162
+ const callback = jest.fn();
163
+ const wrappedCallback = guestModeService.wrapAuthStateCallback(callback);
164
+ const mockUser = { uid: 'test-user' };
165
+
166
+ wrappedCallback(mockUser);
167
+
168
+ expect(callback).toHaveBeenCalledWith(null);
169
+ });
170
+
171
+ it('should call callback with null when user is null', () => {
172
+ const callback = jest.fn();
173
+ const wrappedCallback = guestModeService.wrapAuthStateCallback(callback);
174
+
175
+ wrappedCallback(null);
176
+
177
+ expect(callback).toHaveBeenCalledWith(null);
178
+ });
179
+ });
180
+
181
+ describe('getIsGuestMode and setGuestMode', () => {
182
+ it('should return initial guest mode state', () => {
183
+ expect(guestModeService.getIsGuestMode()).toBe(false);
184
+ });
185
+
186
+ it('should set and get guest mode state', () => {
187
+ guestModeService.setGuestMode(true);
188
+ expect(guestModeService.getIsGuestMode()).toBe(true);
189
+
190
+ guestModeService.setGuestMode(false);
191
+ expect(guestModeService.getIsGuestMode()).toBe(false);
192
+ });
193
+ });
194
+ });
@@ -0,0 +1,270 @@
1
+ /**
2
+ * AuthValidation Tests
3
+ */
4
+
5
+ import {
6
+ validateEmail,
7
+ validatePasswordForLogin,
8
+ validatePasswordForRegister,
9
+ validatePasswordConfirmation,
10
+ validateDisplayName,
11
+ } from '../../../src/infrastructure/utils/AuthValidation';
12
+ import { DEFAULT_PASSWORD_CONFIG } from '../../../src/domain/value-objects/AuthConfig';
13
+ import { initializeAuthPackage } from '../../../src/infrastructure/services/AuthPackage';
14
+
15
+ describe('AuthValidation', () => {
16
+ beforeEach(() => {
17
+ // Reset package config before each test
18
+ initializeAuthPackage();
19
+ });
20
+
21
+ describe('validateEmail', () => {
22
+ it('should reject empty email', () => {
23
+ const result = validateEmail('');
24
+ expect(result.isValid).toBe(false);
25
+ expect(result.error).toBe('Email is required');
26
+ });
27
+
28
+ it('should reject whitespace-only email', () => {
29
+ const result = validateEmail(' ');
30
+ expect(result.isValid).toBe(false);
31
+ expect(result.error).toBe('Email is required');
32
+ });
33
+
34
+ it('should reject invalid email format', () => {
35
+ const result = validateEmail('invalid-email');
36
+ expect(result.isValid).toBe(false);
37
+ expect(result.error).toBe('Please enter a valid email address');
38
+ });
39
+
40
+ it('should accept valid email format', () => {
41
+ const result = validateEmail('test@example.com');
42
+ expect(result.isValid).toBe(true);
43
+ expect(result.error).toBeUndefined();
44
+ });
45
+
46
+ it('should accept valid email with subdomain', () => {
47
+ const result = validateEmail('test@mail.example.com');
48
+ expect(result.isValid).toBe(true);
49
+ expect(result.error).toBeUndefined();
50
+ });
51
+
52
+ it('should trim whitespace', () => {
53
+ const result = validateEmail(' test@example.com ');
54
+ expect(result.isValid).toBe(true);
55
+ expect(result.error).toBeUndefined();
56
+ });
57
+ });
58
+
59
+ describe('validatePasswordForLogin', () => {
60
+ it('should reject empty password', () => {
61
+ const result = validatePasswordForLogin('');
62
+ expect(result.isValid).toBe(false);
63
+ expect(result.error).toBe('Password is required');
64
+ });
65
+
66
+ it('should accept any non-empty password', () => {
67
+ const result = validatePasswordForLogin('any');
68
+ expect(result.isValid).toBe(true);
69
+ expect(result.error).toBeUndefined();
70
+ });
71
+
72
+ it('should accept password with spaces', () => {
73
+ const result = validatePasswordForLogin(' password ');
74
+ expect(result.isValid).toBe(true);
75
+ expect(result.error).toBeUndefined();
76
+ });
77
+ });
78
+
79
+ describe('validatePasswordForRegister', () => {
80
+ const config = DEFAULT_PASSWORD_CONFIG;
81
+
82
+ it('should reject empty password', () => {
83
+ const result = validatePasswordForRegister('', config);
84
+ expect(result.isValid).toBe(false);
85
+ expect(result.error).toBe('Password is required');
86
+ expect(result.requirements.hasMinLength).toBe(false);
87
+ });
88
+
89
+ it('should reject password that is too short', () => {
90
+ const result = validatePasswordForRegister('123', config);
91
+ expect(result.isValid).toBe(false);
92
+ expect(result.error).toBe(`Password must be at least ${config.minLength} characters`);
93
+ expect(result.requirements.hasMinLength).toBe(false);
94
+ });
95
+
96
+ it('should accept password that meets minimum length', () => {
97
+ const result = validatePasswordForRegister('12345678', config);
98
+ expect(result.requirements.hasMinLength).toBe(true);
99
+ });
100
+
101
+ it('should validate uppercase requirement', () => {
102
+ const configWithUppercase = {
103
+ ...config,
104
+ requireUppercase: true,
105
+ };
106
+
107
+ const result = validatePasswordForRegister('password', configWithUppercase);
108
+ expect(result.isValid).toBe(false);
109
+ expect(result.error).toBe('Password must contain at least one uppercase letter');
110
+ expect(result.requirements.hasUppercase).toBe(false);
111
+
112
+ const validResult = validatePasswordForRegister('Password', configWithUppercase);
113
+ expect(validResult.requirements.hasUppercase).toBe(true);
114
+ });
115
+
116
+ it('should validate lowercase requirement', () => {
117
+ const configWithLowercase = {
118
+ ...config,
119
+ requireLowercase: true,
120
+ };
121
+
122
+ const result = validatePasswordForRegister('PASSWORD', configWithLowercase);
123
+ expect(result.isValid).toBe(false);
124
+ expect(result.error).toBe('Password must contain at least one lowercase letter');
125
+ expect(result.requirements.hasLowercase).toBe(false);
126
+
127
+ const validResult = validatePasswordForRegister('Password', configWithLowercase);
128
+ expect(validResult.requirements.hasLowercase).toBe(true);
129
+ });
130
+
131
+ it('should validate number requirement', () => {
132
+ const configWithNumber = {
133
+ ...config,
134
+ requireNumber: true,
135
+ };
136
+
137
+ const result = validatePasswordForRegister('Password', configWithNumber);
138
+ expect(result.isValid).toBe(false);
139
+ expect(result.error).toBe('Password must contain at least one number');
140
+ expect(result.requirements.hasNumber).toBe(false);
141
+
142
+ const validResult = validatePasswordForRegister('Password1', configWithNumber);
143
+ expect(validResult.requirements.hasNumber).toBe(true);
144
+ });
145
+
146
+ it('should validate special character requirement', () => {
147
+ const configWithSpecial = {
148
+ ...config,
149
+ requireSpecialChar: true,
150
+ };
151
+
152
+ const result = validatePasswordForRegister('Password1', configWithSpecial);
153
+ expect(result.isValid).toBe(false);
154
+ expect(result.error).toBe('Password must contain at least one special character');
155
+ expect(result.requirements.hasSpecialChar).toBe(false);
156
+
157
+ const validResult = validatePasswordForRegister('Password1!', configWithSpecial);
158
+ expect(validResult.requirements.hasSpecialChar).toBe(true);
159
+ });
160
+
161
+ it('should accept password that meets all requirements', () => {
162
+ const strictConfig = {
163
+ ...config,
164
+ requireUppercase: true,
165
+ requireLowercase: true,
166
+ requireNumber: true,
167
+ requireSpecialChar: true,
168
+ };
169
+
170
+ const result = validatePasswordForRegister('Password1!', strictConfig);
171
+ expect(result.isValid).toBe(true);
172
+ expect(result.requirements).toEqual({
173
+ hasMinLength: true,
174
+ hasUppercase: true,
175
+ hasLowercase: true,
176
+ hasNumber: true,
177
+ hasSpecialChar: true,
178
+ });
179
+ });
180
+
181
+ it('should skip requirements when disabled', () => {
182
+ const lenientConfig = {
183
+ ...config,
184
+ requireUppercase: false,
185
+ requireLowercase: false,
186
+ requireNumber: false,
187
+ requireSpecialChar: false,
188
+ };
189
+
190
+ const result = validatePasswordForRegister('password', lenientConfig);
191
+ expect(result.requirements).toEqual({
192
+ hasMinLength: true,
193
+ hasUppercase: true, // Should be true when requirement is disabled
194
+ hasLowercase: true, // Should be true when requirement is disabled
195
+ hasNumber: true, // Should be true when requirement is disabled
196
+ hasSpecialChar: true, // Should be true when requirement is disabled
197
+ });
198
+ });
199
+ });
200
+
201
+ describe('validatePasswordConfirmation', () => {
202
+ it('should reject empty confirmation', () => {
203
+ const result = validatePasswordConfirmation('password', '');
204
+ expect(result.isValid).toBe(false);
205
+ expect(result.error).toBe('Please confirm your password');
206
+ });
207
+
208
+ it('should reject mismatched passwords', () => {
209
+ const result = validatePasswordConfirmation('password', 'different');
210
+ expect(result.isValid).toBe(false);
211
+ expect(result.error).toBe('Passwords do not match');
212
+ });
213
+
214
+ it('should accept matching passwords', () => {
215
+ const result = validatePasswordConfirmation('password', 'password');
216
+ expect(result.isValid).toBe(true);
217
+ expect(result.error).toBeUndefined();
218
+ });
219
+
220
+ it('should handle empty passwords that match', () => {
221
+ const result = validatePasswordConfirmation('', '');
222
+ expect(result.isValid).toBe(true);
223
+ expect(result.error).toBeUndefined();
224
+ });
225
+ });
226
+
227
+ describe('validateDisplayName', () => {
228
+ it('should reject empty name', () => {
229
+ const result = validateDisplayName('');
230
+ expect(result.isValid).toBe(false);
231
+ expect(result.error).toBe('Name is required');
232
+ });
233
+
234
+ it('should reject whitespace-only name', () => {
235
+ const result = validateDisplayName(' ');
236
+ expect(result.isValid).toBe(false);
237
+ expect(result.error).toBe('Name is required');
238
+ });
239
+
240
+ it('should reject name that is too short', () => {
241
+ const result = validateDisplayName('A');
242
+ expect(result.isValid).toBe(false);
243
+ expect(result.error).toBe('Name must be at least 2 characters');
244
+ });
245
+
246
+ it('should accept name that meets minimum length', () => {
247
+ const result = validateDisplayName('Al');
248
+ expect(result.isValid).toBe(true);
249
+ expect(result.error).toBeUndefined();
250
+ });
251
+
252
+ it('should use custom minimum length', () => {
253
+ const result = validateDisplayName('Al', 3);
254
+ expect(result.isValid).toBe(false);
255
+ expect(result.error).toBe('Name must be at least 3 characters');
256
+ });
257
+
258
+ it('should trim whitespace', () => {
259
+ const result = validateDisplayName(' John Doe ');
260
+ expect(result.isValid).toBe(true);
261
+ expect(result.error).toBeUndefined();
262
+ });
263
+
264
+ it('should accept name with special characters', () => {
265
+ const result = validateDisplayName('John-O\'Connor');
266
+ expect(result.isValid).toBe(true);
267
+ expect(result.error).toBeUndefined();
268
+ });
269
+ });
270
+ });
File without changes
File without changes
File without changes
File without changes
File without changes
package/src/index.ts CHANGED
File without changes
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Storage Provider Adapter
3
+ * Adapts external storage implementations to our IStorageProvider interface
4
+ */
5
+
6
+ import type { IStorageProvider } from "../services/GuestModeService";
7
+
8
+ export class StorageProviderAdapter implements IStorageProvider {
9
+ private storage: any;
10
+
11
+ constructor(storage: any) {
12
+ this.storage = storage;
13
+ }
14
+
15
+ async get(key: string): Promise<string | null> {
16
+ try {
17
+ if (this.storage.getString) {
18
+ // @umituz/react-native-storage format
19
+ const result = await this.storage.getString(key, null);
20
+ return result?.value ?? null;
21
+ } else if (this.storage.getItem) {
22
+ // AsyncStorage format
23
+ return await this.storage.getItem(key);
24
+ } else {
25
+ throw new Error("Unsupported storage implementation");
26
+ }
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ async set(key: string, value: string): Promise<void> {
33
+ try {
34
+ if (this.storage.setString) {
35
+ // @umituz/react-native-storage format
36
+ await this.storage.setString(key, value);
37
+ } else if (this.storage.setItem) {
38
+ // AsyncStorage format
39
+ await this.storage.setItem(key, value);
40
+ } else {
41
+ throw new Error("Unsupported storage implementation");
42
+ }
43
+ } catch (error) {
44
+ if (__DEV__) {
45
+ console.warn("[StorageProviderAdapter] Failed to set value:", error);
46
+ }
47
+ throw error;
48
+ }
49
+ }
50
+
51
+ async remove(key: string): Promise<void> {
52
+ try {
53
+ if (this.storage.removeItem) {
54
+ // Both AsyncStorage and @umituz/react-native-storage support removeItem
55
+ await this.storage.removeItem(key);
56
+ } else {
57
+ throw new Error("Unsupported storage implementation");
58
+ }
59
+ } catch (error) {
60
+ if (__DEV__) {
61
+ console.warn("[StorageProviderAdapter] Failed to remove value:", error);
62
+ }
63
+ throw error;
64
+ }
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Create storage provider from various storage implementations
70
+ */
71
+ export function createStorageProvider(storage: any): IStorageProvider {
72
+ return new StorageProviderAdapter(storage);
73
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * UI Provider Adapter
3
+ * Adapts external UI implementations to our IUIProvider interface
4
+ */
5
+
6
+ import type { IUIProvider } from "../services/AuthPackage";
7
+
8
+ export class UIProviderAdapter implements IUIProvider {
9
+ private theme: any;
10
+ private localization: any;
11
+
12
+ constructor(theme?: any, localization?: any) {
13
+ this.theme = theme;
14
+ this.localization = localization;
15
+ }
16
+
17
+ getTheme(): any {
18
+ return this.theme;
19
+ }
20
+
21
+ getLocalization(): any {
22
+ return this.localization;
23
+ }
24
+
25
+ updateTheme(theme: any): void {
26
+ this.theme = theme;
27
+ }
28
+
29
+ updateLocalization(localization: any): void {
30
+ this.localization = localization;
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Create UI provider from theme and localization implementations
36
+ */
37
+ export function createUIProvider(theme?: any, localization?: any): IUIProvider {
38
+ return new UIProviderAdapter(theme, localization);
39
+ }
@@ -55,7 +55,11 @@ export class FirebaseAuthProvider implements IAuthProvider {
55
55
  credentials.email.trim(),
56
56
  credentials.password
57
57
  );
58
- return mapToAuthUser(userCredential.user) as AuthUser;
58
+ const user = mapToAuthUser(userCredential.user);
59
+ if (!user) {
60
+ throw new Error("Failed to sign in");
61
+ }
62
+ return user;
59
63
  } catch (error: unknown) {
60
64
  throw mapFirebaseAuthError(error);
61
65
  }
@@ -83,7 +87,11 @@ export class FirebaseAuthProvider implements IAuthProvider {
83
87
  }
84
88
  }
85
89
 
86
- return mapToAuthUser(userCredential.user) as AuthUser;
90
+ const user = mapToAuthUser(userCredential.user);
91
+ if (!user) {
92
+ throw new Error("Failed to create user account");
93
+ }
94
+ return user;
87
95
  } catch (error: unknown) {
88
96
  throw mapFirebaseAuthError(error);
89
97
  }