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/server.js
CHANGED
|
@@ -45,9 +45,12 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
if (spec.debug !== undefined) {
|
|
49
|
-
this.debug = spec.debug;
|
|
50
|
-
}
|
|
48
|
+
if (spec.debug !== undefined) {
|
|
49
|
+
this.debug = spec.debug;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const style_config = spec.style;
|
|
53
|
+
const bundler_config = spec.bundler;
|
|
51
54
|
|
|
52
55
|
// or src_path_client_js as well...
|
|
53
56
|
|
|
@@ -91,16 +94,18 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
91
94
|
|
|
92
95
|
const wp_app = new Webpage({content: Ctrl});
|
|
93
96
|
|
|
94
|
-
const opts_wp_publisher = {
|
|
95
|
-
'webpage': wp_app
|
|
96
|
-
|
|
97
|
-
};
|
|
97
|
+
const opts_wp_publisher = {
|
|
98
|
+
'webpage': wp_app
|
|
99
|
+
|
|
100
|
+
};
|
|
101
|
+
if (bundler_config !== undefined) opts_wp_publisher.bundler = bundler_config;
|
|
102
|
+
|
|
103
|
+
if (this.debug) {
|
|
104
|
+
opts_wp_publisher.debug = this.debug;
|
|
105
|
+
}
|
|
98
106
|
|
|
99
|
-
if (
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (disk_path_client_js) opts_wp_publisher.src_path_client_js = disk_path_client_js;
|
|
107
|
+
if (disk_path_client_js) opts_wp_publisher.src_path_client_js = disk_path_client_js;
|
|
108
|
+
if (style_config !== undefined) opts_wp_publisher.style = style_config;
|
|
104
109
|
|
|
105
110
|
|
|
106
111
|
|
|
@@ -160,12 +165,18 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
160
165
|
const ws_app = this.app = this.website = new Website(opts_website);
|
|
161
166
|
// Be able to treat Webpage as an app?
|
|
162
167
|
|
|
163
|
-
const opts_ws_publisher = {
|
|
164
|
-
'website': ws_app
|
|
165
|
-
};
|
|
166
|
-
if (disk_path_client_js) {
|
|
167
|
-
opts_ws_publisher.disk_path_client_js = disk_path_client_js;
|
|
168
|
-
}
|
|
168
|
+
const opts_ws_publisher = {
|
|
169
|
+
'website': ws_app
|
|
170
|
+
};
|
|
171
|
+
if (disk_path_client_js) {
|
|
172
|
+
opts_ws_publisher.disk_path_client_js = disk_path_client_js;
|
|
173
|
+
}
|
|
174
|
+
if (bundler_config !== undefined) {
|
|
175
|
+
opts_ws_publisher.bundler = bundler_config;
|
|
176
|
+
}
|
|
177
|
+
if (style_config !== undefined) {
|
|
178
|
+
opts_ws_publisher.style = style_config;
|
|
179
|
+
}
|
|
169
180
|
const ws_publisher = new HTTP_Website_Publisher(opts_ws_publisher);
|
|
170
181
|
this._ws_publisher = ws_publisher;
|
|
171
182
|
ws_publisher.on('ready', () => {
|
|
@@ -234,16 +245,33 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
234
245
|
if (this.allowed_addresses && this.allowed_addresses.length) {
|
|
235
246
|
arr_ipv4_addresses = arr_ipv4_addresses.filter(a => this.allowed_addresses.indexOf(a) > -1);
|
|
236
247
|
}
|
|
237
|
-
arr_ipv4_addresses = [...new Set(arr_ipv4_addresses)];
|
|
238
|
-
console.log('IPv4 addresses to bind:', arr_ipv4_addresses);
|
|
239
|
-
let num_to_start = arr_ipv4_addresses.length;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
248
|
+
arr_ipv4_addresses = [...new Set(arr_ipv4_addresses)];
|
|
249
|
+
console.log('IPv4 addresses to bind:', arr_ipv4_addresses);
|
|
250
|
+
let num_to_start = arr_ipv4_addresses.length;
|
|
251
|
+
let started_count = 0;
|
|
252
|
+
let last_error = null;
|
|
253
|
+
let ready_raised = false;
|
|
254
|
+
if (num_to_start === 0) {
|
|
255
|
+
callback('No allowed network interfaces found.');
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const finalize_start = (err) => {
|
|
259
|
+
if (num_to_start !== 0) return;
|
|
260
|
+
if (started_count > 0) {
|
|
261
|
+
if (!ready_raised) {
|
|
262
|
+
console.log('Server ready');
|
|
263
|
+
this.raise('ready');
|
|
264
|
+
ready_raised = true;
|
|
265
|
+
}
|
|
266
|
+
if (callback) callback(null, true);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
const final_error = err || last_error || new Error('No servers started.');
|
|
270
|
+
if (callback) callback(final_error);
|
|
271
|
+
};
|
|
272
|
+
const respond_not_found = (res) => {
|
|
273
|
+
if (!res.headersSent) {
|
|
274
|
+
const body = 'Not Found';
|
|
247
275
|
res.statusCode = 404;
|
|
248
276
|
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
249
277
|
res.setHeader('Content-Length', Buffer.byteLength(body));
|
|
@@ -302,70 +330,66 @@ class JSGUI_Single_Process_Server extends Evented_Class {
|
|
|
302
330
|
process_request(req, res);
|
|
303
331
|
});
|
|
304
332
|
this.http_servers.push(https_server);
|
|
305
|
-
https_server.on('error', (err) => {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
https_server.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
http_server.
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
num_to_start--;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
});
|
|
333
|
+
https_server.on('error', (err) => {
|
|
334
|
+
last_error = err;
|
|
335
|
+
if (err.code === 'EACCES') {
|
|
336
|
+
console.error('Permission denied:', err.message);
|
|
337
|
+
} else if (err.code === 'EADDRINUSE') {
|
|
338
|
+
console.error(`Address ${ipv4_address}:${port} already in use; skipping.`);
|
|
339
|
+
} else {
|
|
340
|
+
console.error('https_server error:', err);
|
|
341
|
+
}
|
|
342
|
+
num_to_start--;
|
|
343
|
+
finalize_start(err);
|
|
344
|
+
});
|
|
345
|
+
https_server.timeout = 10800000;
|
|
346
|
+
https_server.listen(port, ipv4_address, () => {
|
|
347
|
+
console.log('* Server running at https://' + ipv4_address + ':' + port + '/');
|
|
348
|
+
started_count++;
|
|
349
|
+
num_to_start--;
|
|
350
|
+
finalize_start(null);
|
|
351
|
+
});
|
|
352
|
+
} catch (err) {
|
|
353
|
+
console.log('https_server err', err);
|
|
354
|
+
num_to_start--;
|
|
355
|
+
finalize_start(err);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
} else {
|
|
359
|
+
each(arr_ipv4_addresses, (ipv4_address) => {
|
|
360
|
+
try {
|
|
361
|
+
var http_server = http.createServer(function(req, res) {
|
|
362
|
+
process_request(req, res);
|
|
363
|
+
});
|
|
364
|
+
this.http_servers.push(http_server);
|
|
365
|
+
http_server.on('error', (err) => {
|
|
366
|
+
last_error = err;
|
|
367
|
+
if (err.code === 'EACCES') {
|
|
368
|
+
console.error('Permission denied:', err.message);
|
|
369
|
+
} else if (err.code === 'EADDRINUSE') {
|
|
370
|
+
console.error(`Address ${ipv4_address}:${port} already in use; skipping.`);
|
|
371
|
+
} else {
|
|
372
|
+
console.error('http_server error:', err);
|
|
373
|
+
}
|
|
374
|
+
num_to_start--;
|
|
375
|
+
finalize_start(err);
|
|
376
|
+
});
|
|
377
|
+
http_server.timeout = 10800000;
|
|
378
|
+
http_server.listen(port, ipv4_address, () => {
|
|
379
|
+
console.log('* Server running at http://' + ipv4_address + ':' + port + '/');
|
|
380
|
+
started_count++;
|
|
381
|
+
num_to_start--;
|
|
382
|
+
finalize_start(null);
|
|
383
|
+
});
|
|
384
|
+
} catch (err) {
|
|
385
|
+
console.log('http_server err', err);
|
|
386
|
+
num_to_start--;
|
|
387
|
+
finalize_start(err);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
});
|
|
369
393
|
}
|
|
370
394
|
});
|
|
371
395
|
}
|
package/tests/README.md
CHANGED
|
@@ -26,6 +26,9 @@ tests/
|
|
|
26
26
|
├── performance.test.js # Performance benchmarks
|
|
27
27
|
├── error-handling.test.js # Error handling and edge cases
|
|
28
28
|
├── examples-controls.e2e.test.js # Example apps regression (controls)
|
|
29
|
+
├── sass-controls.e2e.test.js # Sass/CSS controls E2E coverage
|
|
30
|
+
├── jsgui3-html-examples.puppeteer.test.js # Puppeteer interaction tests (jsgui3-html examples)
|
|
31
|
+
├── window-examples.puppeteer.test.js # Puppeteer interaction tests (window examples)
|
|
29
32
|
├── test-runner.js # Custom test runner with reporting
|
|
30
33
|
└── README.md # This file
|
|
31
34
|
```
|
|
@@ -51,6 +54,11 @@ npx mocha tests/bundlers.test.js
|
|
|
51
54
|
npm run test:examples:controls
|
|
52
55
|
```
|
|
53
56
|
|
|
57
|
+
### Run Puppeteer Window Example Tests
|
|
58
|
+
```bash
|
|
59
|
+
npm run test:puppeteer:windows
|
|
60
|
+
```
|
|
61
|
+
|
|
54
62
|
### Run Tests with Options
|
|
55
63
|
```bash
|
|
56
64
|
# Debug mode (enables sourcemaps)
|
|
@@ -132,6 +140,32 @@ Regression coverage for a representative set of `examples/controls/*` apps:
|
|
|
132
140
|
- Verifies `/`, `/js/js.js`, and `/css/css.css` routes
|
|
133
141
|
- Ensures HTML rendering works without `Accept-Encoding`
|
|
134
142
|
|
|
143
|
+
### 8. Puppeteer Window Example Tests (`window-examples.puppeteer.test.js`)
|
|
144
|
+
|
|
145
|
+
Browser-level interaction checks for selected window examples:
|
|
146
|
+
|
|
147
|
+
- Minimize/restore window state (class toggling)
|
|
148
|
+
- Tabbed panel tab switching visibility
|
|
149
|
+
- Checkbox label toggling checked state
|
|
150
|
+
- Date picker month header rendering
|
|
151
|
+
|
|
152
|
+
### 9. Sass/CSS Controls E2E Tests (`sass-controls.e2e.test.js`)
|
|
153
|
+
|
|
154
|
+
Server-level integration tests for controls that define `.scss` or `.sass` styles:
|
|
155
|
+
|
|
156
|
+
- Verifies CSS + SCSS mixing order in bundled CSS output
|
|
157
|
+
- Confirms indented Sass compiles and is removed from JS bundles
|
|
158
|
+
- Checks inline CSS sourcemaps include original Sass/SCSS sources
|
|
159
|
+
- Ensures mixed CSS + Sass output preserves order without emitting inaccurate sourcemaps
|
|
160
|
+
|
|
161
|
+
Note: These tests are skipped if the `sass` dependency is not installed.
|
|
162
|
+
|
|
163
|
+
### 10. JSGUI3-HTML Example Puppeteer Tests (`jsgui3-html-examples.puppeteer.test.js`)
|
|
164
|
+
|
|
165
|
+
Browser-level interaction checks for jsgui3-html examples:
|
|
166
|
+
|
|
167
|
+
- MVVM counter interactions, bindings, and validation state
|
|
168
|
+
|
|
135
169
|
## Configuration Examples
|
|
136
170
|
|
|
137
171
|
### Basic Minification
|
|
@@ -205,12 +239,13 @@ Total Test Suites: 8
|
|
|
205
239
|
🎉 All tests passed! The minification, compression, and sourcemap features are working correctly.
|
|
206
240
|
```
|
|
207
241
|
|
|
208
|
-
## Environment Requirements
|
|
242
|
+
## Environment Requirements
|
|
209
243
|
|
|
210
244
|
- Node.js >= 15.0.0
|
|
211
245
|
- Mocha test framework (included in package.json)
|
|
212
|
-
- ESBuild (included in package.json)
|
|
213
|
-
- zlib (built-in Node.js module)
|
|
246
|
+
- ESBuild (included in package.json)
|
|
247
|
+
- zlib (built-in Node.js module)
|
|
248
|
+
- Puppeteer (dev dependency; set `PUPPETEER_EXECUTABLE_PATH` to a local Chrome/Chromium if downloads are disabled)
|
|
214
249
|
|
|
215
250
|
## Troubleshooting
|
|
216
251
|
|
package/tests/bundlers.test.js
CHANGED
|
@@ -16,29 +16,34 @@ this.timeout(30000); // Increase timeout for bundling operations
|
|
|
16
16
|
|
|
17
17
|
beforeEach(async function() {
|
|
18
18
|
// Create a temporary test JS file
|
|
19
|
-
testJsContent = `
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
testJsContent = `
|
|
20
|
+
class Test_Class {
|
|
21
|
+
constructor() {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Test_Class.css = \`
|
|
25
|
+
.test-class {
|
|
26
|
+
color: red;
|
|
27
|
+
font-size: 14px;
|
|
28
|
+
}
|
|
29
|
+
\`;
|
|
30
|
+
|
|
31
|
+
Test_Class.scss = \`
|
|
32
|
+
$accent-color: #33aacc;
|
|
33
|
+
.sass-class {
|
|
34
|
+
color: $accent-color;
|
|
35
|
+
}
|
|
36
|
+
\`;
|
|
37
|
+
|
|
38
|
+
// Test function
|
|
39
|
+
function testFunction() {
|
|
40
|
+
console.log('Hello from test function');
|
|
41
|
+
return 'test result';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Export for testing
|
|
45
|
+
module.exports = { testFunction, Test_Class };
|
|
46
|
+
`;
|
|
42
47
|
|
|
43
48
|
testJsFile = path.join(__dirname, 'temp_test.js');
|
|
44
49
|
await fs.writeFile(testJsFile, testJsContent);
|
|
@@ -242,14 +247,18 @@ this.timeout(30000); // Increase timeout for bundling operations
|
|
|
242
247
|
assert.strictEqual(jsItem.extension, 'js');
|
|
243
248
|
assert.strictEqual(cssItem.extension, 'css');
|
|
244
249
|
|
|
245
|
-
// Verify CSS was extracted
|
|
246
|
-
assert(cssItem.text.includes('.test-class'), 'CSS should contain the test class');
|
|
247
|
-
assert(cssItem.text.includes('color: red'), 'CSS should contain the color property');
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
250
|
+
// Verify CSS was extracted
|
|
251
|
+
assert(cssItem.text.includes('.test-class'), 'CSS should contain the test class');
|
|
252
|
+
assert(cssItem.text.includes('color: red'), 'CSS should contain the color property');
|
|
253
|
+
assert(cssItem.text.includes('.sass-class'), 'CSS should contain the SCSS class');
|
|
254
|
+
assert(cssItem.text.includes('#33aacc'), 'CSS should contain compiled SCSS color value');
|
|
255
|
+
|
|
256
|
+
// Verify JS no longer contains CSS
|
|
257
|
+
assert(!jsItem.text.includes('.test-class'), 'JS should not contain CSS after extraction');
|
|
258
|
+
assert(!jsItem.text.includes('.sass-class'), 'JS should not contain SCSS after extraction');
|
|
259
|
+
assert(!jsItem.text.includes('$accent-color'), 'JS should not contain SCSS variables');
|
|
260
|
+
assert(jsItem.text.includes('testFunction'), 'JS should still contain the test function');
|
|
261
|
+
});
|
|
253
262
|
|
|
254
263
|
it('should handle debug mode with sourcemaps', async function() {
|
|
255
264
|
const bundler = new Advanced_JS_Bundler_Using_ESBuild({
|
|
@@ -326,4 +335,4 @@ this.timeout(30000); // Increase timeout for bundling operations
|
|
|
326
335
|
}
|
|
327
336
|
});
|
|
328
337
|
});
|
|
329
|
-
});
|
|
338
|
+
});
|
|
@@ -46,23 +46,24 @@ describe('Content Analysis Tests', function() {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
49
|
+
TestClass.css = \`
|
|
50
|
+
.test-class {
|
|
51
|
+
background-color: #ffffff;
|
|
52
|
+
border: 1px solid #cccccc;
|
|
53
|
+
padding: 10px;
|
|
54
|
+
margin: 5px;
|
|
55
|
+
}
|
|
56
|
+
.test-class:hover {
|
|
57
|
+
background-color: #f0f0f0;
|
|
58
|
+
}
|
|
59
|
+
\`;
|
|
60
|
+
|
|
61
|
+
TestClass.scss = \`
|
|
62
|
+
$accent-color: #33aacc;
|
|
63
|
+
.sass-class {
|
|
64
|
+
color: $accent-color;
|
|
65
|
+
}
|
|
66
|
+
\`;
|
|
66
67
|
|
|
67
68
|
// Export
|
|
68
69
|
module.exports = { testFunction, testObject, TestClass };
|
|
@@ -638,4 +639,4 @@ describe('Content Analysis Tests', function() {
|
|
|
638
639
|
assert(brotliSize < originalSize * 0.5, 'Brotli should achieve >50% compression on repetitive content');
|
|
639
640
|
});
|
|
640
641
|
});
|
|
641
|
-
});
|
|
642
|
+
});
|