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.
- package/docs/comprehensive-documentation.md +25 -6
- package/docs/configuration-reference.md +46 -11
- package/docs/controls-development.md +54 -26
- package/docs/jsgui3-html-improvement-ideas.md +162 -0
- package/docs/jsgui3-html-improvement-ideas.svg +151 -0
- package/docs/troubleshooting.md +9 -8
- package/examples/controls/14d) window, canvas globe/EarthGlobeRenderer.js +19 -14
- package/examples/controls/14d) window, canvas globe/pipeline/TransformStage.js +5 -5
- package/examples/jsgui3-html/01) mvvm-counter/client.js +648 -0
- package/examples/jsgui3-html/01) mvvm-counter/server.js +21 -0
- package/examples/jsgui3-html/02) date-transform/client.js +764 -0
- package/examples/jsgui3-html/02) date-transform/server.js +21 -0
- package/examples/jsgui3-html/03) form-validation/client.js +1045 -0
- package/examples/jsgui3-html/03) form-validation/server.js +21 -0
- package/examples/jsgui3-html/04) data-grid/client.js +738 -0
- package/examples/jsgui3-html/04) data-grid/server.js +21 -0
- package/examples/jsgui3-html/05) master-detail/client.js +649 -0
- package/examples/jsgui3-html/05) master-detail/server.js +21 -0
- package/examples/jsgui3-html/06) theming/client.js +514 -0
- package/examples/jsgui3-html/06) theming/server.js +21 -0
- package/examples/jsgui3-html/07) mixins/client.js +465 -0
- package/examples/jsgui3-html/07) mixins/server.js +21 -0
- package/examples/jsgui3-html/08) router/client.js +372 -0
- package/examples/jsgui3-html/08) router/server.js +21 -0
- package/examples/jsgui3-html/09) resource-transform/client.js +692 -0
- package/examples/jsgui3-html/09) resource-transform/server.js +21 -0
- package/examples/jsgui3-html/10) binding-debugger/client.js +810 -0
- package/examples/jsgui3-html/10) binding-debugger/server.js +21 -0
- package/examples/jsgui3-html/README.md +48 -0
- package/http/responders/static/Static_Route_HTTP_Responder.js +25 -20
- package/lab/README.md +19 -0
- package/lab/experiments/window_examples_dom_audit.js +241 -0
- package/lab/results/window_examples_dom_audit.json +131 -0
- package/lab/results/window_examples_dom_audit.md +46 -0
- package/package.json +8 -3
- package/publishers/http-webpageorsite-publisher.js +8 -4
- package/resources/processors/bundlers/css-bundler.js +28 -173
- package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +32 -20
- package/resources/processors/bundlers/style-bundler.js +288 -0
- package/resources/processors/compilers/SASS_Compiler.js +88 -0
- package/resources/processors/extractors/js/css_and_js/AST_Node/CSS_And_JS_From_JS_String_Using_AST_Node_Extractor.js +64 -68
- package/resources/website-css-resource.js +24 -20
- package/resources/website-javascript-resource-processor.js +17 -57
- package/resources/website-javascript-resource.js +17 -57
- package/serve-factory.js +38 -24
- package/server.js +116 -92
- package/tests/README.md +38 -3
- package/tests/bundlers.test.js +41 -32
- package/tests/content-analysis.test.js +19 -18
- package/tests/end-to-end.test.js +336 -365
- package/tests/error-handling.test.js +13 -11
- package/tests/examples-controls.e2e.test.js +13 -1
- package/tests/fixtures/end-to-end-client.js +54 -0
- package/tests/fixtures/jsgui3-html/binding_debugger_expectations.json +15 -0
- package/tests/fixtures/jsgui3-html/counter_expectations.json +31 -0
- package/tests/fixtures/jsgui3-html/data_grid_expectations.json +26 -0
- package/tests/fixtures/jsgui3-html/date_transform_expectations.json +26 -0
- package/tests/fixtures/jsgui3-html/form_validation_expectations.json +27 -0
- package/tests/fixtures/jsgui3-html/master_detail_expectations.json +15 -0
- package/tests/fixtures/jsgui3-html/mixins_expectations.json +10 -0
- package/tests/fixtures/jsgui3-html/resource_transform_expectations.json +11 -0
- package/tests/fixtures/jsgui3-html/router_expectations.json +10 -0
- package/tests/fixtures/jsgui3-html/theming_expectations.json +10 -0
- package/tests/jsgui3-html-examples.puppeteer.test.js +537 -0
- package/tests/sass-controls.e2e.test.js +327 -0
- package/tests/test-runner.js +4 -1
- package/tests/window-examples.puppeteer.test.js +455 -0
package/tests/end-to-end.test.js
CHANGED
|
@@ -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
|
|
5
|
-
const path = require('path');
|
|
4
|
+
const path = require('path');
|
|
6
5
|
|
|
7
|
-
// Import server and related classes
|
|
8
|
-
const
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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 =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
assert(
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
//
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
assert(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
//
|
|
255
|
-
|
|
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
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
//
|
|
325
|
-
|
|
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
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
await
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
//
|
|
355
|
-
|
|
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
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
await
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
assert(
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
+
}
|