jsgui3-server 0.0.143 → 0.0.145

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 (67) hide show
  1. package/docs/comprehensive-documentation.md +25 -6
  2. package/docs/configuration-reference.md +46 -11
  3. package/docs/controls-development.md +54 -26
  4. package/docs/jsgui3-html-improvement-ideas.md +162 -0
  5. package/docs/jsgui3-html-improvement-ideas.svg +151 -0
  6. package/docs/troubleshooting.md +9 -8
  7. package/examples/controls/14d) window, canvas globe/EarthGlobeRenderer.js +19 -14
  8. package/examples/controls/14d) window, canvas globe/pipeline/TransformStage.js +5 -5
  9. package/examples/jsgui3-html/01) mvvm-counter/client.js +648 -0
  10. package/examples/jsgui3-html/01) mvvm-counter/server.js +21 -0
  11. package/examples/jsgui3-html/02) date-transform/client.js +764 -0
  12. package/examples/jsgui3-html/02) date-transform/server.js +21 -0
  13. package/examples/jsgui3-html/03) form-validation/client.js +1045 -0
  14. package/examples/jsgui3-html/03) form-validation/server.js +21 -0
  15. package/examples/jsgui3-html/04) data-grid/client.js +738 -0
  16. package/examples/jsgui3-html/04) data-grid/server.js +21 -0
  17. package/examples/jsgui3-html/05) master-detail/client.js +649 -0
  18. package/examples/jsgui3-html/05) master-detail/server.js +21 -0
  19. package/examples/jsgui3-html/06) theming/client.js +514 -0
  20. package/examples/jsgui3-html/06) theming/server.js +21 -0
  21. package/examples/jsgui3-html/07) mixins/client.js +465 -0
  22. package/examples/jsgui3-html/07) mixins/server.js +21 -0
  23. package/examples/jsgui3-html/08) router/client.js +372 -0
  24. package/examples/jsgui3-html/08) router/server.js +21 -0
  25. package/examples/jsgui3-html/09) resource-transform/client.js +692 -0
  26. package/examples/jsgui3-html/09) resource-transform/server.js +21 -0
  27. package/examples/jsgui3-html/10) binding-debugger/client.js +810 -0
  28. package/examples/jsgui3-html/10) binding-debugger/server.js +21 -0
  29. package/examples/jsgui3-html/README.md +48 -0
  30. package/http/responders/static/Static_Route_HTTP_Responder.js +25 -20
  31. package/lab/README.md +19 -0
  32. package/lab/experiments/window_examples_dom_audit.js +241 -0
  33. package/lab/results/window_examples_dom_audit.json +131 -0
  34. package/lab/results/window_examples_dom_audit.md +46 -0
  35. package/package.json +8 -3
  36. package/publishers/http-webpageorsite-publisher.js +8 -4
  37. package/resources/processors/bundlers/css-bundler.js +28 -173
  38. package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +32 -20
  39. package/resources/processors/bundlers/style-bundler.js +288 -0
  40. package/resources/processors/compilers/SASS_Compiler.js +88 -0
  41. package/resources/processors/extractors/js/css_and_js/AST_Node/CSS_And_JS_From_JS_String_Using_AST_Node_Extractor.js +64 -68
  42. package/resources/website-css-resource.js +24 -20
  43. package/resources/website-javascript-resource-processor.js +17 -57
  44. package/resources/website-javascript-resource.js +17 -57
  45. package/serve-factory.js +38 -24
  46. package/server.js +116 -92
  47. package/tests/README.md +38 -3
  48. package/tests/bundlers.test.js +41 -32
  49. package/tests/content-analysis.test.js +19 -18
  50. package/tests/end-to-end.test.js +336 -365
  51. package/tests/error-handling.test.js +13 -11
  52. package/tests/examples-controls.e2e.test.js +13 -1
  53. package/tests/fixtures/end-to-end-client.js +54 -0
  54. package/tests/fixtures/jsgui3-html/binding_debugger_expectations.json +15 -0
  55. package/tests/fixtures/jsgui3-html/counter_expectations.json +31 -0
  56. package/tests/fixtures/jsgui3-html/data_grid_expectations.json +26 -0
  57. package/tests/fixtures/jsgui3-html/date_transform_expectations.json +26 -0
  58. package/tests/fixtures/jsgui3-html/form_validation_expectations.json +27 -0
  59. package/tests/fixtures/jsgui3-html/master_detail_expectations.json +15 -0
  60. package/tests/fixtures/jsgui3-html/mixins_expectations.json +10 -0
  61. package/tests/fixtures/jsgui3-html/resource_transform_expectations.json +11 -0
  62. package/tests/fixtures/jsgui3-html/router_expectations.json +10 -0
  63. package/tests/fixtures/jsgui3-html/theming_expectations.json +10 -0
  64. package/tests/jsgui3-html-examples.puppeteer.test.js +537 -0
  65. package/tests/sass-controls.e2e.test.js +327 -0
  66. package/tests/test-runner.js +4 -1
  67. package/tests/window-examples.puppeteer.test.js +455 -0
