jsgui3-server 0.0.144 → 0.0.146
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/docs/jsgui3-sass-patterns-book/01-vision-and-goals.md +31 -0
- package/docs/jsgui3-sass-patterns-book/02-stack-map.md +40 -0
- package/docs/jsgui3-sass-patterns-book/03-control-local-sass.md +60 -0
- package/docs/jsgui3-sass-patterns-book/04-extension-and-variants.md +76 -0
- package/docs/jsgui3-sass-patterns-book/05-theming-and-tokens.md +54 -0
- package/docs/jsgui3-sass-patterns-book/06-workspace-overrides.md +45 -0
- package/docs/jsgui3-sass-patterns-book/07-resource-and-bundling.md +46 -0
- package/docs/jsgui3-sass-patterns-book/08-examples.md +62 -0
- package/docs/jsgui3-sass-patterns-book/09-testing-and-adoption.md +27 -0
- package/docs/jsgui3-sass-patterns-book/README.md +23 -0
- package/docs/troubleshooting.md +44 -4
- package/examples/controls/14) window, canvas/client.js +1 -1
- package/examples/controls/14b) window, canvas (improved renderer)/client.js +1 -1
- package/examples/controls/14d) window, canvas globe/EarthGlobeRenderer.js +19 -14
- package/examples/controls/14d) window, canvas globe/client.js +1 -1
- package/examples/controls/14d) window, canvas globe/pipeline/TransformStage.js +5 -5
- package/examples/controls/14e) window, canvas multithreaded/client.js +1 -1
- package/examples/controls/14f) window, canvas polyglobe/client.js +1 -1
- package/examples/controls/16) window, form container/client.js +254 -0
- package/examples/controls/16) window, form container/server.js +20 -0
- package/examples/controls/17) window, mvvm binding/client.js +530 -0
- package/examples/controls/17) window, mvvm binding/server.js +20 -0
- 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/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +87 -100
- package/resources/processors/bundlers/js/esbuild/Core_JS_Single_File_Minifying_Bundler_Using_ESBuild.js +89 -60
- package/serve-factory.js +12 -5
- package/server.js +103 -85
- package/tests/README.md +52 -9
- package/tests/bundlers.test.js +53 -47
- package/tests/end-to-end.test.js +336 -365
- package/tests/examples-controls.e2e.test.js +15 -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 +123 -9
- package/tests/test-runner.js +1 -0
- package/tests/window-examples.puppeteer.test.js +217 -1
|
@@ -26,6 +26,7 @@ const sass_mix_client_js = [
|
|
|
26
26
|
"const jsgui = require('jsgui3-html');",
|
|
27
27
|
"const { Control } = jsgui;",
|
|
28
28
|
"const { controls } = jsgui;",
|
|
29
|
+
"const Active_HTML_Document = controls.Active_HTML_Document;",
|
|
29
30
|
"",
|
|
30
31
|
"class Sass_Mix_Control extends Control {",
|
|
31
32
|
" constructor(spec = {}) {",
|
|
@@ -51,7 +52,20 @@ const sass_mix_client_js = [
|
|
|
51
52
|
"}",
|
|
52
53
|
"`;",
|
|
53
54
|
"",
|
|
55
|
+
"class Demo_UI extends Active_HTML_Document {",
|
|
56
|
+
" constructor(spec = {}) {",
|
|
57
|
+
" spec.__type_name = spec.__type_name || 'demo_ui';",
|
|
58
|
+
" super(spec);",
|
|
59
|
+
" const { context } = this;",
|
|
60
|
+
" if (!spec.el) {",
|
|
61
|
+
" const control = new Sass_Mix_Control({ context });",
|
|
62
|
+
" this.body.add(control);",
|
|
63
|
+
" }",
|
|
64
|
+
" }",
|
|
65
|
+
"}",
|
|
66
|
+
"",
|
|
54
67
|
"controls.Sass_Mix_Control = Sass_Mix_Control;",
|
|
68
|
+
"controls.Demo_UI = Demo_UI;",
|
|
55
69
|
"module.exports = jsgui;",
|
|
56
70
|
""
|
|
57
71
|
].join('\n');
|
|
@@ -60,6 +74,7 @@ const sass_only_client_js = [
|
|
|
60
74
|
"const jsgui = require('jsgui3-html');",
|
|
61
75
|
"const { Control } = jsgui;",
|
|
62
76
|
"const { controls } = jsgui;",
|
|
77
|
+
"const Active_HTML_Document = controls.Active_HTML_Document;",
|
|
63
78
|
"",
|
|
64
79
|
"class Sass_Only_Control extends Control {",
|
|
65
80
|
" constructor(spec = {}) {",
|
|
@@ -80,7 +95,20 @@ const sass_only_client_js = [
|
|
|
80
95
|
" color: #000000",
|
|
81
96
|
"`;",
|
|
82
97
|
"",
|
|
98
|
+
"class Demo_UI extends Active_HTML_Document {",
|
|
99
|
+
" constructor(spec = {}) {",
|
|
100
|
+
" spec.__type_name = spec.__type_name || 'demo_ui';",
|
|
101
|
+
" super(spec);",
|
|
102
|
+
" const { context } = this;",
|
|
103
|
+
" if (!spec.el) {",
|
|
104
|
+
" const control = new Sass_Only_Control({ context });",
|
|
105
|
+
" this.body.add(control);",
|
|
106
|
+
" }",
|
|
107
|
+
" }",
|
|
108
|
+
"}",
|
|
109
|
+
"",
|
|
83
110
|
"controls.Sass_Only_Control = Sass_Only_Control;",
|
|
111
|
+
"controls.Demo_UI = Demo_UI;",
|
|
84
112
|
"module.exports = jsgui;",
|
|
85
113
|
""
|
|
86
114
|
].join('\n');
|
|
@@ -89,6 +117,7 @@ const sass_css_mix_client_js = [
|
|
|
89
117
|
"const jsgui = require('jsgui3-html');",
|
|
90
118
|
"const { Control } = jsgui;",
|
|
91
119
|
"const { controls } = jsgui;",
|
|
120
|
+
"const Active_HTML_Document = controls.Active_HTML_Document;",
|
|
92
121
|
"",
|
|
93
122
|
"class Sass_Css_Mix_Control extends Control {",
|
|
94
123
|
" constructor(spec = {}) {",
|
|
@@ -111,7 +140,61 @@ const sass_css_mix_client_js = [
|
|
|
111
140
|
" color: $mix_color",
|
|
112
141
|
"`;",
|
|
113
142
|
"",
|
|
143
|
+
"class Demo_UI extends Active_HTML_Document {",
|
|
144
|
+
" constructor(spec = {}) {",
|
|
145
|
+
" spec.__type_name = spec.__type_name || 'demo_ui';",
|
|
146
|
+
" super(spec);",
|
|
147
|
+
" const { context } = this;",
|
|
148
|
+
" if (!spec.el) {",
|
|
149
|
+
" const control = new Sass_Css_Mix_Control({ context });",
|
|
150
|
+
" this.body.add(control);",
|
|
151
|
+
" }",
|
|
152
|
+
" }",
|
|
153
|
+
"}",
|
|
154
|
+
"",
|
|
114
155
|
"controls.Sass_Css_Mix_Control = Sass_Css_Mix_Control;",
|
|
156
|
+
"controls.Demo_UI = Demo_UI;",
|
|
157
|
+
"module.exports = jsgui;",
|
|
158
|
+
""
|
|
159
|
+
].join('\n');
|
|
160
|
+
|
|
161
|
+
const sass_theme_client_js = [
|
|
162
|
+
"const jsgui = require('jsgui3-html');",
|
|
163
|
+
"const { Control } = jsgui;",
|
|
164
|
+
"const { controls } = jsgui;",
|
|
165
|
+
"const Active_HTML_Document = controls.Active_HTML_Document;",
|
|
166
|
+
"",
|
|
167
|
+
"class Sass_Theme_Control extends Control {",
|
|
168
|
+
" constructor(spec = {}) {",
|
|
169
|
+
" super(spec);",
|
|
170
|
+
" const { context } = this;",
|
|
171
|
+
" if (!spec.el) {",
|
|
172
|
+
" const container = new controls.div({ context, class: 'sass-theme-control' });",
|
|
173
|
+
" this.add(container);",
|
|
174
|
+
" }",
|
|
175
|
+
" }",
|
|
176
|
+
"}",
|
|
177
|
+
"",
|
|
178
|
+
"Sass_Theme_Control.scss = `",
|
|
179
|
+
".sass-theme-control {",
|
|
180
|
+
" color: var(--accent-color);",
|
|
181
|
+
"}",
|
|
182
|
+
"`;",
|
|
183
|
+
"",
|
|
184
|
+
"class Demo_UI extends Active_HTML_Document {",
|
|
185
|
+
" constructor(spec = {}) {",
|
|
186
|
+
" spec.__type_name = spec.__type_name || 'demo_ui';",
|
|
187
|
+
" super(spec);",
|
|
188
|
+
" const { context } = this;",
|
|
189
|
+
" if (!spec.el) {",
|
|
190
|
+
" const control = new Sass_Theme_Control({ context });",
|
|
191
|
+
" this.body.add(control);",
|
|
192
|
+
" }",
|
|
193
|
+
" }",
|
|
194
|
+
"}",
|
|
195
|
+
"",
|
|
196
|
+
"controls.Sass_Theme_Control = Sass_Theme_Control;",
|
|
197
|
+
"controls.Demo_UI = Demo_UI;",
|
|
115
198
|
"module.exports = jsgui;",
|
|
116
199
|
""
|
|
117
200
|
].join('\n');
|
|
@@ -167,7 +250,17 @@ const remove_file_if_exists = async (file_path) => {
|
|
|
167
250
|
}
|
|
168
251
|
};
|
|
169
252
|
|
|
170
|
-
const
|
|
253
|
+
const merge_style_config = (overrides = {}) => {
|
|
254
|
+
const merged = Object.assign({}, style_config, overrides);
|
|
255
|
+
merged.sourcemaps = Object.assign(
|
|
256
|
+
{},
|
|
257
|
+
style_config.sourcemaps || {},
|
|
258
|
+
overrides.sourcemaps || {}
|
|
259
|
+
);
|
|
260
|
+
return merged;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const start_test_server = async ({ client_path, control_name, style_overrides }) => {
|
|
171
264
|
delete require.cache[require.resolve(client_path)];
|
|
172
265
|
const client_module = require(client_path);
|
|
173
266
|
const ctrl = client_module.controls && client_module.controls[control_name];
|
|
@@ -178,7 +271,7 @@ const start_test_server = async ({ client_path, control_name }) => {
|
|
|
178
271
|
src_path_client_js: client_path,
|
|
179
272
|
name: `tests/${control_name}`,
|
|
180
273
|
debug: true,
|
|
181
|
-
style:
|
|
274
|
+
style: merge_style_config(style_overrides)
|
|
182
275
|
});
|
|
183
276
|
|
|
184
277
|
server.allowed_addresses = ['127.0.0.1'];
|
|
@@ -228,7 +321,7 @@ describe('Sass/CSS Control E2E Tests', function() {
|
|
|
228
321
|
const client_path = await write_temp_client_file('temp_sass_mix_client.js', sass_mix_client_js);
|
|
229
322
|
const { server, port } = await start_test_server({
|
|
230
323
|
client_path,
|
|
231
|
-
control_name: '
|
|
324
|
+
control_name: 'Demo_UI'
|
|
232
325
|
});
|
|
233
326
|
|
|
234
327
|
try {
|
|
@@ -266,7 +359,7 @@ describe('Sass/CSS Control E2E Tests', function() {
|
|
|
266
359
|
const client_path = await write_temp_client_file('temp_sass_only_client.js', sass_only_client_js);
|
|
267
360
|
const { server, port } = await start_test_server({
|
|
268
361
|
client_path,
|
|
269
|
-
control_name: '
|
|
362
|
+
control_name: 'Demo_UI'
|
|
270
363
|
});
|
|
271
364
|
|
|
272
365
|
try {
|
|
@@ -280,10 +373,7 @@ describe('Sass/CSS Control E2E Tests', function() {
|
|
|
280
373
|
assert(css_text.includes('color: #336699'), 'Expected Sass variable compilation');
|
|
281
374
|
assert(css_text.includes('.sass-only-control:hover'), 'Expected nested Sass selector output');
|
|
282
375
|
|
|
283
|
-
|
|
284
|
-
assert(Array.isArray(css_sourcemap.sources), 'Expected sourcemap sources array');
|
|
285
|
-
assert(Array.isArray(css_sourcemap.sourcesContent), 'Expected sourcemap sourcesContent array');
|
|
286
|
-
assert(sourcemap_contains(css_sourcemap, '$primary_color'), 'Expected sourcemap to include Sass source content');
|
|
376
|
+
assert(!css_text.includes('/*# sourceMappingURL='), 'Expected no inline sourcemap with multiple style segments');
|
|
287
377
|
|
|
288
378
|
const js_response = await make_request(`${base_url}/js/js.js`);
|
|
289
379
|
assert.strictEqual(js_response.status_code, 200);
|
|
@@ -298,7 +388,7 @@ describe('Sass/CSS Control E2E Tests', function() {
|
|
|
298
388
|
const client_path = await write_temp_client_file('temp_sass_css_mix_client.js', sass_css_mix_client_js);
|
|
299
389
|
const { server, port } = await start_test_server({
|
|
300
390
|
client_path,
|
|
301
|
-
control_name: '
|
|
391
|
+
control_name: 'Demo_UI'
|
|
302
392
|
});
|
|
303
393
|
|
|
304
394
|
try {
|
|
@@ -324,4 +414,28 @@ describe('Sass/CSS Control E2E Tests', function() {
|
|
|
324
414
|
await remove_file_if_exists(client_path);
|
|
325
415
|
}
|
|
326
416
|
});
|
|
417
|
+
|
|
418
|
+
it('should apply scss_sources overrides from server config', async function() {
|
|
419
|
+
const client_path = await write_temp_client_file('temp_sass_theme_client.js', sass_theme_client_js);
|
|
420
|
+
const { server, port } = await start_test_server({
|
|
421
|
+
client_path,
|
|
422
|
+
control_name: 'Demo_UI',
|
|
423
|
+
style_overrides: {
|
|
424
|
+
scss_sources: [':root { --accent-color: #00aa88; }']
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
try {
|
|
429
|
+
const base_url = `http://127.0.0.1:${port}`;
|
|
430
|
+
const css_response = await make_request(`${base_url}/css/css.css`);
|
|
431
|
+
assert.strictEqual(css_response.status_code, 200);
|
|
432
|
+
assert((css_response.headers['content-type'] || '').includes('css'), 'Expected CSS content-type');
|
|
433
|
+
|
|
434
|
+
const css_text = css_response.body;
|
|
435
|
+
assert(css_text.includes('--accent-color: #00aa88'), 'Expected scss_sources to compile into CSS');
|
|
436
|
+
} finally {
|
|
437
|
+
await close_server(server);
|
|
438
|
+
await remove_file_if_exists(client_path);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
327
441
|
});
|
package/tests/test-runner.js
CHANGED
|
@@ -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();
|