@flightdev/ui 2.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +285 -70
  2. package/dist/{chunk-XTDK7ME5.js → chunk-S4DTUQII.js} +246 -19
  3. package/dist/chunk-S4DTUQII.js.map +1 -0
  4. package/dist/core/index.d.ts +423 -3
  5. package/dist/core/index.js +23 -2
  6. package/dist/core/index.js.map +1 -0
  7. package/dist/index.d.ts +2 -3
  8. package/dist/index.js +29 -5
  9. package/dist/index.js.map +1 -0
  10. package/package.json +11 -181
  11. package/.turbo/turbo-build.log +0 -81
  12. package/.turbo/turbo-lint.log +0 -40
  13. package/.turbo/turbo-typecheck.log +0 -4
  14. package/TESTING.md +0 -124
  15. package/dist/adapter-MMD-iHNx.d.ts +0 -424
  16. package/dist/adapters/tier-1/angular.d.ts +0 -60
  17. package/dist/adapters/tier-1/angular.js +0 -2
  18. package/dist/adapters/tier-1/index.d.ts +0 -7
  19. package/dist/adapters/tier-1/index.js +0 -7
  20. package/dist/adapters/tier-1/qwik.d.ts +0 -55
  21. package/dist/adapters/tier-1/qwik.js +0 -2
  22. package/dist/adapters/tier-1/react.d.ts +0 -67
  23. package/dist/adapters/tier-1/react.js +0 -2
  24. package/dist/adapters/tier-1/solid.d.ts +0 -45
  25. package/dist/adapters/tier-1/solid.js +0 -2
  26. package/dist/adapters/tier-1/svelte.d.ts +0 -48
  27. package/dist/adapters/tier-1/svelte.js +0 -2
  28. package/dist/adapters/tier-1/vue.d.ts +0 -47
  29. package/dist/adapters/tier-1/vue.js +0 -2
  30. package/dist/adapters/tier-2/index.d.ts +0 -7
  31. package/dist/adapters/tier-2/index.js +0 -7
  32. package/dist/adapters/tier-2/inferno.d.ts +0 -31
  33. package/dist/adapters/tier-2/inferno.js +0 -2
  34. package/dist/adapters/tier-2/lit.d.ts +0 -34
  35. package/dist/adapters/tier-2/lit.js +0 -2
  36. package/dist/adapters/tier-2/marko.d.ts +0 -59
  37. package/dist/adapters/tier-2/marko.js +0 -2
  38. package/dist/adapters/tier-2/mithril.d.ts +0 -31
  39. package/dist/adapters/tier-2/mithril.js +0 -2
  40. package/dist/adapters/tier-2/preact.d.ts +0 -33
  41. package/dist/adapters/tier-2/preact.js +0 -2
  42. package/dist/adapters/tier-2/stencil.d.ts +0 -52
  43. package/dist/adapters/tier-2/stencil.js +0 -2
  44. package/dist/adapters/tier-3/alpine.d.ts +0 -73
  45. package/dist/adapters/tier-3/alpine.js +0 -2
  46. package/dist/adapters/tier-3/hotwire.d.ts +0 -71
  47. package/dist/adapters/tier-3/hotwire.js +0 -2
  48. package/dist/adapters/tier-3/htmx.d.ts +0 -88
  49. package/dist/adapters/tier-3/htmx.js +0 -2
  50. package/dist/adapters/tier-3/index.d.ts +0 -7
  51. package/dist/adapters/tier-3/index.js +0 -7
  52. package/dist/adapters/tier-3/petite-vue.d.ts +0 -56
  53. package/dist/adapters/tier-3/petite-vue.js +0 -2
  54. package/dist/adapters/tier-3/stimulus.d.ts +0 -63
  55. package/dist/adapters/tier-3/stimulus.js +0 -2
  56. package/dist/adapters/tier-3/vanilla.d.ts +0 -63
  57. package/dist/adapters/tier-3/vanilla.js +0 -2
  58. package/dist/chunk-2SNQ6PTM.js +0 -217
  59. package/dist/chunk-3D4XMIZI.js +0 -136
  60. package/dist/chunk-3HU6GSQ4.js +0 -125
  61. package/dist/chunk-4PZDNFL7.js +0 -148
  62. package/dist/chunk-5IBLFTYL.js +0 -114
  63. package/dist/chunk-64JZJ7OK.js +0 -142
  64. package/dist/chunk-7ZJI3QU2.js +0 -132
  65. package/dist/chunk-CE4FJHQJ.js +0 -133
  66. package/dist/chunk-DTCAUBH5.js +0 -87
  67. package/dist/chunk-NTASPOHG.js +0 -106
  68. package/dist/chunk-OI2AMQLG.js +0 -152
  69. package/dist/chunk-Q7HUE44H.js +0 -106
  70. package/dist/chunk-QH3LOWXU.js +0 -155
  71. package/dist/chunk-QIVAK6BH.js +0 -103
  72. package/dist/chunk-V34XPVGK.js +0 -103
  73. package/dist/chunk-VK7ZPMO7.js +0 -221
  74. package/dist/chunk-X6CNUW6T.js +0 -136
  75. package/dist/chunk-YFGSHW5S.js +0 -121
  76. package/dist/chunk-ZAJVSE7J.js +0 -90
  77. package/docs/ADAPTERS.md +0 -946
  78. package/docs/PATTERNS.md +0 -836
  79. package/src/adapters/tier-1/angular.ts +0 -223
  80. package/src/adapters/tier-1/index.ts +0 -12
  81. package/src/adapters/tier-1/qwik.ts +0 -177
  82. package/src/adapters/tier-1/react.ts +0 -330
  83. package/src/adapters/tier-1/solid.ts +0 -222
  84. package/src/adapters/tier-1/svelte.ts +0 -211
  85. package/src/adapters/tier-1/vue.ts +0 -234
  86. package/src/adapters/tier-2/index.ts +0 -12
  87. package/src/adapters/tier-2/inferno.ts +0 -149
  88. package/src/adapters/tier-2/lit.ts +0 -191
  89. package/src/adapters/tier-2/marko.ts +0 -199
  90. package/src/adapters/tier-2/mithril.ts +0 -152
  91. package/src/adapters/tier-2/preact.ts +0 -133
  92. package/src/adapters/tier-2/stencil.ts +0 -214
  93. package/src/adapters/tier-3/alpine.ts +0 -218
  94. package/src/adapters/tier-3/hotwire.ts +0 -254
  95. package/src/adapters/tier-3/htmx.ts +0 -263
  96. package/src/adapters/tier-3/index.ts +0 -12
  97. package/src/adapters/tier-3/petite-vue.ts +0 -163
  98. package/src/adapters/tier-3/stimulus.ts +0 -233
  99. package/src/adapters/tier-3/vanilla.ts +0 -252
  100. package/src/ambient.d.ts +0 -310
  101. package/src/core/adapter.ts +0 -366
  102. package/src/core/index.ts +0 -56
  103. package/src/core/registry.ts +0 -518
  104. package/src/core/types.ts +0 -461
  105. package/src/htmx.ts +0 -134
  106. package/src/index.ts +0 -263
  107. package/test/__mocks__/stencil-core.ts +0 -19
  108. package/test/__mocks__/stencil-hydrate.ts +0 -15
  109. package/test/adapters/tier-1.test.ts +0 -206
  110. package/test/adapters/tier-2.test.ts +0 -175
  111. package/test/adapters/tier-3.test.ts +0 -284
  112. package/test/contracts/adapter.contract.ts +0 -293
  113. package/test/core/core.test.ts +0 -310
  114. package/test/errors/error-handling.test.ts +0 -454
  115. package/test/integration/htmx.integration.test.ts +0 -246
  116. package/test/integration/react.integration.test.ts +0 -271
  117. package/test/integration/registry.integration.test.ts +0 -308
  118. package/tsconfig.json +0 -22
  119. package/tsup.config.ts +0 -93
  120. package/vitest.config.ts +0 -101
