jsgui3-server 0.0.148 → 0.0.149

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 (134) hide show
  1. package/.github/workflows/control-scan-manifest-check.yml +31 -0
  2. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-071799b982906680f5fd699d.js +40 -0
  3. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-07352945ad5c92654fcb8b65.js +39 -0
  4. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-138a601fadb6191ea314c6fd.js +39 -0
  5. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-171f6c381c2cadf2e9fa7087.js +39 -0
  6. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-1d973388156b84a04373fac9.js +39 -0
  7. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-20e117bc8a10d2cd16234bbe.js +40 -0
  8. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-2b028a82b0e5efddba42425f.js +39 -0
  9. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-4518556cd5c7e059e82b22b8.js +40 -0
  10. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5bac1aa0f213902f718ed74f.js +40 -0
  11. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-5f9996ac7822caf777d92f56.js +39 -0
  12. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-60a92c702e65fd9cf748e3ec.js +39 -0
  13. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6164c1f8f738995c541895d2.js +44 -0
  14. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-6718a85eb9e5aa782dd47a05.js +45 -0
  15. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-69e280f14e37aee76a1d4675.js +39 -0
  16. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7570d1b030d44b111ed59c4c.js +39 -0
  17. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7798c9bbd55e510d5039f936.js +42 -0
  18. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-78cd511ea1ef18ecb03d1be5.js +40 -0
  19. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-7d482e0b95bcb5e3c543118b.js +43 -0
  20. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-80e9476d1127c55b40fdb36f.js +40 -0
  21. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-810ced55d5320a3088a05b13.js +40 -0
  22. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-8423565f1a40e329afc8c6cf.js +40 -0
  23. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-900bef783b8cee36506ec282.js +39 -0
  24. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-a1a37aff6416fdad74040ddf.js +39 -0
  25. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-ad48d5e8eda40f175b4df090.js +39 -0
  26. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-aec5a2d963015528c9099462.js +39 -0
  27. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-af9d34e0f1722fab9e28c269.js +39 -0
  28. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-b818e4015e2f1fe86280b5ab.js +41 -0
  29. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bcb2541adc70b7aba61768c5.js +44 -0
  30. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-bfe89d2c78ed44f95ed7dd73.js +40 -0
  31. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c06f04806a1e688e1187110c.js +40 -0
  32. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-c3f3adf904f585afc544b96a.js +39 -0
  33. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-d45acb873e1d8e32d5e60f2e.js +39 -0
  34. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-db06f132533706f4a0163b8c.js +39 -0
  35. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f660f40d78b135fc8560a862.js +39 -0
  36. package/.jsgui3-server-cache/jsgui3-html-shims/jsgui3-html-controls-shim-f9dee4ec18a96e09bee06bae.js +39 -0
  37. package/README.md +85 -3
  38. package/admin-ui/client.js +8 -8
  39. package/dev-status.svg +139 -0
  40. package/docs/api-reference.md +301 -43
  41. package/docs/books/jsgui3-bundling-research-book/00-table-of-contents.md +35 -0
  42. package/docs/books/jsgui3-bundling-research-book/01-pipeline-and-runtime-semantics.md +34 -0
  43. package/docs/books/jsgui3-bundling-research-book/02-javascript-bundling-core.md +36 -0
  44. package/docs/books/jsgui3-bundling-research-book/03-style-extraction-and-css-compilation.md +35 -0
  45. package/docs/books/jsgui3-bundling-research-book/04-static-publishing-and-delivery.md +39 -0
  46. package/docs/books/jsgui3-bundling-research-book/05-current-limits-and-size-bloat-vectors.md +25 -0
  47. package/docs/books/jsgui3-bundling-research-book/06-unused-module-elimination-strategy.md +77 -0
  48. package/docs/books/jsgui3-bundling-research-book/07-jsgui3-html-control-and-mixin-pruning.md +63 -0
  49. package/docs/books/jsgui3-bundling-research-book/08-test-and-verification-methodology.md +43 -0
  50. package/docs/books/jsgui3-bundling-research-book/09-roadmap-and-rollout.md +42 -0
  51. package/docs/books/jsgui3-bundling-research-book/10-further-research-strategies-and-upgrades.md +211 -0
  52. package/docs/books/jsgui3-bundling-research-book/README.md +35 -0
  53. package/docs/bundling-system-deep-dive.md +9 -4
  54. package/docs/comprehensive-documentation.md +49 -18
  55. package/docs/configuration-reference.md +152 -27
  56. package/docs/core/README.md +19 -0
  57. package/docs/core/jsgui3-server-core-book/00-table-of-contents.md +21 -0
  58. package/docs/core/jsgui3-server-core-book/01-startup-readiness-state-machine.md +41 -0
  59. package/docs/core/jsgui3-server-core-book/02-resource-abstraction-and-lifecycle.md +92 -0
  60. package/docs/core/jsgui3-server-core-book/03-resource-pool-and-event-topology.md +47 -0
  61. package/docs/core/jsgui3-server-core-book/04-sse-publisher-semantics.md +41 -0
  62. package/docs/core/jsgui3-server-core-book/05-serve-factory-resource-wiring.md +46 -0
  63. package/docs/core/jsgui3-server-core-book/06-e2e-testing-methodology.md +48 -0
  64. package/docs/core/jsgui3-server-core-book/07-defect-detection-and-hardening-loop.md +47 -0
  65. package/docs/publishers-guide.md +59 -4
  66. package/docs/resources-guide.md +184 -35
  67. package/docs/simple-server-api-design.md +72 -17
  68. package/docs/system-architecture.md +18 -14
  69. package/examples/controls/15) window, observable SSE/server.js +6 -1
  70. package/examples/controls/19) window, auto observable ui/server.js +9 -0
  71. package/examples/controls/20) window, task manager app/README.md +133 -0
  72. package/examples/controls/20) window, task manager app/client.js +797 -0
  73. package/examples/controls/20) window, task manager app/server.js +178 -0
  74. package/examples/controls/6) window, color_palette/client.js +165 -68
  75. package/examples/controls/9) window, date picker/client.js +362 -76
  76. package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +104 -83
  77. package/examples/jsgui3-html/06) theming/client.js +22 -1
  78. package/examples/jsgui3-html/10) binding-debugger/client.js +137 -1
  79. package/http/responders/static/Static_Route_HTTP_Responder.js +52 -34
  80. package/lab/experiments/capture-color-controls.js +196 -0
  81. package/lab/results/screenshots/color-controls/full_page.png +0 -0
  82. package/lab/results/screenshots/color-controls/section_1_color_grid_12x12.png +0 -0
  83. package/lab/results/screenshots/color-controls/section_2_color_grid_4x2.png +0 -0
  84. package/lab/results/screenshots/color-controls/section_3_color_palette.png +0 -0
  85. package/lab/results/screenshots/color-controls/section_4_palette_comparison.png +0 -0
  86. package/lab/results/screenshots/color-controls/section_5_raw_swatches.png +0 -0
  87. package/lab/results/screenshots/color-controls/section_6_optimized_crayola.png +0 -0
  88. package/lab/results/screenshots/color-controls/section_7_pastel_palette.png +0 -0
  89. package/lab/results/screenshots/color-controls/section_8_extended_144.png +0 -0
  90. package/lab/screenshot-utils.js +248 -0
  91. package/module.js +11 -4
  92. package/package.json +12 -2
  93. package/publishers/Publishers.js +4 -3
  94. package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +5 -5
  95. package/publishers/http-sse-publisher.js +341 -0
  96. package/resources/process-resource.js +950 -0
  97. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +129 -33
  98. package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +18 -7
  99. package/resources/processors/bundlers/js/esbuild/JSGUI3_HTML_Control_Optimizer.js +829 -0
  100. package/resources/remote-process-resource.js +355 -0
  101. package/resources/server-resource-pool.js +354 -41
  102. package/serve-factory.js +441 -259
  103. package/server.js +89 -13
  104. package/tests/README.md +66 -4
  105. package/tests/admin-ui-render.test.js +24 -0
  106. package/tests/assigners.test.js +56 -40
  107. package/tests/bundling-default-control-elimination.puppeteer.test.js +260 -0
  108. package/tests/configuration-validation.test.js +21 -18
  109. package/tests/content-analysis.test.js +7 -6
  110. package/tests/control-optimizer-cache-behavior.test.js +52 -0
  111. package/tests/control-scan-manifest-regression.test.js +144 -0
  112. package/tests/end-to-end.test.js +15 -14
  113. package/tests/error-handling.test.js +222 -179
  114. package/tests/fixtures/bundling-default-button-client.js +37 -0
  115. package/tests/fixtures/bundling-default-window-client.js +34 -0
  116. package/tests/fixtures/control_scan_manifest_expectations.json +48 -0
  117. package/tests/fixtures/resource-monitor-client.js +319 -0
  118. package/tests/helpers/puppeteer-e2e-harness.js +317 -0
  119. package/tests/http-sse-publisher.test.js +136 -0
  120. package/tests/performance.test.js +69 -65
  121. package/tests/process-resource.test.js +138 -0
  122. package/tests/publishers.test.js +7 -7
  123. package/tests/remote-process-resource.test.js +160 -0
  124. package/tests/sass-controls.e2e.test.js +7 -1
  125. package/tests/serve-resources.test.js +270 -0
  126. package/tests/serve.test.js +120 -50
  127. package/tests/server-resource-pool.test.js +106 -0
  128. package/tests/small-controls-bundle-size.test.js +252 -0
  129. package/tests/test-runner.js +13 -1
  130. package/tests/window-examples.puppeteer.test.js +204 -1
  131. package/tests/window-resource-integration.puppeteer.test.js +585 -0
  132. package/tests/temp_invalid.js +0 -7
  133. package/tests/temp_invalid_utf8.js +0 -1
  134. package/tests/temp_malformed.js +0 -10
