cuprite 0.13 → 0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,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;