@useavalon/avalon 0.1.5 → 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/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,107 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
|
|
3
|
-
// Test the enhanced hydration option parsing functionality
|
|
4
|
-
|
|
5
|
-
describe('Hydration Option Parsing - validateRootMargin patterns', () => {
|
|
6
|
-
it('should validate valid rootMargin patterns', () => {
|
|
7
|
-
const validPatterns = ['10px', '10px 20px', '10px 20px 30px 40px', '10%', '-10px', '0px', '100px 50px'];
|
|
8
|
-
const rootMarginRegex = /^(-?\d+(?:\.\d+)?(?:px|%)?(?:\s+-?\d+(?:\.\d+)?(?:px|%)?){0,3})$/;
|
|
9
|
-
|
|
10
|
-
validPatterns.forEach(pattern => {
|
|
11
|
-
expect(rootMarginRegex.test(pattern.trim())).toBe(true);
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should reject invalid rootMargin patterns', () => {
|
|
16
|
-
const invalidPatterns = ['invalid', '', '10px 20px 30px 40px 50px', 'abc', '10px invalid 20px'];
|
|
17
|
-
const rootMarginRegex = /^(-?\d+(?:\.\d+)?(?:px|%)?(?:\s+-?\d+(?:\.\d+)?(?:px|%)?){0,3})$/;
|
|
18
|
-
|
|
19
|
-
invalidPatterns.forEach(pattern => {
|
|
20
|
-
expect(rootMarginRegex.test(pattern.trim())).toBe(false);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('Hydration Option Parsing - directive parsing patterns', () => {
|
|
26
|
-
it('should parse directives correctly', () => {
|
|
27
|
-
const testCases = [
|
|
28
|
-
{ input: 'on:client', expectedDirective: 'on:client' },
|
|
29
|
-
{ input: 'on:visible={{rootMargin: "100px"}}', expectedDirective: 'on:visible' },
|
|
30
|
-
{ input: 'on:idle={{timeout: 10000}}', expectedDirective: 'on:idle' },
|
|
31
|
-
{ input: 'media:screen', expectedDirective: 'media:screen' },
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
testCases.forEach(testCase => {
|
|
35
|
-
const directiveMatch = testCase.input.match(/^([^=\s]+)/);
|
|
36
|
-
const directive = directiveMatch ? directiveMatch[1] : 'on:client';
|
|
37
|
-
expect(directive).toBe(testCase.expectedDirective);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('Hydration Option Parsing - option extraction patterns', () => {
|
|
43
|
-
it('should extract options correctly', () => {
|
|
44
|
-
const testCases = [
|
|
45
|
-
{ input: 'on:visible={{rootMargin: "100px"}}', expectedOptions: 'rootMargin: "100px"' },
|
|
46
|
-
{ input: 'on:visible={{rootMargin: "50px", threshold: 0.5}}', expectedOptions: 'rootMargin: "50px", threshold: 0.5' },
|
|
47
|
-
{ input: 'on:idle={{timeout: 10000}}', expectedOptions: 'timeout: 10000' },
|
|
48
|
-
];
|
|
49
|
-
|
|
50
|
-
testCases.forEach(testCase => {
|
|
51
|
-
const optionsMatch = testCase.input.match(/=\s*\{\{(.+?)\}\}/);
|
|
52
|
-
const extractedOptions = optionsMatch ? optionsMatch[1] : '';
|
|
53
|
-
expect(extractedOptions).toBe(testCase.expectedOptions);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe('Hydration Option Parsing - JSON parsing preparation', () => {
|
|
59
|
-
it('should normalize options strings for JSON parsing', () => {
|
|
60
|
-
const testCases = [
|
|
61
|
-
{ input: 'rootMargin: "100px"', expected: '"rootMargin": "100px"' },
|
|
62
|
-
{ input: "rootMargin: '100px'", expected: '"rootMargin": "100px"' },
|
|
63
|
-
{ input: 'timeout: 5000', expected: '"timeout": 5000' },
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
testCases.forEach(testCase => {
|
|
67
|
-
const normalized = testCase.input
|
|
68
|
-
.replace(/(\w+):/g, '"$1":')
|
|
69
|
-
.replace(/'/g, '"');
|
|
70
|
-
expect(normalized).toBe(testCase.expected);
|
|
71
|
-
const parsed = JSON.parse('{' + normalized + '}');
|
|
72
|
-
expect(parsed).toBeDefined();
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
describe('Hydration Option Parsing - validation ranges', () => {
|
|
78
|
-
it('should validate threshold values (0-1 range)', () => {
|
|
79
|
-
const validThresholds = [0, 0.5, 1, 0.25, 0.75];
|
|
80
|
-
const invalidThresholds = [-0.1, 1.1, 2, -1, '0.5', null, undefined];
|
|
81
|
-
|
|
82
|
-
validThresholds.forEach(threshold => {
|
|
83
|
-
const isValid = typeof threshold === 'number' && threshold >= 0 && threshold <= 1;
|
|
84
|
-
expect(isValid).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
invalidThresholds.forEach(threshold => {
|
|
88
|
-
const isValid = typeof threshold === 'number' && threshold >= 0 && threshold <= 1;
|
|
89
|
-
expect(isValid).toBe(false);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('should validate timeout values (positive numbers up to 60 seconds)', () => {
|
|
94
|
-
const validTimeouts = [1000, 5000, 30000, 60000];
|
|
95
|
-
const invalidTimeouts = [0, -1000, 70000, '5000', null, undefined];
|
|
96
|
-
|
|
97
|
-
validTimeouts.forEach(timeout => {
|
|
98
|
-
const isValid = typeof timeout === 'number' && timeout > 0 && timeout <= 60000;
|
|
99
|
-
expect(isValid).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
invalidTimeouts.forEach(timeout => {
|
|
103
|
-
const isValid = typeof timeout === 'number' && timeout > 0 && timeout <= 60000;
|
|
104
|
-
expect(isValid).toBe(false);
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
});
|
|
@@ -1,427 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for Lit HMR Adapter
|
|
3
|
-
*
|
|
4
|
-
* Verifies Lit-specific HMR functionality including:
|
|
5
|
-
* - Component detection
|
|
6
|
-
* - State preservation
|
|
7
|
-
* - Custom element re-registration
|
|
8
|
-
* - Property and attribute preservation
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, it, expect } from 'vitest';
|
|
12
|
-
import { LitHMRAdapter } from '../adapters/lit-adapter.ts';
|
|
13
|
-
|
|
14
|
-
// Mock HTMLElement for testing
|
|
15
|
-
class MockHTMLElement {
|
|
16
|
-
private _attributes: Map<string, string> = new Map();
|
|
17
|
-
private _children: MockHTMLElement[] = [];
|
|
18
|
-
public scrollTop = 0;
|
|
19
|
-
public scrollLeft = 0;
|
|
20
|
-
public style: Record<string, string> = {};
|
|
21
|
-
public tagName = 'DIV';
|
|
22
|
-
public textContent: string | null = null;
|
|
23
|
-
public innerHTML = '';
|
|
24
|
-
public className = '';
|
|
25
|
-
|
|
26
|
-
getAttribute(name: string): string | null {
|
|
27
|
-
return this._attributes.get(name) || null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
setAttribute(name: string, value: string): void {
|
|
31
|
-
this._attributes.set(name, value);
|
|
32
|
-
if (name === 'class') {
|
|
33
|
-
this.className = value;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
removeAttribute(name: string): void {
|
|
38
|
-
this._attributes.delete(name);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
hasAttribute(name: string): boolean {
|
|
42
|
-
return this._attributes.has(name);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
querySelector(selector: string): MockHTMLElement | null {
|
|
46
|
-
// Simple mock - check if selector matches class or tag
|
|
47
|
-
if (selector.startsWith('.')) {
|
|
48
|
-
const className = selector.substring(1);
|
|
49
|
-
for (const child of this._children) {
|
|
50
|
-
if (child.className === className) {
|
|
51
|
-
return child;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// Return first child for other selectors
|
|
56
|
-
if (this._children.length > 0) {
|
|
57
|
-
return this._children[0];
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
querySelectorAll(selector: string): MockHTMLElement[] {
|
|
63
|
-
return this._children;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
appendChild(child: MockHTMLElement): MockHTMLElement {
|
|
67
|
-
this._children.push(child);
|
|
68
|
-
return child;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
insertBefore(newChild: MockHTMLElement, referenceChild: MockHTMLElement | null): MockHTMLElement {
|
|
72
|
-
if (referenceChild === null || !this._children.includes(referenceChild)) {
|
|
73
|
-
this._children.push(newChild);
|
|
74
|
-
} else {
|
|
75
|
-
const index = this._children.indexOf(referenceChild);
|
|
76
|
-
this._children.splice(index, 0, newChild);
|
|
77
|
-
}
|
|
78
|
-
return newChild;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
remove(): void {
|
|
82
|
-
this._children = [];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
get children(): MockHTMLElement[] {
|
|
86
|
-
return this._children;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
get firstChild(): MockHTMLElement | null {
|
|
90
|
-
return this._children[0] || null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
get attributes(): Array<{ name: string; value: string }> & { length: number } {
|
|
94
|
-
const attrs = Array.from(this._attributes.entries()).map(([name, value]) => ({ name, value }));
|
|
95
|
-
const result = attrs as Array<{ name: string; value: string }> & { length: number };
|
|
96
|
-
return result;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
contains(element: unknown): boolean {
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Mock Lit component class
|
|
105
|
-
class MockLitElement {
|
|
106
|
-
static elementName = 'mock-counter';
|
|
107
|
-
|
|
108
|
-
// Lit-specific properties
|
|
109
|
-
count = 0;
|
|
110
|
-
label = 'Counter';
|
|
111
|
-
|
|
112
|
-
// Lit-specific methods
|
|
113
|
-
render() {
|
|
114
|
-
return `<div>${this.label}: ${this.count}</div>`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
requestUpdate() {
|
|
118
|
-
// Mock update
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
get updateComplete(): Promise<boolean> {
|
|
122
|
-
return Promise.resolve(true);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Mock Lit component with decorators
|
|
127
|
-
class MockDecoratedLitElement {
|
|
128
|
-
static elementName = 'decorated-counter';
|
|
129
|
-
|
|
130
|
-
__litElement = true;
|
|
131
|
-
|
|
132
|
-
count = 0;
|
|
133
|
-
|
|
134
|
-
render() {
|
|
135
|
-
return `<div>Count: ${this.count}</div>`;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
requestUpdate() {
|
|
139
|
-
// Mock update
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
get updateComplete(): Promise<boolean> {
|
|
143
|
-
return Promise.resolve(true);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// Mock Lit component module (default export)
|
|
148
|
-
const MockLitModule = {
|
|
149
|
-
default: MockLitElement,
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
describe('LitHMRAdapter - name', () => {
|
|
153
|
-
it('should have correct name', () => {
|
|
154
|
-
const adapter = new LitHMRAdapter();
|
|
155
|
-
expect(adapter.name).toBe('lit');
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
describe('LitHMRAdapter - canHandle', () => {
|
|
160
|
-
it('should detect Lit components', () => {
|
|
161
|
-
const adapter = new LitHMRAdapter();
|
|
162
|
-
|
|
163
|
-
// Should handle Lit element class
|
|
164
|
-
expect(adapter.canHandle(MockLitElement)).toBe(true);
|
|
165
|
-
|
|
166
|
-
// Should handle decorated Lit element
|
|
167
|
-
expect(adapter.canHandle(MockDecoratedLitElement)).toBe(true);
|
|
168
|
-
|
|
169
|
-
// Should handle module with default export
|
|
170
|
-
expect(adapter.canHandle(MockLitModule)).toBe(true);
|
|
171
|
-
|
|
172
|
-
// Should not handle non-Lit components
|
|
173
|
-
expect(adapter.canHandle(null)).toBe(false);
|
|
174
|
-
expect(adapter.canHandle(undefined)).toBe(false);
|
|
175
|
-
expect(adapter.canHandle({})).toBe(false);
|
|
176
|
-
expect(adapter.canHandle('string')).toBe(false);
|
|
177
|
-
expect(adapter.canHandle(123)).toBe(false);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should detect Lit by prototype methods', () => {
|
|
181
|
-
const adapter = new LitHMRAdapter();
|
|
182
|
-
|
|
183
|
-
// Create a class with Lit-like methods
|
|
184
|
-
class LitLikeElement {
|
|
185
|
-
render() { return ''; }
|
|
186
|
-
requestUpdate() {}
|
|
187
|
-
get updateComplete() { return Promise.resolve(true); }
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
expect(adapter.canHandle(LitLikeElement)).toBe(true);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
it('should detect Lit by function signature', () => {
|
|
194
|
-
const adapter = new LitHMRAdapter();
|
|
195
|
-
|
|
196
|
-
// Create a class that looks like a Lit component in its string representation
|
|
197
|
-
class LitLikeComponent {
|
|
198
|
-
render() {
|
|
199
|
-
// This will show up in toString()
|
|
200
|
-
return 'html`<div>Hello</div>`';
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// The adapter should be able to detect Lit patterns
|
|
205
|
-
// We just verify the method exists and works
|
|
206
|
-
expect(adapter.canHandle).toBeDefined();
|
|
207
|
-
|
|
208
|
-
// Test with a class that has LitElement in its code
|
|
209
|
-
const hasLitPattern = adapter.canHandle(LitLikeComponent);
|
|
210
|
-
// This may or may not detect it depending on toString() output
|
|
211
|
-
// but we verify it doesn't throw
|
|
212
|
-
expect(typeof hasLitPattern).toBe('boolean');
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
describe('LitHMRAdapter - preserveState', () => {
|
|
217
|
-
it('should capture element properties', () => {
|
|
218
|
-
const adapter = new LitHMRAdapter();
|
|
219
|
-
|
|
220
|
-
// Create a mock island with a Lit element
|
|
221
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
222
|
-
island.setAttribute('data-framework', 'lit');
|
|
223
|
-
island.setAttribute('data-src', '/islands/Counter.lit.ts');
|
|
224
|
-
island.setAttribute('data-props', '{"initialCount": 5}');
|
|
225
|
-
island.setAttribute('data-tag-name', 'mock-counter');
|
|
226
|
-
|
|
227
|
-
// Create a mock Lit element
|
|
228
|
-
const litElement = new MockHTMLElement() as unknown as HTMLElement & {
|
|
229
|
-
count: number;
|
|
230
|
-
label: string;
|
|
231
|
-
};
|
|
232
|
-
(litElement as unknown as Record<string, unknown>).count = 10;
|
|
233
|
-
(litElement as unknown as Record<string, unknown>).label = 'Test Counter';
|
|
234
|
-
litElement.setAttribute('data-lit-element', 'true');
|
|
235
|
-
litElement.setAttribute('theme', 'dark');
|
|
236
|
-
|
|
237
|
-
(island as unknown as MockHTMLElement).appendChild(litElement as unknown as MockHTMLElement);
|
|
238
|
-
|
|
239
|
-
// Preserve state - will return null due to missing document global
|
|
240
|
-
// but should not throw
|
|
241
|
-
const state = adapter.preserveState(island);
|
|
242
|
-
|
|
243
|
-
// In a test environment without document, state will be null
|
|
244
|
-
// This is expected behavior - the adapter handles it gracefully
|
|
245
|
-
// In a real browser environment, state would be captured
|
|
246
|
-
if (state) {
|
|
247
|
-
expect(state.framework).toBe('lit');
|
|
248
|
-
expect(state.data).toBeDefined();
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it('should capture DOM state', () => {
|
|
253
|
-
const adapter = new LitHMRAdapter();
|
|
254
|
-
|
|
255
|
-
// Create a mock island with form elements
|
|
256
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
257
|
-
island.setAttribute('data-framework', 'lit');
|
|
258
|
-
island.setAttribute('data-src', '/islands/Form.lit.ts');
|
|
259
|
-
island.setAttribute('data-tag-name', 'mock-form');
|
|
260
|
-
|
|
261
|
-
// Note: Full DOM state capture requires a real DOM environment
|
|
262
|
-
// This test verifies the method doesn't throw
|
|
263
|
-
const state = adapter.preserveState(island);
|
|
264
|
-
|
|
265
|
-
// State may be null in test environment without document global
|
|
266
|
-
// This is expected and handled gracefully
|
|
267
|
-
if (state) {
|
|
268
|
-
expect(state.framework).toBe('lit');
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it('should handle missing elements gracefully', () => {
|
|
273
|
-
const adapter = new LitHMRAdapter();
|
|
274
|
-
|
|
275
|
-
// Create an empty island
|
|
276
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
277
|
-
island.setAttribute('data-framework', 'lit');
|
|
278
|
-
island.setAttribute('data-src', '/islands/Empty.lit.ts');
|
|
279
|
-
|
|
280
|
-
// Preserve state should not throw
|
|
281
|
-
const state = adapter.preserveState(island);
|
|
282
|
-
|
|
283
|
-
// State may be null in test environment
|
|
284
|
-
if (state) {
|
|
285
|
-
expect(state.framework).toBe('lit');
|
|
286
|
-
}
|
|
287
|
-
});
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
describe('LitHMRAdapter - handleError', () => {
|
|
291
|
-
it('should not throw', () => {
|
|
292
|
-
const adapter = new LitHMRAdapter();
|
|
293
|
-
|
|
294
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
295
|
-
const error = new Error('Test error');
|
|
296
|
-
|
|
297
|
-
// handleError requires document global for creating error indicator
|
|
298
|
-
// In test environment, it will log but not throw
|
|
299
|
-
// This is expected behavior
|
|
300
|
-
try {
|
|
301
|
-
adapter.handleError(island, error);
|
|
302
|
-
} catch (e) {
|
|
303
|
-
// Expected in test environment without document
|
|
304
|
-
expect(e instanceof ReferenceError).toBe(true);
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
it('should handle different error types', () => {
|
|
309
|
-
const adapter = new LitHMRAdapter();
|
|
310
|
-
|
|
311
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
312
|
-
|
|
313
|
-
// Test that different error types are handled
|
|
314
|
-
const errors = [
|
|
315
|
-
new Error('custom element already defined'),
|
|
316
|
-
new Error('property not found'),
|
|
317
|
-
new Error('render error'),
|
|
318
|
-
];
|
|
319
|
-
|
|
320
|
-
for (const error of errors) {
|
|
321
|
-
try {
|
|
322
|
-
adapter.handleError(island, error);
|
|
323
|
-
} catch (e) {
|
|
324
|
-
// Expected in test environment without document
|
|
325
|
-
expect(e instanceof ReferenceError).toBe(true);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
describe('LitHMRAdapter - restoreState', () => {
|
|
332
|
-
it('should restore element properties', () => {
|
|
333
|
-
const adapter = new LitHMRAdapter();
|
|
334
|
-
|
|
335
|
-
// Create a mock island with a Lit element
|
|
336
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
337
|
-
island.setAttribute('data-tag-name', 'mock-counter');
|
|
338
|
-
|
|
339
|
-
const litElement = new MockHTMLElement() as unknown as HTMLElement & {
|
|
340
|
-
count: number;
|
|
341
|
-
label: string;
|
|
342
|
-
};
|
|
343
|
-
(litElement as unknown as Record<string, unknown>).count = 0;
|
|
344
|
-
(litElement as unknown as Record<string, unknown>).label = '';
|
|
345
|
-
(island as unknown as MockHTMLElement).appendChild(litElement as unknown as MockHTMLElement);
|
|
346
|
-
|
|
347
|
-
// Create a state snapshot
|
|
348
|
-
const state = {
|
|
349
|
-
framework: 'lit' as const,
|
|
350
|
-
timestamp: Date.now(),
|
|
351
|
-
data: {
|
|
352
|
-
tagName: 'mock-counter',
|
|
353
|
-
elementProperties: {
|
|
354
|
-
count: 10,
|
|
355
|
-
label: 'Restored Counter',
|
|
356
|
-
},
|
|
357
|
-
elementAttributes: {
|
|
358
|
-
theme: 'dark',
|
|
359
|
-
},
|
|
360
|
-
},
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
// Restore state
|
|
364
|
-
adapter.restoreState(island, state);
|
|
365
|
-
|
|
366
|
-
// Verify properties were restored
|
|
367
|
-
expect((litElement as unknown as Record<string, unknown>).count).toBe(10);
|
|
368
|
-
expect((litElement as unknown as Record<string, unknown>).label).toBe('Restored Counter');
|
|
369
|
-
expect(litElement.getAttribute('theme')).toBe('dark');
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
it('should handle missing elements gracefully', () => {
|
|
373
|
-
const adapter = new LitHMRAdapter();
|
|
374
|
-
|
|
375
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
376
|
-
|
|
377
|
-
const state = {
|
|
378
|
-
framework: 'lit' as const,
|
|
379
|
-
timestamp: Date.now(),
|
|
380
|
-
data: {
|
|
381
|
-
tagName: 'missing-element',
|
|
382
|
-
elementProperties: { count: 10 },
|
|
383
|
-
},
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
// Should not throw
|
|
387
|
-
adapter.restoreState(island, state);
|
|
388
|
-
});
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
describe('LitHMRAdapter - unmount', () => {
|
|
392
|
-
it('should clean up element', () => {
|
|
393
|
-
const adapter = new LitHMRAdapter();
|
|
394
|
-
|
|
395
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
396
|
-
island.setAttribute('data-tag-name', 'mock-counter');
|
|
397
|
-
|
|
398
|
-
const litElement = new MockHTMLElement() as unknown as HTMLElement;
|
|
399
|
-
(island as unknown as MockHTMLElement).appendChild(litElement as unknown as MockHTMLElement);
|
|
400
|
-
|
|
401
|
-
// Verify element exists
|
|
402
|
-
expect((island as unknown as MockHTMLElement).children.length).toBe(1);
|
|
403
|
-
|
|
404
|
-
// Unmount - this will call querySelectorAll and remove elements
|
|
405
|
-
adapter.unmount(island);
|
|
406
|
-
|
|
407
|
-
// In the mock implementation, remove() clears the _children array
|
|
408
|
-
// but the parent's children array is not automatically updated
|
|
409
|
-
// This is a limitation of the mock - in a real DOM, the element would be removed
|
|
410
|
-
// We just verify unmount doesn't throw
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
it('should handle missing elements gracefully', () => {
|
|
414
|
-
const adapter = new LitHMRAdapter();
|
|
415
|
-
|
|
416
|
-
const island = new MockHTMLElement() as unknown as HTMLElement;
|
|
417
|
-
|
|
418
|
-
// Should not throw
|
|
419
|
-
adapter.unmount(island);
|
|
420
|
-
});
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Note: Full integration tests for update() method would require:
|
|
424
|
-
// - A real Lit runtime
|
|
425
|
-
// - Custom element registry
|
|
426
|
-
// - DOM environment with custom elements support
|
|
427
|
-
// These are better tested in integration tests or E2E tests
|