@sneat/core 0.1.0

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 (66) hide show
  1. package/eslint.config.js +7 -0
  2. package/ng-package.json +7 -0
  3. package/package.json +14 -0
  4. package/project.json +38 -0
  5. package/src/index.ts +1 -0
  6. package/src/lib/analytics.interface.ts +34 -0
  7. package/src/lib/animations/form-animations.spec.ts +26 -0
  8. package/src/lib/animations/form-animations.ts +11 -0
  9. package/src/lib/animations/index.ts +2 -0
  10. package/src/lib/animations/list-animations.spec.ts +50 -0
  11. package/src/lib/animations/list-animations.ts +44 -0
  12. package/src/lib/app.service.ts +33 -0
  13. package/src/lib/constants.spec.ts +20 -0
  14. package/src/lib/constants.ts +1 -0
  15. package/src/lib/core-models.ts +12 -0
  16. package/src/lib/directives/index.ts +1 -0
  17. package/src/lib/directives/sneat-select-all-on-focus.directive.spec.ts +142 -0
  18. package/src/lib/directives/sneat-select-all-on-focus.directive.ts +36 -0
  19. package/src/lib/environment-config.ts +54 -0
  20. package/src/lib/eq.spec.ts +24 -0
  21. package/src/lib/eq.ts +1 -0
  22. package/src/lib/exclude-undefined.spec.ts +165 -0
  23. package/src/lib/exclude-undefined.ts +47 -0
  24. package/src/lib/form-field.ts +5 -0
  25. package/src/lib/index.ts +21 -0
  26. package/src/lib/interfaces.spec.ts +116 -0
  27. package/src/lib/interfaces.ts +85 -0
  28. package/src/lib/location-href.spec.ts +53 -0
  29. package/src/lib/location-href.ts +9 -0
  30. package/src/lib/logging/interfaces.ts +19 -0
  31. package/src/lib/logging.spec.ts +132 -0
  32. package/src/lib/logging.ts +33 -0
  33. package/src/lib/nav/index.ts +2 -0
  34. package/src/lib/nav/nav-context.ts +16 -0
  35. package/src/lib/nav/routing-state.spec.ts +65 -0
  36. package/src/lib/nav/routing-state.ts +26 -0
  37. package/src/lib/services/index.ts +3 -0
  38. package/src/lib/services/ng-module-preloader.service.spec.ts +72 -0
  39. package/src/lib/services/ng-module-preloader.service.ts +125 -0
  40. package/src/lib/services/sneat-nav.service.spec.ts +95 -0
  41. package/src/lib/services/sneat-nav.service.ts +46 -0
  42. package/src/lib/services/top-menu.service.spec.ts +42 -0
  43. package/src/lib/services/top-menu.service.ts +19 -0
  44. package/src/lib/sneat-enum-keys.ts +2 -0
  45. package/src/lib/sneat-extensions.spec.ts +127 -0
  46. package/src/lib/sneat-extensions.ts +49 -0
  47. package/src/lib/store.spec.ts +156 -0
  48. package/src/lib/store.ts +54 -0
  49. package/src/lib/team-type.spec.ts +8 -0
  50. package/src/lib/team-type.ts +13 -0
  51. package/src/lib/testing/base-test-setup.ts +247 -0
  52. package/src/lib/testing/test-setup-light.ts +1 -0
  53. package/src/lib/testing/test-setup.ts +70 -0
  54. package/src/lib/types/age-group.ts +1 -0
  55. package/src/lib/types/gender.spec.ts +42 -0
  56. package/src/lib/types/gender.ts +12 -0
  57. package/src/lib/types/index.ts +2 -0
  58. package/src/lib/utils/datetimes.spec.ts +144 -0
  59. package/src/lib/utils/datetimes.ts +51 -0
  60. package/src/lib/utils/index.ts +1 -0
  61. package/src/test-setup.ts +3 -0
  62. package/tsconfig.json +13 -0
  63. package/tsconfig.lib.json +19 -0
  64. package/tsconfig.lib.prod.json +7 -0
  65. package/tsconfig.spec.json +31 -0
  66. package/vite.config.mts +10 -0