@@ -1,123 +1,81 @@
1
1
  const assert = require('assert');
2
2
  const { describe, it, before, after } = require('mocha');
3
3
  const http = require('http');
4
- const fs = require('fs').promises;
5
- const path = require('path');
4
+ const path = require('path');
6
5
 
7
- // Import server and related classes
8
- const Server = require('../module');
6
+ // Import server and related classes
7
+ const jsgui_module = require('../module');
8
+ const Server = jsgui_module.Server;
9
9
 
10
10
  describe('End-to-End Integration Tests', function() {
11
11
  this.timeout(30000); // Allow more time for server startup and requests
12
12
 
13
- let server;
14
- let serverPort = 3001; // Use a different port for testing
15
- let testControl;
16
-
17
- before(async function() {
18
- // Create a test control class
19
- testControl = class TestControl {
20
- constructor(spec) {
21
- this.context = spec.context;
22
- this.head = {
23
- add: function(element) {
24
- // Mock add method
25
- }
26
- };
27
- this.body = {
28
- add: function(element) {
29
- // Mock add method
30
- }
31
- };
32
- }
33
-
34
- active() {
35
- // Mock activation
36
- }
37
-
38
- all_html_render() {
39
- return Promise.resolve(`<!DOCTYPE html>
40
- <html>
41
- <head>
42
- <title>Test Page</title>
43
- <script src="/js/js.js"></script>
44
- </head>
45
- <body>
46
- <div id="test-control">
47
- <h1>Test Control</h1>
48
- <p>This is a test control with embedded CSS and JS.</p>
49
- <button onclick="testFunction()">Test Button</button>
50
- </div>
51
- </body>
52
- </html>`);
53
- }
54
- };
55
-
56
- // Add CSS and JS to the control class
57
- testControl.css = `
58
- .test-control {
59
- background-color: #f0f0f0;
60
- padding: 20px;
61
- border: 1px solid #ccc;
62
- font-family: Arial, sans-serif;
63
- }
64
- .test-control h1 {
65
- color: #333;
66
- margin-bottom: 10px;
67
- }
68
- .test-control p {
69
- color: #666;
70
- line-height: 1.5;
71
- }
72
- `;
73
-
74
- testControl.js = `
75
- // Test JavaScript with CSS embedding
76
- const css = \`${testControl.css}\`;
77
-
78
- // Add CSS to document
79
- const style = document.createElement('style');
80
- style.textContent = css;
81
- document.head.appendChild(style);
82
-
83
- // Test function
84
- function testFunction() {
85
- console.log('Test function called');
86
- alert('Test function executed successfully!');
87
- return 'test result';
88
- }
89
-
90
- // Initialize when DOM is ready
91
- document.addEventListener('DOMContentLoaded', function() {
92
- console.log('Test control initialized');
93
- const testDiv = document.getElementById('test-control');
94
- if (testDiv) {
95
- testDiv.style.borderColor = '#007bff';
96
- }
97
- });
98
-
99
- // Export for testing
100
- window.TestControl = { testFunction };
101
- `;
102
- });
103
-
104
- after(async function() {
105
- // Clean up server
106
- if (server) {
107
- await server.stop();
108
- }
109
- });
13
+ let server;
14
+ let serverPort = 3001; // Use a different port for testing
15
+ let testControl;
16
+ let test_client_path;
17
+ let base_serve_options;
18
+ const wait_for_route = async (url, timeout_ms = 20000) => {
19
+ const start_time = Date.now();
20
+ let last_error = null;
21
+ while (Date.now() - start_time < timeout_ms) {
22
+ try {
23
+ const response = await makeRequest(url, {
24
+ 'Accept-Encoding': 'identity'
25
+ });
26
+ if (response.statusCode === 200) return response;
27
+ } catch (error) {
28
+ last_error = error;
29
+ }
30
+ await new Promise((resolve) => setTimeout(resolve, 250));
31
+ }
32
+ const error = last_error || new Error(`Timed out waiting for ${url}`);
33
+ throw error;
34
+ };
35
+ const start_server = async (serve_options) => {
36
+ const server_instance = await Server.serve(serve_options);
37
+ const port = server_instance.port || serve_options.port;
38
+ if (port) {
39
+ await wait_for_route(`http://localhost:${port}/js/js.js`);
40
+ }
41
+ return server_instance;
42
+ };
43
+ const stop_server = async (server_instance) => {
44
+ if (!server_instance) return;
45
+ await new Promise((resolve) => server_instance.close(resolve));
46
+ };
47
+
48
+ before(async function() {
49
+ test_client_path = path.join(__dirname, 'fixtures', 'end-to-end-client.js');
50
+ const test_client = require(test_client_path);
51
+ testControl = test_client.controls && test_client.controls.Test_Control;
52
+ assert(testControl, `Missing exported control jsgui.controls.Test_Control in ${test_client_path}`);
53
+ base_serve_options = {
54
+ ctrl: testControl,
55
+ port: serverPort,
56
+ src_path_client_js: test_client_path
57
+ };
58
+ });
59
+
60
+ afterEach(async function() {
61
+ await stop_server(server);
62
+ server = null;
63
+ });
64
+
65
+ after(async function() {
66
+ // Clean up server
67
+ await stop_server(server);
68
+ server = null;
69
+ });
110
70
 
111
71
  describe('Full Server Integration with Minification and Compression', function() {
112
72
  it('should serve webpage with minified and compressed JavaScript', async function() {
113
- // Start server with minification and compression enabled
114
- server = new Server();
115
- await server.serve({
116
- ctrl: testControl,
117
- port: serverPort,
118
- debug: false, // Enable minification
119
- bundler: {
120
- minify: {
73
+ // Start server with minification and compression enabled
74
+ server = await start_server({
75
+ ...base_serve_options,
76
+ debug: false, // Enable minification
77
+ bundler: {
78
+ minify: {
121
79
  enabled: true,
122
80
  level: 'normal'
123
81
  },
@@ -128,47 +86,56 @@ describe('End-to-End Integration Tests', function() {
128
86
  }
129
87
  });
130
88
 
131
- // Wait for server to be ready
132
- await new Promise(resolve => setTimeout(resolve, 2000));
133
-
134
- // Test gzip compressed JavaScript
135
- const jsResponse = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
136
- 'Accept-Encoding': 'gzip'
137
- });
138
-
139
- assert.strictEqual(jsResponse.statusCode, 200);
140
- assert.strictEqual(jsResponse.headers['content-encoding'], 'gzip');
141
- assert(jsResponse.headers['content-type'].includes('javascript'));
142
-
143
- // Verify the response is actually compressed (should be smaller than original)
144
- const originalSize = Buffer.from(testControl.js, 'utf8').length;
145
- const compressedSize = jsResponse.body.length;
146
- assert(compressedSize < originalSize, 'JavaScript should be compressed');
147
-
148
- // Test brotli compressed JavaScript
149
- const brJsResponse = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
150
- 'Accept-Encoding': 'br'
151
- });
152
-
153
- assert.strictEqual(brJsResponse.statusCode, 200);
154
- assert.strictEqual(brJsResponse.headers['content-encoding'], 'br');
155
-
156
- // Brotli should generally be smaller than gzip for text
157
- assert(brJsResponse.body.length <= jsResponse.body.length,
158
- 'Brotli should be at least as good as gzip');
159
-
160
- // Stop server
161
- await server.stop();
162
- });
163
-
164
- it('should serve webpage with sourcemaps in debug mode', async function() {
165
- // Start server in debug mode
166
- server = new Server();
167
- await server.serve({
168
- ctrl: testControl,
169
- port: serverPort,
170
- debug: true, // Enable sourcemaps
171
- bundler: {
89
+ const js_identity_response = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
90
+ 'Accept-Encoding': 'identity'
91
+ });
92
+
93
+ assert.strictEqual(js_identity_response.statusCode, 200);
94
+ assert(
95
+ !js_identity_response.headers['content-encoding'] ||
96
+ js_identity_response.headers['content-encoding'] === 'identity'
97
+ );
98
+
99
+ // Test gzip compressed JavaScript
100
+ const js_gzip_response = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
101
+ 'Accept-Encoding': 'gzip'
102
+ });
103
+
104
+ assert.strictEqual(js_gzip_response.statusCode, 200);
105
+ assert.strictEqual(js_gzip_response.headers['content-encoding'], 'gzip');
106
+ assert(js_gzip_response.headers['content-type'].includes('javascript'));
107
+
108
+ // Verify the response is actually compressed (should be smaller than identity)
109
+ assert(
110
+ js_gzip_response.body.length < js_identity_response.body.length,
111
+ 'JavaScript should be compressed'
112
+ );
113
+
114
+ // Test brotli compressed JavaScript
115
+ const br_js_response = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
116
+ 'Accept-Encoding': 'br'
117
+ });
118
+
119
+ assert.strictEqual(br_js_response.statusCode, 200);
120
+ assert.strictEqual(br_js_response.headers['content-encoding'], 'br');
121
+
122
+ // Brotli should generally be smaller than gzip for text
123
+ assert(
124
+ br_js_response.body.length <= js_gzip_response.body.length,
125
+ 'Brotli should be at least as good as gzip'
126
+ );
127
+
128
+ // Stop server
129
+ await stop_server(server);
130
+ server = null;
131
+ });
132
+
133
+ it('should serve webpage with sourcemaps in debug mode', async function() {
134
+ // Start server in debug mode
135
+ server = await start_server({
136
+ ...base_serve_options,
137
+ debug: true, // Enable sourcemaps
138
+ bundler: {
172
139
  sourcemaps: {
173
140
  enabled: true,
174
141
  format: 'inline'
@@ -180,34 +147,30 @@ describe('End-to-End Integration Tests', function() {
180
147
  }
181
148
  });
182
149
 
183
- // Wait for server to be ready
184
- await new Promise(resolve => setTimeout(resolve, 2000));
185
-
186
- // Test JavaScript with sourcemaps
187
- const jsResponse = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
188
- 'Accept-Encoding': 'gzip'
189
- });
190
-
191
- assert.strictEqual(jsResponse.statusCode, 200);
192
- assert.strictEqual(jsResponse.headers['content-encoding'], 'gzip');
193
-
194
- // Decompress to check for sourcemap
195
- const zlib = require('zlib');
196
- const decompressed = zlib.gunzipSync(jsResponse.body).toString();
197
- assert(decompressed.includes('//# sourceMappingURL='), 'Debug mode should include inline sourcemaps');
198
-
199
- // Stop server
200
- await server.stop();
201
- });
202
-
203
- it('should serve compressed CSS', async function() {
204
- // Start server with compression
205
- server = new Server();
206
- await server.serve({
207
- ctrl: testControl,
208
- port: serverPort,
209
- debug: false,
210
- bundler: {
150
+ // Test JavaScript with sourcemaps
151
+ const js_gzip_response = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
152
+ 'Accept-Encoding': 'gzip'
153
+ });
154
+
155
+ assert.strictEqual(js_gzip_response.statusCode, 200);
156
+ assert.strictEqual(js_gzip_response.headers['content-encoding'], 'gzip');
157
+
158
+ // Decompress to check for sourcemap
159
+ const zlib = require('zlib');
160
+ const decompressed = zlib.gunzipSync(js_gzip_response.body).toString();
161
+ assert(decompressed.includes('//# sourceMappingURL='), 'Debug mode should include inline sourcemaps');
162
+
163
+ // Stop server
164
+ await stop_server(server);
165
+ server = null;
166
+ });
167
+
168
+ it('should serve compressed CSS', async function() {
169
+ // Start server with compression
170
+ server = await start_server({
171
+ ...base_serve_options,
172
+ debug: false,
173
+ bundler: {
211
174
  compression: {
212
175
  enabled: true,
213
176
  algorithms: ['gzip', 'br']
@@ -215,35 +178,42 @@ describe('End-to-End Integration Tests', function() {
215
178
  }
216
179
  });
217
180
 
218
- // Wait for server to be ready
219
- await new Promise(resolve => setTimeout(resolve, 2000));
220
-
221
- // Test gzip compressed CSS
222
- const cssResponse = await makeRequest(`http://localhost:${serverPort}/css/css.css`, {
223
- 'Accept-Encoding': 'gzip'
224
- });
225
-
226
- assert.strictEqual(cssResponse.statusCode, 200);
227
- assert.strictEqual(cssResponse.headers['content-encoding'], 'gzip');
228
- assert(cssResponse.headers['content-type'].includes('css'));
229
-
230
- // Verify CSS content is compressed
231
- const originalCssSize = Buffer.from(testControl.css, 'utf8').length;
232
- const compressedCssSize = cssResponse.body.length;
233
- assert(compressedCssSize < originalCssSize, 'CSS should be compressed');
234
-
235
- // Stop server
236
- await server.stop();
237
- });
238
-
239
- it('should serve HTML without compression when below threshold', async function() {
240
- // Start server with high compression threshold
241
- server = new Server();
242
- await server.serve({
243
- ctrl: testControl,
244
- port: serverPort,
245
- debug: false,
246
- bundler: {
181
+ const css_identity_response = await makeRequest(`http://localhost:${serverPort}/css/css.css`, {
182
+ 'Accept-Encoding': 'identity'
183
+ });
184
+
185
+ assert.strictEqual(css_identity_response.statusCode, 200);
186
+ assert(
187
+ !css_identity_response.headers['content-encoding'] ||
188
+ css_identity_response.headers['content-encoding'] === 'identity'
189
+ );
190
+
191
+ // Test gzip compressed CSS
192
+ const css_gzip_response = await makeRequest(`http://localhost:${serverPort}/css/css.css`, {
193
+ 'Accept-Encoding': 'gzip'
194
+ });
195
+
196
+ assert.strictEqual(css_gzip_response.statusCode, 200);
197
+ assert.strictEqual(css_gzip_response.headers['content-encoding'], 'gzip');
198
+ assert(css_gzip_response.headers['content-type'].includes('css'));
199
+
200
+ // Verify CSS content is compressed
201
+ assert(
202
+ css_gzip_response.body.length < css_identity_response.body.length,
203
+ 'CSS should be compressed'
204
+ );
205
+
206
+ // Stop server
207
+ await stop_server(server);
208
+ server = null;
209
+ });
210
+
211
+ it('should serve HTML without compression when below threshold', async function() {
212
+ // Start server with high compression threshold
213
+ server = await start_server({
214
+ ...base_serve_options,
215
+ debug: false,
216
+ bundler: {
247
217
  compression: {
248
218
  enabled: true,
249
219
  threshold: 10000 // Very high threshold
@@ -251,99 +221,107 @@ describe('End-to-End Integration Tests', function() {
251
221
  }
252
222
  });
253
223
 
254
- // Wait for server to be ready
255
- await new Promise(resolve => setTimeout(resolve, 2000));
256
-
257
- // Test HTML (should not be compressed due to threshold)
258
- const htmlResponse = await makeRequest(`http://localhost:${serverPort}/`, {
224
+ // Test HTML (should not be compressed due to threshold)
225
+ const htmlResponse = await makeRequest(`http://localhost:${serverPort}/`, {
259
226
  'Accept-Encoding': 'gzip'
260
227
  });
261
228
 
262
229
  assert.strictEqual(htmlResponse.statusCode, 200);
263
230
  assert(!htmlResponse.headers['content-encoding'], 'HTML should not be compressed due to threshold');
264
- assert(htmlResponse.headers['content-type'].includes('html'));
265
-
266
- // Stop server
267
- await server.stop();
268
- });
269
-
270
- it('should handle different minification levels', async function() {
271
- const minificationLevels = ['conservative', 'normal', 'aggressive'];
272
-
273
- for (const level of minificationLevels) {
274
- // Start server with specific minification level
275
- server = new Server();
276
- await server.serve({
277
- ctrl: testControl,
278
- port: serverPort,
279
- debug: false,
280
- bundler: {
281
- minify: {
282
- enabled: true,
283
- level: level
284
- },
285
- compression: {
286
- enabled: false // Disable compression for size comparison
287
- }
288
- }
289
- });
290
-
291
- // Wait for server to be ready
292
- await new Promise(resolve => setTimeout(resolve, 2000));
293
-
294
- // Test JavaScript
295
- const jsResponse = await makeRequest(`http://localhost:${serverPort}/js/js.js`);
296
-
297
- assert.strictEqual(jsResponse.statusCode, 200);
298
- assert(!jsResponse.headers['content-encoding'], 'Should not be compressed');
299
-
300
- // Verify minification occurred (should be smaller than original)
301
- const originalSize = Buffer.from(testControl.js, 'utf8').length;
302
- const minifiedSize = jsResponse.body.length;
303
- assert(minifiedSize < originalSize, `JavaScript should be minified with ${level} level`);
304
-
305
- // Stop server
306
- await server.stop();
307
- }
308
- });
309
-
310
- it('should serve identical content with identity encoding when compression disabled', async function() {
311
- // Start server with compression disabled
312
- server = new Server();
313
- await server.serve({
314
- ctrl: testControl,
315
- port: serverPort,
316
- debug: false,
317
- bundler: {
231
+ assert(htmlResponse.headers['content-type'].includes('html'));
232
+
233
+ // Stop server
234
+ await stop_server(server);
235
+ server = null;
236
+ });
237
+
238
+ it('should handle different minification levels', async function() {
239
+ this.timeout(90000);
240
+ const minification_levels = ['conservative', 'normal', 'aggressive'];
241
+
242
+ server = await start_server({
243
+ ...base_serve_options,
244
+ debug: true,
245
+ bundler: {
246
+ compression: {
247
+ enabled: false
248
+ }
249
+ }
250
+ });
251
+
252
+ const baseline_response = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
253
+ 'Accept-Encoding': 'identity'
254
+ });
255
+ assert.strictEqual(baseline_response.statusCode, 200);
256
+ const baseline_size = baseline_response.body.length;
257
+
258
+ await stop_server(server);
259
+ server = null;
260
+
261
+ for (const level of minification_levels) {
262
+ // Start server with specific minification level
263
+ server = await start_server({
264
+ ...base_serve_options,
265
+ debug: false,
266
+ bundler: {
267
+ minify: {
268
+ enabled: true,
269
+ level: level
270
+ },
271
+ compression: {
272
+ enabled: false // Disable compression for size comparison
273
+ }
274
+ }
275
+ });
276
+
277
+ // Test JavaScript
278
+ const minified_response = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
279
+ 'Accept-Encoding': 'identity'
280
+ });
281
+
282
+ assert.strictEqual(minified_response.statusCode, 200);
283
+ assert(
284
+ minified_response.body.length <= baseline_size,
285
+ `JavaScript should be minified with ${level} level`
286
+ );
287
+
288
+ // Stop server
289
+ await stop_server(server);
290
+ server = null;
291
+ }
292
+ });
293
+
294
+ it('should serve identical content with identity encoding when compression disabled', async function() {
295
+ // Start server with compression disabled
296
+ server = await start_server({
297
+ ...base_serve_options,
298
+ debug: false,
299
+ bundler: {
318
300
  compression: {
319
301
  enabled: false
320
302
  }
321
303
  }
322
304
  });
323
305
 
324
- // Wait for server to be ready
325
- await new Promise(resolve => setTimeout(resolve, 2000));
326
-
327
- // Test with gzip request but compression disabled
328
- const jsResponse = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
306
+ // Test with gzip request but compression disabled
307
+ const jsResponse = await makeRequest(`http://localhost:${serverPort}/js/js.js`, {
329
308
  'Accept-Encoding': 'gzip'
330
309
  });
331
310
 
332
311
  assert.strictEqual(jsResponse.statusCode, 200);
333
- assert(!jsResponse.headers['content-encoding'], 'Should not have content-encoding when compression disabled');
334
-
335
- // Stop server
336
- await server.stop();
337
- });
338
-
339
- it('should handle multiple concurrent requests', async function() {
340
- // Start server
341
- server = new Server();
342
- await server.serve({
343
- ctrl: testControl,
344
- port: serverPort,
345
- debug: false,
346
- bundler: {
312
+ assert(!jsResponse.headers['content-encoding'], 'Should not have content-encoding when compression disabled');
313
+
314
+ // Stop server
315
+ await stop_server(server);
316
+ server = null;
317
+ });
318
+
319
+ it('should handle multiple concurrent requests', async function() {
320
+ // Start server
321
+ server = await start_server({
322
+ ...base_serve_options,
323
+ debug: false,
324
+ bundler: {
347
325
  compression: {
348
326
  enabled: true,
349
327
  algorithms: ['gzip']
@@ -351,11 +329,8 @@ describe('End-to-End Integration Tests', function() {
351
329
  }
352
330
  });
353
331
 
354
- // Wait for server to be ready
355
- await new Promise(resolve => setTimeout(resolve, 2000));
356
-
357
- // Make multiple concurrent requests
358
- const requests = [];
332
+ // Make multiple concurrent requests
333
+ const requests = [];
359
334
  for (let i = 0; i < 5; i++) {
360
335
  requests.push(makeRequest(`http://localhost:${serverPort}/js/js.js`, {
361
336
  'Accept-Encoding': 'gzip'
@@ -365,29 +340,25 @@ describe('End-to-End Integration Tests', function() {
365
340
  const responses = await Promise.all(requests);
366
341
 
367
342
  // All requests should succeed
368
- responses.forEach(response => {
369
- assert.strictEqual(response.statusCode, 200);
370
- assert.strictEqual(response.headers['content-encoding'], 'gzip');
371
- });
372
-
373
- // Stop server
374
- await server.stop();
375
- });
376
-
377
- it('should serve correct content types', async function() {
378
- // Start server
379
- server = new Server();
380
- await server.serve({
381
- ctrl: testControl,
382
- port: serverPort,
383
- debug: false
384
- });
385
-
386
- // Wait for server to be ready
387
- await new Promise(resolve => setTimeout(resolve, 2000));
388
-
389
- // Test different content types
390
- const tests = [
343
+ responses.forEach(response => {
344
+ assert.strictEqual(response.statusCode, 200);
345
+ assert.strictEqual(response.headers['content-encoding'], 'gzip');
346
+ });
347
+
348
+ // Stop server
349
+ await stop_server(server);
350
+ server = null;
351
+ });
352
+
353
+ it('should serve correct content types', async function() {
354
+ // Start server
355
+ server = await start_server({
356
+ ...base_serve_options,
357
+ debug: false
358
+ });
359
+
360
+ // Test different content types
361
+ const tests = [
391
362
  { path: '/', expectedType: 'html' },
392
363
  { path: '/js/js.js', expectedType: 'javascript' },
393
364
  { path: '/css/css.css', expectedType: 'css' }
@@ -397,62 +368,62 @@ describe('End-to-End Integration Tests', function() {
397
368
  const response = await makeRequest(`http://localhost:${serverPort}${test.path}`);
398
369
  assert.strictEqual(response.statusCode, 200);
399
370
  assert(response.headers['content-type'].includes(test.expectedType),
400
- `Should serve correct content type for ${test.path}`);
401
- }
402
-
403
- // Stop server
404
- await server.stop();
405
- });
406
- });
371
+ `Should serve correct content type for ${test.path}`);
372
+ }
373
+
374
+ // Stop server
375
+ await stop_server(server);
376
+ server = null;
377
+ });
378
+ });
407
379
 
408
380
  describe('Error Handling in End-to-End Scenarios', function() {
409
- it('should handle invalid configuration gracefully', async function() {
410
- // Try to start server with invalid configuration
411
- server = new Server();
412
-
413
- try {
414
- await server.serve({
415
- ctrl: testControl,
416
- port: serverPort,
417
- bundler: {
418
- compression: {
381
+ it('should handle invalid configuration gracefully', async function() {
382
+ // Try to start server with invalid configuration
383
+ try {
384
+ server = await start_server({
385
+ ...base_serve_options,
386
+ bundler: {
387
+ compression: {
419
388
  enabled: 'invalid' // Invalid boolean
420
389
  }
421
390
  }
422
- });
423
- assert.fail('Should have thrown error for invalid configuration');
424
- } catch (error) {
425
- assert(error, 'Should throw error for invalid configuration');
426
- }
427
- });
428
-
429
- it('should handle server port conflicts', async function() {
430
- // Start first server
431
- const server1 = new Server();
432
- await server1.serve({
433
- ctrl: testControl,
434
- port: serverPort,
435
- debug: false
436
- });
437
-
438
- // Try to start second server on same port
439
- const server2 = new Server();
440
- try {
441
- await server2.serve({
442
- ctrl: testControl,
443
- port: serverPort, // Same port
444
- debug: false
445
- });
446
- assert.fail('Should have failed to start server on occupied port');
447
- } catch (error) {
448
- assert(error, 'Should throw error for port conflict');
449
- }
450
-
451
- // Clean up
452
- await server1.stop();
453
- });
454
- });
455
- });
391
+ });
392
+ assert.fail('Should have thrown error for invalid configuration');
393
+ } catch (error) {
394
+ assert(error, 'Should throw error for invalid configuration');
395
+ } finally {
396
+ await stop_server(server);
397
+ server = null;
398
+ }
399
+ });
400
+
401
+ it('should handle server port conflicts', async function() {
402
+ // Start first server
403
+ const server1 = await start_server({
404
+ ...base_serve_options,
405
+ debug: false,
406
+ host: '127.0.0.1'
407
+ });
408
+
409
+ // Try to start second server on same port
410
+ try {
411
+ await start_server({
412
+ ...base_serve_options,
413
+ port: serverPort, // Same port
414
+ debug: false,
415
+ host: '127.0.0.1'
416
+ });
417
+ assert.fail('Should have failed to start server on occupied port');
418
+ } catch (error) {
419
+ assert(error, 'Should throw error for port conflict');
420
+ }
421
+
422
+ // Clean up
423
+ await stop_server(server1);
424
+ });
425
+ });
426
+ });
456
427
 
457
428
  // Helper function to make HTTP requests
458
429
  function makeRequest(url, headers = {}) {
@@ -493,4 +464,4 @@ function makeRequest(url, headers = {}) {
493
464
 
494
465
  req.end();
495
466
  });
496
- }
467
+ }