assign-gingerly 0.0.53 → 0.0.55

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 (114) hide show
  1. package/README.md +847 -5
  2. package/assignFrom.js +229 -9
  3. package/assignFrom.ts +338 -9
  4. package/assignGingerly.js +34 -1
  5. package/assignGingerly.ts +54 -1
  6. package/beVigilant.js +73 -0
  7. package/beVigilant.ts +85 -0
  8. package/enhanceAll.js +106 -0
  9. package/enhanceAll.ts +138 -0
  10. package/handlers/join.js +74 -0
  11. package/handlers/join.ts +80 -0
  12. package/handlers/lazyLoad.js +212 -0
  13. package/handlers/lazyLoad.ts +307 -0
  14. package/handlers/lazyLoadSwitch.js +58 -0
  15. package/handlers/lazyLoadSwitch.ts +63 -0
  16. package/handlers/microDataJoin.js +184 -0
  17. package/handlers/microDataJoin.ts +270 -0
  18. package/inferencer/.gitmodules +3 -0
  19. package/inferencer/.vscode/settings.json +2 -0
  20. package/inferencer/InferencedPropagator.js +230 -0
  21. package/inferencer/InferencedPropagator.ts +269 -0
  22. package/inferencer/LICENSE +21 -0
  23. package/inferencer/README.md +524 -0
  24. package/inferencer/Requirements/SupportForPropagator.md +368 -0
  25. package/inferencer/imports.html +7 -0
  26. package/inferencer/inferencer.js +254 -0
  27. package/inferencer/inferencer.ts +292 -0
  28. package/inferencer/package-lock.json +129 -0
  29. package/inferencer/package.json +60 -0
  30. package/inferencer/playwright-report/data/507ad515125e13390ea07de92f22331c913fa068.md +55 -0
  31. package/inferencer/playwright-report/index.html +90 -0
  32. package/inferencer/playwright.config.ts +54 -0
  33. package/inferencer/test-results/.last-run.json +6 -0
  34. package/inferencer/test-results/inferencer-Inferencer-Enha-535bc-inferencer-tests-in-browser-chromium/error-context.md +55 -0
  35. package/inferencer/tests/inferencedPropagator.html +428 -0
  36. package/inferencer/tests/inferencedPropagator.spec.ts +18 -0
  37. package/inferencer/tests/inferencer.html +355 -0
  38. package/inferencer/tests/inferencer.spec.ts +19 -0
  39. package/inferencer/tsconfig.json +19 -0
  40. package/inferencer/types/.kiro/specs/conversion-template/README.md +128 -0
  41. package/inferencer/types/.kiro/specs/conversion-template/design.md +360 -0
  42. package/inferencer/types/.kiro/specs/conversion-template/requirements.md +191 -0
  43. package/inferencer/types/.kiro/specs/conversion-template/tasks.md +174 -0
  44. package/inferencer/types/.kiro/steering/coding-standards.md +53 -0
  45. package/inferencer/types/.kiro/steering/conversion-guide.md +108 -0
  46. package/inferencer/types/.kiro/steering/declarative-configuration.md +108 -0
  47. package/inferencer/types/.kiro/steering/emc-json-serializability.md +306 -0
  48. package/inferencer/types/EnhancementConversionInstructions.md +1854 -0
  49. package/inferencer/types/LICENSE +21 -0
  50. package/inferencer/types/NewCustomElement.md +388 -0
  51. package/inferencer/types/NewCustomElementFeature.md +683 -0
  52. package/inferencer/types/NewEnhancementInstructions.md +705 -0
  53. package/inferencer/types/README.md +2 -0
  54. package/inferencer/types/agrace/types.d.ts +11 -0
  55. package/inferencer/types/assign-gingerly/types.d.ts +572 -0
  56. package/inferencer/types/be-a-beacon/types.d.ts +17 -0
  57. package/inferencer/types/be-bound/types.d.ts +66 -0
  58. package/inferencer/types/be-buttoned-up/types.d.ts +19 -0
  59. package/inferencer/types/be-calculating/types.d.ts +54 -0
  60. package/inferencer/types/be-clonable/types.d.ts +38 -0
  61. package/inferencer/types/be-committed/types.d.ts +22 -0
  62. package/inferencer/types/be-consoling/types.d.ts +24 -0
  63. package/inferencer/types/be-decked-with/types.d.ts +26 -0
  64. package/inferencer/types/be-delible/types.d.ts +27 -0
  65. package/inferencer/types/be-dispatching/types.d.ts +34 -0
  66. package/inferencer/types/be-evanescent/types.d.ts +20 -0
  67. package/inferencer/types/be-flashy/types.d.ts +21 -0
  68. package/inferencer/types/be-gone/types.d.ts +25 -0
  69. package/inferencer/types/be-observing/types.d.ts +55 -0
  70. package/inferencer/types/be-reflective/types.d.ts +78 -0
  71. package/inferencer/types/be-reformable/types.d.ts +49 -0
  72. package/inferencer/types/be-render-neutral/types.d.ts +32 -0
  73. package/inferencer/types/be-switched/types.d.ts +146 -0
  74. package/inferencer/types/be-typed/types.d.ts +32 -0
  75. package/inferencer/types/be-valued/types.d.ts +22 -0
  76. package/inferencer/types/data-props/types.d.ts +34 -0
  77. package/inferencer/types/do-inc/types.d.ts +56 -0
  78. package/inferencer/types/do-invoke/types.d.ts +38 -0
  79. package/inferencer/types/do-merge/types.d.ts +28 -0
  80. package/inferencer/types/do-toggle/types.d.ts +31 -0
  81. package/inferencer/types/face-up/types.d.ts +100 -0
  82. package/inferencer/types/fetch-for/types.d.ts +36 -0
  83. package/inferencer/types/folder-picker/types.d.ts +21 -0
  84. package/inferencer/types/global.d.ts +29 -0
  85. package/inferencer/types/id-generation/types.d.ts +26 -0
  86. package/inferencer/types/inferencer/types.d.ts +46 -0
  87. package/inferencer/types/mount-observer/types.d.ts +363 -0
  88. package/inferencer/types/nested-regex-groups/types.d.ts +107 -0
  89. package/inferencer/types/pipe-in/types.d.ts +52 -0
  90. package/inferencer/types/roundabout/types.d.ts +268 -0
  91. package/inferencer/types/soak-up/types.d.ts +40 -0
  92. package/inferencer/types/templ-maker/types.d.ts +43 -0
  93. package/inferencer/types/time-ticker/types.d.ts +62 -0
  94. package/inferencer/types/truth-sourcer/types.d.ts +44 -0
  95. package/inferencer/upSearch.js +27 -0
  96. package/inferencer/upSearch.ts +26 -0
  97. package/inferencer/withScopePerimeter.js +27 -0
  98. package/inferencer/withScopePerimeter.ts +33 -0
  99. package/inferredAssignments.js +38 -0
  100. package/inferredAssignments.ts +65 -0
  101. package/isAllowedImportPath.js +42 -0
  102. package/isAllowedImportPath.ts +53 -0
  103. package/package.json +57 -3
  104. package/paths.js +231 -0
  105. package/paths.ts +413 -0
  106. package/processHandlerCommands.js +188 -0
  107. package/processHandlerCommands.ts +217 -0
  108. package/resolveIdRef.js +144 -0
  109. package/resolveIdRef.ts +170 -0
  110. package/resolveValues.js +41 -2
  111. package/resolveValues.ts +41 -1
  112. package/transitionHelper.js +109 -0
  113. package/transitionHelper.ts +132 -0
  114. package/types/assign-gingerly/types.d.ts +89 -0