@@ -0,0 +1,252 @@
1
+ const assert = require('assert');
2
+ const { describe, it, before, after } = require('mocha');
3
+ const fs = require('fs').promises;
4
+ const path = require('path');
5
+
6
+ const Advanced_JS_Bundler_Using_ESBuild = require('../resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild');
7
+
8
+ const window_markers = [
9
+ 'Minimize window',
10
+ 'window_manager',
11
+ '__type_name = "window"',
12
+ "__type_name = 'window'",
13
+ "__type_name='window'",
14
+ 'add_class("window")',
15
+ "add_class('window')"
16
+ ];
17
+
18
+ const to_module_literal = (file_path) => file_path.replace(/\\/g, '\\\\');
19
+
20
+ const find_window_markers = (js_text, css_text) => {
21
+ const search_text = `${js_text || ''}\n${css_text || ''}`;
22
+ return window_markers.filter(marker => search_text.includes(marker));
23
+ };
24
+
25
+ const bundle_fixture = async (bundler, fixture_path) => {
26
+ const bundle_result = await bundler.bundle(fixture_path);
27
+ const bundle = bundle_result[0];
28
+
29
+ const js_item = bundle._arr.find(item => item.type === 'JavaScript');
30
+ const css_item = bundle._arr.find(item => item.type === 'CSS');
31
+
32
+ const js_text = (js_item && js_item.text) || '';
33
+ const css_text = (css_item && css_item.text) || '';
34
+ const markers_found = find_window_markers(js_text, css_text);
35
+
36
+ return {
37
+ js_bytes: Buffer.byteLength(js_text, 'utf8'),
38
+ css_bytes: Buffer.byteLength(css_text, 'utf8'),
39
+ markers_found,
40
+ bundle_analysis: bundle.bundle_analysis || null
41
+ };
42
+ };
43
+
44
+ describe('Small Controls Bundle Size and Window Exclusion Tests', function () {
45
+ this.timeout(180000);
46
+
47
+ const fixture_paths = [];
48
+ const fixture_metrics = {};
49
+
50
+ const fixture_dir = __dirname;
51
+ const jsgui_html_root = path.dirname(require.resolve('jsgui3-html'));
52
+
53
+ const active_html_document_path = to_module_literal(path.join(jsgui_html_root, 'controls/organised/1-standard/5-ui/Active_HTML_Document.js'));
54
+ const button_control_path = to_module_literal(path.join(jsgui_html_root, 'controls/organised/0-core/0-basic/0-native-compositional/button.js'));
55
+ const date_picker_control_path = to_module_literal(path.join(jsgui_html_root, 'controls/organised/0-core/0-basic/0-native-compositional/date-picker.js'));
56
+ const color_picker_control_path = to_module_literal(path.join(jsgui_html_root, 'controls/organised/0-core/0-basic/1-compositional/color-picker.js'));
57
+
58
+ const write_fixture = async (file_name, source_text) => {
59
+ const fixture_path = path.join(fixture_dir, file_name);
60
+ await fs.writeFile(fixture_path, source_text, 'utf8');
61
+ fixture_paths.push(fixture_path);
62
+ return fixture_path;
63
+ };
64
+
65
+ before(async function () {
66
+ const broad_import_fixture = await write_fixture('temp_small_controls_broad_client.js', `
67
+ const jsgui = require('jsgui3-html');
68
+ const controls = jsgui.controls;
69
+
70
+ class Tiny_Broad_Import_App extends controls.Active_HTML_Document {
71
+ constructor(spec = {}) {
72
+ super(spec);
73
+ if (!spec.el) {
74
+ const button = new controls.Button({ context: this.context });
75
+ button.add('ok');
76
+ this.body.add(button);
77
+ }
78
+ }
79
+ }
80
+
81
+ module.exports = { Tiny_Broad_Import_App };
82
+ `);
83
+
84
+ const button_only_fixture = await write_fixture('temp_small_controls_button_only_client.js', `
85
+ const Active_HTML_Document = require('${active_html_document_path}');
86
+ const Button = require('${button_control_path}');
87
+
88
+ class Tiny_Button_Only_App extends Active_HTML_Document {
89
+ constructor(spec = {}) {
90
+ super(spec);
91
+ if (!spec.el) {
92
+ const button = new Button({ context: this.context });
93
+ button.add('ok');
94
+ this.body.add(button);
95
+ }
96
+ }
97
+ }
98
+
99
+ module.exports = { Tiny_Button_Only_App };
100
+ `);
101
+
102
+ const date_picker_only_fixture = await write_fixture('temp_small_controls_date_picker_only_client.js', `
103
+ const Active_HTML_Document = require('${active_html_document_path}');
104
+ const Date_Picker = require('${date_picker_control_path}');
105
+
106
+ class Tiny_Date_Picker_Only_App extends Active_HTML_Document {
107
+ constructor(spec = {}) {
108
+ super(spec);
109
+ if (!spec.el) {
110
+ const date_picker = new Date_Picker({ context: this.context });
111
+ this.body.add(date_picker);
112
+ }
113
+ }
114
+ }
115
+
116
+ module.exports = { Tiny_Date_Picker_Only_App };
117
+ `);
118
+
119
+ const color_picker_only_fixture = await write_fixture('temp_small_controls_color_picker_only_client.js', `
120
+ const Active_HTML_Document = require('${active_html_document_path}');
121
+ const Color_Picker = require('${color_picker_control_path}');
122
+
123
+ class Tiny_Color_Picker_Only_App extends Active_HTML_Document {
124
+ constructor(spec = {}) {
125
+ super(spec);
126
+ if (!spec.el) {
127
+ const color_picker = new Color_Picker({ context: this.context });
128
+ this.body.add(color_picker);
129
+ }
130
+ }
131
+ }
132
+
133
+ module.exports = { Tiny_Color_Picker_Only_App };
134
+ `);
135
+
136
+ const default_bundler = new Advanced_JS_Bundler_Using_ESBuild({
137
+ debug: false,
138
+ bundler: {
139
+ minify: {
140
+ enabled: true,
141
+ level: 'normal'
142
+ }
143
+ }
144
+ });
145
+
146
+ const disabled_elimination_bundler = new Advanced_JS_Bundler_Using_ESBuild({
147
+ debug: false,
148
+ bundler: {
149
+ minify: {
150
+ enabled: true,
151
+ level: 'normal'
152
+ },
153
+ elimination: {
154
+ enabled: false,
155
+ jsgui3_html_controls: {
156
+ enabled: false
157
+ }
158
+ }
159
+ }
160
+ });
161
+
162
+ const cache_disabled_bundler = new Advanced_JS_Bundler_Using_ESBuild({
163
+ debug: false,
164
+ bundler: {
165
+ minify: {
166
+ enabled: true,
167
+ level: 'normal'
168
+ },
169
+ elimination: {
170
+ enabled: true,
171
+ jsgui3_html_controls: {
172
+ cache: {
173
+ enabled: false
174
+ }
175
+ }
176
+ }
177
+ }
178
+ });
179
+
180
+ fixture_metrics.broad_import_default = await bundle_fixture(default_bundler, broad_import_fixture);
181
+ fixture_metrics.broad_import_elimination_disabled = await bundle_fixture(disabled_elimination_bundler, broad_import_fixture);
182
+ fixture_metrics.broad_import_cache_disabled = await bundle_fixture(cache_disabled_bundler, broad_import_fixture);
183
+ fixture_metrics.button_only = await bundle_fixture(default_bundler, button_only_fixture);
184
+ fixture_metrics.date_picker_only = await bundle_fixture(default_bundler, date_picker_only_fixture);
185
+ fixture_metrics.color_picker_only = await bundle_fixture(default_bundler, color_picker_only_fixture);
186
+
187
+ console.log('[small-controls-bundle-size] metrics', JSON.stringify(fixture_metrics, null, 2));
188
+ });
189
+
190
+ after(async function () {
191
+ await Promise.all(fixture_paths.map(async fixture_path => {
192
+ try {
193
+ await fs.unlink(fixture_path);
194
+ } catch (err) {
195
+ // Ignore missing temp fixture files.
196
+ }
197
+ }));
198
+ });
199
+
200
+ it('should exclude Window markers in broad jsgui3-html imports by default', function () {
201
+ assert.strictEqual(fixture_metrics.broad_import_default.markers_found.length, 0,
202
+ `Unexpected Window markers in broad default bundle: ${fixture_metrics.broad_import_default.markers_found.join(', ')}`);
203
+ });
204
+
205
+ it('should include Window markers when control elimination is explicitly disabled', function () {
206
+ assert(fixture_metrics.broad_import_elimination_disabled.markers_found.length > 0,
207
+ 'Expected broad import bundle with elimination disabled to contain Window markers');
208
+ });
209
+
210
+ it('should keep elimination behavior when cache is disabled', function () {
211
+ assert.strictEqual(fixture_metrics.broad_import_cache_disabled.markers_found.length, 0,
212
+ `Unexpected Window markers in cache-disabled elimination bundle: ${fixture_metrics.broad_import_cache_disabled.markers_found.join(', ')}`);
213
+ });
214
+
215
+ it('should exclude Window markers when only Button control is directly imported', function () {
216
+ assert.strictEqual(fixture_metrics.button_only.markers_found.length, 0,
217
+ `Unexpected Window markers in button-only bundle: ${fixture_metrics.button_only.markers_found.join(', ')}`);
218
+ });
219
+
220
+ it('should exclude Window markers when only Date_Picker control is directly imported', function () {
221
+ assert.strictEqual(fixture_metrics.date_picker_only.markers_found.length, 0,
222
+ `Unexpected Window markers in date-picker-only bundle: ${fixture_metrics.date_picker_only.markers_found.join(', ')}`);
223
+ });
224
+
225
+ it('should exclude Window markers when only Color_Picker control is directly imported', function () {
226
+ assert.strictEqual(fixture_metrics.color_picker_only.markers_found.length, 0,
227
+ `Unexpected Window markers in color-picker-only bundle: ${fixture_metrics.color_picker_only.markers_found.join(', ')}`);
228
+ });
229
+
230
+ it('should produce smaller JS bundles for direct small-control imports than broad import', function () {
231
+ assert(fixture_metrics.button_only.js_bytes < fixture_metrics.broad_import_elimination_disabled.js_bytes,
232
+ 'Button-only bundle should be smaller than broad import bundle with elimination disabled');
233
+ assert(fixture_metrics.date_picker_only.js_bytes < fixture_metrics.broad_import_elimination_disabled.js_bytes,
234
+ 'Date-picker-only bundle should be smaller than broad import bundle with elimination disabled');
235
+ assert(fixture_metrics.color_picker_only.js_bytes < fixture_metrics.broad_import_elimination_disabled.js_bytes,
236
+ 'Color-picker-only bundle should be smaller than broad import bundle with elimination disabled');
237
+ });
238
+
239
+ it('should reduce broad-import JS size when default elimination scan is active', function () {
240
+ assert(fixture_metrics.broad_import_default.js_bytes < fixture_metrics.broad_import_elimination_disabled.js_bytes,
241
+ 'Default broad-import bundle should be smaller than broad-import bundle with elimination disabled');
242
+ });
243
+
244
+ it('should expose control scan analysis metadata for default broad import bundle', function () {
245
+ const analysis = fixture_metrics.broad_import_default.bundle_analysis &&
246
+ fixture_metrics.broad_import_default.bundle_analysis.jsgui3_html_control_scan;
247
+ assert(analysis, 'Expected bundle_analysis.jsgui3_html_control_scan metadata');
248
+ assert(Array.isArray(analysis.selected_controls), 'Expected selected_controls array in control scan metadata');
249
+ assert(analysis.selected_controls.includes('Button'), 'Expected Button in selected_controls');
250
+ assert(!analysis.selected_controls.includes('Window'), 'Expected Window not to be selected');
251
+ });
252
+ });
@@ -27,14 +27,26 @@ class TestRunner {
27
27
  'assigners.test.js',
28
28
  'publishers.test.js',
29
29
  'configuration-validation.test.js',
30
+ 'admin-ui-render.test.js',
31
+ 'serve.test.js',
32
+ 'serve-resources.test.js',
33
+ 'process-resource.test.js',
34
+ 'remote-process-resource.test.js',
35
+ 'server-resource-pool.test.js',
36
+ 'http-sse-publisher.test.js',
30
37
  'end-to-end.test.js',
31
38
  'content-analysis.test.js',
39
+ 'small-controls-bundle-size.test.js',
40
+ 'control-optimizer-cache-behavior.test.js',
41
+ 'control-scan-manifest-regression.test.js',
32
42
  'performance.test.js',
33
43
  'error-handling.test.js',
34
44
  'examples-controls.e2e.test.js',
35
45
  'sass-controls.e2e.test.js',
36
46
  'jsgui3-html-examples.puppeteer.test.js',
37
- 'window-examples.puppeteer.test.js'
47
+ 'bundling-default-control-elimination.puppeteer.test.js',
48
+ 'window-examples.puppeteer.test.js',
49
+ 'window-resource-integration.puppeteer.test.js'
38
50
  ];
