isomorfeus-puppetmaster 0.3.1 → 0.4.0

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.
@@ -1,842 +0,0 @@
1
- module Isomorfeus
2
- module Puppetmaster
3
- module Driver
4
- module JsdomNode
5
- def node_all_text(node)
6
- @context.eval "AllElementHandles[#{node.handle}].textContent"
7
- end
8
-
9
- def node_click(node, x: nil, y: nil, modifiers: nil)
10
- # modifier_keys: :alt, :control, :meta, :shift
11
- # raise Isomorfeus::Pupppetmaster::InvalidActionError.new(:click) unless visible?
12
- modifiers = [modifiers] if modifiers.is_a?(Symbol)
13
- modifiers = [] unless modifiers
14
- modifiers = modifiers.map {|key| key.to_s.camelize(:lower) }
15
- @context.exec <<~JAVASCRIPT
16
- var options = {button: 0, bubbles: true, cancelable: true};
17
- var window = AllDomHandles[#{node.document.handle}].window;
18
- var modifiers = #{modifiers};
19
- if (modifiers.includes('meta')) { options['metaKey'] = true; }
20
- if (modifiers.includes('control')) { options['ctrlKey'] = true; }
21
- if (modifiers.includes('shift')) { options['shiftKey'] = true; }
22
- if (modifiers.includes('alt')) { options['altKey'] = true; }
23
- var x = #{x ? x : 'null'};
24
- var y = #{y ? y : 'null'};
25
- if (x && y) {
26
- options['clientX'] = x;
27
- options['clientY'] = y;
28
- }
29
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('mousedown', options));
30
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('mouseup', options));
31
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('click', options));
32
- JAVASCRIPT
33
- end
34
-
35
- def node_disabled?(node)
36
- @context.exec <<~JAVASCRIPT
37
- var window = AllDomHandles[#{node.document.handle}].window;
38
- var node = AllElementHandles[#{node.handle}];
39
- const xpath = `parent::optgroup[@disabled] | ancestor::select[@disabled] | parent::fieldset[@disabled] |
40
- ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]`;
41
- return node.disabled || window.document.evaluate(xpath, node, null, window.XPathResult.BOOLEAN_TYPE, null).booleanValue;
42
- JAVASCRIPT
43
- end
44
-
45
- def node_dispatch_event(node, name, event_type = nil, **options)
46
- raise ArgumentError, 'Unknown event' unless Isomorfeus::Puppetmaster::Driver::Jsdom::EVENTS.key?(name.to_sym) || event_type
47
- event_type, opts = *Isomorfeus::Puppetmaster::Driver::Jsdom::EVENTS[name.to_sym] if event_type.nil?
48
- opts.merge!(options)
49
- final_options = options.map { |k,v| "#{k}: '#{v}'" }
50
- @context.exec <<~JAVASCRIPT
51
- var window = AllDomHandles[#{node.document.handle}].window;
52
- var event = new window.#{event_type}('#{name}', { #{final_options.join(', ')} });
53
- AllElementHandles[#{node.handle}].dispatchEvent(event);
54
- JAVASCRIPT
55
- end
56
-
57
- def node_double_click(node, x: nil, y: nil, modifiers: nil)
58
- # modifier_keys: :alt, :control, :meta, :shift
59
- # offset: { x: int, y: int }
60
- modifiers = [modifiers] if modifiers.is_a?(Symbol)
61
- modifiers = [] unless modifiers
62
- modifiers = modifiers.map {|key| key.to_s.camelize(:lower) }
63
- @context.exec <<~JAVASCRIPT
64
- var options = {button: 0, bubbles: true, cancelable: true};
65
- var window = AllDomHandles[#{node.document.handle}].window;
66
- var modifiers = #{modifiers};
67
- if (modifiers.includes('meta')) { options['metaKey'] = true; }
68
- if (modifiers.includes('control')) { options['ctrlKey'] = true; }
69
- if (modifiers.includes('shift')) { options['shiftKey'] = true; }
70
- if (modifiers.includes('alt')) { options['altKey'] = true; }
71
- var x = #{x ? x : 'null'};
72
- var y = #{y ? y : 'null'};
73
- if (x && y) {
74
- options['clientX'] = x;
75
- options['clientY'] = y;
76
- }
77
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('mousedown', options));
78
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('mouseup', options));
79
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('dblclick', options));
80
- JAVASCRIPT
81
- end
82
-
83
- def node_drag_to(node, other_node)
84
- # TODO
85
- if node[:draggable]
86
- await <<~JAVASCRIPT
87
- var window = AllDomHandles[#{node.document.handle}].window;
88
- window.document.addEventListener('mousedown', event => {
89
- window.jsdom_mousedown_prevented = event.defaultPrevented;
90
- }, { once: true, passive: true });
91
- JAVASCRIPT
92
- # TODO use scrollIntoView once chromium bug is fixed
93
- # https://bugs.chromium.org/p/chromium/issues/detail?id=939740&can=2&start=0&num=100&q=mousemove%20scrollintoview&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Summary&groupby=&sort=
94
- await <<~JAVASCRIPT
95
- var window = AllDomHandles[#{node.document.handle}].window;
96
- var node = AllElementHandles[#{node.handle}];
97
- var other_node = AllElementHandles[#{other_node.handle}];
98
- var n = node;
99
- var top = n.offsetTop, left = n.offsetLeft, width = n.offsetWidth, height = n.offsetHeight;
100
- while(n.offsetParent) { n = n.offsetParent; top += n.offsetTop; left += n.offsetLeft; }
101
- var node_in_view = (top >= window.pageYOffset && left >= window.pageXOffset &&
102
- (top + height) <= (window.pageYOffset + window.innerHeight) && (left + width) <= (window.pageXOffset + window.innerWidth));
103
- if (!node_in_view) { node.scrollTo(0,0); };
104
- setTimeout(function(){
105
- var client_rect = node.getBoundingClientRect();
106
- var x = (client_rect.left + (client_rect.width / 2));
107
- var y = (client_rect.top + (client_rect.height / 2));
108
- node.dispatchEvent(new window.MouseEvent('mousemove', {clientX: x, clientY: y, bubbles: true, cancelable: true}));
109
- setTimeout(function(){
110
- node.dispatchEvent(new window.MouseEvent('mousedown', {clientX: x, clientY: y, bubbles: true, cancelable: true}));
111
- if (window.jsdom_mousedown_prevented) {
112
- n = other_node;
113
- top = n.offsetTop; left = n.offsetLeft; width = n.offsetWidth; height = n.offsetHeight;
114
- while(n.offsetParent) { n = n.offsetParent; top += n.offsetTop; left += n.offsetLeft; }
115
- var node_in_view = (top >= window.pageYOffset && left >= window.pageXOffset &&
116
- (top + height) <= (window.pageYOffset + window.innerHeight) && (left + width) <= (window.pageXOffset + window.innerWidth));
117
- if (!node_in_view) { other_node.scrollTo(0,0) };
118
- setTimeout(function(){
119
- client_rect = other_node.getBoundingClientRect();
120
- x = (client_rect.left + (client_rect.width / 2));
121
- y = (client_rect.top + (client_rect.height / 2));
122
- node.dispatchEvent(new window.MouseEvent('mousemove', {clientX: x, clientY: y, bubbles: true, cancelable: true}));
123
- setTimeout(function(){
124
- node.dispatchEvent(new window.MouseEvent('mouseup', {clientX: x, clientY: y, bubbles: true, cancelable: true}));
125
- }, #{@jsdom_reaction_timeout/2});
126
- }, #{@jsdom_reaction_timeout});
127
- } else {
128
- var dt = new window.DataTransfer();
129
- if (node.tagName == 'A'){ dt.setData('text/uri-list', node.href); dt.setData('text', node.href); }
130
- if (node.tagName == 'IMG'){ dt.setData('text/uri-list', node.src); dt.setData('text', node.src); }
131
- var opts = { cancelable: true, bubbles: true, dataTransfer: dt };
132
- var dragEvent = new window.DragEvent('dragstart', opts);
133
- node.dispatchEvent(dragEvent);
134
- n = other_node;
135
- top = n.offsetTop; left = n.offsetLeft; width = n.offsetWidth; height = n.offsetHeight;
136
- while(n.offsetParent) { n = n.offsetParent; top += n.offsetTop; left += n.offsetLeft; }
137
- var node_in_view = (top >= window.pageYOffset && left >= window.pageXOffset &&
138
- (top + height) <= (window.pageYOffset + window.innerHeight) && (left + width) <= (window.pageXOffset + window.innerWidth));
139
- if (!node_in_view) { other_node.scrollTo(0,0); };
140
- setTimeout(function(){
141
- var rect = node.getBoundingClientRect()
142
- var node_center = new window.DOMPoint((rect.left + rect.right)/2, (rect.top + rect.bottom)/2);
143
- var other_rect = other_node.getBoundingClientRect();
144
- var other_point = new window.DOMPoint((other_rect.left + other_rect.right)/2, (other_rect.top + other_rect.bottom)/2);
145
- var entry_point = null;
146
- var slope = (other_point.y - other_point.y) / (other_point.x - node_center.x);
147
- if (other_point.x <= other_point.x) { // left side
148
- var minXy = slope * (other_rect.left - node_center.x) + node_center.y;
149
- if (other_rect.top <= minXy && minXy <= other_rect.bottom) { entry_point = new window.DOMPoint(other_rect.left, minXy); }
150
- }
151
- if (node_center.x >= other_point.x) { // right side
152
- var maxXy = slope * (other_rect.right - node_center.x) + node_center.y;
153
- if (other_rect.top <= maxXy && maxXy <= other_rect.bottom) { entry_point = new window.DOMPoint(other_rect.right, maxXy); }
154
- }
155
- if (node_center.y <= other_point.y) { // top side
156
- var minYx = (other_point.top - node_center.y) / slope + node_center.x;
157
- if (other_rect.left <= minYx && minYx <= other_rect.right) { entry_point = new window.DOMPoint(minYx, other_rect.top); }
158
- }
159
- if (node_center.y >= other_point.y) { // bottom side
160
- var maxYx = (other_rect.bottom - node_center.y) / slope + node_center.x;
161
- if (other_rect.left <= maxYx && maxYx <= other_rect.right) { entry_point = new window.DOMPoint(maxYx, other_rect.bottom); }
162
- }
163
- if (!entry_point) {
164
- entry_point = new window.DOMPoint(node_center.x, node_center.y);
165
- }
166
- var drag_over_event = new window.DragEvent('dragover', {clientX: entry_point.x, clientY: entry_point.y, bubbles: true, cancelable: true});
167
- other_node.dispatchEvent(drag_over_event);
168
- var other_center = new window.DOMPoint((other_rect.left + other_rect.right)/2, (other_rect.top + other_rect.bottom)/2);
169
- drag_over_event = new window.DragEvent('dragover', {clientX: targetCenter.x, clientY: targetCenter.y, bubbles: true, cancelable: true});
170
- other_node.dispatchEvent(drag_over_event);
171
- other_node.dispatchEvent(new window.DragEvent('dragleave', {bubbles: true, cancelable: true}));
172
- if (drag_over_event.defaultPrevented) {
173
- other_node.dispatchEvent(new window.DragEvent('drop', {bubbles: true, cancelable: true}));
174
- }
175
- node.dispatchEvent(new window.DragEvent('dragend', {bubbles: true, cancelable: true}));
176
- client_rect = other_node.getBoundingClientRect();
177
- x = (client_rect.left + (client_rect.width / 2));
178
- y = (client_rect.top + (client_rect.height / 2));
179
- node.dispatchEvent(new window.MouseEvent('mouseup', {clientX: x, clientY: y, bubbles: true, cancelable: true}));
180
- }, #{@jsdom_reaction_timeout});
181
- }
182
- }, #{@jsdom_reaction_timeout/2});
183
- }, #{@jsdom_reaction_timeout});
184
- JAVASCRIPT
185
- sleep (@reaction_timeout * 3) + 0.2
186
- else
187
- await <<~JAVASCRIPT
188
- var window = AllDomHandles[#{node.document.handle}].window;
189
- var node = AllElementHandles[#{node.handle}];
190
- var other_node = AllElementHandles[#{other_node.handle}];
191
- var n = node;
192
- var top = n.offsetTop, left = n.offsetLeft, width = n.offsetWidth, height = n.offsetHeight;
193
- while(n.offsetParent) { n = n.offsetParent; top += n.offsetTop; left += n.offsetLeft; }
194
- var node_in_view = (top >= window.pageYOffset && left >= window.pageXOffset &&
195
- (top + height) <= (window.pageYOffset + window.innerHeight) && (left + width) <= (window.pageXOffset + window.innerWidth));
196
- if (!node_in_view) { res = (n === node); node.scrollTo(0,0); };
197
- setTimeout(function() {
198
- var client_rect = node.getBoundingClientRect();
199
- var x = (client_rect.left + (client_rect.width / 2));
200
- var y = (client_rect.top + (client_rect.height / 2));
201
- node.dispatchEvent(new window.MouseEvent('mousemove', {clientX: x, clientY: y, bubbles: true, cancelable: true}));
202
- setTimeout(function() {
203
- node.dispatchEvent(new window.MouseEvent('mousedown', {button: 0, buttons: 1, clientX: x, clientY: y, bubbles: true, cancelable: true}));
204
- var n = other_node;
205
- var top = n.offsetTop, left = n.offsetLeft, width = n.offsetWidth, height = n.offsetHeight;
206
- while(n.offsetParent) { n = n.offsetParent; top += n.offsetTop; left += n.offsetLeft; }
207
- var other_node_in_view = (top >= window.pageYOffset && left >= window.pageXOffset &&
208
- (top + height) <= (window.pageYOffset + window.innerHeight) && (left + width) <= (window.pageXOffset + window.innerWidth));
209
- if (!other_node_in_view) { other_node.scrollTo(0,0); };
210
- setTimeout(function() {
211
- var other_client_rect = other_node.getBoundingClientRect();
212
- var x = (other_client_rect.left + (other_client_rect.width / 2));
213
- var y = (other_client_rect.top + (other_client_rect.height / 2));
214
- node.dispatchEvent(new window.MouseEvent('mousemove', {button: 0, buttons: 1, clientX: x, clientY: y, bubbles: true, cancelable: true}));
215
- setTimeout(function() {
216
- node.dispatchEvent(new window.MouseEvent('mouseup', {button: 0, buttons: 1, clientX: x, clientY: y, bubbles: true, cancelable: true}));
217
- }, #{@jsdom_reaction_timeout/2});
218
- }, #{@jsdom_reaction_timeout});
219
- }, #{@jsdom_reaction_timeout/2});
220
- }, #{@jsdom_reaction_timeout});
221
- JAVASCRIPT
222
- sleep (@reaction_timeout * 3) + 0.2
223
- end
224
- end
225
-
226
- def node_equal(node, other_node)
227
- @context.eval "AllElementHandles[#{node.handle}] === AllElementHandles[#{other_node.handle}]"
228
- end
229
-
230
- def node_execute_script(node, script, *args)
231
- # TODO this wont work yet
232
- await <<~JAVASCRIPT
233
- var node_handle = #{node.handle};
234
- await AllElementHandles[node_handle].executionContext().evaluateHandle((node, arguments) => {
235
- arguments.unshift(node);
236
- #{script}
237
- }, AllElementHandles[node_handle], #{args[1..-1]});
238
- JAVASCRIPT
239
- end
240
-
241
- def node_evaluate_script(node, script, *args)
242
- # TODO this wont work yet
243
- await <<~JAVASCRIPT
244
- var node_handle = #{node.handle};
245
- await AllElementHandles[node_handle].executionContext().evaluateHandle((node, arguments) => {
246
- arguments.unshift(node);
247
- return #{script};
248
- }, AllElementHandles[node_handle], #{args[1..-1]});
249
- LastResult = await handle.jsonValue();
250
- JAVASCRIPT
251
- end
252
-
253
- def node_find(node, selector)
254
- js_escaped_selector = selector.gsub('\\', '\\\\\\').gsub('"', '\"')
255
- node_data = @context.exec <<~JAVASCRIPT
256
- var node = AllElementHandles[#{node.handle}].querySelector("#{js_escaped_selector}");
257
- if (node) {
258
- var node_handle = RegisterElementHandle(node);
259
- var name = node.nodeName;
260
- var tag = node.tagName.toLowerCase();
261
- var type = null;
262
- if (tag === 'input') { type = node.getAttribute('type'); }
263
- return {handle: node_handle, name: name, tag: tag, type: type, content_editable: node.isContentEditable};
264
- }
265
- JAVASCRIPT
266
- if node_data
267
- node_data[:css_selector] = selector
268
- Isomorfeus::Puppetmaster::Node.new_by_tag(self, node.document, node_data)
269
- else
270
- raise Isomorfeus::Puppetmaster::ElementNotFound.new(selector)
271
- end
272
- rescue ExecJS::RuntimeError => e
273
- raise determine_error(e.message)
274
- end
275
-
276
- def node_find_all(node, selector)
277
- js_escaped_selector = selector.gsub('\\', '\\\\\\').gsub('"', '\"')
278
- node_data_array = @context.exec <<~JAVASCRIPT
279
- var node_array = AllElementHandles[#{node.handle}].querySelectorAll("#{js_escaped_selector}");
280
- var node_data_array = [];
281
- if (node_array) {
282
- for (var i=0; i<node_array.length; i++) {
283
- var node_handle = RegisterElementHandle(node_array[i]);
284
- var name = node_array[i].nodeName;
285
- var tag = node_array[i].tagName.toLowerCase();
286
- var type = null;
287
- if (tag === 'input') { type = node_array[i].getAttribute('type'); }
288
- node_data_array.push({handle: node_handle, name: name, tag: tag, type: type, content_editable: node_array[i].isContentEditable});
289
- }
290
- }
291
- return node_data_array;
292
- JAVASCRIPT
293
- node_data_array.map do |node_data|
294
- node_data[:css_selector] = selector
295
- Isomorfeus::Puppetmaster::Node.new_by_tag(self, node.document, node_data)
296
- end
297
- end
298
-
299
- def node_find_all_xpath(node, query)
300
- js_escaped_query = query.gsub('\\', '\\\\\\').gsub('"', '\"')
301
- node_data_array = @context.exec <<~JAVASCRIPT
302
- var window = AllDomHandles[#{node.document.handle}].window;
303
- var document = window.document;
304
- var xpath_result = document.evaluate("#{js_escaped_query}", AllElementHandles[#{node.handle}], null, window.XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
305
- var node;
306
- var node_data_array = [];
307
- while (node = xpath_result.iterateNext()) {
308
- var node_handle = RegisterElementHandle(node);
309
- var name = node.nodeName;
310
- var tag = node.tagName.toLowerCase();
311
- var type = null;
312
- if (tag === 'input') { type = node.getAttribute('type'); }
313
- node_data_array.push({handle: node_handle, name: name, tag: tag, type: type, content_editable: node.isContentEditable});
314
- }
315
- return node_data_array;
316
- JAVASCRIPT
317
- node_data_array.map do |node_data|
318
- node_data[:xpath_query] = query
319
- Isomorfeus::Puppetmaster::Node.new_by_tag(self, node.document, node_data)
320
- end
321
- end
322
-
323
- def node_find_xpath(node, query)
324
- js_escaped_query = query.gsub('\\', '\\\\\\').gsub('"', '\"')
325
- node_data = @context.exec <<~JAVASCRIPT
326
- var window = AllDomHandles[#{node.document.handle}].window;
327
- var document = window.document;
328
- var xpath_result = document.evaluate("#{js_escaped_query}", AllElementHandles[#{node.handle}], null, window.XPathResult.FIRST_ORDERED_NODE_TYPE, null);
329
- var node = xpath_result.singleNodeValue;
330
- if (node) {
331
- var node_handle = RegisterElementHandle(node);
332
- var name = node.nodeName;
333
- var tag = node.tagName.toLowerCase();
334
- var type = null;
335
- if (tag === 'input') { type = node.getAttribute('type'); }
336
- return {handle: node_handle, name: name, tag: tag, type: type, content_editable: node.isContentEditable};
337
- }
338
- JAVASCRIPT
339
- if node_data
340
- node_data[:xpath_query] = query
341
- Isomorfeus::Puppetmaster::Node.new_by_tag(self, node.document, node_data)
342
- else
343
- raise Isomorfeus::Puppetmaster::ElementNotFound.new(query)
344
- end
345
- rescue ExecJS::ProgramError => e
346
- raise determine_error('invalid xpath query')
347
- end
348
-
349
- def node_focus(node)
350
- await "await AllElementHandles[#{node.handle}].focus();"
351
- end
352
-
353
- def node_get_attribute(node, attribute)
354
- @context.eval "AllElementHandles[#{node.handle}].getAttribute('#{attribute}')"
355
- end
356
-
357
- def node_get_property(node, property)
358
- @context.eval "AllElementHandles[#{node.handle}]['#{property}']"
359
- end
360
-
361
- def node_hover(node)
362
- @context.exec "AllElementHandles[#{node.handle}].hover()"
363
- end
364
-
365
- def node_in_viewport?(node)
366
- await <<~JAVASCRIPT
367
- var node_handle = #{node.handle};
368
- var handle = await AllElementHandles[node_handle].executionContext().evaluateHandle(function(node) {
369
- var top = node.offsetTop, left = node.offsetLeft, width = node.offsetWidth, height = node.offsetHeight;
370
- while(node.offsetParent) { node = node.offsetParent; top += node.offsetTop; left += node.offsetLeft; }
371
- return (top >= window.pageYOffset && left >= window.pageXOffset &&
372
- (top + height) <= (window.pageYOffset + window.innerHeight) && (left + width) <= (window.pageXOffset + window.innerWidth));
373
- }, AllElementHandles[node_handle]);
374
- LastResult = await handle.jsonValue();
375
- JAVASCRIPT
376
- end
377
-
378
- def node_render_base64(_node, **_options)
379
- raise Isomorfeus::Puppetmaster::NotSupported
380
- end
381
-
382
- def node_right_click(node, x: nil, y: nil, modifiers: nil)
383
- # modifier_keys: :alt, :control, :meta, :shift
384
- modifiers = [modifiers] if modifiers.is_a?(Symbol)
385
- modifiers = [] unless modifiers
386
- modifiers = modifiers.map {|key| key.to_s.camelize(:lower) }
387
- await <<~JAVASCRIPT
388
- var options = {button: 2, bubbles: true, cancelable: true};
389
- var window = AllDomHandles[#{node.document.handle}].window;
390
- var modifiers = #{modifiers};
391
- if (modifiers.includes('meta')) { options['metaKey'] = true; }
392
- if (modifiers.includes('control')) { options['ctrlKey'] = true; }
393
- if (modifiers.includes('shift')) { options['shiftKey'] = true; }
394
- if (modifiers.includes('alt')) { options['altKey'] = true; }
395
- var x = #{x ? x : 'null'};
396
- var y = #{y ? y : 'null'};
397
- if (x && y) {
398
- options['clientX'] = x;
399
- options['clientY'] = y;
400
- }
401
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('mousedown', options));
402
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('mouseup', options));
403
- AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('contextmenu', options));
404
- JAVASCRIPT
405
- end
406
-
407
- def node_inner_html(node)
408
- @context.eval("AllElementHandles[#{node.handle}].innerHTML")
409
- end
410
-
411
- def node_save_screenshot(_node, _path, **_options)
412
- raise Isomorfeus::Puppetmaster::NotSupported
413
- end
414
-
415
- def node_scroll_by(node, x, y)
416
- await <<~JAVASCRIPT
417
- await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node) {
418
- node.scrollBy(#{x}, #{y});
419
- }, AllElementHandles[#{node.handle}]);
420
- JAVASCRIPT
421
- end
422
-
423
- def node_scroll_into_view(node)
424
- await <<~JAVASCRIPT
425
- await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node) {
426
- node.scrollIntoView();
427
- }, AllElementHandles[#{node.handle}]);
428
- JAVASCRIPT
429
- end
430
-
431
- def node_scroll_to(node, x, y)
432
- await <<~JAVASCRIPT
433
- await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node) {
434
- node.scrollTo(#{x}, #{y});
435
- }, AllElementHandles[#{node.handle}]);
436
- JAVASCRIPT
437
- end
438
-
439
- def node_select(node)
440
- # In the case of an OPTION tag, the change event should come
441
- # from the parent SELECT
442
- await <<~JAVASCRIPT
443
- await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node){
444
- var xpath = "parent::optgroup[@disabled] | ancestor::select[@disabled] | parent::fieldset[@disabled] | \
445
- ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]";
446
- if (node.disabled || document.evaluate(xpath, node, null, XPathResult.BOOLEAN_TYPE, null).booleanValue) { return false; }
447
- else if (node.value == false && !node.parentNode.multiple) { return false; }
448
- else {
449
- node.parentNode.dispatchEvent(new FocusEvent('focus',{bubbles: true, cancelable: true}));
450
- node.selected = true;
451
- var element;
452
- if (node.nodeName == "OPTION") {
453
- element = node.parentNode;
454
- if (element.nodeName == "OPTGROUP") { element = element.parentNode; }
455
- } else { element = node; }
456
- element.dispatchEvent(new Event('change',{bubbles: true, cancelable: false}));
457
- node.parentNode.dispatchEvent(new FocusEvent('blur',{bubbles: true, cancelable: true}));
458
- return true;
459
- }
460
- }, AllElementHandles[#{node.handle}]);
461
- JAVASCRIPT
462
- end
463
-
464
- def node_style(node, *styles)
465
- await <<~JAVASCRIPT
466
- var handle = await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node, styles){
467
- var style = window.getComputedStyle(node);
468
- if (styles.length > 0) {
469
- return styles.reduce(function(res,name) {
470
- res[name] = style[name];
471
- return res;
472
- }, {});
473
- } else { return style; }
474
- }, AllElementHandles[#{node.handle}], #{styles});
475
- LastResult = await handle.jsonValue();
476
- JAVASCRIPT
477
- end
478
-
479
- def node_type_keys(node, *keys)
480
- cjs = <<~JAVASCRIPT
481
- AllElementHandles[#{node.handle}].focus();
482
- var window = AllDomHandles[#{node.document.handle}].window;
483
- var events = [];
484
- var chars = '';
485
- var tag = AllElementHandles[#{node.handle}].tagName;
486
- JAVASCRIPT
487
- # new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: character.charCodeAt(0), char: character, shiftKey: false });
488
- top_modifiers = []
489
- keys.each do |key|
490
- if key.is_a?(String)
491
- key.each_char do |c|
492
- shift = !! /[[:upper:]]/.match(c)
493
- cjs << <<~JAVASCRIPT
494
- events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
495
- altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
496
- shiftKey: #{shift || need_shift?(top_modifiers)}}));
497
- JAVASCRIPT
498
- cjs << <<~JAVASCRIPT
499
- events.push(new window.KeyboardEvent('keypress', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
500
- altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
501
- shiftKey: #{shift || need_shift?(top_modifiers)}}));
502
- JAVASCRIPT
503
- # hack to make input actually happen, sort of
504
- cjs << <<~JAVASCRIPT
505
- chars = chars + '#{(shift || need_shift?(top_modifiers)) ? c.upcase : c}';
506
- JAVASCRIPT
507
- cjs << <<~JAVASCRIPT
508
- events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
509
- altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
510
- shiftKey: #{shift || need_shift?(top_modifiers)}}));
511
- JAVASCRIPT
512
- end
513
- elsif key.is_a?(Symbol)
514
- if %i[ctrl Ctrl].include?(key)
515
- key = :control
516
- elsif %i[command Command Meta].include?(key)
517
- key = :meta
518
- elsif %i[divide Divide].include?(key)
519
- key = :numpad_divide
520
- elsif %i[decimal Decimal].include?(key)
521
- key = :numpad_decimal
522
- elsif %i[left right up down].include?(key)
523
- key = "arrow_#{key}".to_sym
524
- end
525
- if %i[alt alt_left alt_right control control_left control_rigth meta meta_left meta_right shift shift_left shift_right].include?(key)
526
- top_modifiers << key
527
- cjs << <<~JAVASCRIPT
528
- events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
529
- altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
530
- shiftKey: #{need_shift?(top_modifiers)}}));
531
- JAVASCRIPT
532
- else
533
- cjs << <<~JAVASCRIPT
534
- events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
535
- altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
536
- shiftKey: #{need_shift?(top_modifiers)}}));
537
- JAVASCRIPT
538
- cjs << <<~JAVASCRIPT
539
- events.push(new window.KeyboardEvent('keypress', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
540
- altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
541
- shiftKey: #{need_shift?(top_modifiers)}}));
542
- JAVASCRIPT
543
- cjs << <<~JAVASCRIPT
544
- events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
545
- altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
546
- shiftKey: #{need_shift?(top_modifiers)}}));
547
- JAVASCRIPT
548
- end
549
- elsif key.is_a?(Array)
550
- modifiers = []
551
- key.each do |k|
552
- if k.is_a?(Symbol)
553
- if %i[ctrl Ctrl].include?(k)
554
- k = :control
555
- elsif %i[command Command Meta].include?(k)
556
- k = :meta
557
- elsif %i[divide Divide].include?(k)
558
- k = :numpad_divide
559
- elsif %i[decimal Decimal].include?(k)
560
- k = :numpad_decimal
561
- elsif %i[left right up down].include?(key)
562
- k = "arrow_#{key}".to_sym
563
- end
564
- if %i[alt alt_left alt_right control control_left control_rigth meta meta_left meta_right shift shift_left shift_right].include?(k)
565
- modifiers << k
566
- cjs << <<~JAVASCRIPT
567
- events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
568
- altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
569
- shiftKey: #{need_shift?(modifiers)}}));
570
- JAVASCRIPT
571
- else
572
- cjs << <<~JAVASCRIPT
573
- events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
574
- altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
575
- shiftKey: #{need_shift?(modifiers)}}));
576
- JAVASCRIPT
577
- cjs << <<~JAVASCRIPT
578
- events.push(new window.KeyboardEvent('keypress', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
579
- altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
580
- shiftKey: #{need_shift?(modifiers)}}));
581
- JAVASCRIPT
582
- cjs << <<~JAVASCRIPT
583
- events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
584
- altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
585
- shiftKey: #{need_shift?(modifiers)}}));
586
- JAVASCRIPT
587
- end
588
- elsif k.is_a?(String)
589
- k.each_char do |c|
590
- shift = !! /[[:upper:]]/.match(c)
591
- cjs << <<~JAVASCRIPT
592
- events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
593
- altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
594
- shiftKey: #{shift || need_shift?(modifiers)}}));
595
- JAVASCRIPT
596
- cjs << <<~JAVASCRIPT
597
- events.push(new window.KeyboardEvent('keypress', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
598
- altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
599
- shiftKey: #{shift || need_shift?(modifiers)}}));
600
- JAVASCRIPT
601
- # hack to make input actually happen, sort of
602
- cjs << <<~JAVASCRIPT
603
- chars = chars + '#{(shift || need_shift?(modifiers)) ? c.upcase : c}';
604
- JAVASCRIPT
605
- cjs << <<~JAVASCRIPT
606
- events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
607
- altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
608
- shiftKey: #{shift || need_shift?(modifiers)}}));
609
- JAVASCRIPT
610
- end
611
- end
612
- end
613
- modifiers.reverse.each do |k|
614
- cjs << <<~JAVASCRIPT
615
- events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
616
- altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
617
- shiftKey: #{need_shift?(modifiers)}}));
618
- JAVASCRIPT
619
- end
620
- end
621
- end
622
- top_modifiers.reverse.each do |key|
623
- cjs << <<~JAVASCRIPT
624
- events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
625
- altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
626
- shiftKey: #{need_shift?(top_modifiers)}}));
627
- JAVASCRIPT
628
- end
629
- cjs << <<~JAVASCRIPT
630
- for (i=0; i<events.length; i++) {
631
- AllElementHandles[#{node.handle}].dispatchEvent(events[i]);
632
- }
633
- if (tag === 'INPUT' || tag === 'TEXTAREA') {AllElementHandles[#{node.handle}].value = chars }
634
- JAVASCRIPT
635
- @context.exec cjs
636
- end
637
-
638
- def node_unselect(node)
639
- # In the case of an OPTION tag, the change event should come
640
- # from the parent SELECT
641
- await <<~JAVASCRIPT
642
- await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node){
643
- var xpath = "parent::optgroup[@disabled] | ancestor::select[@disabled] | parent::fieldset[@disabled] | \
644
- ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]";
645
- if (node.disabled || document.evaluate(xpath, node, null, XPathResult.BOOLEAN_TYPE, null).booleanValue) { return false; }
646
- else if (node.value == false && !node.parentNode.multiple) { return false; }
647
- else {
648
- node.parentNode.dispatchEvent(new FocusEvent('focus',{bubbles: true, cancelable: true}));
649
- node.selected = false;
650
- var element;
651
- if (node.nodeName == "OPTION") {
652
- element = node.parentNode;
653
- if (element.nodeName == "OPTGROUP") { element = element.parentNode; }
654
- } else { element = node; }
655
- element.dispatchEvent(new Event('change',{bubbles: true, cancelable: false}));
656
- node.parentNode.dispatchEvent(new FocusEvent('blur',{bubbles: true, cancelable: true}));
657
- return true;
658
- }
659
- }, AllElementHandles[#{node.handle}]);
660
- JAVASCRIPT
661
- end
662
-
663
- def node_value(node)
664
- @context.exec <<~JAVASCRIPT
665
- var node = AllElementHandles[#{node.handle}];
666
- if (node.tagName == "SELECT" && node.multiple) {
667
- var result = []
668
- for (let i = 0, len = node.children.length; i < len; i++) {
669
- var option = node.children[i];
670
- if (option.selected) { result.push(option.value); }
671
- }
672
- return result;
673
- } else if (node.isContentEditable) { return node.textContent; }
674
- else { return node.value; }
675
- JAVASCRIPT
676
- end
677
-
678
- def node_value=(node, value)
679
- raise Isomorfeus::Puppetmaster::ReadOnlyElementError if node.readonly?
680
- real_value = "`#{value}`"
681
- @context.exec <<~JAVASCRIPT
682
- var window = AllDomHandles[#{node.document.handle}].window;
683
- var node = AllElementHandles[#{node.handle}];
684
- var value = #{real_value};
685
- if (node.maxLength >= 0) { value = value.substr(0, node.maxLength); }
686
- node.dispatchEvent(new window.FocusEvent("focus",{bubbles: true, cancelable: true}));
687
- var tag_name = node.tagName.toLowerCase();
688
- if (tag_name === 'input') {
689
- node.value = '';
690
- if (node.type === "number" || node.type === "date") {
691
- for (var i = 0; i < value.length; i++) {
692
- node.dispatchEvent(new window.KeyboardEvent("keydown", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
693
- node.dispatchEvent(new window.KeyboardEvent("keyup", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
694
- node.dispatchEvent(new window.KeyboardEvent("keypress", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
695
- }
696
- node.value = value;
697
- }
698
- else if (node.type == "time") { node.value = new Date(value).toTimeString().split(" ")[0]; }
699
- else if (node.type == "datetime-local") {
700
- value = new Date(value);
701
- var year = value.getFullYear();
702
- var month = ("0" + (value.getMonth() + 1)).slice(-2);
703
- var date = ("0" + value.getDate()).slice(-2);
704
- var hour = ("0" + value.getHours()).slice(-2);
705
- var min = ("0" + value.getMinutes()).slice(-2);
706
- var sec = ("0" + value.getSeconds()).slice(-2);
707
- value = `${year}-${month}-${date}T${hour}:${min}:${sec}`;
708
- for (var i = 0; i < value.length; i++) {
709
- node.dispatchEvent(new window.KeyboardEvent("keydown", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
710
- node.value = node.value + value[i];
711
- node.dispatchEvent(new window.KeyboardEvent("keyup", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
712
- node.dispatchEvent(new window.KeyboardEvent("keypress", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
713
- }
714
- } else if (node.type === 'checkbox' || node.type === 'radio') { node.checked = value; }
715
- else {
716
- for (var i = 0; i < value.length; i++) {
717
- node.dispatchEvent(new window.KeyboardEvent("keydown", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
718
- node.value = node.value + value[i];
719
- node.dispatchEvent(new window.KeyboardEvent("keyup", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
720
- node.dispatchEvent(new window.KeyboardEvent("keypress", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
721
- }
722
- }
723
- } else if (tag_name === 'textarea') {
724
- for (var i = 0; i < value.length; i++) {
725
- node.dispatchEvent(new window.KeyboardEvent("keydown", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
726
- node.value = node.value + value[i];
727
- node.dispatchEvent(new window.KeyboardEvent("keyup", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
728
- node.dispatchEvent(new window.KeyboardEvent("keypress", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
729
- }
730
- }
731
- JAVASCRIPT
732
- real_value
733
- end
734
-
735
- def node_visible_text(node)
736
- # if node is AREA, check visibility of relevant image
737
- text = @context.exec <<~JAVASCRIPT
738
- var node = AllElementHandles[#{node.handle}];
739
- var window = AllDomHandles[#{node.document.handle}].window
740
- var temp_node = node;
741
- var mapName, style;
742
- if (node.tagName === "AREA") {
743
- mapName = document.evaluate("./ancestor::map/@name", node, null, XPathResult.STRING_TYPE, null).stringValue;
744
- temp_node = document.querySelector('img[usemap="#${mapName}"]');
745
- if (temp_node == null) { return ''; }
746
- } else {
747
- temp_node = node;
748
- while (temp_node) {
749
- style = window.getComputedStyle(node);
750
- if (style.display === "none" || style.visibility === "hidden" || parseFloat(style.opacity) === 0) { return ''; }
751
- temp_node = temp_node.parentElement;
752
- }
753
- }
754
- if (node.nodeName == "TEXTAREA" || node instanceof window.SVGElement) { return node.textContent; }
755
- else { return (node.innerText ? node.innerText : node.textContent); }
756
- JAVASCRIPT
757
- text.gsub(/\A[[:space:]&&[^\u00a0]]+/, "").gsub(/[[:space:]&&[^\u00a0]]+\z/, "").gsub(/\n+/, "\n").tr("\u00a0", " ")
758
- end
759
-
760
- def node_visible?(node)
761
- await <<~JAVASCRIPT
762
- var handle = await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node){
763
- if (node.tagName == 'AREA'){
764
- const map_name = document.evaluate('./ancestor::map/@name', node, null, XPathResult.STRING_TYPE, null).stringValue;
765
- node = document.querySelector(`img[usemap='#${map_name}']`);
766
- if (!node){ return false; }
767
- }
768
- var forced_visible = false;
769
- while (node) {
770
- const style = window.getComputedStyle(node);
771
- if (style.visibility == 'visible') { forced_visible = true; }
772
- if ((style.display == 'none') || ((style.visibility == 'hidden') && !forced_visible) || (parseFloat(style.opacity) == 0)) {
773
- return false;
774
- }
775
- node = node.parentElement;
776
- }
777
- return true;
778
- }, AllElementHandles[#{node.handle}]);
779
- LastResult = await handle.jsonValue();
780
- JAVASCRIPT
781
- end
782
-
783
- def node_wait_for(node, selector)
784
- js_escaped_selector = selector.gsub('\\', '\\\\\\').gsub('"', '\"')
785
- node_data = await <<~JAVASCRIPT
786
- var start_time = new Date();
787
- var resolver = function(resolve) {
788
- var node = AllElementHandles[#{node.handle}].querySelector("#{js_escaped_selector}");
789
- if (node) {
790
- var node_handle = RegisterElementHandle(node);
791
- var name = node.nodeName;
792
- var tag = node.tagName.toLowerCase();
793
- var type = null;
794
- if (tag === 'input') { type = node.getAttribute('type'); }
795
- LastResult = {handle: node_handle, name: name, tag: tag, type: type, content_editable: node.isContentEditable};
796
- resolve(true);
797
- }
798
- else if ((new Date() - start_time) > #{@jsdom_timeout}) { resolve(true); }
799
- else { setTimeout(resolver, #{@jsdom_reaction_timeout}, resolve) }
800
- };
801
- var promise = new Promise(function(resolve, reject){ resolver(resolve); });
802
- await promise;
803
- JAVASCRIPT
804
- if node_data
805
- node_data[:css_selector] = selector
806
- Isomorfeus::Puppetmaster::Node.new_by_tag(self, document, node_data)
807
- end
808
- end
809
-
810
- def node_wait_for_xpath(node, query)
811
- js_escaped_query = query.gsub('\\', '\\\\\\').gsub('"', '\"')
812
- node_data = await <<~JAVASCRIPT
813
- var start_time = new Date();
814
- var resolver = function(resolve) {
815
- var window = AllDomHandles[#{document.handle}].window;
816
- var document = window.document;
817
- var xpath_result = document.evaluate("#{js_escaped_query}", AllElementHandles[#{node.handle}], null, window.XPathResult.FIRST_ORDERED_NODE_TYPE, null);
818
- var node = xpath_result.singleNodeValue;
819
- if (node) {
820
- var node_handle = RegisterElementHandle(node);
821
- var name = node.nodeName;
822
- var tag = node.tagName.toLowerCase();
823
- var type = null;
824
- if (tag === 'input') { type = node.getAttribute('type'); }
825
- LastResult = {handle: node_handle, name: name, tag: tag, type: type, content_editable: node.isContentEditable};
826
- resolve(true);
827
- }
828
- else if ((new Date() - start_time) > #{@jsdom_timeout}) { resolve(true); }
829
- else { setTimeout(resolver, #{@jsdom_reaction_timeout}, resolve) }
830
- };
831
- var promise = new Promise(function(resolve, reject){ resolver(resolve); });
832
- await promise;
833
- JAVASCRIPT
834
- if node_data
835
- node_data[:xpath_query] = query
836
- Isomorfeus::Puppetmaster::Node.new_by_tag(self, document, node_data)
837
- end
838
- end
839
- end
840
- end
841
- end
842
- end