cuprite 0.13 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Capybara
4
- module Cuprite
5
- class Error < StandardError; end
6
-
7
- class ClientError < Error
8
- attr_reader :response
9
-
10
- def initialize(response)
11
- @response = response
12
- end
13
- end
14
-
15
- class InvalidSelector < ClientError
16
- def initialize(response, method, selector)
17
- super(response)
18
- @method, @selector = method, selector
19
- end
20
-
21
- def message
22
- "Browser raised error trying to find #{@method}: #{@selector.inspect}"
23
- end
24
- end
25
-
26
- class MouseEventFailed < ClientError
27
- attr_reader :name, :selector, :position
28
-
29
- def initialize(*)
30
- super
31
- data = /\A\w+: (\w+), (.+?), ([\d\.-]+), ([\d\.-]+)/.match(@response)
32
- @name, @selector = data.values_at(1, 2)
33
- @position = data.values_at(3, 4).map(&:to_f)
34
- end
35
-
36
-
37
- def message
38
- "Firing a #{name} at coordinates [#{position.join(", ")}] failed. Cuprite detected " \
39
- "another element with CSS selector \"#{selector}\" at this position. " \
40
- "It may be overlapping the element you are trying to interact with. " \
41
- "If you don't care about overlapping elements, try using node.trigger(\"#{name}\")."
42
- end
43
- end
44
-
45
- class ObsoleteNode < ClientError
46
- attr_reader :node
47
-
48
- def initialize(node, response)
49
- @node = node
50
- super(response)
51
- end
52
-
53
- def message
54
- "The element you are trying to interact with is either not part of the DOM, or is " \
55
- "not currently visible on the page (perhaps display: none is set). " \
56
- "It is possible the element has been replaced by another element and you meant to interact with " \
57
- "the new element. If so you need to do a new find in order to get a reference to the " \
58
- "new element."
59
- end
60
- end
61
- end
62
- end
@@ -1,478 +0,0 @@
1
- class InvalidSelector extends Error {}
2
- class TimedOutPromise extends Error {}
3
- class MouseEventFailed extends Error {}
4
-
5
- const EVENTS = {
6
- FOCUS: ["blur", "focus", "focusin", "focusout"],
7
- MOUSE: ["click", "dblclick", "mousedown", "mouseenter", "mouseleave",
8
- "mousemove", "mouseover", "mouseout", "mouseup", "contextmenu"],
9
- FORM: ["submit"]
10
- }
11
-
12
- class Cuprite {
13
- constructor() {
14
- this._json = JSON; // In case someone overrides it like mootools
15
- }
16
-
17
- find(method, selector, within = document) {
18
- try {
19
- let results = [];
20
-
21
- if (method == "xpath") {
22
- let xpath = document.evaluate(selector, within, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
23
- for (let i = 0; i < xpath.snapshotLength; i++) {
24
- results.push(xpath.snapshotItem(i));
25
- }
26
- } else {
27
- results = Array.from(within.querySelectorAll(selector));
28
- }
29
-
30
- return results;
31
- } catch (error) {
32
- // DOMException.INVALID_EXPRESSION_ERR is undefined, using pure code
33
- if (error.code == DOMException.SYNTAX_ERR || error.code == 51) {
34
- throw new InvalidSelector;
35
- } else {
36
- throw error;
37
- }
38
- }
39
- }
40
-
41
- parents(node) {
42
- let nodes = [];
43
- let parent = node.parentNode;
44
- while (parent != document && parent !== null) {
45
- nodes.push(parent);
46
- parent = parent.parentNode;
47
- }
48
- return nodes;
49
- }
50
-
51
- visibleText(node) {
52
- if (this.isVisible(node)) {
53
- if (node.nodeName == "TEXTAREA") {
54
- return node.textContent;
55
- } else {
56
- if (node instanceof SVGElement) {
57
- return node.textContent;
58
- } else {
59
- return node.innerText;
60
- }
61
- }
62
- }
63
- }
64
-
65
- isVisible(node) {
66
- let mapName, style;
67
- // if node is area, check visibility of relevant image
68
- if (node.tagName === "AREA") {
69
- mapName = document.evaluate("./ancestor::map/@name", node, null, XPathResult.STRING_TYPE, null).stringValue;
70
- node = document.querySelector(`img[usemap="#${mapName}"]`);
71
- if (node == null) {
72
- return false;
73
- }
74
- }
75
-
76
- while (node) {
77
- style = window.getComputedStyle(node);
78
- if (style.display === "none" || style.visibility === "hidden" || parseFloat(style.opacity) === 0) {
79
- return false;
80
- }
81
- node = node.parentElement;
82
- }
83
-
84
- return true;
85
- }
86
-
87
-
88
- isDisabled(node) {
89
- let xpath = "parent::optgroup[@disabled] | \
90
- ancestor::select[@disabled] | \
91
- parent::fieldset[@disabled] | \
92
- ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]";
93
-
94
- return node.disabled || document.evaluate(xpath, node, null, XPathResult.BOOLEAN_TYPE, null).booleanValue;
95
- }
96
-
97
- path(node) {
98
- let nodes = [node];
99
- let parent = node.parentNode;
100
- while (parent !== document && parent !== null) {
101
- nodes.unshift(parent);
102
- parent = parent.parentNode;
103
- }
104
-
105
- let selectors = nodes.map(node => {
106
- let prevSiblings = [];
107
- let xpath = document.evaluate(`./preceding-sibling::${node.tagName}`, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
108
-
109
- for (let i = 0; i < xpath.snapshotLength; i++) {
110
- prevSiblings.push(xpath.snapshotItem(i));
111
- }
112
-
113
- return `${node.tagName}[${(prevSiblings.length + 1)}]`;
114
- });
115
-
116
- return `//${selectors.join("/")}`;
117
- }
118
-
119
- set(node, value) {
120
- if (node.readOnly) return;
121
-
122
- if (node.maxLength >= 0) {
123
- value = value.substr(0, node.maxLength);
124
- }
125
-
126
- let valueBefore = node.value;
127
-
128
- this.trigger(node, "focus");
129
- this.setValue(node, "");
130
-
131
- if (node.type == "number" || node.type == "date" || node.type == "range") {
132
- this.setValue(node, value);
133
- this.input(node);
134
- } else if (node.type == "time") {
135
- this.setValue(node, new Date(value).toTimeString().split(" ")[0]);
136
- this.input(node);
137
- } else if (node.type == "datetime-local") {
138
- value = new Date(value);
139
- let year = value.getFullYear();
140
- let month = ("0" + (value.getMonth() + 1)).slice(-2);
141
- let date = ("0" + value.getDate()).slice(-2);
142
- let hour = ("0" + value.getHours()).slice(-2);
143
- let min = ("0" + value.getMinutes()).slice(-2);
144
- let sec = ("0" + value.getSeconds()).slice(-2);
145
- this.setValue(node, `${year}-${month}-${date}T${hour}:${min}:${sec}`);
146
- this.input(node);
147
- } else {
148
- for (let i = 0; i < value.length; i++) {
149
- let char = value[i];
150
- let keyCode = this.characterToKeyCode(char);
151
- // call the following functions in order, if one returns false (preventDefault),
152
- // stop the call chain
153
- [
154
- () => this.keyupdowned(node, "keydown", keyCode),
155
- () => this.keypressed(node, false, false, false, false, char.charCodeAt(0), char.charCodeAt(0)),
156
- () => {
157
- this.setValue(node, node.value + char)
158
- this.input(node)
159
- }
160
- ].some(fn => fn())
161
-
162
- this.keyupdowned(node, "keyup", keyCode);
163
- }
164
- }
165
-
166
- if (valueBefore !== node.value) {
167
- this.changed(node);
168
- }
169
- this.trigger(node, "blur");
170
- }
171
-
172
- setValue(node, value) {
173
- let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
174
- let nativeTextareaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
175
-
176
- if (node.tagName.toLowerCase() === 'input') {
177
- return nativeInputValueSetter.call(node, value);
178
- }
179
- return nativeTextareaValueSetter.call(node, value);
180
- }
181
-
182
- input(node) {
183
- let event = document.createEvent("HTMLEvents");
184
- event.initEvent("input", true, false);
185
- node.dispatchEvent(event);
186
- }
187
-
188
- /**
189
- * @return {boolean} false when an event handler called preventDefault()
190
- */
191
- keyupdowned(node, eventName, keyCode) {
192
- let event = document.createEvent("UIEvents");
193
- event.initEvent(eventName, true, true);
194
- event.keyCode = keyCode;
195
- event.charCode = 0;
196
- return !node.dispatchEvent(event);
197
- }
198
-
199
- /**
200
- * @return {boolean} false when an event handler called preventDefault()
201
- */
202
- keypressed(node, altKey, ctrlKey, shiftKey, metaKey, keyCode, charCode) {
203
- event = document.createEvent("UIEvents");
204
- event.initEvent("keypress", true, true);
205
- event.window = window;
206
- event.altKey = altKey;
207
- event.ctrlKey = ctrlKey;
208
- event.shiftKey = shiftKey;
209
- event.metaKey = metaKey;
210
- event.keyCode = keyCode;
211
- event.charCode = charCode;
212
- return !node.dispatchEvent(event);
213
- }
214
-
215
- characterToKeyCode(char) {
216
- const specialKeys = {
217
- 96: 192, // `
218
- 45: 189, // -
219
- 61: 187, // =
220
- 91: 219, // [
221
- 93: 221, // ]
222
- 92: 220, // \
223
- 59: 186, // ;
224
- 39: 222, // '
225
- 44: 188, // ,
226
- 46: 190, // .
227
- 47: 191, // /
228
- 127: 46, // delete
229
- 126: 192, // ~
230
- 33: 49, // !
231
- 64: 50, // @
232
- 35: 51, // #
233
- 36: 52, // $
234
- 37: 53, // %
235
- 94: 54, // ^
236
- 38: 55, // &
237
- 42: 56, // *
238
- 40: 57, // (
239
- 41: 48, // )
240
- 95: 189, // _
241
- 43: 187, // +
242
- 123: 219, // {
243
- 125: 221, // }
244
- 124: 220, // |
245
- 58: 186, // :
246
- 34: 222, // "
247
- 60: 188, // <
248
- 62: 190, // >
249
- 63: 191, // ?
250
- }
251
-
252
- let code = char.toUpperCase().charCodeAt(0);
253
- return specialKeys[code] || code;
254
- }
255
-
256
- scrollIntoViewport(node) {
257
- let areaImage = this._getAreaImage(node);
258
-
259
- if (areaImage) {
260
- return this.scrollIntoViewport(areaImage);
261
- } else {
262
- node.scrollIntoViewIfNeeded();
263
-
264
- if (!this._isInViewport(node)) {
265
- node.scrollIntoView({block: "center", inline: "center", behavior: "instant"});
266
- return this._isInViewport(node);
267
- }
268
-
269
- return true;
270
- }
271
- }
272
-
273
- mouseEventTest(node, name, x, y) {
274
- let frameOffset = this._frameOffset();
275
- x -= frameOffset.left;
276
- y -= frameOffset.top;
277
-
278
- let element = document.elementFromPoint(x, y);
279
-
280
- let el = element;
281
- while (el) {
282
- if (el == node) {
283
- return true;
284
- } else {
285
- el = el.parentNode;
286
- }
287
- }
288
-
289
- let selector = element && this._getSelector(element) || "none";
290
- throw new MouseEventFailed([name, selector, x, y].join(", "));
291
- }
292
-
293
- _getAreaImage(node) {
294
- if ("area" == node.tagName.toLowerCase()) {
295
- let map = node.parentNode;
296
- if (map.tagName.toLowerCase() != "map") {
297
- throw new Error("the area is not within a map");
298
- }
299
-
300
- let mapName = map.getAttribute("name");
301
- if (typeof mapName === "undefined" || mapName === null) {
302
- throw new Error("area's parent map must have a name");
303
- }
304
-
305
- mapName = `#${mapName.toLowerCase()}`;
306
- let imageNode = this.find("css", `img[usemap='${mapName}']`)[0];
307
- if (typeof imageNode === "undefined" || imageNode === null) {
308
- throw new Error("no image matches the map");
309
- }
310
-
311
- return imageNode;
312
- }
313
- }
314
-
315
- _frameOffset() {
316
- let win = window;
317
- let offset = { top: 0, left: 0 };
318
-
319
- while (win.frameElement) {
320
- let rect = win.frameElement.getClientRects()[0];
321
- let style = win.getComputedStyle(win.frameElement);
322
- win = win.parent;
323
-
324
- offset.top += rect.top + parseInt(style.getPropertyValue("padding-top"), 10)
325
- offset.left += rect.left + parseInt(style.getPropertyValue("padding-left"), 10)
326
- }
327
-
328
- return offset;
329
- }
330
-
331
- _getSelector(el) {
332
- let selector = (el.tagName != 'HTML') ? this._getSelector(el.parentNode) + " " : "";
333
- selector += el.tagName.toLowerCase();
334
- if (el.id) { selector += `#${el.id}` };
335
- el.classList.forEach(c => selector += `.${c}`);
336
- return selector;
337
- }
338
-
339
- _isInViewport(node) {
340
- let rect = node.getBoundingClientRect();
341
- return rect.top >= 0 &&
342
- rect.left >= 0 &&
343
- rect.bottom <= window.innerHeight &&
344
- rect.right <= window.innerWidth;
345
- }
346
-
347
- select(node, value) {
348
- if (this.isDisabled(node)) {
349
- return false;
350
- } else if (value == false && !node.parentNode.multiple) {
351
- return false;
352
- } else {
353
- this.trigger(node.parentNode, "focus");
354
-
355
- node.selected = value;
356
- this.changed(node);
357
-
358
- this.trigger(node.parentNode, "blur");
359
- return true;
360
- }
361
- }
362
-
363
- changed(node) {
364
- let element;
365
- let event = document.createEvent("HTMLEvents");
366
- event.initEvent("change", true, false);
367
-
368
- // In the case of an OPTION tag, the change event should come
369
- // from the parent SELECT
370
- if (node.nodeName == "OPTION") {
371
- element = node.parentNode
372
- if (element.nodeName == "OPTGROUP") {
373
- element = element.parentNode
374
- }
375
- element
376
- } else {
377
- element = node
378
- }
379
-
380
- element.dispatchEvent(event)
381
- }
382
-
383
- trigger(node, name, options = {}) {
384
- let event;
385
-
386
- if (EVENTS.MOUSE.indexOf(name) != -1) {
387
- event = document.createEvent("MouseEvent");
388
- event.initMouseEvent(
389
- name, true, true, window, 0,
390
- options["screenX"] || 0, options["screenY"] || 0,
391
- options["clientX"] || 0, options["clientY"] || 0,
392
- options["ctrlKey"] || false,
393
- options["altKey"] || false,
394
- options["shiftKey"] || false,
395
- options["metaKey"] || false,
396
- options["button"] || 0, null
397
- )
398
- } else if (EVENTS.FOCUS.indexOf(name) != -1) {
399
- event = this.obtainEvent(name);
400
- } else if (EVENTS.FORM.indexOf(name) != -1) {
401
- event = this.obtainEvent(name);
402
- } else {
403
- throw "Unknown event";
404
- }
405
-
406
- node.dispatchEvent(event);
407
- }
408
-
409
- obtainEvent(name) {
410
- let event = document.createEvent("HTMLEvents");
411
- event.initEvent(name, true, true);
412
- return event;
413
- }
414
-
415
- getAttributes(node) {
416
- let attrs = {};
417
- for (let i = 0, len = node.attributes.length; i < len; i++) {
418
- let attr = node.attributes[i];
419
- attrs[attr.name] = attr.value.replace("\n", "\\n");
420
- }
421
-
422
- return this._json.stringify(attrs);
423
- }
424
-
425
- getAttribute(node, name) {
426
- if (name == "checked" || name == "selected") {
427
- return node[name];
428
- } else {
429
- return node.getAttribute(name);
430
- }
431
- }
432
-
433
- value(node) {
434
- if (node.tagName == "SELECT" && node.multiple) {
435
- let result = []
436
-
437
- for (let i = 0, len = node.children.length; i < len; i++) {
438
- let option = node.children[i];
439
- if (option.selected) {
440
- result.push(option.value);
441
- }
442
- }
443
-
444
- return result;
445
- } else {
446
- return node.value;
447
- }
448
- }
449
-
450
- deleteText(node) {
451
- let range = document.createRange();
452
- range.selectNodeContents(node);
453
- window.getSelection().removeAllRanges();
454
- window.getSelection().addRange(range);
455
- window.getSelection().deleteFromDocument();
456
- }
457
-
458
- containsSelection(node) {
459
- let selectedNode = document.getSelection().focusNode;
460
-
461
- if (!selectedNode) {
462
- return false;
463
- }
464
-
465
- if (selectedNode.nodeType == 3) {
466
- selectedNode = selectedNode.parentNode;
467
- }
468
-
469
- return node.contains(selectedNode);
470
- }
471
-
472
- // This command is purely for testing error handling
473
- browserError() {
474
- throw new Error("zomg");
475
- }
476
- }
477
-
478
- window._cuprite = new Cuprite;