@@ -0,0 +1,54 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ /**
4
+ * Read environment variables from file.
5
+ * https://github.com/motdotla/dotenv
6
+ */
7
+ // require('dotenv').config();
8
+
9
+ /**
10
+ * See https://playwright.dev/docs/test-configuration.
11
+ */
12
+ export default defineConfig({
13
+ testDir: './tests',
14
+ /* Run tests in files in parallel */
15
+ fullyParallel: true,
16
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
17
+ forbidOnly: !!process.env.CI,
18
+ /* Retry on CI only */
19
+ retries: process.env.CI ? 2 : 0,
20
+ /* Opt out of parallel tests on CI. */
21
+ workers: process.env.CI ? 1 : undefined,
22
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
23
+ reporter: [ ['html', { open: 'never' }] ],
24
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
25
+ use: {
26
+ /* Base URL to use in actions like `await page.goto('/')`. */
27
+ baseURL: 'http://localhost:8000',
28
+ },
29
+
30
+ /* Configure projects for major browsers */
31
+ projects: [
32
+ {
33
+ name: 'chromium',
34
+ use: { ...devices['Desktop Chrome'] },
35
+ },
36
+
37
+ {
38
+ name: 'firefox',
39
+ use: { ...devices['Desktop Firefox'] },
40
+ },
41
+
42
+ {
43
+ name: 'webkit',
44
+ use: { ...devices['Desktop Safari'] },
45
+ },
46
+ ],
47
+
48
+ /* Run your local dev server before starting the tests */
49
+ webServer: {
50
+ command: 'npm run serve',
51
+ port: 8000,
52
+ reuseExistingServer: !process.env.CI,
53
+ },
54
+ });
@@ -0,0 +1,6 @@
1
+ {
2
+ "status": "failed",
3
+ "failedTests": [
4
+ "9918838107f578fb07d7-213aa187388a2c928788"
5
+ ]
6
+ }
@@ -0,0 +1,55 @@
1
+ # Instructions
2
+
3
+ - Following Playwright test failed.
4
+ - Explain why, be concise, respect Playwright best practices.
5
+ - Provide a snippet of code with the fix, if possible.
6
+
7
+ # Test info
8
+
9
+ - Name: inferencer.spec.ts >> Inferencer Enhancement › should run all inferencer tests in browser
10
+ - Location: tests\inferencer.spec.ts:3:1
11
+
12
+ # Error details
13
+
14
+ ```
15
+ Error: expect(received).toBe(expected) // Object.is equality
16
+
17
+ Expected: 28
18
+ Received: 25
19
+ ```
20
+
21
+ # Page snapshot
22
+
23
+ ```yaml
24
+ - generic [active] [ref=e1]:
25
+ - heading "Inferencer Enhancement Tests" [level=1] [ref=e2]
26
+ - generic [ref=e3]:
27
+ - 'heading "Results: 25/28 tests passed" [level=2] [ref=e4]'
28
+ - generic [ref=e5]: "✓ should set value on text input ✓ should set checked on checkbox ✓ should set checked on radio button ✓ should set value on textarea ✓ should set value on select ✓ should set textContent on div ✓ should set dateTime on time element ✓ should set value on data element ✓ should set value on progress element ✓ should set value on meter element ✓ should set value on output element ✓ should use itemprop as property name ✓ should set display (textContent) on div ✓ should set display (textContent) on time element ✓ should set display (ariaValueText) on meter element ✗ should be accessible via enh.infer: Enhancement not accessible via enh.infer ✓ should handle multiple value assignments ✓ should handle both value and display assignments ✓ should infer \"input\" event for input element ✓ should infer \"input\" event for textarea element ✓ should infer \"input\" event for select element ✓ should infer \"submit\" event for form element ✓ should infer \"toggle\" event for details element ✓ should infer \"close\" event for dialog element ✓ should infer \"click\" event for button element ✓ should infer \"click\" event for div element ✗ should access eventType via enh.infer.eventType: Cannot read properties of undefined (reading 'eventType') ✗ should return correct eventType for different elements via getter: Cannot read properties of undefined (reading 'eventType')"
29
+ ```
30
+
31
+ # Test source
32
+
33
+ ```ts
34
+ 1 | import { test, expect } from '@playwright/test';
35
+ 2 |
36
+ 3 | test('Inferencer Enhancement › should run all inferencer tests in browser', async ({ page }) => {
37
+ 4 | // Start local server and navigate
38
+ 5 | await page.goto('http://localhost:8000/tests/inferencer.html');
39
+ 6 |
40
+ 7 | // Wait for tests to complete
41
+ 8 | await page.waitForFunction(() => window.testResults !== undefined, { timeout: 5000 });
42
+ 9 |
43
+ 10 | // Get test results
44
+ 11 | const results = await page.evaluate(() => window.testResults);
45
+ 12 |
46
+ 13 | // Log results
47
+ 14 | console.log(`Inferencer tests: ${results.passed}/${results.total} passed`);
48
+ 15 | results.results.forEach((result: string) => console.log(result));
49
+ 16 |
50
+ 17 | // Assert all tests passed
51
+ > 18 | expect(results.passed).toBe(results.total);
52
+ | ^ Error: expect(received).toBe(expected) // Object.is equality
53
+ 19 | });
54
+ 20 |
55
+ ```
@@ -0,0 +1,428 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>InferencedPropagator Tests</title>
7
+ <!-- #include virtual="/imports.html" -->
8
+ </head>
9
+ <body>
10
+ <h1>InferencedPropagator Tests</h1>
11
+ <div id="results"></div>
12
+ <div id="test-container"></div>
13
+
14
+ <script type="module">
15
+ import { Infer, inferEventType } from '../inferencer.js';
16
+ import { InferencedPropagator } from '../InferencedPropagator.js';
17
+
18
+ const results = [];
19
+ let testCount = 0;
20
+ let passCount = 0;
21
+ const container = document.getElementById('test-container');
22
+
23
+ function assert(condition, message) {
24
+ if (!condition) throw new Error(message);
25
+ }
26
+
27
+ function assertEqual(actual, expected, label) {
28
+ if (actual !== expected) {
29
+ throw new Error(`${label}: expected '${expected}', got '${actual}'`);
30
+ }
31
+ }
32
+
33
+ async function test(name, fn) {
34
+ testCount++;
35
+ try {
36
+ await fn();
37
+ passCount++;
38
+ results.push(`✓ ${name}`);
39
+ console.log(`✓ ${name}`);
40
+ } catch (error) {
41
+ results.push(`✗ ${name}: ${error.message}`);
42
+ console.error(`✗ ${name}:`, error);
43
+ }
44
+ }
45
+
46
+ // Helper: wait a tick for async wiring to complete
47
+ function tick() {
48
+ return new Promise(resolve => setTimeout(resolve, 0));
49
+ }
50
+
51
+ // Helper: wait for a rAF cycle (for polling strategy)
52
+ function raf() {
53
+ return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
54
+ }
55
+
56
+ // ─── Constructor & Basic Shape ───────────────────────────────────
57
+
58
+ await test('should extend EventTarget', () => {
59
+ const input = document.createElement('input');
60
+ const infer = new Infer(input);
61
+ const propagator = new InferencedPropagator(infer);
62
+ assert(propagator instanceof EventTarget, 'should be instance of EventTarget');
63
+ });
64
+
65
+ await test('should have destroy method', () => {
66
+ const input = document.createElement('input');
67
+ const infer = new Infer(input);
68
+ const propagator = new InferencedPropagator(infer);
69
+ assert(typeof propagator.destroy === 'function', 'destroy should be a function');
70
+ });
71
+
72
+ // ─── Strategy 1: Attribute-Reflected Properties ──────────────────
73
+
74
+ await test('should detect aria property changes via MutationObserver', async () => {
75
+ const div = document.createElement('div');
76
+ container.appendChild(div);
77
+ const infer = new Infer(div);
78
+ const propagator = new InferencedPropagator(infer);
79
+
80
+ let fired = false;
81
+ propagator.addEventListener('ariaLabel', () => { fired = true; });
82
+
83
+ await tick();
84
+
85
+ div.ariaLabel = 'hello';
86
+ // MutationObserver is async, wait a tick
87
+ await tick();
88
+ await tick();
89
+
90
+ assert(fired, 'should have dispatched event for ariaLabel change');
91
+ propagator.destroy();
92
+ container.removeChild(div);
93
+ });
94
+
95
+ await test('should detect href changes on anchor via attribute observation', async () => {
96
+ const a = document.createElement('a');
97
+ a.href = 'http://example.com';
98
+ container.appendChild(a);
99
+ const infer = new Infer(a);
100
+ const propagator = new InferencedPropagator(infer);
101
+
102
+ let fired = false;
103
+ propagator.addEventListener('href', () => { fired = true; });
104
+
105
+ await tick();
106
+
107
+ a.href = 'http://example.org';
108
+ await tick();
109
+ await tick();
110
+
111
+ assert(fired, 'should have dispatched event for href change');
112
+ propagator.destroy();
113
+ container.removeChild(a);
114
+ });
115
+
116
+ await test('should detect output value changes via setter interception', async () => {
117
+ const output = document.createElement('output');
118
+ output.value = 'initial';
119
+ container.appendChild(output);
120
+ const infer = new Infer(output);
121
+ const propagator = new InferencedPropagator(infer);
122
+
123
+ let fired = false;
124
+ propagator.addEventListener('value', () => { fired = true; });
125
+
126
+ await tick();
127
+
128
+ output.value = 'changed';
129
+ await tick();
130
+
131
+ assert(fired, 'should have dispatched event for output value change');
132
+ propagator.destroy();
133
+ container.removeChild(output);
134
+ });
135
+
136
+ // ─── Strategy 2: Hybrid (Native Event + Setter) ─────────────────
137
+
138
+ await test('should detect input value change from user event', async () => {
139
+ const input = document.createElement('input');
140
+ input.type = 'text';
141
+ container.appendChild(input);
142
+ const infer = new Infer(input);
143
+ const propagator = new InferencedPropagator(infer);
144
+
145
+ let fired = false;
146
+ propagator.addEventListener('value', () => { fired = true; });
147
+
148
+ await tick();
149
+
150
+ // Simulate user-driven input event
151
+ input.value = 'typed';
152
+ input.dispatchEvent(new Event('input', { bubbles: true }));
153
+
154
+ await tick();
155
+
156
+ assert(fired, 'should have dispatched event for user-driven input change');
157
+ propagator.destroy();
158
+ container.removeChild(input);
159
+ });
160
+
161
+ await test('should detect input value change from programmatic set', async () => {
162
+ const input = document.createElement('input');
163
+ input.type = 'text';
164
+ container.appendChild(input);
165
+ const infer = new Infer(input);
166
+ const propagator = new InferencedPropagator(infer);
167
+
168
+ let fired = false;
169
+ propagator.addEventListener('value', () => { fired = true; });
170
+
171
+ await tick();
172
+
173
+ // Programmatic change (no event dispatched by browser)
174
+ input.value = 'programmatic';
175
+
176
+ await tick();
177
+
178
+ assert(fired, 'should have dispatched event for programmatic input value change');
179
+ propagator.destroy();
180
+ container.removeChild(input);
181
+ });
182
+
183
+ await test('should detect checkbox checked change programmatically', async () => {
184
+ const checkbox = document.createElement('input');
185
+ checkbox.type = 'checkbox';
186
+ container.appendChild(checkbox);
187
+ const infer = new Infer(checkbox);
188
+ const propagator = new InferencedPropagator(infer);
189
+
190
+ let fired = false;
191
+ propagator.addEventListener('checked', () => { fired = true; });
192
+
193
+ await tick();
194
+
195
+ checkbox.checked = true;
196
+
197
+ await tick();
198
+
199
+ assert(fired, 'should have dispatched event for programmatic checked change');
200
+ propagator.destroy();
201
+ container.removeChild(checkbox);
202
+ });
203
+
204
+ // ─── Strategy 3: Setter Interception (Custom Elements) ───────────
205
+
206
+ await test('should detect property changes on custom element via setter interception', async () => {
207
+ // Define a simple custom element with a getter/setter
208
+ if (!customElements.get('test-prop-element')) {
209
+ class TestPropElement extends HTMLElement {
210
+ #myProp = '';
211
+ get myProp() { return this.#myProp; }
212
+ set myProp(v) { this.#myProp = v; }
213
+ }
214
+ customElements.define('test-prop-element', TestPropElement);
215
+ }
216
+
217
+ const el = document.createElement('test-prop-element');
218
+ container.appendChild(el);
219
+ const infer = new Infer(el);
220
+ const propagator = new InferencedPropagator(infer);
221
+
222
+ let fired = false;
223
+ propagator.addEventListener('myProp', () => { fired = true; });
224
+
225
+ await tick();
226
+
227
+ el.myProp = 'new value';
228
+
229
+ await tick();
230
+
231
+ assert(fired, 'should have dispatched event for custom element property change');
232
+ propagator.destroy();
233
+ container.removeChild(el);
234
+ });
235
+
236
+ await test('setter interception should preserve original behavior', async () => {
237
+ const el = document.createElement('test-prop-element');
238
+ container.appendChild(el);
239
+ const infer = new Infer(el);
240
+ const propagator = new InferencedPropagator(infer);
241
+
242
+ propagator.addEventListener('myProp', () => {});
243
+ await tick();
244
+
245
+ el.myProp = 'test value';
246
+ assertEqual(el.myProp, 'test value', 'getter should still return correct value');
247
+
248
+ propagator.destroy();
249
+ container.removeChild(el);
250
+ });
251
+
252
+ // ─── Strategy 4: Polling Fallback ────────────────────────────────
253
+
254
+ await test('should fall back to polling for unknown properties', async () => {
255
+ const div = document.createElement('div');
256
+ container.appendChild(div);
257
+ div.customThing = 'initial';
258
+ const infer = new Infer(div);
259
+ const propagator = new InferencedPropagator(infer);
260
+
261
+ let fired = false;
262
+ propagator.addEventListener('customThing', () => { fired = true; });
263
+
264
+ await tick();
265
+
266
+ div.customThing = 'changed';
267
+
268
+ // Polling uses rAF, wait for a couple frames
269
+ await raf();
270
+ await raf();
271
+
272
+ assert(fired, 'should have dispatched event via polling for expando property');
273
+ propagator.destroy();
274
+ container.removeChild(div);
275
+ });
276
+
277
+ // ─── Lazy Wiring (only wire on addEventListener) ─────────────────
278
+
279
+ await test('should not wire anything until addEventListener is called', async () => {
280
+ const input = document.createElement('input');
281
+ container.appendChild(input);
282
+ const infer = new Infer(input);
283
+ const propagator = new InferencedPropagator(infer);
284
+
285
+ // No addEventListener called — changing value should not cause errors
286
+ input.value = 'no listener';
287
+ await tick();
288
+
289
+ // Now add listener
290
+ let fired = false;
291
+ propagator.addEventListener('value', () => { fired = true; });
292
+ await tick();
293
+
294
+ input.value = 'with listener';
295
+ await tick();
296
+
297
+ assert(fired, 'should dispatch after listener is wired');
298
+ propagator.destroy();
299
+ container.removeChild(input);
300
+ });
301
+
302
+ await test('should only wire once per property even with multiple listeners', async () => {
303
+ const input = document.createElement('input');
304
+ container.appendChild(input);
305
+ const infer = new Infer(input);
306
+ const propagator = new InferencedPropagator(infer);
307
+
308
+ let count = 0;
309
+ propagator.addEventListener('value', () => { count++; });
310
+ propagator.addEventListener('value', () => { count++; });
311
+ await tick();
312
+
313
+ input.value = 'multi';
314
+ await tick();
315
+
316
+ // Both listeners fire, but we only get one event dispatch per change
317
+ // Each listener sees the same event
318
+ assertEqual(count, 2, 'both listeners should fire');
319
+ propagator.destroy();
320
+ container.removeChild(input);
321
+ });
322
+
323
+ // ─── Snapshot / Initial Value Detection ──────────────────────────
324
+
325
+ await test('should dispatch if value changes during async wiring', async () => {
326
+ const div = document.createElement('div');
327
+ div.ariaLabel = 'before';
328
+ container.appendChild(div);
329
+ const infer = new Infer(div);
330
+ const propagator = new InferencedPropagator(infer);
331
+
332
+ let fired = false;
333
+ // Immediately change value — this may happen during wiring
334
+ propagator.addEventListener('ariaLabel', () => { fired = true; });
335
+ div.ariaLabel = 'during';
336
+
337
+ await tick();
338
+ await tick();
339
+
340
+ assert(fired, 'should have dispatched due to value change during setup');
341
+ propagator.destroy();
342
+ container.removeChild(div);
343
+ });
344
+
345
+ // ─── Cleanup / Destroy ───────────────────────────────────────────
346
+
347
+ await test('destroy should stop dispatching events', async () => {
348
+ const div = document.createElement('div');
349
+ div.ariaLabel = 'start';
350
+ container.appendChild(div);
351
+ const infer = new Infer(div);
352
+ const propagator = new InferencedPropagator(infer);
353
+
354
+ let count = 0;
355
+ propagator.addEventListener('ariaLabel', () => { count++; });
356
+ await tick();
357
+
358
+ div.ariaLabel = 'change1';
359
+ await tick();
360
+ await tick();
361
+
362
+ const countAfterFirst = count;
363
+ propagator.destroy();
364
+
365
+ div.ariaLabel = 'change2';
366
+ await tick();
367
+ await tick();
368
+
369
+ assertEqual(count, countAfterFirst, 'should not fire after destroy');
370
+ container.removeChild(div);
371
+ });
372
+
373
+ // ─── Infer.getPropagator() Integration ───────────────────────────
374
+
375
+ await test('Infer.getPropagator() should return an InferencedPropagator for built-in elements', async () => {
376
+ const input = document.createElement('input');
377
+ container.appendChild(input);
378
+ const infer = new Infer(input);
379
+ const propagator = await infer.getPropagator();
380
+
381
+ assert(propagator instanceof InferencedPropagator, 'should be InferencedPropagator');
382
+ propagator.destroy();
383
+ container.removeChild(input);
384
+ });
385
+
386
+ await test('Infer.getPropagator() should cache the propagator', async () => {
387
+ const input = document.createElement('input');
388
+ container.appendChild(input);
389
+ const infer = new Infer(input);
390
+ const p1 = await infer.getPropagator();
391
+ const p2 = await infer.getPropagator();
392
+
393
+ assert(p1 === p2, 'should return the same instance on subsequent calls');
394
+ p1.destroy();
395
+ container.removeChild(input);
396
+ });
397
+
398
+ await test('Infer.getPropagator() should return native propagator for roundabout elements', async () => {
399
+ // Define a custom element with a propagator property
400
+ if (!customElements.get('test-roundabout-el')) {
401
+ class TestRoundaboutEl extends HTMLElement {
402
+ propagator = new EventTarget();
403
+ }
404
+ customElements.define('test-roundabout-el', TestRoundaboutEl);
405
+ }
406
+
407
+ const el = document.createElement('test-roundabout-el');
408
+ container.appendChild(el);
409
+ const infer = new Infer(el);
410
+ const propagator = await infer.getPropagator();
411
+
412
+ assert(propagator === el.propagator, 'should return native propagator from element');
413
+ assert(!(propagator instanceof InferencedPropagator), 'should NOT be InferencedPropagator');
414
+ container.removeChild(el);
415
+ });
416
+
417
+ // ─── Display results ─────────────────────────────────────────────
418
+
419
+ const resultsDiv = document.getElementById('results');
420
+ resultsDiv.innerHTML = `
421
+ <h2>Results: ${passCount}/${testCount} tests passed</h2>
422
+ <pre>${results.join('\n')}</pre>
423
+ `;
424
+
425
+ window.testResults = { passed: passCount, total: testCount, results };
426
+ </script>
427
+ </body>
428
+ </html>
@@ -0,0 +1,18 @@
1
+ import { test, expect } from '@playwright/test';
2
+
3
+ test('InferencedPropagator › should run all propagator tests in browser', async ({ page }) => {
4
+ await page.goto('http://localhost:8000/tests/inferencedPropagator.html');
5
+
6
+ // Wait for tests to complete
7
+ await page.waitForFunction(() => window.testResults !== undefined, { timeout: 10000 });
8
+
9
+ // Get test results
10
+ const results = await page.evaluate(() => window.testResults);
11
+
12
+ // Log results
13
+ console.log(`InferencedPropagator tests: ${results.passed}/${results.total} passed`);
14
+ results.results.forEach((result: string) => console.log(result));
15
+
16
+ // Assert all tests passed
17
+ expect(results.passed).toBe(results.total);
18
+ });