jsgui3-server 0.0.142 → 0.0.144
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/publishers-guide.md +48 -32
- package/docs/troubleshooting.md +9 -8
- package/examples/controls/15) window, observable SSE/README.md +29 -17
- 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 +7 -2
- package/publishers/http-observable-publisher.js +318 -125
- package/publishers/http-webpageorsite-publisher.js +6 -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 +28 -21
- package/server.js +13 -7
- package/tests/README.md +31 -3
- package/tests/bundlers.test.js +41 -32
- package/tests/content-analysis.test.js +19 -18
- package/tests/error-handling.test.js +13 -11
- package/tests/observable-sse.test.js +5 -5
- package/tests/sass-controls.e2e.test.js +327 -0
- package/tests/test-runner.js +3 -1
- package/tests/window-examples.puppeteer.test.js +239 -0
|
@@ -284,11 +284,30 @@ body {
|
|
|
284
284
|
}
|
|
285
285
|
`;
|
|
286
286
|
|
|
287
|
-
controls.MyControl = MyControl;
|
|
288
|
-
module.exports = jsgui;
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
|
|
287
|
+
controls.MyControl = MyControl;
|
|
288
|
+
module.exports = jsgui;
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
SCSS/SASS: You can also set `MyControl.scss` or `MyControl.sass` using template literals. These are compiled to CSS during bundling and removed from the JS output, just like `.css`. CSS and SCSS blocks can be mixed in a control; the bundler preserves their order during compilation. If you mix indented `.sass` with `.scss`/`.css`, each block is compiled independently to preserve order. Inline CSS sourcemaps are emitted only when a single compilation pass is used; mixed syntax skips inline maps to keep them accurate.
|
|
292
|
+
|
|
293
|
+
To enable inline CSS sourcemaps for Sass/SCSS outputs, pass a `style` configuration:
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
Server.serve({
|
|
297
|
+
ctrl: MyControl,
|
|
298
|
+
src_path_client_js: require.resolve('./client.js'),
|
|
299
|
+
debug: true,
|
|
300
|
+
style: {
|
|
301
|
+
sourcemaps: {
|
|
302
|
+
enabled: true,
|
|
303
|
+
inline: true,
|
|
304
|
+
include_sources: true
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
2. Create server:
|
|
292
311
|
|
|
293
312
|
```javascript
|
|
294
313
|
// server.js
|
|
@@ -1400,4 +1419,4 @@ MIT License - see LICENSE file for details.
|
|
|
1400
1419
|
---
|
|
1401
1420
|
|
|
1402
1421
|
This documentation provides a comprehensive overview of JSGUI3 Server. For more detailed information about specific components, see the individual files in the `docs/` directory and the examples in `examples/`.</content>
|
|
1403
|
-
<parameter name="filePath">c:\\Users\\james\\Documents\\repos\\jsgui3-server\\docs\\comprehensive-documentation.md
|
|
1422
|
+
<parameter name="filePath">c:\\Users\\james\\Documents\\repos\\jsgui3-server\\docs\\comprehensive-documentation.md
|
|
@@ -198,16 +198,51 @@ Configuration values are resolved in this order (later sources override earlier
|
|
|
198
198
|
});
|
|
199
199
|
```
|
|
200
200
|
|
|
201
|
-
#### `config`
|
|
202
|
-
- **Type:** `string`
|
|
203
|
-
- **Description:** Path to configuration file
|
|
204
|
-
- **Default:** `'jsgui.config.js'` (if exists)
|
|
205
|
-
- **Example:**
|
|
206
|
-
```javascript
|
|
207
|
-
Server.serve({
|
|
208
|
-
config: './my-config.js'
|
|
209
|
-
});
|
|
210
|
-
```
|
|
201
|
+
#### `config`
|
|
202
|
+
- **Type:** `string`
|
|
203
|
+
- **Description:** Path to configuration file
|
|
204
|
+
- **Default:** `'jsgui.config.js'` (if exists)
|
|
205
|
+
- **Example:**
|
|
206
|
+
```javascript
|
|
207
|
+
Server.serve({
|
|
208
|
+
config: './my-config.js'
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### `style`
|
|
213
|
+
- **Type:** `object`
|
|
214
|
+
- **Description:** Style pipeline options for CSS/SCSS/Sass extraction and compilation.
|
|
215
|
+
- **Default:** `{}` (inherits debug behavior for sourcemaps)
|
|
216
|
+
- **Example:**
|
|
217
|
+
```javascript
|
|
218
|
+
Server.serve({
|
|
219
|
+
ctrl: MyControl,
|
|
220
|
+
debug: true,
|
|
221
|
+
style: {
|
|
222
|
+
sourcemaps: {
|
|
223
|
+
enabled: true,
|
|
224
|
+
inline: true,
|
|
225
|
+
include_sources: true
|
|
226
|
+
},
|
|
227
|
+
load_paths: ['styles', 'controls'],
|
|
228
|
+
output_style: 'expanded',
|
|
229
|
+
quiet_dependencies: true,
|
|
230
|
+
compile_css_with_sass: true
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Style options:**
|
|
236
|
+
- `sourcemaps.enabled` (`boolean`): Enable CSS sourcemaps. Defaults to `true` when `debug` is enabled.
|
|
237
|
+
- `sourcemaps.inline` (`boolean`): Inline sourcemaps into compiled CSS (default `true`).
|
|
238
|
+
- `sourcemaps.include_sources` (`boolean`): Embed sources content in the sourcemap (default `true`).
|
|
239
|
+
- `load_paths` (`string[]`): Sass load paths for `@use`/`@import`.
|
|
240
|
+
- `output_style` (`string`): Sass output style (e.g., `expanded`, `compressed`).
|
|
241
|
+
- `quiet_dependencies` (`boolean`): Suppress dependency warnings during Sass compilation.
|
|
242
|
+
- `compile_css_with_sass` (`boolean`): Compile `.css` blocks through Sass when mixing with SCSS (default `true`).
|
|
243
|
+
- `scss_sources` / `sass_sources` (`string[]`): Extra Sass/SCSS sources appended during compilation.
|
|
244
|
+
|
|
245
|
+
Inline CSS sourcemaps are emitted only when a single compilation pass is possible. Mixed `.sass` plus `.scss`/`.css` inputs skip inline maps to avoid inaccurate mappings.
|
|
211
246
|
|
|
212
247
|
## Environment Variables
|
|
213
248
|
|
|
@@ -805,4 +840,4 @@ Solution: port: 3000 instead of port: "3000"
|
|
|
805
840
|
|
|
806
841
|
---
|
|
807
842
|
|
|
808
|
-
This configuration reference provides comprehensive coverage of all JSGUI3 Server configuration options. Remember that most options have sensible defaults, so you only need to specify what differs from the defaults for your use case.
|
|
843
|
+
This configuration reference provides comprehensive coverage of all JSGUI3 Server configuration options. Remember that most options have sensible defaults, so you only need to specify what differs from the defaults for your use case.
|
|
@@ -74,23 +74,44 @@ class My_Custom_Control extends Active_HTML_Document {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
// Static CSS definition
|
|
77
|
-
My_Custom_Control.css = `
|
|
78
|
-
* { margin: 0; padding: 0; }
|
|
79
|
-
body {
|
|
80
|
-
overflow-x: hidden;
|
|
77
|
+
My_Custom_Control.css = `
|
|
78
|
+
* { margin: 0; padding: 0; }
|
|
79
|
+
body {
|
|
80
|
+
overflow-x: hidden;
|
|
81
81
|
overflow-y: hidden;
|
|
82
82
|
background-color: #E0E0E0;
|
|
83
83
|
}
|
|
84
84
|
.my-custom-control {
|
|
85
85
|
padding: 20px;
|
|
86
86
|
background: #f0f0f0;
|
|
87
|
-
}
|
|
88
|
-
`;
|
|
89
|
-
|
|
90
|
-
// Register control globally
|
|
91
|
-
controls.My_Custom_Control = My_Custom_Control;
|
|
92
|
-
module.exports = jsgui;
|
|
93
|
-
```
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
// Register control globally
|
|
91
|
+
controls.My_Custom_Control = My_Custom_Control;
|
|
92
|
+
module.exports = jsgui;
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Note: You can also define `My_Custom_Control.scss` or `My_Custom_Control.sass` using template literals. These are compiled to CSS during bundling and removed from the JS output, just like `.css`. CSS and SCSS blocks can be mixed in a control; the bundler preserves their order during compilation. If you mix indented `.sass` with `.scss`/`.css`, each block is compiled independently to preserve order. Inline CSS sourcemaps are emitted only when a single compilation pass is used; mixed syntax skips inline maps to keep them accurate.
|
|
96
|
+
|
|
97
|
+
To enable inline CSS sourcemaps for Sass/SCSS outputs, pass a `style` configuration when serving:
|
|
98
|
+
|
|
99
|
+
```javascript
|
|
100
|
+
Server.serve({
|
|
101
|
+
ctrl: My_Custom_Control,
|
|
102
|
+
src_path_client_js: require.resolve('./client.js'),
|
|
103
|
+
debug: true,
|
|
104
|
+
style: {
|
|
105
|
+
sourcemaps: {
|
|
106
|
+
enabled: true,
|
|
107
|
+
inline: true,
|
|
108
|
+
include_sources: true
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
E2E coverage for Sass/CSS controls (including sourcemaps) lives in `tests/sass-controls.e2e.test.js`.
|
|
94
115
|
|
|
95
116
|
#### Control (Base)
|
|
96
117
|
|
|
@@ -114,20 +135,27 @@ module.exports = jsgui;
|
|
|
114
135
|
- Child control containment
|
|
115
136
|
- Positioning and sizing
|
|
116
137
|
|
|
117
|
-
```javascript
|
|
118
|
-
const window = new controls.Window({
|
|
119
|
-
context,
|
|
120
|
-
title: 'My Window',
|
|
121
|
-
pos: [100, 100], // [x, y] position
|
|
122
|
-
size: [400, 300] // [width, height]
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Add content to window
|
|
126
|
-
window.inner.add(childControl);
|
|
127
|
-
this.body.add(window);
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
```javascript
|
|
139
|
+
const window = new controls.Window({
|
|
140
|
+
context,
|
|
141
|
+
title: 'My Window',
|
|
142
|
+
pos: [100, 100], // [x, y] position
|
|
143
|
+
size: [400, 300] // [width, height]
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Add content to window
|
|
147
|
+
window.inner.add(childControl);
|
|
148
|
+
this.body.add(window);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Lab results (from `lab/results/window_examples_dom_audit.md`, generated 2025-12-19):
|
|
152
|
+
|
|
153
|
+
- Window example renders one window, a title bar, and three control buttons.
|
|
154
|
+
- Tabbed panel example renders two tabs with a default checked input.
|
|
155
|
+
- Checkbox example renders one checkbox input with the expected label.
|
|
156
|
+
- Date picker example renders a native date input on the server.
|
|
157
|
+
|
|
158
|
+
#### Panel
|
|
131
159
|
|
|
132
160
|
**Purpose:** Basic container for grouping controls.
|
|
133
161
|
|
|
@@ -983,4 +1011,4 @@ Use browser developer tools to:
|
|
|
983
1011
|
|
|
984
1012
|
---
|
|
985
1013
|
|
|
986
|
-
This guide provides the foundation for developing controls in JSGUI3. For specific control implementations, refer to the examples in the `examples/` directory and the base classes in `controls/`.
|
|
1014
|
+
This guide provides the foundation for developing controls in JSGUI3. For specific control implementations, refer to the examples in the `examples/` directory and the base classes in `controls/`.
|
package/docs/publishers-guide.md
CHANGED
|
@@ -104,16 +104,18 @@ const publisher = new HTTP_Function_Publisher({
|
|
|
104
104
|
|
|
105
105
|
**Purpose:** Streams observable data to clients using Server-Sent Events (SSE).
|
|
106
106
|
|
|
107
|
-
**Key Features:**
|
|
108
|
-
- Real-time streaming of observable events
|
|
109
|
-
- SSE protocol support (`text/event-stream`)
|
|
110
|
-
- Chunked transfer encoding for long-running connections
|
|
111
|
-
- Integration with `fnl` observables
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
**Key Features:**
|
|
108
|
+
- Real-time streaming of observable events
|
|
109
|
+
- SSE protocol support (`text/event-stream`)
|
|
110
|
+
- Chunked transfer encoding for long-running connections
|
|
111
|
+
- Integration with `fnl` observables
|
|
112
|
+
- Connection cleanup on client disconnect
|
|
113
|
+
- Optional `pause()`, `resume()`, `stop()` controls
|
|
114
|
+
|
|
115
|
+
**Usage:**
|
|
116
|
+
```javascript
|
|
117
|
+
const { observable } = require('fnl');
|
|
118
|
+
const Observable_Publisher = require('jsgui3-server/publishers/http-observable-publisher');
|
|
117
119
|
|
|
118
120
|
// Create a hot observable that emits continuously
|
|
119
121
|
let tick_count = 0;
|
|
@@ -136,13 +138,28 @@ const publisher = new Observable_Publisher({
|
|
|
136
138
|
obs: tick_stream
|
|
137
139
|
});
|
|
138
140
|
|
|
139
|
-
// Register with server router
|
|
140
|
-
server.server_router.set_route('/api/stream', publisher, publisher.handle_http);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
141
|
+
// Register with server router
|
|
142
|
+
server.server_router.set_route('/api/stream', publisher, publisher.handle_http);
|
|
143
|
+
|
|
144
|
+
// Optional: control the stream from server-side code
|
|
145
|
+
publisher.pause();
|
|
146
|
+
publisher.resume();
|
|
147
|
+
publisher.stop();
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Optional Control (HTTP):**
|
|
151
|
+
```javascript
|
|
152
|
+
// From browser or any HTTP client:
|
|
153
|
+
await fetch('/api/stream', {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: { 'Content-Type': 'application/json' },
|
|
156
|
+
body: JSON.stringify({ action: 'pause' }) // 'resume' | 'stop' | 'status'
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Client-Side Consumption:**
|
|
161
|
+
```javascript
|
|
162
|
+
// In browser, use EventSource API
|
|
146
163
|
const eventSource = new EventSource('/api/stream');
|
|
147
164
|
|
|
148
165
|
eventSource.onmessage = (event) => {
|
|
@@ -159,20 +176,19 @@ eventSource.onerror = () => {
|
|
|
159
176
|
};
|
|
160
177
|
```
|
|
161
178
|
|
|
162
|
-
**SSE Protocol:**
|
|
163
|
-
The publisher sends events in SSE format:
|
|
164
|
-
```
|
|
165
|
-
HTTP/1.1 200 OK
|
|
166
|
-
Content-Type: text/event-stream
|
|
167
|
-
Transfer-Encoding: chunked
|
|
168
|
-
|
|
169
|
-
OK
|
|
170
|
-
|
|
171
|
-
data:{"tick":1,"timestamp":1234567890,"message":"Server tick #1"}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
```
|
|
179
|
+
**SSE Protocol:**
|
|
180
|
+
The publisher sends events in SSE format:
|
|
181
|
+
```
|
|
182
|
+
HTTP/1.1 200 OK
|
|
183
|
+
Content-Type: text/event-stream
|
|
184
|
+
Transfer-Encoding: chunked
|
|
185
|
+
|
|
186
|
+
data: OK
|
|
187
|
+
|
|
188
|
+
data: {"tick":1,"timestamp":1234567890,"message":"Server tick #1"}
|
|
189
|
+
|
|
190
|
+
data: {"tick":2,"timestamp":1234567891,"message":"Server tick #2"}
|
|
191
|
+
```
|
|
176
192
|
|
|
177
193
|
**See Also:** [Observable SSE Demo](../examples/controls/15)%20window,%20observable%20SSE/) for a complete working example.
|
|
178
194
|
|
|
@@ -386,4 +402,4 @@ Publishers provide comprehensive logging:
|
|
|
386
402
|
|
|
387
403
|
---
|
|
388
404
|
|
|
389
|
-
This guide provides the foundation for understanding and extending the publisher system. For specific publisher implementations, refer to their individual source files in the `publishers/` directory.
|
|
405
|
+
This guide provides the foundation for understanding and extending the publisher system. For specific publisher implementations, refer to their individual source files in the `publishers/` directory.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -197,13 +197,14 @@ input.js:1:0: ERROR: Expected identifier but found "}"
|
|
|
197
197
|
|
|
198
198
|
2. **Verify CSS definition:**
|
|
199
199
|
```javascript
|
|
200
|
-
MyControl.css = `
|
|
201
|
-
.my-control {
|
|
202
|
-
padding: 20px;
|
|
203
|
-
background: #f0f0f0;
|
|
204
|
-
}
|
|
205
|
-
`;
|
|
206
|
-
```
|
|
200
|
+
MyControl.css = `
|
|
201
|
+
.my-control {
|
|
202
|
+
padding: 20px;
|
|
203
|
+
background: #f0f0f0;
|
|
204
|
+
}
|
|
205
|
+
`;
|
|
206
|
+
```
|
|
207
|
+
You can also use `MyControl.scss` or `MyControl.sass` with template literals; these compile to CSS during bundling (ensure the `sass` dependency is installed). To see inline CSS sourcemaps in devtools, enable `style.sourcemaps` (or run with `debug: true`).
|
|
207
208
|
|
|
208
209
|
3. **Check activation:**
|
|
209
210
|
```javascript
|
|
@@ -746,4 +747,4 @@ This minimal setup helps isolate whether the issue is with your specific code or
|
|
|
746
747
|
|
|
747
748
|
---
|
|
748
749
|
|
|
749
|
-
Remember: Most issues can be resolved by carefully checking the console output, verifying file paths, and ensuring proper control lifecycle management. Start with the basics and work systematically through the possible causes.
|
|
750
|
+
Remember: Most issues can be resolved by carefully checking the console output, verifying file paths, and ensuring proper control lifecycle management. Start with the basics and work systematically through the possible causes.
|
|
@@ -52,23 +52,35 @@ node server.js
|
|
|
52
52
|
|
|
53
53
|
2. **Reactive UI Updates**: The `Observable_Demo_UI` control updates progress bars, status text, and log entries in real-time as events arrive.
|
|
54
54
|
|
|
55
|
-
## The SSE Protocol
|
|
56
|
-
|
|
57
|
-
Server-Sent Events use HTTP chunked transfer encoding:
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
HTTP/1.1 200 OK
|
|
61
|
-
Content-Type: text/event-stream
|
|
62
|
-
Transfer-Encoding: chunked
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
data: {"progress": 20, "stage": "Processing..."}
|
|
69
|
-
|
|
70
|
-
...
|
|
71
|
-
```
|
|
55
|
+
## The SSE Protocol
|
|
56
|
+
|
|
57
|
+
Server-Sent Events use HTTP chunked transfer encoding:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
HTTP/1.1 200 OK
|
|
61
|
+
Content-Type: text/event-stream
|
|
62
|
+
Transfer-Encoding: chunked
|
|
63
|
+
|
|
64
|
+
data: OK
|
|
65
|
+
|
|
66
|
+
data: {"progress": 10, "stage": "Loading..."}
|
|
67
|
+
|
|
68
|
+
data: {"progress": 20, "stage": "Processing..."}
|
|
69
|
+
|
|
70
|
+
...
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Optional Pause/Resume/Stop Control
|
|
74
|
+
|
|
75
|
+
`HTTP_Observable_Publisher` also supports controlling the published observable:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
await fetch('/api/tick-stream', {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'Content-Type': 'application/json' },
|
|
81
|
+
body: JSON.stringify({ action: 'pause' }) // 'resume' | 'stop' | 'status'
|
|
82
|
+
});
|
|
83
|
+
```
|
|
72
84
|
|
|
73
85
|
## Observable Pattern Benefits
|
|
74
86
|
|
package/lab/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Lab Experiments
|
|
2
|
+
|
|
3
|
+
This directory collects small, targeted experiments that validate specific UI behaviors or markup assumptions.
|
|
4
|
+
|
|
5
|
+
## Experiments
|
|
6
|
+
|
|
7
|
+
- `lab/experiments/window_examples_dom_audit.js`
|
|
8
|
+
- Server-rendered DOM checks for selected window examples.
|
|
9
|
+
- Writes results to `lab/results/window_examples_dom_audit.json` and `.md`.
|
|
10
|
+
|
|
11
|
+
## Running
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
node lab/experiments/window_examples_dom_audit.js
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Results
|
|
18
|
+
|
|
19
|
+
Experiment outputs are stored under `lab/results/` for traceability and documentation updates.
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const Server_Page_Context = require('../../page-context');
|
|
5
|
+
|
|
6
|
+
const repo_root_path = path.join(__dirname, '..', '..');
|
|
7
|
+
const examples_controls_root_path = path.join(repo_root_path, 'examples', 'controls');
|
|
8
|
+
const results_dir_path = path.join(repo_root_path, 'lab', 'results');
|
|
9
|
+
|
|
10
|
+
const count_occurrences = (haystack, needle) => {
|
|
11
|
+
let idx = 0;
|
|
12
|
+
let count = 0;
|
|
13
|
+
while (true) {
|
|
14
|
+
idx = haystack.indexOf(needle, idx);
|
|
15
|
+
if (idx === -1) return count;
|
|
16
|
+
count++;
|
|
17
|
+
idx += needle.length;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const count_class_occurrences = (html, class_name) => {
|
|
22
|
+
const class_regex = new RegExp(`class="[^"]*\\b${class_name}\\b`, 'g');
|
|
23
|
+
const matches = html.match(class_regex);
|
|
24
|
+
return matches ? matches.length : 0;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const count_regex_occurrences = (html, regex) => {
|
|
28
|
+
const matches = html.match(regex);
|
|
29
|
+
return matches ? matches.length : 0;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const render_example_html = ({ dir_name, ctrl_name }) => {
|
|
33
|
+
const example_dir_path = path.join(examples_controls_root_path, dir_name);
|
|
34
|
+
const example_client_path = path.join(example_dir_path, 'client.js');
|
|
35
|
+
|
|
36
|
+
const jsgui = require(example_client_path);
|
|
37
|
+
const ctrl = jsgui.controls && jsgui.controls[ctrl_name];
|
|
38
|
+
if (!ctrl) {
|
|
39
|
+
throw new Error(`Missing exported control jsgui.controls.${ctrl_name} in ${example_client_path}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const context = new Server_Page_Context();
|
|
43
|
+
const ctrl_instance = new ctrl({ context });
|
|
44
|
+
if (typeof ctrl_instance.all_html_render === 'function') {
|
|
45
|
+
return ctrl_instance.all_html_render();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof ctrl_instance.render === 'function') {
|
|
49
|
+
return ctrl_instance.render();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new Error(`Control ${ctrl_name} does not expose render methods`);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const make_count_check = (html, id, description, needle, expected) => {
|
|
56
|
+
const actual = count_occurrences(html, needle);
|
|
57
|
+
return {
|
|
58
|
+
id,
|
|
59
|
+
description,
|
|
60
|
+
expected,
|
|
61
|
+
actual,
|
|
62
|
+
pass: actual === expected
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const make_class_count_check = (html, id, description, class_name, expected) => {
|
|
67
|
+
const actual = count_class_occurrences(html, class_name);
|
|
68
|
+
return {
|
|
69
|
+
id,
|
|
70
|
+
description,
|
|
71
|
+
expected,
|
|
72
|
+
actual,
|
|
73
|
+
pass: actual === expected
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const make_contains_check = (html, id, description, needle) => {
|
|
78
|
+
const actual = html.includes(needle);
|
|
79
|
+
return {
|
|
80
|
+
id,
|
|
81
|
+
description,
|
|
82
|
+
expected: true,
|
|
83
|
+
actual,
|
|
84
|
+
pass: actual === true
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const make_regex_count_check = (html, id, description, regex, expected) => {
|
|
89
|
+
const actual = count_regex_occurrences(html, regex);
|
|
90
|
+
return {
|
|
91
|
+
id,
|
|
92
|
+
description,
|
|
93
|
+
expected,
|
|
94
|
+
actual,
|
|
95
|
+
pass: actual === expected
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const examples = [
|
|
100
|
+
{
|
|
101
|
+
dir_name: '1) window',
|
|
102
|
+
ctrl_name: 'Demo_UI',
|
|
103
|
+
checks: (html) => [
|
|
104
|
+
make_count_check(html, 'window_count', 'Window control count', 'data-jsgui-type="window"', 1),
|
|
105
|
+
make_contains_check(html, 'window_title_text', 'Window title text present', 'jsgui3-html Window Control'),
|
|
106
|
+
make_count_check(html, 'window_button_count', 'Window button count', 'data-jsgui-type="button"', 3)
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
dir_name: '4) window, tabbed panel',
|
|
111
|
+
ctrl_name: 'Demo_UI',
|
|
112
|
+
checks: (html) => [
|
|
113
|
+
make_class_count_check(html, 'tab_label_count', 'Tab label count', 'tab-label', 2),
|
|
114
|
+
make_contains_check(html, 'tab_label_one', 'Tab 1 label text present', 'tab 1'),
|
|
115
|
+
make_contains_check(html, 'tab_label_two', 'Tab 2 label text present', 'tab 2'),
|
|
116
|
+
make_count_check(html, 'tab_input_checked', 'Default checked tab input count', 'checked="checked"', 1)
|
|
117
|
+
]
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
dir_name: '8) window, checkbox/a)',
|
|
121
|
+
ctrl_name: 'Demo_UI',
|
|
122
|
+
checks: (html) => [
|
|
123
|
+
make_regex_count_check(
|
|
124
|
+
html,
|
|
125
|
+
'checkbox_input_count',
|
|
126
|
+
'Checkbox input count',
|
|
127
|
+
/\stype="checkbox"/g,
|
|
128
|
+
1
|
|
129
|
+
),
|
|
130
|
+
make_contains_check(html, 'checkbox_label_text', 'Checkbox label text present', 'A checkbox')
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
dir_name: '9) window, date picker',
|
|
135
|
+
ctrl_name: 'Demo_UI',
|
|
136
|
+
checks: (html) => [
|
|
137
|
+
make_class_count_check(html, 'date_picker_present', 'Date picker container present', 'date-picker', 1),
|
|
138
|
+
make_regex_count_check(
|
|
139
|
+
html,
|
|
140
|
+
'date_input_count',
|
|
141
|
+
'Date input type count',
|
|
142
|
+
/\stype="date"/g,
|
|
143
|
+
1
|
|
144
|
+
)
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
const build_results = () => {
|
|
150
|
+
const results = {
|
|
151
|
+
generated_at: new Date().toISOString(),
|
|
152
|
+
examples: []
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
for (const example of examples) {
|
|
156
|
+
const html = render_example_html(example);
|
|
157
|
+
const checks = example.checks(html);
|
|
158
|
+
const passed = checks.filter((check) => check.pass).length;
|
|
159
|
+
const failed = checks.length - passed;
|
|
160
|
+
|
|
161
|
+
results.examples.push({
|
|
162
|
+
dir_name: example.dir_name,
|
|
163
|
+
ctrl_name: example.ctrl_name,
|
|
164
|
+
checks,
|
|
165
|
+
summary: {
|
|
166
|
+
total: checks.length,
|
|
167
|
+
passed,
|
|
168
|
+
failed
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
results.summary = results.examples.reduce(
|
|
174
|
+
(acc, example) => {
|
|
175
|
+
acc.total += example.summary.total;
|
|
176
|
+
acc.passed += example.summary.passed;
|
|
177
|
+
acc.failed += example.summary.failed;
|
|
178
|
+
return acc;
|
|
179
|
+
},
|
|
180
|
+
{ total: 0, passed: 0, failed: 0 }
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return results;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const render_markdown = (results) => {
|
|
187
|
+
const lines = [];
|
|
188
|
+
lines.push('# Window Examples DOM Audit');
|
|
189
|
+
lines.push('');
|
|
190
|
+
lines.push(`Generated at: ${results.generated_at}`);
|
|
191
|
+
lines.push('');
|
|
192
|
+
lines.push(`Total checks: ${results.summary.total}`);
|
|
193
|
+
lines.push(`Passed: ${results.summary.passed}`);
|
|
194
|
+
lines.push(`Failed: ${results.summary.failed}`);
|
|
195
|
+
lines.push('');
|
|
196
|
+
|
|
197
|
+
for (const example of results.examples) {
|
|
198
|
+
lines.push(`## ${example.dir_name}`);
|
|
199
|
+
lines.push('');
|
|
200
|
+
lines.push(`Control: ${example.ctrl_name}`);
|
|
201
|
+
lines.push('');
|
|
202
|
+
lines.push('| Check | Expected | Actual | Pass |');
|
|
203
|
+
lines.push('| --- | --- | --- | --- |');
|
|
204
|
+
for (const check of example.checks) {
|
|
205
|
+
const expected = Array.isArray(check.expected) ? check.expected.join(', ') : String(check.expected);
|
|
206
|
+
const actual = Array.isArray(check.actual) ? check.actual.join(', ') : String(check.actual);
|
|
207
|
+
lines.push(`| ${check.description} | ${expected} | ${actual} | ${check.pass ? 'yes' : 'no'} |`);
|
|
208
|
+
}
|
|
209
|
+
lines.push('');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return lines.join('\n');
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const write_results = (results) => {
|
|
216
|
+
fs.mkdirSync(results_dir_path, { recursive: true });
|
|
217
|
+
const json_path = path.join(results_dir_path, 'window_examples_dom_audit.json');
|
|
218
|
+
const md_path = path.join(results_dir_path, 'window_examples_dom_audit.md');
|
|
219
|
+
|
|
220
|
+
fs.writeFileSync(json_path, JSON.stringify(results, null, 2));
|
|
221
|
+
fs.writeFileSync(md_path, render_markdown(results));
|
|
222
|
+
|
|
223
|
+
return { json_path, md_path };
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const main = () => {
|
|
227
|
+
const results = build_results();
|
|
228
|
+
const { json_path, md_path } = write_results(results);
|
|
229
|
+
|
|
230
|
+
console.log('Window examples DOM audit complete.');
|
|
231
|
+
console.log(`JSON: ${json_path}`);
|
|
232
|
+
console.log(`Markdown: ${md_path}`);
|
|
233
|
+
|
|
234
|
+
if (results.summary.failed > 0) {
|
|
235
|
+
process.exitCode = 1;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
if (require.main === module) {
|
|
240
|
+
main();
|
|
241
|
+
}
|