create-elit 3.3.2 → 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,530 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatPage Component Unit Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// CRITICAL: Set up ALL mocks BEFORE importing ChatPage 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 () => ({ messages: [] })
|
|
49
|
+
};
|
|
50
|
+
const mockFetch = async () => mockFetchResponse;
|
|
51
|
+
|
|
52
|
+
// Mock WebSocket - prevent real connection attempts
|
|
53
|
+
class MockWebSocket {
|
|
54
|
+
url: string;
|
|
55
|
+
readyState: number = 3; // Start as closed to prevent connection attempts
|
|
56
|
+
onopen: ((event: Event) => void) | null = null;
|
|
57
|
+
onmessage: ((event: MessageEvent) => void) | null = null;
|
|
58
|
+
onerror: ((event: Event) => void) | null = null;
|
|
59
|
+
onclose: ((event: CloseEvent) => void) | null = null;
|
|
60
|
+
|
|
61
|
+
constructor(url: string) {
|
|
62
|
+
this.url = url;
|
|
63
|
+
// Immediately close to prevent hanging
|
|
64
|
+
if (this.onclose) {
|
|
65
|
+
this.onclose(new CloseEvent('close'));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
send(data: string) {
|
|
70
|
+
// Do nothing
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
close() {
|
|
75
|
+
this.readyState = 3;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
addEventListener(_type: string, _listener: any) {
|
|
79
|
+
// Do nothing
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
removeEventListener(_type: string, _listener: any) {
|
|
83
|
+
// Do nothing
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Mock window.addEventListener
|
|
88
|
+
const eventListeners: Record<string, Function[]> = {};
|
|
89
|
+
const addEventListenerMock = (event: string, handler: Function) => {
|
|
90
|
+
if (!eventListeners[event]) {
|
|
91
|
+
eventListeners[event] = [];
|
|
92
|
+
}
|
|
93
|
+
eventListeners[event].push(handler);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Mock requestAnimationFrame
|
|
97
|
+
let rafId = 0;
|
|
98
|
+
const rafCallbacks: Map<number, FrameRequestCallback> = new Map();
|
|
99
|
+
const mockRequestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
100
|
+
const id = ++rafId;
|
|
101
|
+
rafCallbacks.set(id, callback);
|
|
102
|
+
return id;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const mockCancelAnimationFrame = (id: number) => {
|
|
106
|
+
rafCallbacks.delete(id);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Mock setInterval/clearInterval - don't actually run intervals in tests
|
|
110
|
+
let intervalId = 0;
|
|
111
|
+
const mockSetInterval = (_callback: () => void, _ms: number) => {
|
|
112
|
+
return ++intervalId;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const mockClearInterval = (_id: number) => {
|
|
116
|
+
// Do nothing
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Mock location
|
|
120
|
+
const mockLocation = {
|
|
121
|
+
host: 'localhost:3000',
|
|
122
|
+
hostname: 'localhost',
|
|
123
|
+
port: '3000',
|
|
124
|
+
protocol: 'http:',
|
|
125
|
+
href: 'http://localhost:3000/',
|
|
126
|
+
origin: 'http://localhost:3000'
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Mock router
|
|
130
|
+
const mockRouter = {
|
|
131
|
+
push: mockFn() as any,
|
|
132
|
+
replace: mockFn() as any,
|
|
133
|
+
go: mockFn() as any,
|
|
134
|
+
back: mockFn() as any,
|
|
135
|
+
forward: mockFn() as any,
|
|
136
|
+
currentPath: '/',
|
|
137
|
+
currentState: null
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// SETUP GLOBALS BEFORE IMPORT
|
|
141
|
+
// Clear real localStorage if it exists
|
|
142
|
+
if (typeof localStorage !== 'undefined') {
|
|
143
|
+
localStorage.clear();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Set up global mocks
|
|
147
|
+
(global as any).localStorage = localStorageMock;
|
|
148
|
+
(globalThis as any).localStorage = localStorageMock;
|
|
149
|
+
(global as any).fetch = mockFetch;
|
|
150
|
+
(globalThis as any).fetch = mockFetch;
|
|
151
|
+
(global as any).WebSocket = MockWebSocket;
|
|
152
|
+
(globalThis as any).WebSocket = MockWebSocket;
|
|
153
|
+
(global as any).window = {
|
|
154
|
+
addEventListener: addEventListenerMock,
|
|
155
|
+
removeEventListener: mockFn(),
|
|
156
|
+
localStorage: localStorageMock,
|
|
157
|
+
location: mockLocation
|
|
158
|
+
};
|
|
159
|
+
(globalThis as any).window = (global as any).window;
|
|
160
|
+
(global as any).location = mockLocation;
|
|
161
|
+
(globalThis as any).location = mockLocation;
|
|
162
|
+
(global as any).setInterval = mockSetInterval;
|
|
163
|
+
(globalThis as any).setInterval = mockSetInterval;
|
|
164
|
+
(global as any).clearInterval = mockClearInterval;
|
|
165
|
+
(globalThis as any).clearInterval = mockClearInterval;
|
|
166
|
+
(global as any).requestAnimationFrame = mockRequestAnimationFrame;
|
|
167
|
+
(global as any).cancelAnimationFrame = mockCancelAnimationFrame;
|
|
168
|
+
(globalThis as any).requestAnimationFrame = mockRequestAnimationFrame;
|
|
169
|
+
(globalThis as any).cancelAnimationFrame = mockCancelAnimationFrame;
|
|
170
|
+
|
|
171
|
+
// NOW import the component (after mocks are set up)
|
|
172
|
+
import { ChatPage } from './ChatPage';
|
|
173
|
+
import type { VNode } from 'elit/types';
|
|
174
|
+
|
|
175
|
+
// Helper function to render VNode to HTML string
|
|
176
|
+
function renderToString(vNode: VNode | string | number | undefined | null): string {
|
|
177
|
+
if (vNode == null || vNode === false) return '';
|
|
178
|
+
if (typeof vNode !== 'object') return String(vNode);
|
|
179
|
+
|
|
180
|
+
const { tagName, props, children } = vNode;
|
|
181
|
+
const attrs = props ? Object.entries(props)
|
|
182
|
+
.filter(([k, v]) => v != null && v !== false && k !== 'children' && k !== 'ref' && !k.startsWith('on'))
|
|
183
|
+
.map(([k, v]) => {
|
|
184
|
+
if (k === 'className' || k === 'class') return `class="${Array.isArray(v) ? v.join(' ') : v}"`;
|
|
185
|
+
if (k === 'style') return `style="${typeof v === 'string' ? v : Object.entries(v).map(([sk, sv]) => `${sk.replace(/([A-Z])/g, '-$1').toLowerCase()}:${sv}`).join(';')}"`;
|
|
186
|
+
if (v === true) return k;
|
|
187
|
+
return `${k}="${v}"`;
|
|
188
|
+
})
|
|
189
|
+
.join(' ') : '';
|
|
190
|
+
|
|
191
|
+
const childrenStr = children && children.length > 0
|
|
192
|
+
? children.map(c => renderToString(c as any)).join('')
|
|
193
|
+
: '';
|
|
194
|
+
|
|
195
|
+
return `<${tagName}${attrs ? ' ' + attrs : ''}>${childrenStr}</${tagName}>`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
describe('ChatPage Component', () => {
|
|
199
|
+
// Clear REAL browser localStorage before any tests run
|
|
200
|
+
beforeAll(() => {
|
|
201
|
+
if (typeof localStorage !== 'undefined') {
|
|
202
|
+
localStorage.clear();
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
beforeEach(() => {
|
|
207
|
+
// Clear RAF callbacks
|
|
208
|
+
rafCallbacks.clear();
|
|
209
|
+
rafId = 0;
|
|
210
|
+
|
|
211
|
+
// Clear localStorage before each test
|
|
212
|
+
localStorageMock.clear();
|
|
213
|
+
|
|
214
|
+
// Also clear REAL browser localStorage
|
|
215
|
+
if (typeof localStorage !== 'undefined') {
|
|
216
|
+
localStorage.clear();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Reset mock router
|
|
220
|
+
(mockRouter.push as any).mockClear();
|
|
221
|
+
(mockRouter.replace as any).mockClear();
|
|
222
|
+
|
|
223
|
+
// Reset fetch mock
|
|
224
|
+
mockFetchResponse = {
|
|
225
|
+
ok: true,
|
|
226
|
+
json: async () => ({ messages: [] })
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// Clear event listeners
|
|
230
|
+
Object.keys(eventListeners).forEach(key => {
|
|
231
|
+
delete eventListeners[key];
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
afterEach(() => {
|
|
236
|
+
// Clean up event listeners after each test
|
|
237
|
+
Object.keys(eventListeners).forEach(key => {
|
|
238
|
+
delete eventListeners[key];
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Clear RAF callbacks after each test
|
|
242
|
+
rafCallbacks.clear();
|
|
243
|
+
rafId = 0;
|
|
244
|
+
|
|
245
|
+
// Clear REAL browser localStorage after each test
|
|
246
|
+
if (typeof localStorage !== 'undefined') {
|
|
247
|
+
localStorage.clear();
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe('authentication', () => {
|
|
252
|
+
it('should render page when authenticated', () => {
|
|
253
|
+
// Set both token and user
|
|
254
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
255
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
256
|
+
|
|
257
|
+
const page = ChatPage(mockRouter as any);
|
|
258
|
+
|
|
259
|
+
expect(page).toBeDefined();
|
|
260
|
+
expect(page.tagName).toBe('div');
|
|
261
|
+
expect(page.props?.className).toBe('chat-page');
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should not redirect when authenticated', () => {
|
|
265
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
266
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
267
|
+
|
|
268
|
+
ChatPage(mockRouter as any);
|
|
269
|
+
|
|
270
|
+
// Should not redirect when authenticated
|
|
271
|
+
expect(mockRouter.push.calls.length).toBe(0);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('should handle missing authentication gracefully', () => {
|
|
275
|
+
// Clear authentication
|
|
276
|
+
localStorageMock.clear();
|
|
277
|
+
|
|
278
|
+
// Component should still render (may redirect, but shouldn't crash)
|
|
279
|
+
const page = ChatPage(mockRouter as any);
|
|
280
|
+
expect(page).toBeDefined();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
describe('page structure', () => {
|
|
285
|
+
beforeEach(() => {
|
|
286
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
287
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should render chat-container', () => {
|
|
291
|
+
const page = ChatPage(mockRouter as any);
|
|
292
|
+
expect(page.props?.className).toBe('chat-page');
|
|
293
|
+
|
|
294
|
+
// Find the chat-container child
|
|
295
|
+
const children = page.children as VNode[];
|
|
296
|
+
const container = children[0];
|
|
297
|
+
expect(container?.props?.className).toBe('chat-container');
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('should render chat-header', () => {
|
|
301
|
+
const page = ChatPage(mockRouter as any);
|
|
302
|
+
const html = renderToString(page);
|
|
303
|
+
|
|
304
|
+
expect(html).toContain('chat-header');
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should render page title', () => {
|
|
308
|
+
const page = ChatPage(mockRouter as any);
|
|
309
|
+
const html = renderToString(page);
|
|
310
|
+
|
|
311
|
+
expect(html).toContain('Chat Room');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('should render messages area', () => {
|
|
315
|
+
const page = ChatPage(mockRouter as any);
|
|
316
|
+
const html = renderToString(page);
|
|
317
|
+
|
|
318
|
+
expect(html).toContain('chat-messages');
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should render input area', () => {
|
|
322
|
+
const page = ChatPage(mockRouter as any);
|
|
323
|
+
const html = renderToString(page);
|
|
324
|
+
|
|
325
|
+
expect(html).toContain('chat-input-area');
|
|
326
|
+
expect(html).toContain('chat-input');
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('should render send button', () => {
|
|
330
|
+
const page = ChatPage(mockRouter as any);
|
|
331
|
+
const html = renderToString(page);
|
|
332
|
+
|
|
333
|
+
expect(html).toContain('Send');
|
|
334
|
+
expect(html).toContain('btn-primary');
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('should render back button', () => {
|
|
338
|
+
const page = ChatPage(mockRouter as any);
|
|
339
|
+
const html = renderToString(page);
|
|
340
|
+
|
|
341
|
+
expect(html).toContain('Back to Profile');
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('should have proper CSS classes', () => {
|
|
345
|
+
const page = ChatPage(mockRouter as any);
|
|
346
|
+
const html = renderToString(page);
|
|
347
|
+
|
|
348
|
+
expect(html).toContain('chat-page');
|
|
349
|
+
expect(html).toContain('chat-container');
|
|
350
|
+
expect(html).toContain('chat-header');
|
|
351
|
+
expect(html).toContain('chat-title');
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
describe('message loading', () => {
|
|
356
|
+
beforeEach(() => {
|
|
357
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
358
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('should show empty state when no messages', () => {
|
|
362
|
+
(global as any).fetch = async () => ({
|
|
363
|
+
ok: true,
|
|
364
|
+
json: async () => ({ messages: [] })
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const page = ChatPage(mockRouter as any);
|
|
368
|
+
|
|
369
|
+
// Should render without errors
|
|
370
|
+
expect(page).toBeDefined();
|
|
371
|
+
const html = renderToString(page);
|
|
372
|
+
expect(html).toContain('chat-messages');
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('should call fetch on mount', async () => {
|
|
376
|
+
const fetchSpy = mockFn();
|
|
377
|
+
(global as any).fetch = async () => {
|
|
378
|
+
fetchSpy();
|
|
379
|
+
return {
|
|
380
|
+
ok: true,
|
|
381
|
+
json: async () => ({ messages: [] })
|
|
382
|
+
};
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
ChatPage(mockRouter as any);
|
|
386
|
+
|
|
387
|
+
// Wait for async operations
|
|
388
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
389
|
+
|
|
390
|
+
// Fetch should have been called
|
|
391
|
+
expect(fetchSpy.calls.length).toBeGreaterThan(0);
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe('input functionality', () => {
|
|
396
|
+
beforeEach(() => {
|
|
397
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
398
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it('should render text input with correct attributes', () => {
|
|
402
|
+
const page = ChatPage(mockRouter as any);
|
|
403
|
+
const html = renderToString(page);
|
|
404
|
+
|
|
405
|
+
expect(html).toContain('type="text"');
|
|
406
|
+
expect(html).toContain('chat-input');
|
|
407
|
+
expect(html).toContain('Type your message...');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('should have form element', () => {
|
|
411
|
+
const page = ChatPage(mockRouter as any);
|
|
412
|
+
const html = renderToString(page);
|
|
413
|
+
|
|
414
|
+
expect(html).toContain('<form');
|
|
415
|
+
expect(html).toContain('chat-input-area');
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
describe('error handling', () => {
|
|
420
|
+
beforeEach(() => {
|
|
421
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
422
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should handle fetch error gracefully', async () => {
|
|
426
|
+
(global as any).fetch = async () => ({
|
|
427
|
+
ok: false,
|
|
428
|
+
json: async () => ({ messages: [] })
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const page = ChatPage(mockRouter as any);
|
|
432
|
+
|
|
433
|
+
// Wait for async operations
|
|
434
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
435
|
+
|
|
436
|
+
// Should handle the error gracefully
|
|
437
|
+
expect(page).toBeDefined();
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it('should handle network error gracefully', async () => {
|
|
441
|
+
(global as any).fetch = async () => {
|
|
442
|
+
throw new Error('Network error');
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
const page = ChatPage(mockRouter as any);
|
|
446
|
+
|
|
447
|
+
// Wait for async operations
|
|
448
|
+
await new Promise(resolve => setTimeout(resolve, 50));
|
|
449
|
+
|
|
450
|
+
// Should handle the error gracefully
|
|
451
|
+
expect(page).toBeDefined();
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
describe('navigation', () => {
|
|
456
|
+
beforeEach(() => {
|
|
457
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
458
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('should have back button that navigates to profile', () => {
|
|
462
|
+
const page = ChatPage(mockRouter as any);
|
|
463
|
+
const html = renderToString(page);
|
|
464
|
+
|
|
465
|
+
expect(html).toContain('Back to Profile');
|
|
466
|
+
expect(html).toContain('btn-secondary');
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
describe('user display', () => {
|
|
471
|
+
beforeEach(() => {
|
|
472
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
473
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
it('should display user name in header', () => {
|
|
477
|
+
const page = ChatPage(mockRouter as any);
|
|
478
|
+
const html = renderToString(page);
|
|
479
|
+
|
|
480
|
+
expect(html).toContain('Logged in as Test User');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('should show logged in user name', () => {
|
|
484
|
+
const page = ChatPage(mockRouter as any);
|
|
485
|
+
const html = renderToString(page);
|
|
486
|
+
|
|
487
|
+
expect(html).toContain('Test User');
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
describe('component consistency', () => {
|
|
492
|
+
beforeEach(() => {
|
|
493
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
494
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('should always return the same structure', () => {
|
|
498
|
+
const page1 = ChatPage(mockRouter as any);
|
|
499
|
+
const page2 = ChatPage(mockRouter as any);
|
|
500
|
+
|
|
501
|
+
expect(page1.tagName).toBe(page2.tagName);
|
|
502
|
+
expect(page1.props?.className).toBe(page2.props?.className);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
it('should render without errors', () => {
|
|
506
|
+
expect(() => {
|
|
507
|
+
const page = ChatPage(mockRouter as any);
|
|
508
|
+
renderToString(page);
|
|
509
|
+
}).not.toThrow();
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
describe('shared state integration', () => {
|
|
514
|
+
beforeEach(() => {
|
|
515
|
+
localStorageMock.setItem('token', 'fake-token');
|
|
516
|
+
localStorageMock.setItem('user', JSON.stringify({ id: '123', name: 'Test User', email: 'test@example.com', bio: 'Test bio', avatar: '' }));
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('should initialize shared state for messages', () => {
|
|
520
|
+
// Mock location to return correct host
|
|
521
|
+
(global as any).location = mockLocation;
|
|
522
|
+
(globalThis as any).location = mockLocation;
|
|
523
|
+
|
|
524
|
+
const page = ChatPage(mockRouter as any);
|
|
525
|
+
|
|
526
|
+
// Should create shared state without crashing
|
|
527
|
+
expect(page).toBeDefined();
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
});
|