create-elit 3.3.3 → 3.3.4
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.
- package/dist/templates/README.md +1 -0
- package/dist/templates/elit.config.ts +16 -0
- package/dist/templates/package.json +4 -1
- package/dist/templates/src/client.test.ts +292 -0
- package/dist/templates/src/components/Footer.test.ts +226 -0
- package/dist/templates/src/components/Header.test.ts +493 -0
- package/dist/templates/src/pages/ChatListPage.test.ts +603 -0
- package/dist/templates/src/pages/ChatPage.test.ts +530 -0
- package/dist/templates/src/pages/ForgotPasswordPage.test.ts +484 -0
- package/dist/templates/src/pages/HomePage.test.ts +601 -0
- package/dist/templates/src/pages/LoginPage.test.ts +619 -0
- package/dist/templates/src/pages/PrivateChatPage.test.ts +556 -0
- package/dist/templates/src/pages/ProfilePage.test.ts +628 -0
- package/dist/templates/src/pages/RegisterPage.test.ts +661 -0
- package/package.json +1 -1
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoginPage Component Unit Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// CRITICAL: Set up ALL mocks BEFORE importing LoginPage component
|
|
6
|
+
// The component reads localStorage during import, so mocks must be set up first
|
|
7
|
+
|
|
8
|
+
// Simple mock function to track calls
|
|
9
|
+
function mockFn() {
|
|
10
|
+
const calls: any[] = [];
|
|
11
|
+
const fn = (...args: any[]) => {
|
|
12
|
+
calls.push(args);
|
|
13
|
+
return undefined;
|
|
14
|
+
};
|
|
15
|
+
fn.calls = calls;
|
|
16
|
+
fn.mockClear = () => {
|
|
17
|
+
calls.length = 0;
|
|
18
|
+
};
|
|
19
|
+
return fn;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Mock localStorage
|
|
23
|
+
const localStorageMock = (() => {
|
|
24
|
+
let store: Record<string, string> = {};
|
|
25
|
+
return {
|
|
26
|
+
getItem: (key: string): string | null => store[key] || null,
|
|
27
|
+
setItem: (key: string, value: string): void => {
|
|
28
|
+
store[key] = value;
|
|
29
|
+
},
|
|
30
|
+
removeItem: (key: string): void => {
|
|
31
|
+
delete store[key];
|
|
32
|
+
},
|
|
33
|
+
clear: (): void => {
|
|
34
|
+
store = {};
|
|
35
|
+
},
|
|
36
|
+
get length(): number {
|
|
37
|
+
return Object.keys(store).length;
|
|
38
|
+
},
|
|
39
|
+
key: (index: number): string | null => {
|
|
40
|
+
return Object.keys(store)[index] || null;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
|
|
45
|
+
// Mock fetch
|
|
46
|
+
let mockFetchResponse: any = {
|
|
47
|
+
ok: true,
|
|
48
|
+
json: async () => ({ token: 'fake-token', user: { id: '123', name: 'Test User' } })
|
|
49
|
+
};
|
|
50
|
+
const mockFetch = async () => mockFetchResponse;
|
|
51
|
+
|
|
52
|
+
// Mock window.addEventListener and dispatchEvent
|
|
53
|
+
const eventListeners: Record<string, Function[]> = {};
|
|
54
|
+
const addEventListenerMock = (event: string, handler: Function) => {
|
|
55
|
+
if (!eventListeners[event]) {
|
|
56
|
+
eventListeners[event] = [];
|
|
57
|
+
}
|
|
58
|
+
eventListeners[event].push(handler);
|
|
59
|
+
};
|
|
60
|
+
const dispatchEventMock = (event: Event) => {
|
|
61
|
+
const listeners = eventListeners[event.type];
|
|
62
|
+
if (listeners) {
|
|
63
|
+
listeners.forEach((listener: Function) => listener(event));
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Mock requestAnimationFrame
|
|
68
|
+
let rafId = 0;
|
|
69
|
+
const rafCallbacks: Map<number, FrameRequestCallback> = new Map();
|
|
70
|
+
const mockRequestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
71
|
+
const id = ++rafId;
|
|
72
|
+
rafCallbacks.set(id, callback);
|
|
73
|
+
return id;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const mockCancelAnimationFrame = (id: number) => {
|
|
77
|
+
rafCallbacks.delete(id);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Mock router
|
|
81
|
+
const mockRouter = {
|
|
82
|
+
push: mockFn() as any,
|
|
83
|
+
replace: mockFn() as any,
|
|
84
|
+
go: mockFn() as any,
|
|
85
|
+
back: mockFn() as any,
|
|
86
|
+
forward: mockFn() as any,
|
|
87
|
+
currentPath: '/',
|
|
88
|
+
currentState: null
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// SETUP GLOBALS BEFORE IMPORT
|
|
92
|
+
// Clear real localStorage if it exists
|
|
93
|
+
if (typeof localStorage !== 'undefined') {
|
|
94
|
+
localStorage.clear();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Set up global mocks
|
|
98
|
+
(global as any).localStorage = localStorageMock;
|
|
99
|
+
(globalThis as any).localStorage = localStorageMock;
|
|
100
|
+
(global as any).fetch = mockFetch;
|
|
101
|
+
(globalThis as any).fetch = mockFetch;
|
|
102
|
+
(global as any).window = {
|
|
103
|
+
addEventListener: addEventListenerMock,
|
|
104
|
+
removeEventListener: mockFn(),
|
|
105
|
+
dispatchEvent: dispatchEventMock,
|
|
106
|
+
localStorage: localStorageMock
|
|
107
|
+
};
|
|
108
|
+
(globalThis as any).window = (global as any).window;
|
|
109
|
+
(global as any).requestAnimationFrame = mockRequestAnimationFrame;
|
|
110
|
+
(global as any).cancelAnimationFrame = mockCancelAnimationFrame;
|
|
111
|
+
(globalThis as any).requestAnimationFrame = mockRequestAnimationFrame;
|
|
112
|
+
(globalThis as any).cancelAnimationFrame = mockCancelAnimationFrame;
|
|
113
|
+
|
|
114
|
+
// NOW import the component (after mocks are set up)
|
|
115
|
+
import { LoginPage } from './LoginPage';
|
|
116
|
+
import type { VNode } from 'elit/types';
|
|
117
|
+
|
|
118
|
+
// Helper function to render VNode to HTML string
|
|
119
|
+
function renderToString(vNode: VNode | string | number | undefined | null): string {
|
|
120
|
+
if (vNode == null || vNode === false) return '';
|
|
121
|
+
if (typeof vNode !== 'object') return String(vNode);
|
|
122
|
+
|
|
123
|
+
const { tagName, props, children } = vNode;
|
|
124
|
+
const attrs = props ? Object.entries(props)
|
|
125
|
+
.filter(([k, v]) => v != null && v !== false && k !== 'children' && k !== 'ref' && !k.startsWith('on'))
|
|
126
|
+
.map(([k, v]) => {
|
|
127
|
+
if (k === 'className' || k === 'class') return `class="${Array.isArray(v) ? v.join(' ') : v}"`;
|
|
128
|
+
if (k === 'style') return `style="${typeof v === 'string' ? v : Object.entries(v).map(([sk, sv]) => `${sk.replace(/([A-Z])/g, '-$1').toLowerCase()}:${sv}`).join(';')}"`;
|
|
129
|
+
if (v === true) return k;
|
|
130
|
+
return `${k}="${v}"`;
|
|
131
|
+
})
|
|
132
|
+
.join(' ') : '';
|
|
133
|
+
|
|
134
|
+
const childrenStr = children && children.length > 0
|
|
135
|
+
? children.map(c => renderToString(c as any)).join('')
|
|
136
|
+
: '';
|
|
137
|
+
|
|
138
|
+
return `<${tagName}${attrs ? ' ' + attrs : ''}>${childrenStr}</${tagName}>`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Helper function to find child by tag name
|
|
142
|
+
function findChildByTagName(vNode: VNode, tagName: string): VNode | null {
|
|
143
|
+
if (vNode && typeof vNode === 'object' && 'tagName' in vNode) {
|
|
144
|
+
if (vNode.tagName === tagName) {
|
|
145
|
+
return vNode;
|
|
146
|
+
}
|
|
147
|
+
if (vNode.children) {
|
|
148
|
+
for (const child of vNode.children) {
|
|
149
|
+
if (typeof child === 'object' && child !== null) {
|
|
150
|
+
const found = findChildByTagName(child as VNode, tagName);
|
|
151
|
+
if (found) return found;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Helper function to find children by tag name
|
|
160
|
+
function findChildrenByTagName(vNode: VNode, tagName: string): VNode[] {
|
|
161
|
+
const results: VNode[] = [];
|
|
162
|
+
|
|
163
|
+
function search(node: VNode | string | number | null | undefined) {
|
|
164
|
+
if (node && typeof node === 'object' && 'tagName' in node) {
|
|
165
|
+
if (node.tagName === tagName) {
|
|
166
|
+
results.push(node);
|
|
167
|
+
}
|
|
168
|
+
if (node.children) {
|
|
169
|
+
for (const child of node.children) {
|
|
170
|
+
search(child as any);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
search(vNode);
|
|
177
|
+
return results;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
describe('LoginPage Component', () => {
|
|
181
|
+
// Clear REAL browser localStorage before any tests run
|
|
182
|
+
beforeAll(() => {
|
|
183
|
+
if (typeof localStorage !== 'undefined') {
|
|
184
|
+
localStorage.clear();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
beforeEach(() => {
|
|
189
|
+
// Clear RAF callbacks FIRST
|
|
190
|
+
rafCallbacks.clear();
|
|
191
|
+
rafId = 0;
|
|
192
|
+
|
|
193
|
+
// Clear localStorage before each test
|
|
194
|
+
localStorageMock.clear();
|
|
195
|
+
|
|
196
|
+
// Also clear REAL browser localStorage
|
|
197
|
+
if (typeof localStorage !== 'undefined') {
|
|
198
|
+
localStorage.clear();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Reset mock router
|
|
202
|
+
(mockRouter.push as any).mockClear();
|
|
203
|
+
(mockRouter.replace as any).mockClear();
|
|
204
|
+
|
|
205
|
+
// Reset fetch mock
|
|
206
|
+
mockFetchResponse = {
|
|
207
|
+
ok: true,
|
|
208
|
+
json: async () => ({ token: 'fake-token', user: { id: '123', name: 'Test User' } })
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Clear event listeners
|
|
212
|
+
Object.keys(eventListeners).forEach(key => {
|
|
213
|
+
delete eventListeners[key];
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
afterEach(() => {
|
|
218
|
+
// Clean up event listeners after each test
|
|
219
|
+
Object.keys(eventListeners).forEach(key => {
|
|
220
|
+
delete eventListeners[key];
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Clear RAF callbacks after each test
|
|
224
|
+
rafCallbacks.clear();
|
|
225
|
+
rafId = 0;
|
|
226
|
+
|
|
227
|
+
// Clear REAL browser localStorage after each test
|
|
228
|
+
if (typeof localStorage !== 'undefined') {
|
|
229
|
+
localStorage.clear();
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe('authentication redirect', () => {
|
|
234
|
+
it('should redirect to profile if already logged in', () => {
|
|
235
|
+
localStorageMock.setItem('token', 'existing-token');
|
|
236
|
+
LoginPage(mockRouter as any);
|
|
237
|
+
|
|
238
|
+
expect(mockRouter.push.calls.length).toBeGreaterThan(0);
|
|
239
|
+
expect(mockRouter.push.calls[0][0]).toBe('/profile');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should not redirect if not logged in', () => {
|
|
243
|
+
LoginPage(mockRouter as any);
|
|
244
|
+
|
|
245
|
+
expect(mockRouter.push.calls.length).toBe(0);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
describe('page structure', () => {
|
|
250
|
+
it('should render auth-page', () => {
|
|
251
|
+
const page = LoginPage(mockRouter as any);
|
|
252
|
+
|
|
253
|
+
expect(page).toBeDefined();
|
|
254
|
+
expect(page.tagName).toBe('div');
|
|
255
|
+
expect(page.props?.className).toBe('auth-page');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should render auth-container', () => {
|
|
259
|
+
const page = LoginPage(mockRouter as any);
|
|
260
|
+
const html = renderToString(page);
|
|
261
|
+
|
|
262
|
+
expect(html).toContain('auth-container');
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should render auth-branding', () => {
|
|
266
|
+
const page = LoginPage(mockRouter as any);
|
|
267
|
+
const html = renderToString(page);
|
|
268
|
+
|
|
269
|
+
expect(html).toContain('auth-branding');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should render auth-form-wrapper', () => {
|
|
273
|
+
const page = LoginPage(mockRouter as any);
|
|
274
|
+
const html = renderToString(page);
|
|
275
|
+
|
|
276
|
+
expect(html).toContain('auth-form-wrapper');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should render auth-form-card', () => {
|
|
280
|
+
const page = LoginPage(mockRouter as any);
|
|
281
|
+
const html = renderToString(page);
|
|
282
|
+
|
|
283
|
+
expect(html).toContain('auth-form-card');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('branding section', () => {
|
|
288
|
+
it('should render branding title', () => {
|
|
289
|
+
const page = LoginPage(mockRouter as any);
|
|
290
|
+
const html = renderToString(page);
|
|
291
|
+
|
|
292
|
+
expect(html).toContain('Welcome Back');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('should render branding description', () => {
|
|
296
|
+
const page = LoginPage(mockRouter as any);
|
|
297
|
+
const html = renderToString(page);
|
|
298
|
+
|
|
299
|
+
expect(html).toContain('Sign in to continue to your account');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should render branding features', () => {
|
|
303
|
+
const page = LoginPage(mockRouter as any);
|
|
304
|
+
const html = renderToString(page);
|
|
305
|
+
|
|
306
|
+
expect(html).toContain('branding-features');
|
|
307
|
+
expect(html).toContain('Lightning fast performance');
|
|
308
|
+
expect(html).toContain('Secure and reliable');
|
|
309
|
+
expect(html).toContain('24/7 support available');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should render feature icons', () => {
|
|
313
|
+
const page = LoginPage(mockRouter as any);
|
|
314
|
+
const html = renderToString(page);
|
|
315
|
+
|
|
316
|
+
expect(html).toContain('feature-icon');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('form header', () => {
|
|
321
|
+
it('should render auth header', () => {
|
|
322
|
+
const page = LoginPage(mockRouter as any);
|
|
323
|
+
const html = renderToString(page);
|
|
324
|
+
|
|
325
|
+
expect(html).toContain('auth-header');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('should render auth title', () => {
|
|
329
|
+
const page = LoginPage(mockRouter as any);
|
|
330
|
+
const html = renderToString(page);
|
|
331
|
+
|
|
332
|
+
expect(html).toContain('Sign In');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('should render auth subtitle', () => {
|
|
336
|
+
const page = LoginPage(mockRouter as any);
|
|
337
|
+
const html = renderToString(page);
|
|
338
|
+
|
|
339
|
+
expect(html).toContain('Enter your credentials to access your account');
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
describe('error handling', () => {
|
|
344
|
+
it('should have error state capability', () => {
|
|
345
|
+
const page = LoginPage(mockRouter as any);
|
|
346
|
+
// Error state is reactive
|
|
347
|
+
expect(page).toBeDefined();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('should render auth-error when error exists', () => {
|
|
351
|
+
const page = LoginPage(mockRouter as any);
|
|
352
|
+
// Error is reactive and only shows when error.value is set
|
|
353
|
+
expect(page).toBeDefined();
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
describe('form elements', () => {
|
|
358
|
+
it('should render form element', () => {
|
|
359
|
+
const page = LoginPage(mockRouter as any);
|
|
360
|
+
const html = renderToString(page);
|
|
361
|
+
|
|
362
|
+
expect(html).toContain('<form');
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should render email input', () => {
|
|
366
|
+
const page = LoginPage(mockRouter as any);
|
|
367
|
+
const html = renderToString(page);
|
|
368
|
+
|
|
369
|
+
expect(html).toContain('type="email"');
|
|
370
|
+
expect(html).toContain('id="email"');
|
|
371
|
+
expect(html).toContain('form-input');
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should render email label', () => {
|
|
375
|
+
const page = LoginPage(mockRouter as any);
|
|
376
|
+
const html = renderToString(page);
|
|
377
|
+
|
|
378
|
+
expect(html).toContain('Email Address');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('should render email icon', () => {
|
|
382
|
+
const page = LoginPage(mockRouter as any);
|
|
383
|
+
const html = renderToString(page);
|
|
384
|
+
|
|
385
|
+
expect(html).toContain('📧');
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should render password input', () => {
|
|
389
|
+
const page = LoginPage(mockRouter as any);
|
|
390
|
+
const html = renderToString(page);
|
|
391
|
+
|
|
392
|
+
expect(html).toContain('type="password"');
|
|
393
|
+
expect(html).toContain('id="password"');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should render password label', () => {
|
|
397
|
+
const page = LoginPage(mockRouter as any);
|
|
398
|
+
const html = renderToString(page);
|
|
399
|
+
|
|
400
|
+
expect(html).toContain('Password');
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
it('should render password icon', () => {
|
|
404
|
+
const page = LoginPage(mockRouter as any);
|
|
405
|
+
const html = renderToString(page);
|
|
406
|
+
|
|
407
|
+
expect(html).toContain('🔒');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('should have correct placeholders', () => {
|
|
411
|
+
const page = LoginPage(mockRouter as any);
|
|
412
|
+
const html = renderToString(page);
|
|
413
|
+
|
|
414
|
+
expect(html).toContain('your@email.com');
|
|
415
|
+
expect(html).toContain('••••••••');
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
describe('form options', () => {
|
|
420
|
+
it('should render remember me checkbox', () => {
|
|
421
|
+
const page = LoginPage(mockRouter as any);
|
|
422
|
+
const html = renderToString(page);
|
|
423
|
+
|
|
424
|
+
expect(html).toContain('Remember me');
|
|
425
|
+
expect(html).toContain('checkbox');
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('should render forgot password button', () => {
|
|
429
|
+
const page = LoginPage(mockRouter as any);
|
|
430
|
+
const html = renderToString(page);
|
|
431
|
+
|
|
432
|
+
expect(html).toContain('Forgot password?');
|
|
433
|
+
expect(html).toContain('link-button');
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
describe('submit button', () => {
|
|
438
|
+
it('should render submit button', () => {
|
|
439
|
+
const page = LoginPage(mockRouter as any);
|
|
440
|
+
const html = renderToString(page);
|
|
441
|
+
|
|
442
|
+
expect(html).toContain('Sign In');
|
|
443
|
+
expect(html).toContain('btn-primary');
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
it('should have loading state capability', () => {
|
|
447
|
+
const page = LoginPage(mockRouter as any);
|
|
448
|
+
// Submit button text is reactive based on isLoading state
|
|
449
|
+
expect(page).toBeDefined();
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
describe('social login', () => {
|
|
454
|
+
it('should render auth divider', () => {
|
|
455
|
+
const page = LoginPage(mockRouter as any);
|
|
456
|
+
const html = renderToString(page);
|
|
457
|
+
|
|
458
|
+
expect(html).toContain('auth-divider');
|
|
459
|
+
expect(html).toContain('OR');
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it('should render social login section', () => {
|
|
463
|
+
const page = LoginPage(mockRouter as any);
|
|
464
|
+
const html = renderToString(page);
|
|
465
|
+
|
|
466
|
+
expect(html).toContain('social-login');
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('should render Google button', () => {
|
|
470
|
+
const page = LoginPage(mockRouter as any);
|
|
471
|
+
const html = renderToString(page);
|
|
472
|
+
|
|
473
|
+
expect(html).toContain('Continue with Google');
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('should render GitHub button', () => {
|
|
477
|
+
const page = LoginPage(mockRouter as any);
|
|
478
|
+
const html = renderToString(page);
|
|
479
|
+
|
|
480
|
+
expect(html).toContain('Continue with GitHub');
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
describe('footer section', () => {
|
|
485
|
+
it('should render auth footer', () => {
|
|
486
|
+
const page = LoginPage(mockRouter as any);
|
|
487
|
+
const html = renderToString(page);
|
|
488
|
+
|
|
489
|
+
expect(html).toContain('auth-footer');
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('should render footer text', () => {
|
|
493
|
+
const page = LoginPage(mockRouter as any);
|
|
494
|
+
const html = renderToString(page);
|
|
495
|
+
|
|
496
|
+
expect(html).toContain("Don't have an account?");
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should render sign up button', () => {
|
|
500
|
+
const page = LoginPage(mockRouter as any);
|
|
501
|
+
const html = renderToString(page);
|
|
502
|
+
|
|
503
|
+
expect(html).toContain('Sign up');
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
describe('CSS classes', () => {
|
|
508
|
+
it('should have correct CSS classes', () => {
|
|
509
|
+
const page = LoginPage(mockRouter as any);
|
|
510
|
+
const html = renderToString(page);
|
|
511
|
+
|
|
512
|
+
expect(html).toContain('auth-page');
|
|
513
|
+
expect(html).toContain('auth-container');
|
|
514
|
+
expect(html).toContain('auth-branding');
|
|
515
|
+
expect(html).toContain('branding-content');
|
|
516
|
+
expect(html).toContain('branding-title');
|
|
517
|
+
expect(html).toContain('branding-description');
|
|
518
|
+
});
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
describe('form structure', () => {
|
|
522
|
+
it('should have form-group', () => {
|
|
523
|
+
const page = LoginPage(mockRouter as any);
|
|
524
|
+
const html = renderToString(page);
|
|
525
|
+
|
|
526
|
+
expect(html).toContain('form-group');
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it('should have input-wrapper', () => {
|
|
530
|
+
const page = LoginPage(mockRouter as any);
|
|
531
|
+
const html = renderToString(page);
|
|
532
|
+
|
|
533
|
+
expect(html).toContain('input-wrapper');
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('should have input-icon', () => {
|
|
537
|
+
const page = LoginPage(mockRouter as any);
|
|
538
|
+
const html = renderToString(page);
|
|
539
|
+
|
|
540
|
+
expect(html).toContain('input-icon');
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('should have form-options', () => {
|
|
544
|
+
const page = LoginPage(mockRouter as any);
|
|
545
|
+
const html = renderToString(page);
|
|
546
|
+
|
|
547
|
+
expect(html).toContain('form-options');
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
describe('component consistency', () => {
|
|
552
|
+
it('should always return the same structure', () => {
|
|
553
|
+
const page1 = LoginPage(mockRouter as any);
|
|
554
|
+
const page2 = LoginPage(mockRouter as any);
|
|
555
|
+
|
|
556
|
+
expect(page1.tagName).toBe(page2.tagName);
|
|
557
|
+
expect(page1.props?.className).toBe(page2.props?.className);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should render without errors', () => {
|
|
561
|
+
expect(() => {
|
|
562
|
+
const page = LoginPage(mockRouter as any);
|
|
563
|
+
renderToString(page);
|
|
564
|
+
}).not.toThrow();
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('should have h1 element', () => {
|
|
568
|
+
const page = LoginPage(mockRouter as any);
|
|
569
|
+
const h1Elements = findChildrenByTagName(page, 'h1');
|
|
570
|
+
|
|
571
|
+
expect(h1Elements.length).toBeGreaterThan(0);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it('should have h2 element', () => {
|
|
575
|
+
const page = LoginPage(mockRouter as any);
|
|
576
|
+
const h2Elements = findChildrenByTagName(page, 'h2');
|
|
577
|
+
|
|
578
|
+
expect(h2Elements.length).toBeGreaterThan(0);
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
describe('reactive state', () => {
|
|
583
|
+
it('should have email state', () => {
|
|
584
|
+
const page = LoginPage(mockRouter as any);
|
|
585
|
+
expect(page).toBeDefined();
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('should have password state', () => {
|
|
589
|
+
const page = LoginPage(mockRouter as any);
|
|
590
|
+
expect(page).toBeDefined();
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
it('should have error state', () => {
|
|
594
|
+
const page = LoginPage(mockRouter as any);
|
|
595
|
+
expect(page).toBeDefined();
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
it('should have isLoading state', () => {
|
|
599
|
+
const page = LoginPage(mockRouter as any);
|
|
600
|
+
expect(page).toBeDefined();
|
|
601
|
+
});
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
describe('navigation', () => {
|
|
605
|
+
it('should have forgot password link', () => {
|
|
606
|
+
const page = LoginPage(mockRouter as any);
|
|
607
|
+
const html = renderToString(page);
|
|
608
|
+
|
|
609
|
+
expect(html).toContain('Forgot password?');
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it('should have sign up link', () => {
|
|
613
|
+
const page = LoginPage(mockRouter as any);
|
|
614
|
+
const html = renderToString(page);
|
|
615
|
+
|
|
616
|
+
expect(html).toContain('Sign up');
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
});
|