39
51
  }
40
52
 
@@ -166,6 +166,35 @@ const get_checkbox_state = async (page) => {
166
166
  return page.$eval('.checkbox input[type="checkbox"]', (el) => el.checked);
167
167
  };
168
168
 
169
+ const set_input_value_with_events = async (page, selector, next_value, event_names = ['input']) => {
170
+ await page.$eval(selector, (el, value, events) => {
171
+ el.value = value;
172
+ for (const event_name of events) {
173
+ el.dispatchEvent(new Event(event_name, { bubbles: true }));
174
+ }
175
+ }, String(next_value), event_names);
176
+ };
177
+
178
+ const click_datetime_tab = async (page, root_selector, tab_label_fragment) => {
179
+ const click_result = await page.evaluate((picker_root_selector, text_fragment) => {
180
+ const root_el = document.querySelector(picker_root_selector);
181
+ if (!root_el) return false;
182
+ const tab_el = Array.from(root_el.querySelectorAll('.dtp-tab')).find((tab) => {
183
+ const tab_text = String(tab.textContent || '').toLowerCase();
184
+ return tab_text.includes(String(text_fragment || '').toLowerCase());
185
+ });
186
+ if (!tab_el) return false;
187
+ tab_el.click();
188
+ return true;
189
+ }, root_selector, tab_label_fragment);
190
+
191
+ return click_result === true;
192
+ };
193
+
194
+ const get_text_content = async (page, selector) => {
195
+ return page.$eval(selector, (el) => (el.textContent || '').trim());
196
+ };
197
+
169
198
  describe('Window Examples Puppeteer Tests', function () {
170
199
  this.timeout(180000);
171
200
 
@@ -432,7 +461,68 @@ describe('Window Examples Puppeteer Tests', function () {
432
461
  }
433
462
  });
434
463
 
435
- it('date picker renders native input in "9) window, date picker"', async function () {
464
+ it('color picker controls stay interactive in "6) window, color_palette"', async function () {
465
+ const { server, port } = await start_example_server({
466
+ dir_name: '6) window, color_palette',
467
+ ctrl_name: 'Demo_UI'
468
+ });
469
+
470
+ let page;
471
+ try {
472
+ page = await open_example_page(port);
473
+ await page.waitForSelector('.demo-color-picker-default .cp-hex-input');
474
+ await page.waitForSelector('.demo-color-picker-compact .cp-rgb-r');
475
+
476
+ const wheel_drawn = await page.$eval('.demo-color-picker-default .cp-wheel-canvas', (canvas) => {
477
+ const ctx = canvas.getContext('2d');
478
+ if (!ctx) return false;
479
+ const pixel = ctx.getImageData(90, 2, 1, 1).data;
480
+ return pixel[3] > 0;
481
+ });
482
+ assert.strictEqual(wheel_drawn, true, 'Expected Color_Picker hue wheel canvas to be drawn');
483
+
484
+ const initial_hex = await page.$eval('.demo-color-picker-default .cp-hex-input', (el) => el.value);
485
+ await set_input_value_with_events(page, '.demo-color-picker-default .cp-slider-h', 0, ['input']);
486
+ await page.waitForFunction(
487
+ (selector, before_hex) => {
488
+ const el = document.querySelector(selector);
489
+ return !!el && String(el.value || '').toLowerCase() !== String(before_hex || '').toLowerCase();
490
+ },
491
+ {},
492
+ '.demo-color-picker-default .cp-hex-input',
493
+ initial_hex
494
+ );
495
+
496
+ await set_input_value_with_events(page, '.demo-color-picker-compact .cp-rgb-r', 0, ['input']);
497
+ await set_input_value_with_events(page, '.demo-color-picker-compact .cp-rgb-g', 255, ['input']);
498
+ await set_input_value_with_events(page, '.demo-color-picker-compact .cp-rgb-b', 0, ['input']);
499
+
500
+ await page.waitForFunction(() => {
501
+ const readout_el = document.querySelector('.demo-color-picker-readout');
502
+ if (!readout_el) return false;
503
+ const text = String(readout_el.textContent || '');
504
+ return /rgba\(\s*0,\s*255,\s*0/i.test(text);
505
+ });
506
+
507
+ await capture_screenshot(page, '6) window, color_palette', 'after_picker_interaction');
508
+
509
+ await drag_window_by(page, '.window .title.bar', 90, 60);
510
+ await page.waitForTimeout(120);
511
+
512
+ await set_input_value_with_events(page, '.demo-color-picker-default .cp-slider-l', 25, ['input']);
513
+ await page.waitForFunction(() => {
514
+ const hex_el = document.querySelector('.demo-color-picker-default .cp-hex-input');
515
+ return !!hex_el && /^#[0-9a-f]{6}$/i.test(String(hex_el.value || ''));
516
+ });
517
+ } finally {
518
+ if (page) {
519
+ await page.close();
520
+ }
521
+ await stop_example_server(server);
522
+ }
523
+ });
524
+
525
+ it('date and datetime picker interactions work in "9) window, date picker"', async function () {
436
526
  const { server, port } = await start_example_server({
437
527
  dir_name: '9) window, date picker',
438
528
  ctrl_name: 'Demo_UI'
@@ -442,9 +532,122 @@ describe('Window Examples Puppeteer Tests', function () {
442
532
  try {
443
533
  page = await open_example_page(port);
444
534
  await page.waitForSelector('input.date-picker[type="date"]');
535
+ await page.waitForSelector('.demo-datetime-picker.datetime-picker');
445
536
 
446
537
  const input_type = await page.$eval('input.date-picker', (el) => el.getAttribute('type'));
447
538
  assert.strictEqual(input_type, 'date', 'Expected native date input');
539
+
540
+ const date_attrs = await page.$eval('input.date-picker', (el) => ({
541
+ min: el.getAttribute('min'),
542
+ max: el.getAttribute('max'),
543
+ lang: el.getAttribute('lang')
544
+ }));
545
+ assert.strictEqual(date_attrs.min, '2026-01-01');
546
+ assert.strictEqual(date_attrs.max, '2026-12-31');
547
+ assert.strictEqual(date_attrs.lang, 'en-GB');
548
+
549
+ await set_input_value_with_events(page, 'input.date-picker', '2026-07-04', ['input', 'change']);
550
+ await page.waitForFunction(() => {
551
+ const out_el = document.querySelector('.demo-date-output');
552
+ return !!out_el && String(out_el.textContent || '').includes('2026-07-04');
553
+ });
554
+
555
+ const initial_datetime_text = await get_text_content(page, '.demo-datetime-output');
556
+ const initial_time_display = await get_text_content(page, '.demo-datetime-picker .tp-display-time');
557
+ const clicked_time_tab = await click_datetime_tab(page, '.demo-datetime-picker', 'time');
558
+ assert.strictEqual(clicked_time_tab, true, 'Expected to locate and click DateTime "Time" tab');
559
+
560
+ await page.waitForFunction(() => {
561
+ const tp = document.querySelector('.demo-datetime-picker .time-picker');
562
+ return !!tp && window.getComputedStyle(tp).display !== 'none';
563
+ });
564
+
565
+ const did_click_spinners = await page.evaluate(() => {
566
+ const hour_spinner = document.querySelector('.demo-datetime-picker .tp-spinner-up.tp-h-up');
567
+ const minute_spinner = document.querySelector('.demo-datetime-picker .tp-spinner-up.tp-m-up');
568
+ if (!hour_spinner || !minute_spinner) return false;
569
+ hour_spinner.dispatchEvent(new MouseEvent('click', { bubbles: true }));
570
+ minute_spinner.dispatchEvent(new MouseEvent('click', { bubbles: true }));
571
+ return true;
572
+ });
573
+ assert.strictEqual(did_click_spinners, true, 'Expected DateTime spinner controls to be present');
574
+ await page.waitForFunction(
575
+ (before_output_text, before_time_text) => {
576
+ const out_el = document.querySelector('.demo-datetime-output');
577
+ const time_el = document.querySelector('.demo-datetime-picker .tp-display-time');
578
+ if (!out_el && !time_el) return false;
579
+ const after_text = String(out_el.textContent || '');
580
+ const after_time_text = String((time_el && time_el.textContent) || '').trim();
581
+ return (
582
+ after_text !== before_output_text
583
+ || after_time_text !== before_time_text
584
+ ) && /DateTime value:\s*\d{4}-\d{2}-\d{2}T.+/.test(after_text);
585
+ },
586
+ {},
587
+ initial_datetime_text,
588
+ initial_time_display
589
+ );
590
+
591
+ const clicked_date_tab = await click_datetime_tab(page, '.demo-datetime-picker', 'date');
592
+ assert.strictEqual(clicked_date_tab, true, 'Expected to locate and click DateTime "Date" tab');
593
+ await page.waitForFunction(() => {
594
+ const mv = document.querySelector('.demo-datetime-picker .month-view');
595
+ return !!mv && window.getComputedStyle(mv).display !== 'none';
596
+ });
597
+
598
+ await drag_window_by(page, '.window .title.bar', 80, 50);
599
+ await resize_window_by(page, '.window .bottom-right.resize-handle', 100, 80);
600
+ await page.waitForTimeout(120);
601
+
602
+ await set_input_value_with_events(page, 'input.date-picker', '2026-09-09', ['input', 'change']);
603
+ await page.waitForFunction(() => {
604
+ const out_el = document.querySelector('.demo-date-output');
605
+ return !!out_el && String(out_el.textContent || '').includes('2026-09-09');
606
+ });
607
+
608
+ await capture_screenshot(page, '9) window, date picker', 'after_datetime_interaction');
609
+ } finally {
610
+ if (page) {
611
+ await page.close();
612
+ }
613
+ await stop_example_server(server);
614
+ }
615
+ });
616
+
617
+ it('shared Date_Picker controls mirror values in "9b) window, shared data.model mirrored date pickers"', async function () {
618
+ const { server, port } = await start_example_server({
619
+ dir_name: '9b) window, shared data.model mirrored date pickers',
620
+ ctrl_name: 'Demo_UI'
621
+ });
622
+
623
+ let page;
624
+ try {
625
+ page = await open_example_page(port);
626
+ await page.waitForSelector('input.date-picker');
627
+
628
+ await page.waitForFunction(() => document.querySelectorAll('input.date-picker').length >= 2);
629
+ const set_date_value_at_index = async (index, next_value) => {
630
+ await page.evaluate((picker_index, value) => {
631
+ const inputs = Array.from(document.querySelectorAll('input.date-picker'));
632
+ const target = inputs[picker_index];
633
+ if (!target) return;
634
+ target.value = value;
635
+ target.dispatchEvent(new Event('input', { bubbles: true }));
636
+ target.dispatchEvent(new Event('change', { bubbles: true }));
637
+ }, index, next_value);
638
+ };
639
+
640
+ await set_date_value_at_index(0, '2026-05-10');
641
+ await page.waitForFunction((expected_value) => {
642
+ const inputs = Array.from(document.querySelectorAll('input.date-picker'));
643
+ return inputs.length >= 2 && inputs[1].value === expected_value;
644
+ }, {}, '2026-05-10');
645
+
646
+ await set_date_value_at_index(1, '2026-11-21');
647
+ await page.waitForFunction((expected_value) => {
648
+ const inputs = Array.from(document.querySelectorAll('input.date-picker'));
649
+ return inputs.length >= 2 && inputs[0].value === expected_value;
650
+ }, {}, '2026-11-21');
448
651
  } finally {
449
652
  if (page) {
450
653
  await page.close();