isomorfeus-puppetmaster 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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