@@ -1,284 +0,0 @@
1
- /**
2
- * @flightdev/ui - Tier 3 Adapter Tests
3
- *
4
- * Tests for HTML-first adapters: HTMX, Alpine.js, Hotwire, Stimulus, Petite-vue, Vanilla.
5
- * These adapters work differently - they render HTML templates, not components.
6
- */
7
-
8
- import { describe, it, expect } from 'vitest';
9
- import { runAdapterContractTests } from '../contracts/adapter.contract.js';
10
-
11
- // ============================================================================
12
- // Tier 3 Adapter Imports
13
- // ============================================================================
14
-
15
- import { htmx, HTMXAdapter, hxGet, hxPost, hx } from '../../src/adapters/tier-3/htmx.js';
16
- import { alpine, AlpineAdapter, xData, xFor, xIf } from '../../src/adapters/tier-3/alpine.js';
17
- import { hotwire, HotwireAdapter, turboFrame, turboStream } from '../../src/adapters/tier-3/hotwire.js';
18
- import { stimulus, StimulusAdapter } from '../../src/adapters/tier-3/stimulus.js';
19
- import { petiteVue, PetiteVueAdapter, vScope } from '../../src/adapters/tier-3/petite-vue.js';
20
- import { vanilla, VanillaAdapter } from '../../src/adapters/tier-3/vanilla.js';
21
-
22
- // ============================================================================
23
- // Contract Tests
24
- // ============================================================================
25
-
26
- describe('Tier 3 Adapters', () => {
27
- // Tier 3 adapters can render HTML templates directly
28
- runAdapterContractTests(() => htmx(), 'HTMX', { useHtmlComponent: true });
29
- runAdapterContractTests(() => alpine(), 'Alpine', { useHtmlComponent: true });
30
- runAdapterContractTests(() => hotwire(), 'Hotwire', { useHtmlComponent: true });
31
- runAdapterContractTests(() => stimulus(), 'Stimulus', { useHtmlComponent: true });
32
- runAdapterContractTests(() => petiteVue(), 'Petite-vue', { useHtmlComponent: true });
33
- runAdapterContractTests(() => vanilla(), 'Vanilla', { useHtmlComponent: true });
34
- });
35
-
36
- // ============================================================================
37
- // HTMX Specific Tests
38
- // ============================================================================
39
-
40
- describe('HTMX Adapter - Specific', () => {
41
- it('creates adapter with default options', () => {
42
- const adapter = htmx();
43
- expect(adapter).toBeInstanceOf(HTMXAdapter);
44
- expect(adapter.id).toBe('htmx');
45
- expect(adapter.tier).toBe('tier-3');
46
- });
47
-
48
- it('does not support CSR (server-driven)', () => {
49
- const adapter = htmx();
50
- expect(adapter.capabilities.csr).toBe(false);
51
- });
52
-
53
- it('accepts extensions option', () => {
54
- const adapter = htmx({ extensions: ['json-enc', 'loading-states'] });
55
- expect(adapter).toBeDefined();
56
- });
57
-
58
- it('accepts version option', () => {
59
- const adapter = htmx({ version: '2.0.0' });
60
- expect(adapter).toBeDefined();
61
- });
62
- });
63
-
64
- describe('HTMX Template Helpers', () => {
65
- it('hxGet generates correct attributes', () => {
66
- const attrs = hxGet('/api/data');
67
- expect(attrs['hx-get']).toBe('/api/data');
68
- });
69
-
70
- it('hxGet accepts options', () => {
71
- const attrs = hxGet('/api/data', {
72
- target: '#results',
73
- swap: 'innerHTML',
74
- trigger: 'click',
75
- });
76
- expect(attrs['hx-get']).toBe('/api/data');
77
- expect(attrs['hx-target']).toBe('#results');
78
- expect(attrs['hx-swap']).toBe('innerHTML');
79
- expect(attrs['hx-trigger']).toBe('click');
80
- });
81
-
82
- it('hxPost generates correct attributes', () => {
83
- const attrs = hxPost('/api/submit');
84
- expect(attrs['hx-post']).toBe('/api/submit');
85
- });
86
-
87
- it('hx creates element string', () => {
88
- const element = hx('button', { 'hx-get': '/api' }, 'Click me');
89
- expect(element).toContain('<button');
90
- expect(element).toContain('hx-get="/api"');
91
- expect(element).toContain('Click me');
92
- expect(element).toContain('</button>');
93
- });
94
- });
95
-
96
- // ============================================================================
97
- // Alpine.js Specific Tests
98
- // ============================================================================
99
-
100
- describe('Alpine Adapter - Specific', () => {
101
- it('creates adapter with default options', () => {
102
- const adapter = alpine();
103
- expect(adapter).toBeInstanceOf(AlpineAdapter);
104
- expect(adapter.id).toBe('alpine');
105
- expect(adapter.tier).toBe('tier-3');
106
- });
107
-
108
- it('accepts plugins option', () => {
109
- const adapter = alpine({ plugins: ['intersect', 'persist', 'collapse'] });
110
- expect(adapter).toBeDefined();
111
- });
112
-
113
- it('accepts version option', () => {
114
- const adapter = alpine({ version: '3.14.0' });
115
- expect(adapter).toBeDefined();
116
- });
117
-
118
- it('supports CSR (Alpine hydrates on client)', () => {
119
- const adapter = alpine();
120
- expect(adapter.capabilities.csr).toBe(true);
121
- });
122
- });
123
-
124
- describe('Alpine Template Helpers', () => {
125
- it('xData generates div with x-data attribute', () => {
126
- const html = xData({ count: 0 }, '<span x-text="count"></span>');
127
- expect(html).toContain('x-data=');
128
- expect(html).toContain('count');
129
- expect(html).toContain('<span x-text="count"></span>');
130
- });
131
-
132
- it('xFor generates template with x-for', () => {
133
- const html = xFor('item in items', '<li x-text="item"></li>');
134
- expect(html).toContain('<template x-for="item in items">');
135
- expect(html).toContain('</template>');
136
- });
137
-
138
- it('xIf generates template with x-if', () => {
139
- const html = xIf('show', '<span>Visible</span>');
140
- expect(html).toContain('<template x-if="show">');
141
- expect(html).toContain('</template>');
142
- });
143
- });
144
-
145
- // ============================================================================
146
- // Hotwire Specific Tests
147
- // ============================================================================
148
-
149
- describe('Hotwire Adapter - Specific', () => {
150
- it('creates adapter with default options', () => {
151
- const adapter = hotwire();
152
- expect(adapter).toBeInstanceOf(HotwireAdapter);
153
- expect(adapter.id).toBe('hotwire');
154
- expect(adapter.tier).toBe('tier-3');
155
- });
156
-
157
- it('accepts turboVersion option', () => {
158
- const adapter = hotwire({ turboVersion: '8.0.0' });
159
- expect(adapter).toBeDefined();
160
- });
161
-
162
- it('accepts stimulusVersion option', () => {
163
- const adapter = hotwire({ stimulusVersion: '3.2.0' });
164
- expect(adapter).toBeDefined();
165
- });
166
- });
167
-
168
- describe('Hotwire Template Helpers', () => {
169
- it('turboFrame generates turbo-frame element', () => {
170
- const html = turboFrame('user-list', '<ul><li>User 1</li></ul>');
171
- expect(html).toContain('<turbo-frame id="user-list"');
172
- expect(html).toContain('</turbo-frame>');
173
- expect(html).toContain('<ul><li>User 1</li></ul>');
174
- });
175
-
176
- it('turboFrame accepts options', () => {
177
- const html = turboFrame('comments', '<div>Comments</div>', {
178
- src: '/comments',
179
- loading: 'lazy',
180
- });
181
- expect(html).toContain('src="/comments"');
182
- expect(html).toContain('loading="lazy"');
183
- });
184
-
185
- it('turboStream generates turbo-stream element', () => {
186
- // turboStream takes: action, target, content
187
- const html = turboStream('append', 'messages', '<div>New message</div>');
188
- expect(html).toContain('<turbo-stream');
189
- expect(html).toContain('action="append"');
190
- expect(html).toContain('target="messages"');
191
- });
192
- });
193
-
194
- // ============================================================================
195
- // Stimulus Specific Tests
196
- // ============================================================================
197
-
198
- describe('Stimulus Adapter - Specific', () => {
199
- it('creates adapter with default options', () => {
200
- const adapter = stimulus();
201
- expect(adapter).toBeInstanceOf(StimulusAdapter);
202
- expect(adapter.id).toBe('stimulus');
203
- expect(adapter.tier).toBe('tier-3');
204
- });
205
-
206
- it('supports CSR (Stimulus controllers run on client)', () => {
207
- const adapter = stimulus();
208
- expect(adapter.capabilities.csr).toBe(true);
209
- });
210
-
211
- it('accepts version option', () => {
212
- const adapter = stimulus({ version: '3.2.0' });
213
- expect(adapter).toBeDefined();
214
- });
215
- });
216
-
217
- // ============================================================================
218
- // Petite-vue Specific Tests
219
- // ============================================================================
220
-
221
- describe('Petite-vue Adapter - Specific', () => {
222
- it('creates adapter with default options', () => {
223
- const adapter = petiteVue();
224
- expect(adapter).toBeInstanceOf(PetiteVueAdapter);
225
- expect(adapter.id).toBe('petite-vue');
226
- expect(adapter.tier).toBe('tier-3');
227
- });
228
-
229
- it('supports CSR (client-side hydration)', () => {
230
- const adapter = petiteVue();
231
- expect(adapter.capabilities.csr).toBe(true);
232
- });
233
-
234
- it('accepts version option', () => {
235
- const adapter = petiteVue({ version: '0.4.0' });
236
- expect(adapter).toBeDefined();
237
- });
238
- });
239
-
240
- describe('Petite-vue Template Helpers', () => {
241
- it('vScope generates div with v-scope', () => {
242
- const html = vScope({ count: 0 }, '<button @click="count++">{{ count }}</button>');
243
- expect(html).toContain('v-scope=');
244
- expect(html).toContain('count');
245
- });
246
- });
247
-
248
- // ============================================================================
249
- // Vanilla Web Components Specific Tests
250
- // ============================================================================
251
-
252
- describe('Vanilla Adapter - Specific', () => {
253
- it('creates adapter with default options', () => {
254
- const adapter = vanilla();
255
- expect(adapter).toBeInstanceOf(VanillaAdapter);
256
- expect(adapter.id).toBe('vanilla');
257
- expect(adapter.tier).toBe('tier-3');
258
- });
259
-
260
- it('supports islands (Web Components are natural islands)', () => {
261
- const adapter = vanilla();
262
- expect(adapter.capabilities.islands).toBe(true);
263
- });
264
-
265
- it('accepts declarativeShadowDom option', () => {
266
- const adapter = vanilla({ declarativeShadowDom: true });
267
- expect(adapter).toBeDefined();
268
- });
269
-
270
- it('supports createIsland method', () => {
271
- const adapter = vanilla();
272
- expect(typeof adapter.createIsland).toBe('function');
273
- });
274
-
275
- it('createIsland returns valid island', () => {
276
- const adapter = vanilla();
277
- const island = adapter.createIsland('my-counter', { start: 5 });
278
-
279
- expect(island.id).toBeDefined();
280
- expect(island.component).toBe('my-counter');
281
- expect(island.props).toEqual({ start: 5 });
282
- expect(island.placeholder).toContain('<my-counter');
283
- });
284
- });
@@ -1,293 +0,0 @@
1
- /**
2
- * @flightdev/ui - Contract Tests
3
- *
4
- * These tests verify that all UI adapters conform to the UIAdapterV2 contract.
5
- * Contract testing ensures consistent behavior across all 18 adapters.
6
- *
7
- * @module @flightdev/ui/tests
8
- */
9
-
10
- import { describe, it, expect, beforeAll } from 'vitest';
11
- import type { UIAdapterV2, AdapterCapabilities, RenderResult } from '../../src/core/types.js';
12
-
13
- // ============================================================================
14
- // Test Utilities
15
- // ============================================================================
16
-
17
- /**
18
- * Mock component for testing adapters.
19
- * Each adapter handles this differently based on its framework.
20
- */
21
- const createMockComponent = () => ({
22
- component: () => '<div>Test Component</div>',
23
- props: { message: 'Hello, Flight!' },
24
- id: 'test-component-1',
25
- });
26
-
27
- /**
28
- * Mock HTML template function for HTML-first adapters.
29
- */
30
- const createHtmlComponent = () => ({
31
- component: (props: { message: string }) => `<div>${props.message}</div>`,
32
- props: { message: 'Hello, Flight!' },
33
- id: 'test-html-1',
34
- });
35
-
36
- // ============================================================================
37
- // Contract Test Suite
38
- // ============================================================================
39
-
40
- /**
41
- * Runs the standard contract tests for any UIAdapterV2 implementation.
42
- * This ensures all adapters behave consistently.
43
- */
44
- export function runAdapterContractTests(
45
- adapterFactory: () => UIAdapterV2,
46
- adapterName: string,
47
- options: {
48
- /** Use HTML component instead of framework component */
49
- useHtmlComponent?: boolean;
50
- /** Skip renderToString test (for adapters that need special setup) */
51
- skipRenderTest?: boolean;
52
- } = {}
53
- ): void {
54
- describe(`${adapterName} Adapter - Contract Tests`, () => {
55
- let adapter: UIAdapterV2;
56
-
57
- beforeAll(() => {
58
- adapter = adapterFactory();
59
- });
60
-
61
- // ====================================================================
62
- // Identity Tests
63
- // ====================================================================
64
-
65
- describe('Identity', () => {
66
- it('has a valid id (lowercase, no spaces)', () => {
67
- expect(adapter.id).toBeDefined();
68
- expect(typeof adapter.id).toBe('string');
69
- expect(adapter.id).toMatch(/^[a-z][a-z0-9-]*$/);
70
- });
71
-
72
- it('has a human-readable name', () => {
73
- expect(adapter.name).toBeDefined();
74
- expect(typeof adapter.name).toBe('string');
75
- expect(adapter.name.length).toBeGreaterThan(0);
76
- });
77
-
78
- it('has a framework identifier', () => {
79
- expect(adapter.framework).toBeDefined();
80
- expect(typeof adapter.framework).toBe('string');
81
- });
82
-
83
- it('has a valid tier', () => {
84
- expect(adapter.tier).toBeDefined();
85
- expect(['tier-1', 'tier-2', 'tier-3']).toContain(adapter.tier);
86
- });
87
- });
88
-
89
- // ====================================================================
90
- // Capabilities Tests
91
- // ====================================================================
92
-
93
- describe('Capabilities', () => {
94
- it('has a capabilities object', () => {
95
- expect(adapter.capabilities).toBeDefined();
96
- expect(typeof adapter.capabilities).toBe('object');
97
- });
98
-
99
- it('has all required capability flags as booleans', () => {
100
- const requiredCapabilities: (keyof AdapterCapabilities)[] = [
101
- 'streaming',
102
- 'partialHydration',
103
- 'islands',
104
- 'resumable',
105
- 'ssg',
106
- 'csr',
107
- 'serverComponents',
108
- ];
109
-
110
- for (const cap of requiredCapabilities) {
111
- expect(
112
- typeof adapter.capabilities[cap],
113
- `capability '${String(cap)}' should be a boolean`
114
- ).toBe('boolean');
115
- }
116
- });
117
-
118
- it('tier-1 adapters should support SSG', () => {
119
- if (adapter.tier === 'tier-1') {
120
- expect(adapter.capabilities.ssg).toBe(true);
121
- }
122
- });
123
-
124
- it('tier-3 adapters typically do not support CSR', () => {
125
- // This is a soft check - some tier-3 adapters may support CSR
126
- if (adapter.tier === 'tier-3' && adapter.id !== 'alpine' && adapter.id !== 'petite-vue') {
127
- // HTMX, Hotwire are server-driven
128
- }
129
- });
130
- });
131
-
132
- // ====================================================================
133
- // Render Contract Tests
134
- // ====================================================================
135
-
136
- describe('Render Contract', () => {
137
- it('renderToString is a function', () => {
138
- expect(typeof adapter.renderToString).toBe('function');
139
- });
140
-
141
- if (!options.skipRenderTest) {
142
- it('renderToString returns a Promise', async () => {
143
- const component = options.useHtmlComponent
144
- ? createHtmlComponent()
145
- : createMockComponent();
146
-
147
- const resultPromise = adapter.renderToString(component);
148
- expect(resultPromise).toBeInstanceOf(Promise);
149
- });
150
-
151
- it('renderToString result has html property', async () => {
152
- const component = options.useHtmlComponent
153
- ? createHtmlComponent()
154
- : createMockComponent();
155
-
156
- const result = await adapter.renderToString(component);
157
-
158
- expect(result).toBeDefined();
159
- expect(typeof result.html).toBe('string');
160
- });
161
-
162
- it('renderToString result may have timing info', async () => {
163
- const component = options.useHtmlComponent
164
- ? createHtmlComponent()
165
- : createMockComponent();
166
-
167
- const result = await adapter.renderToString(component);
168
-
169
- if (result.timing) {
170
- expect(typeof result.timing.total).toBe('number');
171
- expect(result.timing.total).toBeGreaterThanOrEqual(0);
172
- }
173
- });
174
- }
175
- });
176
-
177
- // ====================================================================
178
- // Streaming Contract Tests
179
- // ====================================================================
180
-
181
- describe('Streaming Contract', () => {
182
- it('renderToStream is defined if streaming capability is true (advisory)', () => {
183
- // Note: This is an advisory check. Some adapters may declare
184
- // streaming capability but delegate to renderToString internally.
185
- if (adapter.capabilities.streaming && adapter.renderToStream) {
186
- expect(typeof adapter.renderToStream).toBe('function');
187
- }
188
- });
189
-
190
- // Only run actual streaming test if not skipping render tests
191
- // (some adapters need real framework components to stream)
192
- if (!options.skipRenderTest) {
193
- it('renderToStream returns stream, done, and abort when available', () => {
194
- if (adapter.renderToStream) {
195
- const component = options.useHtmlComponent
196
- ? createHtmlComponent()
197
- : createMockComponent();
198
-
199
- const result = adapter.renderToStream(component);
200
-
201
- expect(result.stream).toBeInstanceOf(ReadableStream);
202
- expect(result.done).toBeInstanceOf(Promise);
203
- expect(typeof result.abort).toBe('function');
204
- }
205
- });
206
- }
207
- });
208
-
209
- // ====================================================================
210
- // Hydration Contract Tests
211
- // ====================================================================
212
-
213
- describe('Hydration Contract', () => {
214
- it('getHydrationScript is a function', () => {
215
- expect(typeof adapter.getHydrationScript).toBe('function');
216
- });
217
-
218
- it('getHydrationScript returns a string', () => {
219
- const mockResult: RenderResult = {
220
- html: '<div>Test</div>',
221
- hydrationData: { props: {} },
222
- };
223
-
224
- const script = adapter.getHydrationScript(mockResult);
225
-
226
- expect(typeof script).toBe('string');
227
- });
228
-
229
- it('getHydrationScript contains script tag or is valid JS', () => {
230
- const mockResult: RenderResult = {
231
- html: '<div>Test</div>',
232
- hydrationData: { props: {} },
233
- };
234
-
235
- const script = adapter.getHydrationScript(mockResult);
236
-
237
- // Should be either a script tag or module code
238
- const hasScriptTag = script.includes('<script');
239
- const hasModuleCode = script.includes('export') || script.includes('window.');
240
- const isEmpty = script.trim().length === 0;
241
-
242
- expect(hasScriptTag || hasModuleCode || isEmpty).toBe(true);
243
- });
244
-
245
- it('getClientEntry is a function', () => {
246
- expect(typeof adapter.getClientEntry).toBe('function');
247
- });
248
-
249
- it('getClientEntry returns client-side code', () => {
250
- const entry = adapter.getClientEntry();
251
-
252
- expect(typeof entry).toBe('string');
253
- // Should export a hydrate function or be client code
254
- const isClientCode =
255
- entry.includes('hydrate') ||
256
- entry.includes('mount') ||
257
- entry.includes('render') ||
258
- entry.includes('console.log');
259
-
260
- expect(isClientCode).toBe(true);
261
- });
262
- });
263
-
264
- // ====================================================================
265
- // Islands Contract Tests
266
- // ====================================================================
267
-
268
- describe('Islands Contract', () => {
269
- it('createIsland is available (all adapters have default implementation)', () => {
270
- // BaseUIAdapter provides a default createIsland implementation
271
- expect(typeof adapter.createIsland).toBe('function');
272
- });
273
-
274
- it('createIsland returns valid island object', () => {
275
- if (adapter.createIsland) {
276
- const island = adapter.createIsland('my-component', { prop: 'value' });
277
-
278
- expect(island.id).toBeDefined();
279
- expect(typeof island.id).toBe('string');
280
- expect(island.component).toBe('my-component');
281
- expect(island.props).toEqual({ prop: 'value' });
282
- expect(typeof island.placeholder).toBe('string');
283
- }
284
- });
285
- });
286
- });
287
- }
288
-
289
- // ============================================================================
290
- // Export for direct usage
291
- // ============================================================================
292
-
293
- export { createMockComponent, createHtmlComponent };