jsgui3-server 0.0.144 → 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/jsgui3-html-improvement-ideas.md +162 -0
- package/docs/jsgui3-html-improvement-ideas.svg +151 -0
- 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/package.json +3 -3
- package/publishers/http-webpageorsite-publisher.js +3 -1
- package/serve-factory.js +12 -5
- package/server.js +103 -85
- package/tests/README.md +7 -0
- package/tests/end-to-end.test.js +336 -365
- 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/test-runner.js +1 -0
- package/tests/window-examples.puppeteer.test.js +217 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
2
3
|
const path = require('path');
|
|
3
4
|
const { describe, it, before, after } = require('mocha');
|
|
4
5
|
|
|
@@ -7,6 +8,7 @@ const { get_free_port } = require('../port-utils');
|
|
|
7
8
|
|
|
8
9
|
const repo_root_path = path.join(__dirname, '..');
|
|
9
10
|
const examples_controls_root_path = path.join(repo_root_path, 'examples', 'controls');
|
|
11
|
+
const screenshots_root_path = path.join(repo_root_path, 'tests', 'screenshots', 'windows');
|
|
10
12
|
|
|
11
13
|
let puppeteer;
|
|
12
14
|
let browser;
|
|
@@ -65,11 +67,38 @@ const open_example_page = async (port) => {
|
|
|
65
67
|
await page.setViewport({ width: 1280, height: 720 });
|
|
66
68
|
|
|
67
69
|
const base_url = `http://127.0.0.1:${port}/`;
|
|
68
|
-
await page.goto(base_url, { waitUntil: '
|
|
70
|
+
await page.goto(base_url, { waitUntil: 'load' });
|
|
69
71
|
|
|
70
72
|
return page;
|
|
71
73
|
};
|
|
72
74
|
|
|
75
|
+
const normalize_screenshot_label = (value) => {
|
|
76
|
+
return String(value)
|
|
77
|
+
.toLowerCase()
|
|
78
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
79
|
+
.replace(/^_+|_+$/g, '');
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const build_screenshot_name = (story_name, step_name) => {
|
|
83
|
+
const story_label = normalize_screenshot_label(story_name);
|
|
84
|
+
const step_label = normalize_screenshot_label(step_name);
|
|
85
|
+
return `${story_label}__${step_label}`;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const ensure_screenshots_dir = () => {
|
|
89
|
+
fs.mkdirSync(screenshots_root_path, { recursive: true });
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const capture_screenshot = async (page, story_name, step_name) => {
|
|
93
|
+
ensure_screenshots_dir();
|
|
94
|
+
const screenshot_name = build_screenshot_name(story_name, step_name);
|
|
95
|
+
const file_path = path.join(screenshots_root_path, `${screenshot_name}.png`);
|
|
96
|
+
await page.screenshot({ path: file_path, fullPage: true });
|
|
97
|
+
const stats = fs.statSync(file_path);
|
|
98
|
+
assert.ok(stats.size > 0, `Expected screenshot to be written: ${file_path}`);
|
|
99
|
+
return file_path;
|
|
100
|
+
};
|
|
101
|
+
|
|
73
102
|
const wait_for_class_state = async (page, selector, class_name, expected) => {
|
|
74
103
|
await page.waitForFunction(
|
|
75
104
|
(selector_arg, class_arg, expected_arg) => {
|
|
@@ -85,6 +114,46 @@ const wait_for_class_state = async (page, selector, class_name, expected) => {
|
|
|
85
114
|
);
|
|
86
115
|
};
|
|
87
116
|
|
|
117
|
+
const get_window_bcr = async (page, selector = '.window') => {
|
|
118
|
+
return page.$eval(selector, (el) => {
|
|
119
|
+
const rect = el.getBoundingClientRect();
|
|
120
|
+
return {
|
|
121
|
+
left: rect.left,
|
|
122
|
+
top: rect.top,
|
|
123
|
+
width: rect.width,
|
|
124
|
+
height: rect.height,
|
|
125
|
+
right: rect.right,
|
|
126
|
+
bottom: rect.bottom
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const drag_window_by = async (page, selector, delta_x, delta_y) => {
|
|
132
|
+
const handle = await page.$(selector);
|
|
133
|
+
assert(handle, `Missing drag handle for selector: ${selector}`);
|
|
134
|
+
const box = await handle.boundingBox();
|
|
135
|
+
assert(box, `Missing bounding box for selector: ${selector}`);
|
|
136
|
+
const start_x = box.x + box.width / 2;
|
|
137
|
+
const start_y = box.y + box.height / 2;
|
|
138
|
+
await page.mouse.move(start_x, start_y);
|
|
139
|
+
await page.mouse.down();
|
|
140
|
+
await page.mouse.move(start_x + delta_x, start_y + delta_y, { steps: 10 });
|
|
141
|
+
await page.mouse.up();
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const resize_window_by = async (page, selector, delta_x, delta_y) => {
|
|
145
|
+
const handle = await page.$(selector);
|
|
146
|
+
assert(handle, `Missing resize handle for selector: ${selector}`);
|
|
147
|
+
const box = await handle.boundingBox();
|
|
148
|
+
assert(box, `Missing bounding box for selector: ${selector}`);
|
|
149
|
+
const start_x = box.x + box.width / 2;
|
|
150
|
+
const start_y = box.y + box.height / 2;
|
|
151
|
+
await page.mouse.move(start_x, start_y);
|
|
152
|
+
await page.mouse.down();
|
|
153
|
+
await page.mouse.move(start_x + delta_x, start_y + delta_y, { steps: 10 });
|
|
154
|
+
await page.mouse.up();
|
|
155
|
+
};
|
|
156
|
+
|
|
88
157
|
const get_tab_page_display = async (page) => {
|
|
89
158
|
return page.evaluate(() => {
|
|
90
159
|
return Array.from(document.querySelectorAll('.tab-page')).map((page_el) => {
|
|
@@ -137,11 +206,158 @@ describe('Window Examples Puppeteer Tests', function () {
|
|
|
137
206
|
const button_handles = await page.$$('.window .title.bar button.button');
|
|
138
207
|
assert.strictEqual(button_handles.length, 3, 'Expected minimize/maximize/close buttons');
|
|
139
208
|
|
|
209
|
+
await capture_screenshot(page, '1) window', 'before_minimize');
|
|
140
210
|
await button_handles[0].click();
|
|
141
211
|
await wait_for_class_state(page, '.window', 'minimized', true);
|
|
212
|
+
await capture_screenshot(page, '1) window', 'after_minimize');
|
|
142
213
|
|
|
143
214
|
await button_handles[0].click();
|
|
144
215
|
await wait_for_class_state(page, '.window', 'minimized', false);
|
|
216
|
+
await capture_screenshot(page, '1) window', 'after_restore');
|
|
217
|
+
} finally {
|
|
218
|
+
if (page) {
|
|
219
|
+
await page.close();
|
|
220
|
+
}
|
|
221
|
+
await stop_example_server(server);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('maximize button toggles window state in "1) window"', async function () {
|
|
226
|
+
const { server, port } = await start_example_server({
|
|
227
|
+
dir_name: '1) window',
|
|
228
|
+
ctrl_name: 'Demo_UI'
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
let page;
|
|
232
|
+
try {
|
|
233
|
+
page = await open_example_page(port);
|
|
234
|
+
await page.waitForSelector('.window .title.bar');
|
|
235
|
+
|
|
236
|
+
const button_handles = await page.$$('.window .title.bar button.button');
|
|
237
|
+
assert.strictEqual(button_handles.length, 3, 'Expected minimize/maximize/close buttons');
|
|
238
|
+
|
|
239
|
+
await capture_screenshot(page, '1) window', 'before_maximize');
|
|
240
|
+
await button_handles[1].click();
|
|
241
|
+
await wait_for_class_state(page, '.window', 'maximized', true);
|
|
242
|
+
await capture_screenshot(page, '1) window', 'after_maximize');
|
|
243
|
+
|
|
244
|
+
await button_handles[1].click();
|
|
245
|
+
await wait_for_class_state(page, '.window', 'maximized', false);
|
|
246
|
+
await capture_screenshot(page, '1) window', 'after_unmaximize');
|
|
247
|
+
} finally {
|
|
248
|
+
if (page) {
|
|
249
|
+
await page.close();
|
|
250
|
+
}
|
|
251
|
+
await stop_example_server(server);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('dragging the title bar moves the window in "1) window"', async function () {
|
|
256
|
+
const { server, port } = await start_example_server({
|
|
257
|
+
dir_name: '1) window',
|
|
258
|
+
ctrl_name: 'Demo_UI'
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
let page;
|
|
262
|
+
try {
|
|
263
|
+
page = await open_example_page(port);
|
|
264
|
+
await page.waitForSelector('.window .title.bar');
|
|
265
|
+
|
|
266
|
+
const initial_bcr = await get_window_bcr(page);
|
|
267
|
+
await capture_screenshot(page, '1) window', 'before_drag');
|
|
268
|
+
|
|
269
|
+
await drag_window_by(page, '.window .title.bar', 120, 80);
|
|
270
|
+
await page.waitForTimeout(150);
|
|
271
|
+
|
|
272
|
+
const moved_bcr = await get_window_bcr(page);
|
|
273
|
+
assert.ok(
|
|
274
|
+
moved_bcr.left > initial_bcr.left + 10,
|
|
275
|
+
`Expected window to move right (from ${initial_bcr.left} to ${moved_bcr.left})`
|
|
276
|
+
);
|
|
277
|
+
assert.ok(
|
|
278
|
+
moved_bcr.top > initial_bcr.top + 10,
|
|
279
|
+
`Expected window to move down (from ${initial_bcr.top} to ${moved_bcr.top})`
|
|
280
|
+
);
|
|
281
|
+
await capture_screenshot(page, '1) window', 'after_drag');
|
|
282
|
+
} finally {
|
|
283
|
+
if (page) {
|
|
284
|
+
await page.close();
|
|
285
|
+
}
|
|
286
|
+
await stop_example_server(server);
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('resizing updates window bounds in "1) window"', async function () {
|
|
291
|
+
const { server, port } = await start_example_server({
|
|
292
|
+
dir_name: '1) window',
|
|
293
|
+
ctrl_name: 'Demo_UI'
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
let page;
|
|
297
|
+
try {
|
|
298
|
+
page = await open_example_page(port);
|
|
299
|
+
await page.waitForSelector('.window .bottom-right.resize-handle');
|
|
300
|
+
|
|
301
|
+
const initial_bcr = await get_window_bcr(page);
|
|
302
|
+
await capture_screenshot(page, '1) window', 'before_resize');
|
|
303
|
+
|
|
304
|
+
await resize_window_by(page, '.window .bottom-right.resize-handle', 120, 80);
|
|
305
|
+
await page.waitForTimeout(150);
|
|
306
|
+
|
|
307
|
+
const resized_bcr = await get_window_bcr(page);
|
|
308
|
+
assert.ok(
|
|
309
|
+
resized_bcr.width > initial_bcr.width + 20,
|
|
310
|
+
`Expected width to increase (from ${initial_bcr.width} to ${resized_bcr.width})`
|
|
311
|
+
);
|
|
312
|
+
assert.ok(
|
|
313
|
+
resized_bcr.height > initial_bcr.height + 20,
|
|
314
|
+
`Expected height to increase (from ${initial_bcr.height} to ${resized_bcr.height})`
|
|
315
|
+
);
|
|
316
|
+
await capture_screenshot(page, '1) window', 'after_resize');
|
|
317
|
+
} finally {
|
|
318
|
+
if (page) {
|
|
319
|
+
await page.close();
|
|
320
|
+
}
|
|
321
|
+
await stop_example_server(server);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('focus and close behaviors work in "2) two windows"', async function () {
|
|
326
|
+
const { server, port } = await start_example_server({
|
|
327
|
+
dir_name: '2) two windows',
|
|
328
|
+
ctrl_name: 'Demo_UI'
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
let page;
|
|
332
|
+
try {
|
|
333
|
+
page = await open_example_page(port);
|
|
334
|
+
await page.waitForSelector('.window .title.bar');
|
|
335
|
+
|
|
336
|
+
const window_handles = await page.$$('.window');
|
|
337
|
+
assert.strictEqual(window_handles.length, 2, 'Expected two windows');
|
|
338
|
+
|
|
339
|
+
await capture_screenshot(page, '2) two windows', 'initial');
|
|
340
|
+
|
|
341
|
+
const title_handle = await window_handles[1].$('.title.bar');
|
|
342
|
+
assert(title_handle, 'Missing title bar for second window');
|
|
343
|
+
await title_handle.click();
|
|
344
|
+
|
|
345
|
+
await page.waitForFunction(() => {
|
|
346
|
+
const windows = document.querySelectorAll('.window');
|
|
347
|
+
if (windows.length < 2) return false;
|
|
348
|
+
const z_first = parseInt(window.getComputedStyle(windows[0]).zIndex, 10) || 0;
|
|
349
|
+
const z_second = parseInt(window.getComputedStyle(windows[1]).zIndex, 10) || 0;
|
|
350
|
+
return z_second > z_first;
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
await capture_screenshot(page, '2) two windows', 'after_focus');
|
|
354
|
+
|
|
355
|
+
const button_handles = await window_handles[1].$$('.title.bar button.button');
|
|
356
|
+
assert.strictEqual(button_handles.length, 3, 'Expected minimize/maximize/close buttons');
|
|
357
|
+
await button_handles[2].click();
|
|
358
|
+
|
|
359
|
+
await page.waitForFunction(() => document.querySelectorAll('.window').length === 1);
|
|
360
|
+
await capture_screenshot(page, '2) two windows', 'after_close');
|
|
145
361
|
} finally {
|
|
146
362
|
if (page) {
|
|
147
363
|
await page.close();
|