@useavalon/avalon 0.1.4 → 0.1.6
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/package.json +31 -58
- package/src/vite-plugin/plugin.ts +22 -4
- package/src/build/README.md +0 -310
- package/src/client/tests/css-hmr-handler.test.ts +0 -360
- package/src/client/tests/framework-adapter.test.ts +0 -519
- package/src/client/tests/hmr-coordinator.test.ts +0 -176
- package/src/client/tests/hydration-option-parsing.test.ts +0 -107
- package/src/client/tests/lit-adapter.test.ts +0 -427
- package/src/client/tests/preact-adapter.test.ts +0 -353
- package/src/client/tests/qwik-adapter.test.ts +0 -343
- package/src/client/tests/react-adapter.test.ts +0 -317
- package/src/client/tests/solid-adapter.test.ts +0 -396
- package/src/client/tests/svelte-adapter.test.ts +0 -387
- package/src/client/tests/vue-adapter.test.ts +0 -407
- package/src/components/tests/component-analyzer.test.ts +0 -96
- package/src/components/tests/component-detection.test.ts +0 -347
- package/src/components/tests/persistent-islands.test.ts +0 -398
- package/src/core/components/tests/enhanced-framework-detector.test.ts +0 -577
- package/src/core/components/tests/framework-registry.test.ts +0 -465
- package/src/core/integrations/README.md +0 -282
- package/src/core/layout/tests/enhanced-layout-resolver.test.ts +0 -477
- package/src/core/layout/tests/layout-cache-optimization.test.ts +0 -149
- package/src/core/layout/tests/layout-composer.test.ts +0 -486
- package/src/core/layout/tests/layout-data-loader.test.ts +0 -443
- package/src/core/layout/tests/layout-discovery.test.ts +0 -253
- package/src/core/layout/tests/layout-matcher.test.ts +0 -480
- package/src/core/modules/tests/framework-module-resolver.test.ts +0 -263
- package/src/core/modules/tests/module-resolution-integration.test.ts +0 -117
- package/src/islands/discovery/tests/island-discovery.test.ts +0 -881
- package/src/middleware/__tests__/discovery.test.ts +0 -107
- package/src/types/tests/layout-types.test.ts +0 -197
- package/src/vite-plugin/tests/image-optimization.test.ts +0 -54
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for React HMR Adapter
|
|
3
|
-
*
|
|
4
|
-
* Verifies React-specific HMR functionality including:
|
|
5
|
-
* - Component detection
|
|
6
|
-
* - State preservation
|
|
7
|
-
* - Fast Refresh integration
|
|
8
|
-
* - Error handling
|
|
9
|
-
*
|
|
10
|
-
* Requirements: 2.1
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { describe, it, expect } from 'vitest';
|
|
14
|
-
import { ReactHMRAdapter } from '../adapters/react-adapter.ts';
|
|
15
|
-
import type { StateSnapshot } from '../framework-adapter.ts';
|
|
16
|
-
|
|
17
|
-
// Mock HTMLElement for testing
|
|
18
|
-
class MockHTMLElement {
|
|
19
|
-
private attributes: Map<string, string> = new Map();
|
|
20
|
-
private _children: MockHTMLElement[] = [];
|
|
21
|
-
public scrollTop = 0;
|
|
22
|
-
public scrollLeft = 0;
|
|
23
|
-
public style: Record<string, string> = {};
|
|
24
|
-
|
|
25
|
-
getAttribute(name: string): string | null {
|
|
26
|
-
return this.attributes.get(name) || null;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
setAttribute(name: string, value: string): void {
|
|
30
|
-
this.attributes.set(name, value);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
removeAttribute(name: string): void {
|
|
34
|
-
this.attributes.delete(name);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
hasAttribute(name: string): boolean {
|
|
38
|
-
return this.attributes.has(name);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
querySelector(selector: string): MockHTMLElement | null {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
querySelectorAll(selector: string): MockHTMLElement[] {
|
|
46
|
-
return [];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
contains(element: unknown): boolean {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
insertBefore(newNode: unknown, referenceNode: unknown): void {
|
|
54
|
-
// Mock implementation
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
get firstChild(): unknown {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
dispatchEvent(event: any): boolean {
|
|
62
|
-
return true;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Mock React components for testing
|
|
67
|
-
function MockFunctionComponent(props: Record<string, unknown>) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
class MockClassComponent {
|
|
72
|
-
isReactComponent = true;
|
|
73
|
-
|
|
74
|
-
render() {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const MockReactElement = {
|
|
80
|
-
$typeof: Symbol.for('react.element'),
|
|
81
|
-
type: MockFunctionComponent,
|
|
82
|
-
props: {},
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
describe('ReactHMRAdapter - initialization', () => {
|
|
86
|
-
it('should create adapter with correct name', () => {
|
|
87
|
-
const adapter = new ReactHMRAdapter();
|
|
88
|
-
|
|
89
|
-
expect(adapter).toBeDefined();
|
|
90
|
-
expect(adapter.name).toBe('react');
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe('ReactHMRAdapter - canHandle', () => {
|
|
95
|
-
it('should handle function component', () => {
|
|
96
|
-
const adapter = new ReactHMRAdapter();
|
|
97
|
-
|
|
98
|
-
const result = adapter.canHandle(MockFunctionComponent);
|
|
99
|
-
expect(result).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should handle class component', () => {
|
|
103
|
-
const adapter = new ReactHMRAdapter();
|
|
104
|
-
|
|
105
|
-
const result = adapter.canHandle(MockClassComponent);
|
|
106
|
-
expect(result).toBe(true);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should handle React element', () => {
|
|
110
|
-
const adapter = new ReactHMRAdapter();
|
|
111
|
-
|
|
112
|
-
const result = adapter.canHandle(MockReactElement);
|
|
113
|
-
expect(result).toBe(true);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should handle arrow function', () => {
|
|
117
|
-
const adapter = new ReactHMRAdapter();
|
|
118
|
-
const ArrowComponent = () => null;
|
|
119
|
-
|
|
120
|
-
const result = adapter.canHandle(ArrowComponent);
|
|
121
|
-
expect(result).toBe(true);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('should not handle non-React component', () => {
|
|
125
|
-
const adapter = new ReactHMRAdapter();
|
|
126
|
-
|
|
127
|
-
expect(adapter.canHandle(null)).toBe(false);
|
|
128
|
-
expect(adapter.canHandle(undefined)).toBe(false);
|
|
129
|
-
expect(adapter.canHandle('string')).toBe(false);
|
|
130
|
-
expect(adapter.canHandle(123)).toBe(false);
|
|
131
|
-
expect(adapter.canHandle({})).toBe(false);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('ReactHMRAdapter - preserveState', () => {
|
|
136
|
-
it('should return valid snapshot', () => {
|
|
137
|
-
const adapter = new ReactHMRAdapter();
|
|
138
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
139
|
-
|
|
140
|
-
// Set up mock island attributes
|
|
141
|
-
mockIsland.setAttribute('data-src', '/islands/TestComponent.tsx');
|
|
142
|
-
mockIsland.setAttribute('data-props', JSON.stringify({ count: 5 }));
|
|
143
|
-
|
|
144
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
145
|
-
|
|
146
|
-
// In test environment without DOM, preserveState returns null
|
|
147
|
-
// This is expected behavior - the adapter gracefully handles missing DOM
|
|
148
|
-
if (snapshot) {
|
|
149
|
-
expect(snapshot.framework).toBe('react');
|
|
150
|
-
expect(typeof snapshot.timestamp).toBe('number');
|
|
151
|
-
expect(snapshot.data).toBeDefined();
|
|
152
|
-
} else {
|
|
153
|
-
// Graceful degradation when DOM is not available
|
|
154
|
-
expect(snapshot).toBeNull();
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should capture component name', () => {
|
|
159
|
-
const adapter = new ReactHMRAdapter();
|
|
160
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
161
|
-
|
|
162
|
-
mockIsland.setAttribute('data-src', '/islands/Counter.tsx');
|
|
163
|
-
mockIsland.setAttribute('data-props', '{}');
|
|
164
|
-
|
|
165
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
166
|
-
|
|
167
|
-
// In test environment without DOM, preserveState returns null
|
|
168
|
-
if (snapshot) {
|
|
169
|
-
expect(snapshot.data.componentName).toBe('Counter');
|
|
170
|
-
} else {
|
|
171
|
-
// Expected in test environment
|
|
172
|
-
expect(snapshot).toBeNull();
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('should capture props', () => {
|
|
177
|
-
const adapter = new ReactHMRAdapter();
|
|
178
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
179
|
-
|
|
180
|
-
const props = { count: 10, name: 'test' };
|
|
181
|
-
mockIsland.setAttribute('data-src', '/islands/TestComponent.tsx');
|
|
182
|
-
mockIsland.setAttribute('data-props', JSON.stringify(props));
|
|
183
|
-
|
|
184
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
185
|
-
|
|
186
|
-
// In test environment without DOM, preserveState returns null
|
|
187
|
-
if (snapshot) {
|
|
188
|
-
expect(snapshot.data.capturedProps).toBeDefined();
|
|
189
|
-
expect(snapshot.data.capturedProps).toEqual(props);
|
|
190
|
-
} else {
|
|
191
|
-
// Expected in test environment
|
|
192
|
-
expect(snapshot).toBeNull();
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('should handle missing props', () => {
|
|
197
|
-
const adapter = new ReactHMRAdapter();
|
|
198
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
199
|
-
|
|
200
|
-
mockIsland.setAttribute('data-src', '/islands/TestComponent.tsx');
|
|
201
|
-
// No data-props attribute
|
|
202
|
-
|
|
203
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
204
|
-
|
|
205
|
-
// In test environment without DOM, preserveState returns null
|
|
206
|
-
if (snapshot) {
|
|
207
|
-
expect(snapshot.data.capturedProps).toBeDefined();
|
|
208
|
-
expect(Object.keys(snapshot.data.capturedProps || {}).length).toBe(0);
|
|
209
|
-
} else {
|
|
210
|
-
// Expected in test environment
|
|
211
|
-
expect(snapshot).toBeNull();
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('should handle invalid JSON props', () => {
|
|
216
|
-
const adapter = new ReactHMRAdapter();
|
|
217
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
218
|
-
|
|
219
|
-
mockIsland.setAttribute('data-src', '/islands/TestComponent.tsx');
|
|
220
|
-
mockIsland.setAttribute('data-props', 'invalid json');
|
|
221
|
-
|
|
222
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
223
|
-
|
|
224
|
-
// Should return null on error
|
|
225
|
-
expect(snapshot).toBeNull();
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
describe('ReactHMRAdapter - restoreState', () => {
|
|
230
|
-
it('should call base implementation', () => {
|
|
231
|
-
const adapter = new ReactHMRAdapter();
|
|
232
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
233
|
-
|
|
234
|
-
const snapshot: StateSnapshot = {
|
|
235
|
-
framework: 'react',
|
|
236
|
-
timestamp: Date.now(),
|
|
237
|
-
data: {},
|
|
238
|
-
dom: {
|
|
239
|
-
scrollPosition: { x: 50, y: 100 },
|
|
240
|
-
},
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
// Should not throw
|
|
244
|
-
adapter.restoreState(mockIsland, snapshot);
|
|
245
|
-
|
|
246
|
-
// Verify DOM state was restored
|
|
247
|
-
expect(mockIsland.scrollLeft).toBe(50);
|
|
248
|
-
expect(mockIsland.scrollTop).toBe(100);
|
|
249
|
-
});
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
describe('ReactHMRAdapter - handleError', () => {
|
|
253
|
-
it('should add React-specific error info', () => {
|
|
254
|
-
const adapter = new ReactHMRAdapter();
|
|
255
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
256
|
-
|
|
257
|
-
const error = new Error('Invalid hook call');
|
|
258
|
-
|
|
259
|
-
// In test environment without DOM, handleError may fail
|
|
260
|
-
// This is expected - the adapter requires DOM APIs
|
|
261
|
-
try {
|
|
262
|
-
adapter.handleError(mockIsland, error);
|
|
263
|
-
|
|
264
|
-
// If it succeeds, verify error attributes were set
|
|
265
|
-
expect(mockIsland.getAttribute('data-hmr-error')).toBe('true');
|
|
266
|
-
expect(mockIsland.getAttribute('data-hmr-error-message')).toBe('Invalid hook call');
|
|
267
|
-
} catch (e) {
|
|
268
|
-
// Expected in test environment without DOM
|
|
269
|
-
// The adapter gracefully handles missing DOM APIs
|
|
270
|
-
expect((e as Error).message.includes('document is not defined')).toBe(true);
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it('should provide hooks hint', () => {
|
|
275
|
-
const adapter = new ReactHMRAdapter();
|
|
276
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
277
|
-
|
|
278
|
-
const error = new Error('Hooks can only be called inside the body of a function component');
|
|
279
|
-
|
|
280
|
-
// The adapter should recognize hooks-related errors and provide helpful hints
|
|
281
|
-
// This is tested indirectly through the error message
|
|
282
|
-
try {
|
|
283
|
-
adapter.handleError(mockIsland, error);
|
|
284
|
-
} catch (e) {
|
|
285
|
-
// Expected in test environment
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// The actual hint is added to the error indicator element
|
|
289
|
-
// which requires full DOM support to test properly
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
describe('ReactHMRAdapter - extractComponentName', () => {
|
|
294
|
-
it('should extract from various paths', () => {
|
|
295
|
-
const adapter = new ReactHMRAdapter();
|
|
296
|
-
|
|
297
|
-
// Access private method through type assertion for testing
|
|
298
|
-
const extractName = (adapter as any).extractComponentName.bind(adapter);
|
|
299
|
-
|
|
300
|
-
expect(extractName('/islands/Counter.tsx')).toBe('Counter');
|
|
301
|
-
expect(extractName('/islands/Button.jsx')).toBe('Button');
|
|
302
|
-
expect(extractName('/src/components/Card.ts')).toBe('Card');
|
|
303
|
-
expect(extractName('/nested/path/Component.js')).toBe('Component');
|
|
304
|
-
expect(extractName('SimpleComponent.tsx')).toBe('SimpleComponent');
|
|
305
|
-
});
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
describe('ReactHMRAdapter - singleton instance', () => {
|
|
309
|
-
it('should export singleton', async () => {
|
|
310
|
-
// Import the singleton
|
|
311
|
-
const { reactAdapter } = await import('../adapters/react-adapter.ts');
|
|
312
|
-
|
|
313
|
-
expect(reactAdapter).toBeDefined();
|
|
314
|
-
expect(reactAdapter.name).toBe('react');
|
|
315
|
-
expect(reactAdapter instanceof ReactHMRAdapter).toBe(true);
|
|
316
|
-
});
|
|
317
|
-
});
|
|
@@ -1,396 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Solid HMR Adapter
|
|
3
|
-
*
|
|
4
|
-
* Verifies Solid-specific HMR functionality including:
|
|
5
|
-
* - Component detection
|
|
6
|
-
* - State preservation
|
|
7
|
-
* - Solid Refresh integration
|
|
8
|
-
* - Signal subscription preservation
|
|
9
|
-
* - Error handling
|
|
10
|
-
*
|
|
11
|
-
* Requirements: 2.5
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { describe, it, expect } from 'vitest';
|
|
15
|
-
import { SolidHMRAdapter } from '../adapters/solid-adapter.ts';
|
|
16
|
-
import type { StateSnapshot } from '../framework-adapter.ts';
|
|
17
|
-
|
|
18
|
-
// Mock HTMLElement for testing
|
|
19
|
-
class MockHTMLElement {
|
|
20
|
-
private attributes: Map<string, string> = new Map();
|
|
21
|
-
private _children: MockHTMLElement[] = [];
|
|
22
|
-
public scrollTop = 0;
|
|
23
|
-
public scrollLeft = 0;
|
|
24
|
-
public style: Record<string, string> = {};
|
|
25
|
-
public dataset: Record<string, string> = {};
|
|
26
|
-
|
|
27
|
-
getAttribute(name: string): string | null {
|
|
28
|
-
return this.attributes.get(name) || null;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
setAttribute(name: string, value: string): void {
|
|
32
|
-
this.attributes.set(name, value);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
removeAttribute(name: string): void {
|
|
36
|
-
this.attributes.delete(name);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
hasAttribute(name: string): boolean {
|
|
40
|
-
return this.attributes.has(name);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
querySelector(selector: string): MockHTMLElement | null {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
querySelectorAll(selector: string): MockHTMLElement[] {
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
contains(element: unknown): boolean {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
insertBefore(newNode: unknown, referenceNode: unknown): void {
|
|
56
|
-
// Mock implementation
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get firstChild(): unknown {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
get children(): MockHTMLElement[] {
|
|
64
|
-
return this._children;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
dispatchEvent(event: any): boolean {
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Mock Solid components for testing
|
|
73
|
-
function MockSolidComponent(props: Record<string, unknown>) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Add Solid marker
|
|
78
|
-
(MockSolidComponent as any).__solid = true;
|
|
79
|
-
|
|
80
|
-
function MockSolidComponentWithSignal(props: Record<string, unknown>) {
|
|
81
|
-
// Simulate a component that uses createSignal
|
|
82
|
-
const code = `
|
|
83
|
-
function Component() {
|
|
84
|
-
const [count, setCount] = createSignal(0);
|
|
85
|
-
return <div>{count()}</div>;
|
|
86
|
-
}
|
|
87
|
-
`;
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function MockSolidComponentWithEffect(props: Record<string, unknown>) {
|
|
92
|
-
// Simulate a component that uses createEffect
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Add Solid patterns to function strings
|
|
97
|
-
Object.defineProperty(MockSolidComponentWithSignal, 'toString', {
|
|
98
|
-
value: () => 'function() { createSignal(0); }',
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
Object.defineProperty(MockSolidComponentWithEffect, 'toString', {
|
|
102
|
-
value: () => 'function() { createEffect(() => {}); }',
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const MockSolidModule = {
|
|
106
|
-
default: MockSolidComponent,
|
|
107
|
-
__solid: true,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
describe('SolidHMRAdapter - initialization', () => {
|
|
111
|
-
it('should create adapter with correct name', () => {
|
|
112
|
-
const adapter = new SolidHMRAdapter();
|
|
113
|
-
|
|
114
|
-
expect(adapter).toBeDefined();
|
|
115
|
-
expect(adapter.name).toBe('solid');
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe('SolidHMRAdapter - canHandle', () => {
|
|
120
|
-
it('should handle Solid component with marker', () => {
|
|
121
|
-
const adapter = new SolidHMRAdapter();
|
|
122
|
-
|
|
123
|
-
const result = adapter.canHandle(MockSolidComponent);
|
|
124
|
-
expect(result).toBe(true);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should handle component with createSignal', () => {
|
|
128
|
-
const adapter = new SolidHMRAdapter();
|
|
129
|
-
|
|
130
|
-
const result = adapter.canHandle(MockSolidComponentWithSignal);
|
|
131
|
-
expect(result).toBe(true);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should handle component with createEffect', () => {
|
|
135
|
-
const adapter = new SolidHMRAdapter();
|
|
136
|
-
|
|
137
|
-
const result = adapter.canHandle(MockSolidComponentWithEffect);
|
|
138
|
-
expect(result).toBe(true);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should handle function component', () => {
|
|
142
|
-
const adapter = new SolidHMRAdapter();
|
|
143
|
-
const ArrowComponent = () => null;
|
|
144
|
-
|
|
145
|
-
// Solid components are just functions, so any function could be a Solid component
|
|
146
|
-
const result = adapter.canHandle(ArrowComponent);
|
|
147
|
-
expect(result).toBe(true);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it('should handle module with default export', () => {
|
|
151
|
-
const adapter = new SolidHMRAdapter();
|
|
152
|
-
|
|
153
|
-
const result = adapter.canHandle(MockSolidModule);
|
|
154
|
-
expect(result).toBe(true);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should not handle non-Solid component', () => {
|
|
158
|
-
const adapter = new SolidHMRAdapter();
|
|
159
|
-
|
|
160
|
-
expect(adapter.canHandle(null)).toBe(false);
|
|
161
|
-
expect(adapter.canHandle(undefined)).toBe(false);
|
|
162
|
-
expect(adapter.canHandle('string')).toBe(false);
|
|
163
|
-
expect(adapter.canHandle(123)).toBe(false);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it('should not handle plain object without default', () => {
|
|
167
|
-
const adapter = new SolidHMRAdapter();
|
|
168
|
-
|
|
169
|
-
const result = adapter.canHandle({ foo: 'bar' });
|
|
170
|
-
expect(result).toBe(false);
|
|
171
|
-
});
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
describe('SolidHMRAdapter - preserveState', () => {
|
|
175
|
-
it('should return valid snapshot', () => {
|
|
176
|
-
const adapter = new SolidHMRAdapter();
|
|
177
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
178
|
-
|
|
179
|
-
// Set up mock island attributes
|
|
180
|
-
mockIsland.setAttribute('data-src', '/islands/SolidCounter.solid.tsx');
|
|
181
|
-
mockIsland.setAttribute('data-props', JSON.stringify({ count: 5 }));
|
|
182
|
-
mockIsland.dataset.solidRenderId = 'solid-123';
|
|
183
|
-
|
|
184
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
185
|
-
|
|
186
|
-
// In test environment without DOM, preserveState returns null
|
|
187
|
-
// This is expected behavior - the adapter gracefully handles missing DOM
|
|
188
|
-
if (snapshot) {
|
|
189
|
-
expect(snapshot.framework).toBe('solid');
|
|
190
|
-
expect(typeof snapshot.timestamp).toBe('number');
|
|
191
|
-
expect(snapshot.data).toBeDefined();
|
|
192
|
-
} else {
|
|
193
|
-
// Graceful degradation when DOM is not available
|
|
194
|
-
expect(snapshot).toBeNull();
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('should capture component name', () => {
|
|
199
|
-
const adapter = new SolidHMRAdapter();
|
|
200
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
201
|
-
|
|
202
|
-
mockIsland.setAttribute('data-src', '/islands/Counter.solid.tsx');
|
|
203
|
-
mockIsland.setAttribute('data-props', '{}');
|
|
204
|
-
|
|
205
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
206
|
-
|
|
207
|
-
// In test environment without DOM, preserveState returns null
|
|
208
|
-
if (snapshot) {
|
|
209
|
-
expect(snapshot.data.componentName).toBe('Counter');
|
|
210
|
-
} else {
|
|
211
|
-
// Expected in test environment
|
|
212
|
-
expect(snapshot).toBeNull();
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
it('should capture render ID', () => {
|
|
217
|
-
const adapter = new SolidHMRAdapter();
|
|
218
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
219
|
-
|
|
220
|
-
mockIsland.setAttribute('data-src', '/islands/TestComponent.solid.tsx');
|
|
221
|
-
mockIsland.setAttribute('data-props', '{}');
|
|
222
|
-
mockIsland.dataset.solidRenderId = 'solid-456';
|
|
223
|
-
|
|
224
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
225
|
-
|
|
226
|
-
// In test environment without DOM, preserveState returns null
|
|
227
|
-
if (snapshot) {
|
|
228
|
-
expect(snapshot.data.renderId).toBe('solid-456');
|
|
229
|
-
} else {
|
|
230
|
-
// Expected in test environment
|
|
231
|
-
expect(snapshot).toBeNull();
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('should capture props', () => {
|
|
236
|
-
const adapter = new SolidHMRAdapter();
|
|
237
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
238
|
-
|
|
239
|
-
const props = { count: 10, name: 'test' };
|
|
240
|
-
mockIsland.setAttribute('data-src', '/islands/TestComponent.solid.tsx');
|
|
241
|
-
mockIsland.setAttribute('data-props', JSON.stringify(props));
|
|
242
|
-
|
|
243
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
244
|
-
|
|
245
|
-
// In test environment without DOM, preserveState returns null
|
|
246
|
-
if (snapshot) {
|
|
247
|
-
expect(snapshot.data.capturedProps).toBeDefined();
|
|
248
|
-
expect(snapshot.data.capturedProps).toEqual(props);
|
|
249
|
-
} else {
|
|
250
|
-
// Expected in test environment
|
|
251
|
-
expect(snapshot).toBeNull();
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it('should handle missing props', () => {
|
|
256
|
-
const adapter = new SolidHMRAdapter();
|
|
257
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
258
|
-
|
|
259
|
-
mockIsland.setAttribute('data-src', '/islands/TestComponent.solid.tsx');
|
|
260
|
-
// No data-props attribute
|
|
261
|
-
|
|
262
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
263
|
-
|
|
264
|
-
// In test environment without DOM, preserveState returns null
|
|
265
|
-
if (snapshot) {
|
|
266
|
-
expect(snapshot.data.capturedProps).toBeDefined();
|
|
267
|
-
expect(Object.keys(snapshot.data.capturedProps || {}).length).toBe(0);
|
|
268
|
-
} else {
|
|
269
|
-
// Expected in test environment
|
|
270
|
-
expect(snapshot).toBeNull();
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
it('should handle invalid JSON props', () => {
|
|
275
|
-
const adapter = new SolidHMRAdapter();
|
|
276
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
277
|
-
|
|
278
|
-
mockIsland.setAttribute('data-src', '/islands/TestComponent.solid.tsx');
|
|
279
|
-
mockIsland.setAttribute('data-props', 'invalid json');
|
|
280
|
-
|
|
281
|
-
const snapshot = adapter.preserveState(mockIsland);
|
|
282
|
-
|
|
283
|
-
// Should return null on error
|
|
284
|
-
expect(snapshot).toBeNull();
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
describe('SolidHMRAdapter - restoreState', () => {
|
|
289
|
-
it('should call base implementation', () => {
|
|
290
|
-
const adapter = new SolidHMRAdapter();
|
|
291
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
292
|
-
|
|
293
|
-
const snapshot: StateSnapshot = {
|
|
294
|
-
framework: 'solid',
|
|
295
|
-
timestamp: Date.now(),
|
|
296
|
-
data: {},
|
|
297
|
-
dom: {
|
|
298
|
-
scrollPosition: { x: 50, y: 100 },
|
|
299
|
-
},
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
// Should not throw
|
|
303
|
-
adapter.restoreState(mockIsland, snapshot);
|
|
304
|
-
|
|
305
|
-
// Verify DOM state was restored
|
|
306
|
-
expect(mockIsland.scrollLeft).toBe(50);
|
|
307
|
-
expect(mockIsland.scrollTop).toBe(100);
|
|
308
|
-
});
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
describe('SolidHMRAdapter - handleError', () => {
|
|
312
|
-
it('should add Solid-specific error info', () => {
|
|
313
|
-
const adapter = new SolidHMRAdapter();
|
|
314
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
315
|
-
|
|
316
|
-
const error = new Error('Signal must be called as a function');
|
|
317
|
-
|
|
318
|
-
// In test environment without DOM, handleError may fail
|
|
319
|
-
// This is expected - the adapter requires DOM APIs
|
|
320
|
-
try {
|
|
321
|
-
adapter.handleError(mockIsland, error);
|
|
322
|
-
|
|
323
|
-
// If it succeeds, verify error attributes were set
|
|
324
|
-
expect(mockIsland.getAttribute('data-hmr-error')).toBe('true');
|
|
325
|
-
expect(mockIsland.getAttribute('data-hmr-error-message')).toBe('Signal must be called as a function');
|
|
326
|
-
} catch (e) {
|
|
327
|
-
// Expected in test environment without DOM
|
|
328
|
-
// The adapter gracefully handles missing DOM APIs
|
|
329
|
-
expect((e as Error).message.includes('document is not defined')).toBe(true);
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('should provide signal hint', () => {
|
|
334
|
-
const adapter = new SolidHMRAdapter();
|
|
335
|
-
const mockIsland = new MockHTMLElement() as unknown as HTMLElement;
|
|
336
|
-
|
|
337
|
-
const error = new Error('Signal is not defined');
|
|
338
|
-
|
|
339
|
-
// The adapter should recognize signal-related errors and provide helpful hints
|
|
340
|
-
// This is tested indirectly through the error message
|
|
341
|
-
try {
|
|
342
|
-
adapter.handleError(mockIsland, error);
|
|
343
|
-
} catch (e) {
|
|
344
|
-
// Expected in test environment
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// The actual hint is added to the error indicator element
|
|
348
|
-
// which requires full DOM support to test properly
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
describe('SolidHMRAdapter - extractComponentName', () => {
|
|
353
|
-
it('should extract from various paths', () => {
|
|
354
|
-
const adapter = new SolidHMRAdapter();
|
|
355
|
-
|
|
356
|
-
// Access private method through type assertion for testing
|
|
357
|
-
const extractName = (adapter as any).extractComponentName.bind(adapter);
|
|
358
|
-
|
|
359
|
-
expect(extractName('/islands/Counter.solid.tsx')).toBe('Counter');
|
|
360
|
-
expect(extractName('/islands/Button.solid.jsx')).toBe('Button');
|
|
361
|
-
expect(extractName('/src/components/Card.tsx')).toBe('Card');
|
|
362
|
-
expect(extractName('/nested/path/Component.jsx')).toBe('Component');
|
|
363
|
-
expect(extractName('SimpleComponent.solid.tsx')).toBe('SimpleComponent');
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
describe('SolidHMRAdapter - generateComponentId', () => {
|
|
368
|
-
it('should create valid ID', () => {
|
|
369
|
-
const adapter = new SolidHMRAdapter();
|
|
370
|
-
|
|
371
|
-
// Access private method through type assertion for testing
|
|
372
|
-
const generateId = (adapter as any).generateComponentId.bind(adapter);
|
|
373
|
-
|
|
374
|
-
const id1 = generateId('/islands/Counter.solid.tsx');
|
|
375
|
-
expect(typeof id1).toBe('string');
|
|
376
|
-
expect(id1.includes('/')).toBe(false);
|
|
377
|
-
expect(id1.includes('.')).toBe(false);
|
|
378
|
-
|
|
379
|
-
const id2 = generateId('/islands/Counter.solid.tsx');
|
|
380
|
-
expect(id1).toBe(id2);
|
|
381
|
-
|
|
382
|
-
const id3 = generateId('/islands/Button.solid.tsx');
|
|
383
|
-
expect(id1 === id3).toBe(false);
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
describe('SolidHMRAdapter - singleton instance', () => {
|
|
388
|
-
it('should export singleton', async () => {
|
|
389
|
-
// Import the singleton
|
|
390
|
-
const { solidAdapter } = await import('../adapters/solid-adapter.ts');
|
|
391
|
-
|
|
392
|
-
expect(solidAdapter).toBeDefined();
|
|
393
|
-
expect(solidAdapter.name).toBe('solid');
|
|
394
|
-
expect(solidAdapter instanceof SolidHMRAdapter).toBe(true);
|
|
395
|
-
});
|
|
396
|
-
});
|