@@ -0,0 +1,49 @@
1
+ export interface ISneatExtension {
2
+ id: string;
3
+ title: string;
4
+ emoji: string;
5
+ }
6
+
7
+ const assetsExtension: ISneatExtension = {
8
+ id: 'assets',
9
+ title: 'Assets',
10
+ emoji: '🏡',
11
+ };
12
+
13
+ // const contactsExtension: ISneatExtension = {
14
+ // id: 'contacts',
15
+ // title: 'Contacts',
16
+ // emoji: '📇'
17
+ // }
18
+
19
+ const documentsExtension: ISneatExtension = {
20
+ id: 'documents',
21
+ title: 'Documents',
22
+ emoji: '📄',
23
+ };
24
+
25
+ const sizesExtension: ISneatExtension = {
26
+ id: 'sizes',
27
+ title: 'Sizes',
28
+ emoji: '📏',
29
+ };
30
+
31
+ const calendariumExtension: ISneatExtension = {
32
+ id: 'calendarium',
33
+ title: 'Calendar',
34
+ emoji: '🗓️',
35
+ };
36
+
37
+ export const defaultFamilyExtension: ISneatExtension[] = [
38
+ assetsExtension,
39
+ calendariumExtension,
40
+ documentsExtension,
41
+ sizesExtension,
42
+ ];
43
+
44
+ export const defaultFamilyMemberExtensions: ISneatExtension[] = [
45
+ assetsExtension,
46
+ calendariumExtension,
47
+ documentsExtension,
48
+ sizesExtension,
49
+ ];
@@ -0,0 +1,156 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ STORE_ID_FIRESTORE,
4
+ STORE_TYPE_FIRESTORE,
5
+ STORE_TYPE_GITHUB,
6
+ STORE_ID_GITHUB_COM,
7
+ GITLAB_REPO_PREFIX,
8
+ storeCanProvideListOfProjects,
9
+ storeRefToId,
10
+ parseStoreRef,
11
+ IStoreRef,
12
+ } from './store';
13
+
14
+ describe('store', () => {
15
+ describe('constants', () => {
16
+ it('should have correct value for STORE_ID_FIRESTORE', () => {
17
+ expect(STORE_ID_FIRESTORE).toBe('firestore');
18
+ });
19
+
20
+ it('should have correct value for STORE_TYPE_FIRESTORE', () => {
21
+ expect(STORE_TYPE_FIRESTORE).toBe('firestore');
22
+ });
23
+
24
+ it('should have correct value for STORE_TYPE_GITHUB', () => {
25
+ expect(STORE_TYPE_GITHUB).toBe('github');
26
+ });
27
+
28
+ it('should have correct value for STORE_ID_GITHUB_COM', () => {
29
+ expect(STORE_ID_GITHUB_COM).toBe('github.com');
30
+ });
31
+
32
+ it('should have correct value for GITLAB_REPO_PREFIX', () => {
33
+ expect(GITLAB_REPO_PREFIX).toBe('gitlab.');
34
+ });
35
+ });
36
+
37
+ describe('storeCanProvideListOfProjects', () => {
38
+ it('should return false for firestore store', () => {
39
+ expect(storeCanProvideListOfProjects(STORE_ID_FIRESTORE)).toBe(false);
40
+ });
41
+
42
+ it('should return false for github.com store', () => {
43
+ expect(storeCanProvideListOfProjects(STORE_ID_GITHUB_COM)).toBe(false);
44
+ });
45
+
46
+ it('should return true for custom store', () => {
47
+ expect(storeCanProvideListOfProjects('custom-store')).toBe(true);
48
+ });
49
+
50
+ it('should return true for gitlab store', () => {
51
+ expect(storeCanProvideListOfProjects('gitlab.example.com')).toBe(true);
52
+ });
53
+ });
54
+
55
+ describe('storeRefToId', () => {
56
+ it('should return type for firestore without url', () => {
57
+ const ref: IStoreRef = { type: 'firestore' };
58
+ expect(storeRefToId(ref)).toBe('firestore');
59
+ });
60
+
61
+ it('should return url for firestore with url', () => {
62
+ const ref: IStoreRef = { type: 'firestore', url: 'custom-firestore' };
63
+ expect(storeRefToId(ref)).toBe('custom-firestore');
64
+ });
65
+
66
+ it('should return type for github without url', () => {
67
+ const ref: IStoreRef = { type: 'github' };
68
+ expect(storeRefToId(ref)).toBe('github');
69
+ });
70
+
71
+ it('should return url for github with url', () => {
72
+ const ref: IStoreRef = { type: 'github', url: 'github.com' };
73
+ expect(storeRefToId(ref)).toBe('github.com');
74
+ });
75
+
76
+ it('should return url for gitlab with url', () => {
77
+ const ref: IStoreRef = { type: 'gitlab', url: 'gitlab.example.com' };
78
+ expect(storeRefToId(ref)).toBe('gitlab.example.com');
79
+ });
80
+
81
+ it('should throw error for gitlab without url', () => {
82
+ const ref: IStoreRef = { type: 'gitlab' };
83
+ expect(() => storeRefToId(ref)).toThrow(
84
+ 'store with type "agent" must have URL',
85
+ );
86
+ });
87
+
88
+ it('should return url for agent with url', () => {
89
+ const ref: IStoreRef = { type: 'agent', url: 'http://example.com' };
90
+ expect(storeRefToId(ref)).toBe('http://example.com');
91
+ });
92
+
93
+ it('should throw error for agent without url', () => {
94
+ const ref: IStoreRef = { type: 'agent' };
95
+ expect(() => storeRefToId(ref)).toThrow(
96
+ 'store with type "agent" must have URL',
97
+ );
98
+ });
99
+
100
+ it('should return empty string for browser type', () => {
101
+ const ref: IStoreRef = { type: 'browser' };
102
+ expect(storeRefToId(ref)).toBe('');
103
+ });
104
+ });
105
+
106
+ describe('parseStoreRef', () => {
107
+ it('should throw error for undefined storeId', () => {
108
+ expect(() => parseStoreRef(undefined)).toThrow(
109
+ 'storeId is a required parameter',
110
+ );
111
+ });
112
+
113
+ it('should throw error for empty storeId', () => {
114
+ expect(() => parseStoreRef('')).toThrow(
115
+ 'storeId is a required parameter',
116
+ );
117
+ });
118
+
119
+ it('should parse firestore store', () => {
120
+ const result = parseStoreRef('firestore');
121
+ expect(result).toEqual({ type: 'firestore' });
122
+ });
123
+
124
+ it('should parse github store', () => {
125
+ const result = parseStoreRef('github');
126
+ expect(result).toEqual({ type: 'github' });
127
+ });
128
+
129
+ it('should parse github.com as github type', () => {
130
+ const result = parseStoreRef('github.com');
131
+ expect(result).toEqual({ type: 'github' });
132
+ });
133
+
134
+ it('should parse http- prefix as agent with http url', () => {
135
+ const result = parseStoreRef('http-example.com');
136
+ expect(result).toEqual({ type: 'agent', url: 'http://example.com' });
137
+ });
138
+
139
+ it('should parse https- prefix as agent with https url', () => {
140
+ const result = parseStoreRef('https-example.com');
141
+ expect(result).toEqual({ type: 'agent', url: 'https://example.com' });
142
+ });
143
+
144
+ it('should throw error for unsupported format', () => {
145
+ expect(() => parseStoreRef('unsupported-format')).toThrow(
146
+ 'unsupported format of store id:unsupported-format',
147
+ );
148
+ });
149
+
150
+ it('should throw error for random string', () => {
151
+ expect(() => parseStoreRef('random-string')).toThrow(
152
+ 'unsupported format of store id:random-string',
153
+ );
154
+ });
155
+ });
156
+ });
@@ -0,0 +1,54 @@
1
+ // Migrated
2
+ export const STORE_ID_FIRESTORE = 'firestore';
3
+ export const STORE_TYPE_FIRESTORE = 'firestore';
4
+
5
+ export const STORE_TYPE_GITHUB = 'github';
6
+ export const STORE_ID_GITHUB_COM = 'github.com';
7
+
8
+ export const GITLAB_REPO_PREFIX = 'gitlab.';
9
+
10
+ export function storeCanProvideListOfProjects(storeId: string): boolean {
11
+ return !(storeId === STORE_ID_FIRESTORE || storeId === STORE_ID_GITHUB_COM);
12
+ }
13
+
14
+ export type StoreType = 'firestore' | 'agent' | 'browser' | 'github' | 'gitlab';
15
+
16
+ export interface IStoreRef {
17
+ type: StoreType;
18
+ id?: string;
19
+ url?: string;
20
+ }
21
+
22
+ export function storeRefToId(ref: IStoreRef): string {
23
+ switch (ref.type) {
24
+ case 'firestore':
25
+ case 'github':
26
+ return ref.url || ref.type;
27
+ case 'gitlab':
28
+ case 'agent':
29
+ if (ref.url) {
30
+ return ref.url;
31
+ }
32
+ throw new Error('store with type "agent" must have URL');
33
+ default:
34
+ return ``;
35
+ }
36
+ }
37
+
38
+ export function parseStoreRef(storeId?: string): IStoreRef {
39
+ if (!storeId) {
40
+ throw new Error('storeId is a required parameter');
41
+ }
42
+ switch (storeId) {
43
+ case 'firestore':
44
+ case 'github':
45
+ return { type: storeId };
46
+ case 'github.com':
47
+ return { type: 'github' };
48
+ default:
49
+ if (storeId.startsWith('http-') || storeId.startsWith('https-')) {
50
+ return { type: 'agent', url: storeId.replace('-', '://') };
51
+ }
52
+ throw new Error('unsupported format of store id:' + storeId);
53
+ }
54
+ }
@@ -0,0 +1,8 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SpaceTypeFamily } from './team-type';
3
+
4
+ describe('team-type', () => {
5
+ it('should have correct value for SpaceTypeFamily', () => {
6
+ expect(SpaceTypeFamily).toBe('family');
7
+ });
8
+ });
@@ -0,0 +1,13 @@
1
+ export const SpaceTypeFamily = 'family';
2
+
3
+ export type SpaceType =
4
+ | 'family'
5
+ | 'private'
6
+ | 'company'
7
+ | 'team'
8
+ | 'parish'
9
+ | 'educator'
10
+ | 'realtor'
11
+ | 'sport_club'
12
+ | 'cohabit'
13
+ | 'unknown';
@@ -0,0 +1,247 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import '@analogjs/vitest-angular/setup-zone';
3
+ import { TestBed } from '@angular/core/testing';
4
+ import {
5
+ BrowserDynamicTestingModule,
6
+ platformBrowserDynamicTesting,
7
+ } from '@angular/platform-browser-dynamic/testing';
8
+
9
+ export function setupAngularTestingEnvironment() {
10
+ try {
11
+ TestBed.initTestEnvironment(
12
+ BrowserDynamicTestingModule,
13
+ platformBrowserDynamicTesting(),
14
+ );
15
+ } catch {
16
+ // ignore
17
+ }
18
+ }
19
+
20
+ export function setupGlobalMocks() {
21
+ if (typeof window !== 'undefined') {
22
+ // Wrap URL constructor to handle invalid bases gracefully (fixes Ionic/Stencil asset loading in tests)
23
+ const OriginalURL = (window as any).URL;
24
+ (window as any).URL = class extends OriginalURL {
25
+ constructor(url: string, base?: string | URL) {
26
+ try {
27
+ super(url, base);
28
+ } catch {
29
+ // If the base is invalid, try with a default base
30
+ try {
31
+ super(url, 'http://localhost/');
32
+ } catch {
33
+ // If URL is completely invalid, fall back to localhost with the path
34
+ // This handles cases where Ionic/Stencil tries to load assets in tests
35
+ const fallbackUrl = url.startsWith('/') ? `http://localhost${url}` : 'http://localhost/';
36
+ super(fallbackUrl);
37
+ }
38
+ }
39
+ }
40
+ };
41
+
42
+ // Set document.baseURI to prevent Ionic/Stencil "Invalid URL" errors
43
+ if ((window as any).document) {
44
+ Object.defineProperty((window as any).document, 'baseURI', {
45
+ get: () => 'http://localhost/',
46
+ configurable: true,
47
+ });
48
+
49
+ // Ensure document.dir is defined to prevent Ionic's isRTL() from throwing
50
+ // "Cannot read properties of undefined (reading 'toLowerCase')"
51
+ if ((window as any).document.dir === undefined) {
52
+ (window as any).document.dir = '';
53
+ }
54
+ }
55
+
56
+ if (!(window as any).location) {
57
+ (window as any).location = {
58
+ href: 'http://localhost',
59
+ pathname: '/',
60
+ search: '',
61
+ hash: '',
62
+ host: 'localhost',
63
+ hostname: 'localhost',
64
+ origin: 'http://localhost',
65
+ port: '',
66
+ protocol: 'http:',
67
+ assign: () => {
68
+ /* ignore */
69
+ },
70
+ replace: () => {
71
+ /* ignore */
72
+ },
73
+ reload: () => {
74
+ /* ignore */
75
+ },
76
+ };
77
+ }
78
+
79
+ if (!window.matchMedia) {
80
+ Object.defineProperty(window, 'matchMedia', {
81
+ writable: true,
82
+ value: vi.fn().mockImplementation((query) => ({
83
+ matches: false,
84
+ media: query,
85
+ onchange: null,
86
+ addListener: vi.fn(), // deprecated
87
+ removeListener: vi.fn(), // deprecated
88
+ addEventListener: vi.fn(),
89
+ removeEventListener: vi.fn(),
90
+ dispatchEvent: vi.fn(),
91
+ })),
92
+ });
93
+ }
94
+
95
+ if (!window.performance) {
96
+ (window as any).performance = {
97
+ mark: vi.fn(),
98
+ measure: vi.fn(),
99
+ clearMarks: vi.fn(),
100
+ getEntriesByName: vi.fn(() => []),
101
+ getEntriesByType: vi.fn(() => []),
102
+ now: vi.fn(() => Date.now()),
103
+ };
104
+ }
105
+
106
+ if (!(window as any).CSSStyleSheet) {
107
+ (window as any).CSSStyleSheet = class {
108
+ replaceSync() {
109
+ /* ignore */
110
+ }
111
+ replace() {
112
+ return Promise.resolve();
113
+ }
114
+ };
115
+ }
116
+
117
+ if ((window as any).CSSStyleSheet) {
118
+ (window as any).CSSStyleSheet.prototype.replaceSync = function () {
119
+ /* ignore */
120
+ };
121
+ (window as any).CSSStyleSheet.prototype.replace = function () {
122
+ return Promise.resolve();
123
+ };
124
+ }
125
+
126
+ // Mock Object.getOwnPropertyDescriptor(win.document.adoptedStyleSheets, "length")
127
+ // to avoid TypeError in Stencil: https://github.com/ionic-team/stencil/issues/5323
128
+ const originalGetOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
129
+ Object.getOwnPropertyDescriptor = function (obj, prop) {
130
+ if (obj === undefined || obj === null) {
131
+ return undefined;
132
+ }
133
+ try {
134
+ if (
135
+ obj === (window.document as any).adoptedStyleSheets &&
136
+ prop === 'length'
137
+ ) {
138
+ return {
139
+ writable: true,
140
+ enumerable: true,
141
+ configurable: true,
142
+ value: (obj as any).length,
143
+ };
144
+ }
145
+ } catch {
146
+ // ignore
147
+ }
148
+ try {
149
+ return originalGetOwnPropertyDescriptor(obj, prop);
150
+ } catch {
151
+ return undefined;
152
+ }
153
+ };
154
+
155
+ if (!(window as any).CSS) {
156
+ (window as any).CSS = {
157
+ supports: () => false,
158
+ };
159
+ }
160
+
161
+ if (!(window as any).CSSParser) {
162
+ (window as any).CSSParser = class {
163
+ parseFromString() {
164
+ return {
165
+ cssRules: [],
166
+ };
167
+ }
168
+ };
169
+ }
170
+
171
+ const mockCollection = vi.fn().mockImplementation((...args) => {
172
+ const path = args
173
+ .map((arg) => (typeof arg === 'string' ? arg : arg?.path || 'ref'))
174
+ .join('/');
175
+ return {
176
+ type: 'collection',
177
+ path,
178
+ toJSON: () => ({ path }),
179
+ };
180
+ });
181
+
182
+ const mockDoc = vi.fn().mockImplementation((...args) => {
183
+ const path = args
184
+ .map((arg) => (typeof arg === 'string' ? arg : arg?.path || 'ref'))
185
+ .join('/');
186
+ return {
187
+ type: 'document',
188
+ path,
189
+ toJSON: () => ({ path }),
190
+ };
191
+ });
192
+
193
+ (window as any).collection = mockCollection;
194
+ (window as any).doc = mockDoc;
195
+ (global as any).collection = mockCollection;
196
+ (global as any).doc = mockDoc;
197
+
198
+ const MockMutationObserver = class {
199
+ observe() {
200
+ /* ignore */
201
+ }
202
+ disconnect() {
203
+ /* ignore */
204
+ }
205
+ takeRecords() {
206
+ return [];
207
+ }
208
+ };
209
+ (window as any).MutationObserver = MockMutationObserver;
210
+ (global as any).MutationObserver = MockMutationObserver;
211
+
212
+ const MockIntersectionObserver = class {
213
+ observe() {
214
+ /* ignore */
215
+ }
216
+ unobserve() {
217
+ /* ignore */
218
+ }
219
+ disconnect() {
220
+ /* ignore */
221
+ }
222
+ takeRecords() {
223
+ return [];
224
+ }
225
+ };
226
+ (window as any).IntersectionObserver = MockIntersectionObserver;
227
+ (global as any).IntersectionObserver = MockIntersectionObserver;
228
+ }
229
+
230
+ // Global mocks for fetch if not available
231
+ if (!global.fetch) {
232
+ (global as any).fetch = vi.fn().mockImplementation(() =>
233
+ Promise.resolve({
234
+ json: () => Promise.resolve({}),
235
+ ok: true,
236
+ }),
237
+ );
238
+ (global as any).Request = vi.fn();
239
+ (global as any).Response = vi.fn();
240
+ (global as any).Headers = vi.fn();
241
+ }
242
+ }
243
+
244
+ export function setupBaseTestEnvironment() {
245
+ setupAngularTestingEnvironment();
246
+ setupGlobalMocks();
247
+ }
@@ -0,0 +1 @@
1
+ import '@analogjs/vitest-angular/setup-zone';
@@ -0,0 +1,70 @@
1
+ import '@analogjs/vitest-angular/setup-zone';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import {
4
+ setupAngularTestingEnvironment,
5
+ setupGlobalMocks,
6
+ } from './base-test-setup';
7
+ import { ErrorLogger } from '../logging/interfaces';
8
+ import { Firestore } from '@angular/fire/firestore';
9
+ import { Auth } from '@angular/fire/auth';
10
+ import { AngularFirestore } from '@angular/fire/compat/firestore';
11
+ import { AngularFireAuth } from '@angular/fire/compat/auth';
12
+ import { of } from 'rxjs';
13
+ import { AnalyticsService } from '../analytics.interface';
14
+
15
+ export function configureGlobalTestBed() {
16
+ try {
17
+ TestBed.configureTestingModule({
18
+ providers: [
19
+ {
20
+ provide: ErrorLogger,
21
+ useValue: {
22
+ logError: vi.fn(),
23
+ logErrorHandler: () => {
24
+ return vi.fn();
25
+ },
26
+ },
27
+ },
28
+ {
29
+ provide: Firestore,
30
+ useValue: {
31
+ type: 'Firestore',
32
+ toJSON: () => ({}),
33
+ },
34
+ },
35
+ {
36
+ provide: Auth,
37
+ useValue: {
38
+ onIdTokenChanged: vi.fn(() => () => void 0),
39
+ onAuthStateChanged: vi.fn(() => () => void 0),
40
+ },
41
+ },
42
+ {
43
+ provide: AnalyticsService,
44
+ useValue: {
45
+ logEvent: vi.fn(),
46
+ identify: vi.fn(),
47
+ loggedOut: vi.fn(),
48
+ setCurrentScreen: vi.fn(),
49
+ },
50
+ },
51
+ {
52
+ provide: AngularFirestore,
53
+ useValue: {
54
+ collection: () => ({ valueChanges: () => of([]) }),
55
+ doc: () => ({ valueChanges: () => of(null) }),
56
+ },
57
+ },
58
+ { provide: AngularFireAuth, useValue: { authState: of(null) } },
59
+ ],
60
+ });
61
+ } catch {
62
+ // ignore
63
+ }
64
+ }
65
+
66
+ export function setupTestEnvironment() {
67
+ setupAngularTestingEnvironment();
68
+ setupGlobalMocks();
69
+ configureGlobalTestBed();
70
+ }
@@ -0,0 +1 @@
1
+ export type AgeGroupID = 'adult' | 'child' | 'pet' | 'undisclosed' | 'unknown';
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ GenderUndisclosed,
4
+ GenderUnknown,
5
+ GenderMale,
6
+ GenderFemale,
7
+ GenderOther,
8
+ } from './gender';
9
+
10
+ describe('Gender constants', () => {
11
+ it('should have correct value for GenderUndisclosed', () => {
12
+ expect(GenderUndisclosed).toBe('undisclosed');
13
+ });
14
+
15
+ it('should have correct value for GenderUnknown', () => {
16
+ expect(GenderUnknown).toBe('unknown');
17
+ });
18
+
19
+ it('should have correct value for GenderMale', () => {
20
+ expect(GenderMale).toBe('male');
21
+ });
22
+
23
+ it('should have correct value for GenderFemale', () => {
24
+ expect(GenderFemale).toBe('female');
25
+ });
26
+
27
+ it('should have correct value for GenderOther', () => {
28
+ expect(GenderOther).toBe('other');
29
+ });
30
+
31
+ it('should have all unique values', () => {
32
+ const values = [
33
+ GenderUndisclosed,
34
+ GenderUnknown,
35
+ GenderMale,
36
+ GenderFemale,
37
+ GenderOther,
38
+ ];
39
+ const uniqueValues = new Set(values);
40
+ expect(uniqueValues.size).toBe(values.length);
41
+ });
42
+ });
@@ -0,0 +1,12 @@
1
+ export const GenderUndisclosed = 'undisclosed';
2
+ export const GenderUnknown = 'unknown';
3
+ export const GenderMale = 'male';
4
+ export const GenderFemale = 'female';
5
+ export const GenderOther = 'other';
6
+
7
+ export type Gender =
8
+ | typeof GenderUndisclosed
9
+ | typeof GenderUnknown
10
+ | typeof GenderMale
11
+ | typeof GenderFemale
12
+ | typeof GenderOther;
@@ -0,0 +1,2 @@
1
+ export * from './age-group';
2
+ export * from './gender';