isomorfeus-puppetmaster 0.1.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.
- checksums.yaml +7 -0
- data/README.md +191 -0
- data/lib/isomorfeus-puppetmaster.rb +43 -0
- data/lib/isomorfeus/puppetmaster.rb +62 -0
- data/lib/isomorfeus/puppetmaster/console_message.rb +19 -0
- data/lib/isomorfeus/puppetmaster/cookie.rb +46 -0
- data/lib/isomorfeus/puppetmaster/document.rb +160 -0
- data/lib/isomorfeus/puppetmaster/driver/jsdom.rb +370 -0
- data/lib/isomorfeus/puppetmaster/driver/jsdom_document.rb +908 -0
- data/lib/isomorfeus/puppetmaster/driver/jsdom_node.rb +836 -0
- data/lib/isomorfeus/puppetmaster/driver/puppeteer.rb +401 -0
- data/lib/isomorfeus/puppetmaster/driver/puppeteer_document.rb +944 -0
- data/lib/isomorfeus/puppetmaster/driver/puppeteer_node.rb +866 -0
- data/lib/isomorfeus/puppetmaster/driver_registration.rb +19 -0
- data/lib/isomorfeus/puppetmaster/dsl.rb +40 -0
- data/lib/isomorfeus/puppetmaster/errors.rb +90 -0
- data/lib/isomorfeus/puppetmaster/iframe.rb +17 -0
- data/lib/isomorfeus/puppetmaster/node.rb +241 -0
- data/lib/isomorfeus/puppetmaster/node/checkbox.rb +17 -0
- data/lib/isomorfeus/puppetmaster/node/content_editable.rb +18 -0
- data/lib/isomorfeus/puppetmaster/node/filechooser.rb +9 -0
- data/lib/isomorfeus/puppetmaster/node/input.rb +21 -0
- data/lib/isomorfeus/puppetmaster/node/radiobutton.rb +13 -0
- data/lib/isomorfeus/puppetmaster/node/select.rb +36 -0
- data/lib/isomorfeus/puppetmaster/node/textarea.rb +7 -0
- data/lib/isomorfeus/puppetmaster/request.rb +17 -0
- data/lib/isomorfeus/puppetmaster/response.rb +26 -0
- data/lib/isomorfeus/puppetmaster/rspec/features.rb +23 -0
- data/lib/isomorfeus/puppetmaster/rspec/matcher_proxies.rb +80 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers.rb +164 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/base.rb +98 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/become_closed.rb +33 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/compound.rb +88 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/have_current_path.rb +29 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/have_selector.rb +69 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/have_text.rb +33 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/have_title.rb +29 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/match_selector.rb +27 -0
- data/lib/isomorfeus/puppetmaster/rspec/matchers/match_style.rb +38 -0
- data/lib/isomorfeus/puppetmaster/self_forwardable.rb +31 -0
- data/lib/isomorfeus/puppetmaster/server.rb +128 -0
- data/lib/isomorfeus/puppetmaster/server/checker.rb +40 -0
- data/lib/isomorfeus/puppetmaster/server/middleware.rb +60 -0
- data/lib/isomorfeus/puppetmaster/server_registration.rb +37 -0
- data/lib/isomorfeus/puppetmaster/version.rb +3 -0
- metadata +282 -0
@@ -0,0 +1,836 @@
|
|
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
|
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
|
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 tag = node.tagName.toLowerCase();
|
260
|
+
var type = null;
|
261
|
+
if (tag === 'input') { type = node.getAttribute('type'); }
|
262
|
+
return {handle: node_handle, tag: tag, type: type, content_editable: node.isContentEditable};
|
263
|
+
}
|
264
|
+
JAVASCRIPT
|
265
|
+
if node_data
|
266
|
+
node_data[:css_selector] = selector
|
267
|
+
Isomorfeus::Puppetmaster::Node.new_by_tag(self, node.document, node_data)
|
268
|
+
else
|
269
|
+
raise Isomorfeus::Puppetmaster::ElementNotFound.new(selector)
|
270
|
+
end
|
271
|
+
rescue ExecJS::RuntimeError => e
|
272
|
+
raise determine_error(e.message)
|
273
|
+
end
|
274
|
+
|
275
|
+
def node_find_all(node, selector)
|
276
|
+
js_escaped_selector = selector.gsub('\\', '\\\\\\').gsub('"', '\"')
|
277
|
+
node_data_array = @context.exec <<~JAVASCRIPT
|
278
|
+
var node_array = AllElementHandles[#{node.handle}].querySelectorAll("#{js_escaped_selector}");
|
279
|
+
var node_data_array = [];
|
280
|
+
if (node_array) {
|
281
|
+
for (var i=0; i<node_array.length; i++) {
|
282
|
+
var node_handle = RegisterElementHandle(node_array[i]);
|
283
|
+
var tag = node_array[i].tagName.toLowerCase();
|
284
|
+
var type = null;
|
285
|
+
if (tag === 'input') { type = node_array[i].getAttribute('type'); }
|
286
|
+
node_data_array.push({handle: node_handle, tag: tag, type: type, content_editable: node_array[i].isContentEditable});
|
287
|
+
}
|
288
|
+
}
|
289
|
+
return node_data_array;
|
290
|
+
JAVASCRIPT
|
291
|
+
node_data_array.map do |node_data|
|
292
|
+
node_data[:css_selector] = selector
|
293
|
+
Isomorfeus::Puppetmaster::Node.new_by_tag(self, node.document, node_data)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def node_find_all_xpath(node, query)
|
298
|
+
js_escaped_query = query.gsub('\\', '\\\\\\').gsub('"', '\"')
|
299
|
+
node_data_array = @context.exec <<~JAVASCRIPT
|
300
|
+
var window = AllDomHandles[#{node.document.handle}].window;
|
301
|
+
var document = window.document;
|
302
|
+
var xpath_result = document.evaluate("#{js_escaped_query}", AllElementHandles[#{node.handle}], null, window.XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
|
303
|
+
var node;
|
304
|
+
var node_data_array = [];
|
305
|
+
while (node = xpath_result.iterateNext) {
|
306
|
+
var node_handle = RegisterElementHandle(node);
|
307
|
+
var tag = node.tagName.toLowerCase();
|
308
|
+
var type = null;
|
309
|
+
if (tag === 'input') { type = node.getAttribute('type'); }
|
310
|
+
node_data_array.push({handle: node_handle, tag: tag, type: type, content_editable: node.isContentEditable});
|
311
|
+
}
|
312
|
+
return node_data_array;
|
313
|
+
JAVASCRIPT
|
314
|
+
node_data_array.map do |node_data|
|
315
|
+
node_data[:xpath_query] = query
|
316
|
+
Isomorfeus::Puppetmaster::Node.new_by_tag(self, node.document, node_data)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def node_find_xpath(node, query)
|
321
|
+
js_escaped_query = query.gsub('\\', '\\\\\\').gsub('"', '\"')
|
322
|
+
node_data = @context.exec <<~JAVASCRIPT
|
323
|
+
var window = AllDomHandles[#{node.document.handle}].window;
|
324
|
+
var document = window.document;
|
325
|
+
var xpath_result = document.evaluate("#{js_escaped_query}", AllElementHandles[#{node.handle}], null, window.XPathResult.FIRST_ORDERED_NODE_TYPE, null);
|
326
|
+
var node = xpath_result.singleNodeValue;
|
327
|
+
if (node) {
|
328
|
+
var node_handle = RegisterElementHandle(node);
|
329
|
+
var tag = node.tagName.toLowerCase();
|
330
|
+
var type = null;
|
331
|
+
if (tag === 'input') { type = node.getAttribute('type'); }
|
332
|
+
return {handle: node_handle, tag: tag, type: type, content_editable: node.isContentEditable};
|
333
|
+
}
|
334
|
+
JAVASCRIPT
|
335
|
+
if node_data
|
336
|
+
node_data[:xpath_query] = query
|
337
|
+
Isomorfeus::Puppetmaster::Node.new_by_tag(self, node.document, node_data)
|
338
|
+
else
|
339
|
+
raise Isomorfeus::Puppetmaster::ElementNotFound.new(query)
|
340
|
+
end
|
341
|
+
rescue ExecJS::ProgramError => e
|
342
|
+
raise determine_error('invalid xpath query')
|
343
|
+
end
|
344
|
+
|
345
|
+
def node_focus(node)
|
346
|
+
await "await AllElementHandles[#{node.handle}].focus();"
|
347
|
+
end
|
348
|
+
|
349
|
+
def node_get_attribute(node, attribute)
|
350
|
+
@context.eval "AllElementHandles[#{node.handle}].getAttribute('#{attribute}')"
|
351
|
+
end
|
352
|
+
|
353
|
+
def node_get_property(node, property)
|
354
|
+
@context.eval "AllElementHandles[#{node.handle}]['#{property}']"
|
355
|
+
end
|
356
|
+
|
357
|
+
def node_hover(node)
|
358
|
+
@context.exec "AllElementHandles[#{node.handle}].hover()"
|
359
|
+
end
|
360
|
+
|
361
|
+
def node_in_viewport?(node)
|
362
|
+
await <<~JAVASCRIPT
|
363
|
+
var node_handle = #{node.handle};
|
364
|
+
var handle = await AllElementHandles[node_handle].executionContext().evaluateHandle(function(node) {
|
365
|
+
var top = node.offsetTop, left = node.offsetLeft, width = node.offsetWidth, height = node.offsetHeight;
|
366
|
+
while(node.offsetParent) { node = node.offsetParent; top += node.offsetTop; left += node.offsetLeft; }
|
367
|
+
return (top >= window.pageYOffset && left >= window.pageXOffset &&
|
368
|
+
(top + height) <= (window.pageYOffset + window.innerHeight) && (left + width) <= (window.pageXOffset + window.innerWidth));
|
369
|
+
}, AllElementHandles[node_handle]);
|
370
|
+
LastResult = await handle.jsonValue();
|
371
|
+
JAVASCRIPT
|
372
|
+
end
|
373
|
+
|
374
|
+
def node_render_base64(_node, **_options)
|
375
|
+
raise Isomorfeus::Puppetmaster::NotSupported
|
376
|
+
end
|
377
|
+
|
378
|
+
def node_right_click(node, x: nil, y: nil, modifiers: nil)
|
379
|
+
# modifier_keys: :alt, :control, :meta, :shift
|
380
|
+
modifiers = [modifiers] if modifiers.is_a?(Symbol)
|
381
|
+
modifiers = [] unless modifiers
|
382
|
+
modifiers = modifiers.map {|key| key.to_s.camelize(:lower) }
|
383
|
+
await <<~JAVASCRIPT
|
384
|
+
var options = {button: 2, bubbles: true, cancelable: true};
|
385
|
+
var window = AllDomHandles[#{node.document.handle}].window;
|
386
|
+
var modifiers = #{modifiers};
|
387
|
+
if (modifiers.includes('meta')) { options['metaKey'] = true; }
|
388
|
+
if (modifiers.includes('control')) { options['ctrlKey'] = true; }
|
389
|
+
if (modifiers.includes('shift')) { options['shiftKey'] = true; }
|
390
|
+
if (modifiers.includes('alt')) { options['altKey'] = true; }
|
391
|
+
var x = #{x ? x : 'null'};
|
392
|
+
var y = #{y ? y : 'null'};
|
393
|
+
if (x && y) {
|
394
|
+
options['clientX'] = x;
|
395
|
+
options['clientY'] = y;
|
396
|
+
}
|
397
|
+
AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('mousedown', options));
|
398
|
+
AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('mouseup', options));
|
399
|
+
AllElementHandles[#{node.handle}].dispatchEvent(new window.MouseEvent('contextmenu', options));
|
400
|
+
JAVASCRIPT
|
401
|
+
end
|
402
|
+
|
403
|
+
def node_inner_html(node)
|
404
|
+
@context.eval("AllElementHandles[#{node.handle}].innerHTML")
|
405
|
+
end
|
406
|
+
|
407
|
+
def node_save_screenshot(_node, _path, **_options)
|
408
|
+
raise Isomorfeus::Puppetmaster::NotSupported
|
409
|
+
end
|
410
|
+
|
411
|
+
def node_scroll_by(node, x, y)
|
412
|
+
await <<~JAVASCRIPT
|
413
|
+
await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node) {
|
414
|
+
node.scrollBy(#{x}, #{y});
|
415
|
+
}, AllElementHandles[#{node.handle}]);
|
416
|
+
JAVASCRIPT
|
417
|
+
end
|
418
|
+
|
419
|
+
def node_scroll_into_view(node)
|
420
|
+
await <<~JAVASCRIPT
|
421
|
+
await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node) {
|
422
|
+
node.scrollIntoView();
|
423
|
+
}, AllElementHandles[#{node.handle}]);
|
424
|
+
JAVASCRIPT
|
425
|
+
end
|
426
|
+
|
427
|
+
def node_scroll_to(node, x, y)
|
428
|
+
await <<~JAVASCRIPT
|
429
|
+
await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node) {
|
430
|
+
node.scrollTo(#{x}, #{y});
|
431
|
+
}, AllElementHandles[#{node.handle}]);
|
432
|
+
JAVASCRIPT
|
433
|
+
end
|
434
|
+
|
435
|
+
def node_select(node)
|
436
|
+
# In the case of an OPTION tag, the change event should come
|
437
|
+
# from the parent SELECT
|
438
|
+
await <<~JAVASCRIPT
|
439
|
+
await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node){
|
440
|
+
var xpath = "parent::optgroup[@disabled] | ancestor::select[@disabled] | parent::fieldset[@disabled] | \
|
441
|
+
ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]";
|
442
|
+
if (node.disabled || document.evaluate(xpath, node, null, XPathResult.BOOLEAN_TYPE, null).booleanValue) { return false; }
|
443
|
+
else if (node.value == false && !node.parentNode.multiple) { return false; }
|
444
|
+
else {
|
445
|
+
node.parentNode.dispatchEvent(new FocusEvent('focus',{bubbles: true, cancelable: true}));
|
446
|
+
node.selected = true;
|
447
|
+
var element;
|
448
|
+
if (node.nodeName == "OPTION") {
|
449
|
+
element = node.parentNode;
|
450
|
+
if (element.nodeName == "OPTGROUP") { element = element.parentNode; }
|
451
|
+
} else { element = node; }
|
452
|
+
element.dispatchEvent(new Event('change',{bubbles: true, cancelable: false}));
|
453
|
+
node.parentNode.dispatchEvent(new FocusEvent('blur',{bubbles: true, cancelable: true}));
|
454
|
+
return true;
|
455
|
+
}
|
456
|
+
}, AllElementHandles[#{node.handle}]);
|
457
|
+
JAVASCRIPT
|
458
|
+
end
|
459
|
+
|
460
|
+
def node_style(node, *styles)
|
461
|
+
await <<~JAVASCRIPT
|
462
|
+
var handle = await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node, styles){
|
463
|
+
var style = window.getComputedStyle(node);
|
464
|
+
if (styles.length > 0) {
|
465
|
+
return styles.reduce(function(res,name) {
|
466
|
+
res[name] = style[name];
|
467
|
+
return res;
|
468
|
+
}, {});
|
469
|
+
} else { return style; }
|
470
|
+
}, AllElementHandles[#{node.handle}], #{styles});
|
471
|
+
LastResult = await handle.jsonValue();
|
472
|
+
JAVASCRIPT
|
473
|
+
end
|
474
|
+
|
475
|
+
def node_type_keys(node, *keys)
|
476
|
+
cjs = <<~JAVASCRIPT
|
477
|
+
AllElementHandles[#{node.handle}].focus();
|
478
|
+
var window = AllDomHandles[#{node.document.handle}].window;
|
479
|
+
var events = [];
|
480
|
+
var chars = '';
|
481
|
+
var tag = AllElementHandles[#{node.handle}].tagName;
|
482
|
+
JAVASCRIPT
|
483
|
+
# new KeyboardEvent("keydown", { bubbles: true, cancelable: true, key: character.charCodeAt(0), char: character, shiftKey: false });
|
484
|
+
top_modifiers = []
|
485
|
+
keys.each do |key|
|
486
|
+
if key.is_a?(String)
|
487
|
+
key.each_char do |c|
|
488
|
+
shift = !! /[[:upper:]]/.match(c)
|
489
|
+
cjs << <<~JAVASCRIPT
|
490
|
+
events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
|
491
|
+
altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
|
492
|
+
shiftKey: #{shift || need_shift?(top_modifiers)}}));
|
493
|
+
JAVASCRIPT
|
494
|
+
cjs << <<~JAVASCRIPT
|
495
|
+
events.push(new window.KeyboardEvent('keypress', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
|
496
|
+
altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
|
497
|
+
shiftKey: #{shift || need_shift?(top_modifiers)}}));
|
498
|
+
JAVASCRIPT
|
499
|
+
# hack to make input actually happen, sort of
|
500
|
+
cjs << <<~JAVASCRIPT
|
501
|
+
chars = chars + '#{(shift || need_shift?(top_modifiers)) ? c.upcase : c}';
|
502
|
+
JAVASCRIPT
|
503
|
+
cjs << <<~JAVASCRIPT
|
504
|
+
events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
|
505
|
+
altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
|
506
|
+
shiftKey: #{shift || need_shift?(top_modifiers)}}));
|
507
|
+
JAVASCRIPT
|
508
|
+
end
|
509
|
+
elsif key.is_a?(Symbol)
|
510
|
+
if %i[ctrl Ctrl].include?(key)
|
511
|
+
key = :control
|
512
|
+
elsif %i[command Command Meta].include?(key)
|
513
|
+
key = :meta
|
514
|
+
elsif %i[divide Divide].include?(key)
|
515
|
+
key = :numpad_divide
|
516
|
+
elsif %i[decimal Decimal].include?(key)
|
517
|
+
key = :numpad_decimal
|
518
|
+
elsif %i[left right up down].include?(key)
|
519
|
+
key = "arrow_#{key}".to_sym
|
520
|
+
end
|
521
|
+
if %i[alt alt_left alt_right control control_left control_rigth meta meta_left meta_right shift shift_left shift_right].include?(key)
|
522
|
+
top_modifiers << key
|
523
|
+
cjs << <<~JAVASCRIPT
|
524
|
+
events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
|
525
|
+
altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
|
526
|
+
shiftKey: #{need_shift?(top_modifiers)}}));
|
527
|
+
JAVASCRIPT
|
528
|
+
else
|
529
|
+
cjs << <<~JAVASCRIPT
|
530
|
+
events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
|
531
|
+
altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
|
532
|
+
shiftKey: #{need_shift?(top_modifiers)}}));
|
533
|
+
JAVASCRIPT
|
534
|
+
cjs << <<~JAVASCRIPT
|
535
|
+
events.push(new window.KeyboardEvent('keypress', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
|
536
|
+
altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
|
537
|
+
shiftKey: #{need_shift?(top_modifiers)}}));
|
538
|
+
JAVASCRIPT
|
539
|
+
cjs << <<~JAVASCRIPT
|
540
|
+
events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
|
541
|
+
altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
|
542
|
+
shiftKey: #{need_shift?(top_modifiers)}}));
|
543
|
+
JAVASCRIPT
|
544
|
+
end
|
545
|
+
elsif key.is_a?(Array)
|
546
|
+
modifiers = []
|
547
|
+
key.each do |k|
|
548
|
+
if k.is_a?(Symbol)
|
549
|
+
if %i[ctrl Ctrl].include?(k)
|
550
|
+
k = :control
|
551
|
+
elsif %i[command Command Meta].include?(k)
|
552
|
+
k = :meta
|
553
|
+
elsif %i[divide Divide].include?(k)
|
554
|
+
k = :numpad_divide
|
555
|
+
elsif %i[decimal Decimal].include?(k)
|
556
|
+
k = :numpad_decimal
|
557
|
+
elsif %i[left right up down].include?(key)
|
558
|
+
k = "arrow_#{key}".to_sym
|
559
|
+
end
|
560
|
+
if %i[alt alt_left alt_right control control_left control_rigth meta meta_left meta_right shift shift_left shift_right].include?(k)
|
561
|
+
modifiers << k
|
562
|
+
cjs << <<~JAVASCRIPT
|
563
|
+
events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
|
564
|
+
altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
|
565
|
+
shiftKey: #{need_shift?(modifiers)}}));
|
566
|
+
JAVASCRIPT
|
567
|
+
else
|
568
|
+
cjs << <<~JAVASCRIPT
|
569
|
+
events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
|
570
|
+
altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
|
571
|
+
shiftKey: #{need_shift?(modifiers)}}));
|
572
|
+
JAVASCRIPT
|
573
|
+
cjs << <<~JAVASCRIPT
|
574
|
+
events.push(new window.KeyboardEvent('keypress', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
|
575
|
+
altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
|
576
|
+
shiftKey: #{need_shift?(modifiers)}}));
|
577
|
+
JAVASCRIPT
|
578
|
+
cjs << <<~JAVASCRIPT
|
579
|
+
events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
|
580
|
+
altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
|
581
|
+
shiftKey: #{need_shift?(modifiers)}}));
|
582
|
+
JAVASCRIPT
|
583
|
+
end
|
584
|
+
elsif k.is_a?(String)
|
585
|
+
k.each_char do |c|
|
586
|
+
shift = !! /[[:upper:]]/.match(c)
|
587
|
+
cjs << <<~JAVASCRIPT
|
588
|
+
events.push(new window.KeyboardEvent('keydown', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
|
589
|
+
altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
|
590
|
+
shiftKey: #{shift || need_shift?(modifiers)}}));
|
591
|
+
JAVASCRIPT
|
592
|
+
cjs << <<~JAVASCRIPT
|
593
|
+
events.push(new window.KeyboardEvent('keypress', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
|
594
|
+
altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
|
595
|
+
shiftKey: #{shift || need_shift?(modifiers)}}));
|
596
|
+
JAVASCRIPT
|
597
|
+
# hack to make input actually happen, sort of
|
598
|
+
cjs << <<~JAVASCRIPT
|
599
|
+
chars = chars + '#{(shift || need_shift?(modifiers)) ? c.upcase : c}';
|
600
|
+
JAVASCRIPT
|
601
|
+
cjs << <<~JAVASCRIPT
|
602
|
+
events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{c}'.charCodeAt(0), char: '#{c}',
|
603
|
+
altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
|
604
|
+
shiftKey: #{shift || need_shift?(modifiers)}}));
|
605
|
+
JAVASCRIPT
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
609
|
+
modifiers.reverse.each do |k|
|
610
|
+
cjs << <<~JAVASCRIPT
|
611
|
+
events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{k.to_s.camelize}', code: '#{k.to_s.camelize}',
|
612
|
+
altKey: #{need_alt?(modifiers)}, ctrlKey: #{need_control?(modifiers)}, metaKey: #{need_meta?(modifiers)},
|
613
|
+
shiftKey: #{need_shift?(modifiers)}}));
|
614
|
+
JAVASCRIPT
|
615
|
+
end
|
616
|
+
end
|
617
|
+
end
|
618
|
+
top_modifiers.reverse.each do |key|
|
619
|
+
cjs << <<~JAVASCRIPT
|
620
|
+
events.push(new window.KeyboardEvent('keyup', { bubbles: true, cancelable: true, key: '#{key.to_s.camelize}', code: '#{key.to_s.camelize}',
|
621
|
+
altKey: #{need_alt?(top_modifiers)}, ctrlKey: #{need_control?(top_modifiers)}, metaKey: #{need_meta?(top_modifiers)},
|
622
|
+
shiftKey: #{need_shift?(top_modifiers)}}));
|
623
|
+
JAVASCRIPT
|
624
|
+
end
|
625
|
+
cjs << <<~JAVASCRIPT
|
626
|
+
for (i=0; i<events.length; i++) {
|
627
|
+
AllElementHandles[#{node.handle}].dispatchEvent(events[i]);
|
628
|
+
}
|
629
|
+
if (tag === 'INPUT' || tag === 'TEXTAREA') {AllElementHandles[#{node.handle}].value = chars }
|
630
|
+
JAVASCRIPT
|
631
|
+
@context.exec cjs
|
632
|
+
end
|
633
|
+
|
634
|
+
def node_unselect(node)
|
635
|
+
# In the case of an OPTION tag, the change event should come
|
636
|
+
# from the parent SELECT
|
637
|
+
await <<~JAVASCRIPT
|
638
|
+
await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node){
|
639
|
+
var xpath = "parent::optgroup[@disabled] | ancestor::select[@disabled] | parent::fieldset[@disabled] | \
|
640
|
+
ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]";
|
641
|
+
if (node.disabled || document.evaluate(xpath, node, null, XPathResult.BOOLEAN_TYPE, null).booleanValue) { return false; }
|
642
|
+
else if (node.value == false && !node.parentNode.multiple) { return false; }
|
643
|
+
else {
|
644
|
+
node.parentNode.dispatchEvent(new FocusEvent('focus',{bubbles: true, cancelable: true}));
|
645
|
+
node.selected = false;
|
646
|
+
var element;
|
647
|
+
if (node.nodeName == "OPTION") {
|
648
|
+
element = node.parentNode;
|
649
|
+
if (element.nodeName == "OPTGROUP") { element = element.parentNode; }
|
650
|
+
} else { element = node; }
|
651
|
+
element.dispatchEvent(new Event('change',{bubbles: true, cancelable: false}));
|
652
|
+
node.parentNode.dispatchEvent(new FocusEvent('blur',{bubbles: true, cancelable: true}));
|
653
|
+
return true;
|
654
|
+
}
|
655
|
+
}, AllElementHandles[#{node.handle}]);
|
656
|
+
JAVASCRIPT
|
657
|
+
end
|
658
|
+
|
659
|
+
def node_value(node)
|
660
|
+
@context.exec <<~JAVASCRIPT
|
661
|
+
var node = AllElementHandles[#{node.handle}];
|
662
|
+
if (node.tagName == "SELECT" && node.multiple) {
|
663
|
+
var result = []
|
664
|
+
for (let i = 0, len = node.children.length; i < len; i++) {
|
665
|
+
var option = node.children[i];
|
666
|
+
if (option.selected) { result.push(option.value); }
|
667
|
+
}
|
668
|
+
return result;
|
669
|
+
} else if (node.isContentEditable) { return node.textContent; }
|
670
|
+
else { return node.value; }
|
671
|
+
JAVASCRIPT
|
672
|
+
end
|
673
|
+
|
674
|
+
def node_value=(node, value)
|
675
|
+
raise Isomorfeus::Puppetmaster::ReadOnlyElementError if node.readonly?
|
676
|
+
real_value = "`#{value}`"
|
677
|
+
@context.exec <<~JAVASCRIPT
|
678
|
+
var window = AllDomHandles[#{node.document.handle}].window;
|
679
|
+
var node = AllElementHandles[#{node.handle}];
|
680
|
+
var value = #{real_value};
|
681
|
+
if (node.maxLength >= 0) { value = value.substr(0, node.maxLength); }
|
682
|
+
node.dispatchEvent(new window.FocusEvent("focus",{bubbles: true, cancelable: true}));
|
683
|
+
var tag_name = node.tagName.toLowerCase();
|
684
|
+
if (tag_name === 'input') {
|
685
|
+
node.value = '';
|
686
|
+
if (node.type === "number" || node.type === "date") {
|
687
|
+
for (var i = 0; i < value.length; i++) {
|
688
|
+
node.dispatchEvent(new window.KeyboardEvent("keydown", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
689
|
+
node.dispatchEvent(new window.KeyboardEvent("keyup", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
690
|
+
node.dispatchEvent(new window.KeyboardEvent("keypress", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
691
|
+
}
|
692
|
+
node.value = value;
|
693
|
+
}
|
694
|
+
else if (node.type == "time") { node.value = new Date(value).toTimeString().split(" ")[0]; }
|
695
|
+
else if (node.type == "datetime-local") {
|
696
|
+
value = new Date(value);
|
697
|
+
var year = value.getFullYear();
|
698
|
+
var month = ("0" + (value.getMonth() + 1)).slice(-2);
|
699
|
+
var date = ("0" + value.getDate()).slice(-2);
|
700
|
+
var hour = ("0" + value.getHours()).slice(-2);
|
701
|
+
var min = ("0" + value.getMinutes()).slice(-2);
|
702
|
+
var sec = ("0" + value.getSeconds()).slice(-2);
|
703
|
+
value = `${year}-${month}-${date}T${hour}:${min}:${sec}`;
|
704
|
+
for (var i = 0; i < value.length; i++) {
|
705
|
+
node.dispatchEvent(new window.KeyboardEvent("keydown", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
706
|
+
node.value = node.value + value[i];
|
707
|
+
node.dispatchEvent(new window.KeyboardEvent("keyup", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
708
|
+
node.dispatchEvent(new window.KeyboardEvent("keypress", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
709
|
+
}
|
710
|
+
} else if (node.type === 'checkbox' || node.type === 'radio') { node.checked = value; }
|
711
|
+
else {
|
712
|
+
for (var i = 0; i < value.length; i++) {
|
713
|
+
node.dispatchEvent(new window.KeyboardEvent("keydown", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
714
|
+
node.value = node.value + value[i];
|
715
|
+
node.dispatchEvent(new window.KeyboardEvent("keyup", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
716
|
+
node.dispatchEvent(new window.KeyboardEvent("keypress", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
717
|
+
}
|
718
|
+
}
|
719
|
+
} else if (tag_name === 'textarea') {
|
720
|
+
for (var i = 0; i < value.length; i++) {
|
721
|
+
node.dispatchEvent(new window.KeyboardEvent("keydown", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
722
|
+
node.value = node.value + value[i];
|
723
|
+
node.dispatchEvent(new window.KeyboardEvent("keyup", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
724
|
+
node.dispatchEvent(new window.KeyboardEvent("keypress", {key: value[i], code: value.charCodeAt(0), bubbles: true, cancelable: true}));
|
725
|
+
}
|
726
|
+
}
|
727
|
+
JAVASCRIPT
|
728
|
+
real_value
|
729
|
+
end
|
730
|
+
|
731
|
+
def node_visible_text(node)
|
732
|
+
# if node is AREA, check visibility of relevant image
|
733
|
+
text = @context.exec <<~JAVASCRIPT
|
734
|
+
var node = AllElementHandles[#{node.handle}];
|
735
|
+
var window = AllDomHandles[#{node.document.handle}].window
|
736
|
+
var temp_node = node;
|
737
|
+
var mapName, style;
|
738
|
+
if (node.tagName === "AREA") {
|
739
|
+
mapName = document.evaluate("./ancestor::map/@name", node, null, XPathResult.STRING_TYPE, null).stringValue;
|
740
|
+
temp_node = document.querySelector('img[usemap="#${mapName}"]');
|
741
|
+
if (temp_node == null) { return ''; }
|
742
|
+
} else {
|
743
|
+
temp_node = node;
|
744
|
+
while (temp_node) {
|
745
|
+
style = window.getComputedStyle(node);
|
746
|
+
if (style.display === "none" || style.visibility === "hidden" || parseFloat(style.opacity) === 0) { return ''; }
|
747
|
+
temp_node = temp_node.parentElement;
|
748
|
+
}
|
749
|
+
}
|
750
|
+
if (node.nodeName == "TEXTAREA" || node instanceof window.SVGElement) { return node.textContent; }
|
751
|
+
else { return (node.innerText ? node.innerText : node.textContent); }
|
752
|
+
JAVASCRIPT
|
753
|
+
text.gsub(/\A[[:space:]&&[^\u00a0]]+/, "").gsub(/[[:space:]&&[^\u00a0]]+\z/, "").gsub(/\n+/, "\n").tr("\u00a0", " ")
|
754
|
+
end
|
755
|
+
|
756
|
+
def node_visible?(node)
|
757
|
+
await <<~JAVASCRIPT
|
758
|
+
var handle = await AllElementHandles[#{node.handle}].executionContext().evaluateHandle(function(node){
|
759
|
+
if (node.tagName == 'AREA'){
|
760
|
+
const map_name = document.evaluate('./ancestor::map/@name', node, null, XPathResult.STRING_TYPE, null).stringValue;
|
761
|
+
node = document.querySelector(`img[usemap='#${map_name}']`);
|
762
|
+
if (!node){ return false; }
|
763
|
+
}
|
764
|
+
var forced_visible = false;
|
765
|
+
while (node) {
|
766
|
+
const style = window.getComputedStyle(node);
|
767
|
+
if (style.visibility == 'visible') { forced_visible = true; }
|
768
|
+
if ((style.display == 'none') || ((style.visibility == 'hidden') && !forced_visible) || (parseFloat(style.opacity) == 0)) {
|
769
|
+
return false;
|
770
|
+
}
|
771
|
+
node = node.parentElement;
|
772
|
+
}
|
773
|
+
return true;
|
774
|
+
}, AllElementHandles[#{node.handle}]);
|
775
|
+
LastResult = await handle.jsonValue();
|
776
|
+
JAVASCRIPT
|
777
|
+
end
|
778
|
+
|
779
|
+
def node_wait_for(node, selector)
|
780
|
+
js_escaped_selector = selector.gsub('\\', '\\\\\\').gsub('"', '\"')
|
781
|
+
node_data = await <<~JAVASCRIPT
|
782
|
+
var start_time = new Date();
|
783
|
+
var resolver = function(resolve) {
|
784
|
+
var node = AllElementHandles[#{node.handle}].querySelector("#{js_escaped_selector}");
|
785
|
+
if (node) {
|
786
|
+
var node_handle = RegisterElementHandle(node);
|
787
|
+
var tag = node.tagName.toLowerCase();
|
788
|
+
var type = null;
|
789
|
+
if (tag === 'input') { type = node.getAttribute('type'); }
|
790
|
+
LastResult = {handle: node_handle, tag: tag, type: type, content_editable: node.isContentEditable};
|
791
|
+
resolve(true);
|
792
|
+
}
|
793
|
+
else if ((new Date() - start_time) > #{@jsdom_timeout}) { resolve(true); }
|
794
|
+
else { setTimeout(resolver, #{@jsdom_reaction_timeout}, resolve) }
|
795
|
+
};
|
796
|
+
var promise = new Promise(function(resolve, reject){ resolver(resolve); });
|
797
|
+
await promise;
|
798
|
+
JAVASCRIPT
|
799
|
+
if node_data
|
800
|
+
node_data[:css_selector] = selector
|
801
|
+
Isomorfeus::Puppetmaster::Node.new_by_tag(self, document, node_data)
|
802
|
+
end
|
803
|
+
end
|
804
|
+
|
805
|
+
def node_wait_for_xpath(node, query)
|
806
|
+
js_escaped_query = query.gsub('\\', '\\\\\\').gsub('"', '\"')
|
807
|
+
node_data = await <<~JAVASCRIPT
|
808
|
+
var start_time = new Date();
|
809
|
+
var resolver = function(resolve) {
|
810
|
+
var window = AllDomHandles[#{document.handle}].window;
|
811
|
+
var document = window.document;
|
812
|
+
var xpath_result = document.evaluate("#{js_escaped_query}", AllElementHandles[#{node.handle}], null, window.XPathResult.FIRST_ORDERED_NODE_TYPE, null);
|
813
|
+
var node = xpath_result.singleNodeValue;
|
814
|
+
if (node) {
|
815
|
+
var node_handle = RegisterElementHandle(node);
|
816
|
+
var tag = node.tagName.toLowerCase();
|
817
|
+
var type = null;
|
818
|
+
if (tag === 'input') { type = node.getAttribute('type'); }
|
819
|
+
LastResult = {handle: node_handle, tag: tag, type: type, content_editable: node.isContentEditable};
|
820
|
+
resolve(true);
|
821
|
+
}
|
822
|
+
else if ((new Date() - start_time) > #{@jsdom_timeout}) { resolve(true); }
|
823
|
+
else { setTimeout(resolver, #{@jsdom_reaction_timeout}, resolve) }
|
824
|
+
};
|
825
|
+
var promise = new Promise(function(resolve, reject){ resolver(resolve); });
|
826
|
+
await promise;
|
827
|
+
JAVASCRIPT
|
828
|
+
if node_data
|
829
|
+
node_data[:xpath_query] = query
|
830
|
+
Isomorfeus::Puppetmaster::Node.new_by_tag(self, document, node_data)
|
831
|
+
end
|
832
|
+
end
|
833
|
+
end
|
834
|
+
end
|
835
|
+
end
|
836
|
+
end
|