selenium-webdriver 4.44.0 → 4.45.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 +4 -4
- data/CHANGES +12 -0
- data/Gemfile +1 -4
- data/README.md +1 -1
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/webdriver/atoms/findElements.js +272 -68
- data/lib/selenium/webdriver/atoms/getAttribute.js +152 -17
- data/lib/selenium/webdriver/atoms/isDisplayed.js +385 -39
- data/lib/selenium/webdriver/chromium/options.rb +3 -1
- data/lib/selenium/webdriver/chromium/profile.rb +6 -0
- data/lib/selenium/webdriver/common/driver_finder.rb +32 -23
- data/lib/selenium/webdriver/common/local_driver.rb +4 -8
- data/lib/selenium/webdriver/common/service.rb +1 -11
- data/lib/selenium/webdriver/remote/http/curb.rb +6 -8
- data/lib/selenium/webdriver/safari/options.rb +8 -4
- data/lib/selenium/webdriver/safari.rb +1 -6
- data/lib/selenium/webdriver/support/guards/guard.rb +10 -11
- data/lib/selenium/webdriver/support/guards.rb +2 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/selenium-webdriver.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b441a6e8d993250a3931dd136edc5749b16ab9b0b08bdb673d1e8b4f734418f0
|
|
4
|
+
data.tar.gz: 784b3693ca66a564c57d68cab9b132e84c44225fb5615fb6d4587d9abaf2643c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e317799f8e4cae55972263b3c866f08d48cf9029df08e3d1263cebc65da213e5e5758637d38edbf27fd36fe8f7c852b7358022e4ab8b860438fa41d1dbf465a2
|
|
7
|
+
data.tar.gz: 94213e5c29e70daf13fc950994531983e1e453ebe169f8e43888e76dfa5a85ea1ee854653b516456547f467ce3ca7bb1c7e6d5e9a41380e1de55ced708f79c54
|
data/CHANGES
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
4.45.0 (2026-06-15)
|
|
2
|
+
=========================
|
|
3
|
+
* Support CDP versions: v147, v148, v149
|
|
4
|
+
* deprecate curb http client support (#17443)
|
|
5
|
+
* move Ruby bindings to use typescript get attribute atom (#17524)
|
|
6
|
+
* Move atoms to use the typescript versions (#17532)
|
|
7
|
+
* deprecate Chromium Profile classes (#17557)
|
|
8
|
+
* update bazel test tags (#17558)
|
|
9
|
+
* separate concerns between Service, DriverFinder, and Options (#17564)
|
|
10
|
+
* fix using environment variables to set drivers (#17571)
|
|
11
|
+
* create more obvious test guard keywords as aliases (#17636)
|
|
12
|
+
|
|
1
13
|
4.44.0 (2026-05-12)
|
|
2
14
|
=========================
|
|
3
15
|
* Support CDP versions: v146, v147, v148
|
data/Gemfile
CHANGED
|
@@ -5,8 +5,5 @@ Dir["#{__dir__}/*.gemspec"].each do |spec|
|
|
|
5
5
|
gemspec name: File.basename(spec, '.gemspec')
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
# ActiveSupport 8.x requires Ruby 3.2+ (dependency of Steep)
|
|
9
|
-
gem 'activesupport', '~> 7.0', require: false, platforms: %i[mri mingw x64_mingw]
|
|
10
|
-
gem 'curb', '~> 1.0.5', require: false, platforms: %i[mri mingw x64_mingw]
|
|
11
8
|
gem 'debug', '~> 1.7', require: false, platforms: %i[mri mingw x64_mingw]
|
|
12
|
-
gem 'steep', '~>
|
|
9
|
+
gem 'steep', '~> 2.0', require: false, platforms: %i[mri mingw x64_mingw]
|
data/README.md
CHANGED
data/bin/linux/selenium-manager
CHANGED
|
Binary file
|
data/bin/macos/selenium-manager
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -1,68 +1,272 @@
|
|
|
1
|
-
function()
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
1
|
+
(function findElements(target, root) {
|
|
2
|
+
const INVALID_SELECTOR = 'invalid selector';
|
|
3
|
+
const INVALID_ARGUMENT = 'invalid argument';
|
|
4
|
+
const NO_SUCH_ELEMENT = 'no such element';
|
|
5
|
+
function botError(code, message) {
|
|
6
|
+
const err = new Error(message);
|
|
7
|
+
err['code'] = code;
|
|
8
|
+
return err;
|
|
9
|
+
}
|
|
10
|
+
function cssEscapeId(s) {
|
|
11
|
+
return s.replace(/[\s'"\\#.:;,!?+<>=~*^$|%&@`{}\-/\[\]()]/g, (c) => '\\' + c);
|
|
12
|
+
}
|
|
13
|
+
function classNameMany(target, root) {
|
|
14
|
+
if (!target) {
|
|
15
|
+
throw botError(INVALID_SELECTOR, 'No class name specified');
|
|
16
|
+
}
|
|
17
|
+
target = target.trim();
|
|
18
|
+
if (target.indexOf(' ') !== -1) {
|
|
19
|
+
throw botError(INVALID_SELECTOR, 'Compound class names not permitted');
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
return Array.from(root.querySelectorAll('.' + target.replace(/\\/g, '\\\\').replace(/\./g, '\\.')));
|
|
23
|
+
}
|
|
24
|
+
catch (_e) {
|
|
25
|
+
throw botError(INVALID_SELECTOR, 'An invalid or illegal class name was specified');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function cssMany(target, root) {
|
|
29
|
+
try {
|
|
30
|
+
return Array.from(root.querySelectorAll(target));
|
|
31
|
+
}
|
|
32
|
+
catch (_e) {
|
|
33
|
+
throw botError(INVALID_SELECTOR, 'An invalid or illegal CSS selector was specified: ' + target);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function idMany(target, root) {
|
|
37
|
+
if (!target) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
if (!/^\d/.test(target)) {
|
|
41
|
+
try {
|
|
42
|
+
return Array.from(root.querySelectorAll('#' + cssEscapeId(target)));
|
|
43
|
+
}
|
|
44
|
+
catch (_e) {
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return Array.from(root.querySelectorAll('*')).filter(el => el.getAttribute('id') === target);
|
|
49
|
+
}
|
|
50
|
+
function getLinkText(el) {
|
|
51
|
+
const text = el.innerText !== undefined ? el.innerText : el.textContent || '';
|
|
52
|
+
return text.replace(/^[\s]+|[\s]+$/g, '');
|
|
53
|
+
}
|
|
54
|
+
function linkTextMany(target, root, partial) {
|
|
55
|
+
return Array.from(root.querySelectorAll('a')).filter(el => {
|
|
56
|
+
const text = getLinkText(el);
|
|
57
|
+
return partial ? text.indexOf(target) !== -1 : text === target;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function nameMany(target, root) {
|
|
61
|
+
return Array.from(root.querySelectorAll('[name="' + target.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"]'));
|
|
62
|
+
}
|
|
63
|
+
function tagNameMany(target, root) {
|
|
64
|
+
if (target === '') {
|
|
65
|
+
throw botError(INVALID_SELECTOR, 'Unable to locate an element with the tagName ""');
|
|
66
|
+
}
|
|
67
|
+
return Array.from(root.getElementsByTagName(target));
|
|
68
|
+
}
|
|
69
|
+
const DEFAULT_NS_RESOLVER = (function () {
|
|
70
|
+
const ns = { svg: 'http://www.w3.org/2000/svg' };
|
|
71
|
+
return (prefix) => ns[prefix] || null;
|
|
72
|
+
})();
|
|
73
|
+
const ORDERED_NODE_SNAPSHOT_TYPE = 7;
|
|
74
|
+
function xpathMany(target, root) {
|
|
75
|
+
const doc = root.documentElement ? root : root.ownerDocument;
|
|
76
|
+
if (!doc.documentElement) {
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
let resolver = null;
|
|
81
|
+
if (target.indexOf(':') !== -1) {
|
|
82
|
+
const reversedNs = {};
|
|
83
|
+
const allNodes = doc.getElementsByTagName('*');
|
|
84
|
+
for (let i = 0; i < allNodes.length; i++) {
|
|
85
|
+
const n = allNodes[i];
|
|
86
|
+
const ns = n.namespaceURI;
|
|
87
|
+
if (ns && !reversedNs[ns]) {
|
|
88
|
+
let prefix = n.lookupPrefix(ns);
|
|
89
|
+
if (!prefix) {
|
|
90
|
+
const m = ns.match('.*/(\\w+)/?$');
|
|
91
|
+
prefix = m ? m[1] : 'xhtml';
|
|
92
|
+
}
|
|
93
|
+
reversedNs[ns] = prefix;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const namespaces = {};
|
|
97
|
+
for (const key in reversedNs) {
|
|
98
|
+
namespaces[reversedNs[key]] = key;
|
|
99
|
+
}
|
|
100
|
+
resolver = (prefix) => namespaces[prefix || ''] || null;
|
|
101
|
+
}
|
|
102
|
+
let result = null;
|
|
103
|
+
try {
|
|
104
|
+
result = doc.evaluate(target, root, resolver, ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
105
|
+
}
|
|
106
|
+
catch (te) {
|
|
107
|
+
if (te.name === 'TypeError') {
|
|
108
|
+
const fallback = doc.createNSResolver ? doc.createNSResolver(doc.documentElement) : DEFAULT_NS_RESOLVER;
|
|
109
|
+
result = doc.evaluate(target, root, fallback, ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
throw te;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!result) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const results = [];
|
|
119
|
+
for (let i = 0; i < result.snapshotLength; i++) {
|
|
120
|
+
const node = result.snapshotItem(i);
|
|
121
|
+
if (!node || node.nodeType !== Node.ELEMENT_NODE) {
|
|
122
|
+
throw botError(INVALID_SELECTOR, 'The result of the xpath expression "' + target + '" is: ' + node + '. It should be an element.');
|
|
123
|
+
}
|
|
124
|
+
results.push(node);
|
|
125
|
+
}
|
|
126
|
+
return results;
|
|
127
|
+
}
|
|
128
|
+
catch (ex) {
|
|
129
|
+
if (ex.name === 'NS_ERROR_ILLEGAL_VALUE') {
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
if (ex.code === INVALID_SELECTOR) {
|
|
133
|
+
throw ex;
|
|
134
|
+
}
|
|
135
|
+
throw botError(INVALID_SELECTOR, 'Unable to locate an element with the xpath expression ' + target + ' because of the following error:\n' + ex);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function getClientRect(element) {
|
|
139
|
+
const r = element.getBoundingClientRect();
|
|
140
|
+
return { left: r.left, top: r.top, width: r.width, height: r.height };
|
|
141
|
+
}
|
|
142
|
+
function resolveAnchor(selector) {
|
|
143
|
+
if (selector instanceof Element) {
|
|
144
|
+
return selector;
|
|
145
|
+
}
|
|
146
|
+
if (typeof selector === 'function') {
|
|
147
|
+
return resolveAnchor(selector());
|
|
148
|
+
}
|
|
149
|
+
if (selector && typeof selector === 'object') {
|
|
150
|
+
const found = findElements(selector);
|
|
151
|
+
if (!found.length) {
|
|
152
|
+
throw botError(NO_SUCH_ELEMENT, 'No element has been found by ' + JSON.stringify(selector));
|
|
153
|
+
}
|
|
154
|
+
return found[0];
|
|
155
|
+
}
|
|
156
|
+
throw botError(INVALID_ARGUMENT, 'Selector is of wrong type: ' + JSON.stringify(selector));
|
|
157
|
+
}
|
|
158
|
+
function makeProximityFilter(selector, test) {
|
|
159
|
+
return (candidate) => test(getClientRect(resolveAnchor(selector)), getClientRect(candidate));
|
|
160
|
+
}
|
|
161
|
+
const RELATIVE_STRATEGIES = {
|
|
162
|
+
above: (sel) => makeProximityFilter(sel, (a, c) => c.top + c.height <= a.top),
|
|
163
|
+
below: (sel) => makeProximityFilter(sel, (a, c) => c.top >= a.top + a.height),
|
|
164
|
+
left: (sel) => makeProximityFilter(sel, (a, c) => c.left + c.width <= a.left),
|
|
165
|
+
right: (sel) => makeProximityFilter(sel, (a, c) => c.left >= a.left + a.width),
|
|
166
|
+
near: (sel, distArg) => {
|
|
167
|
+
const distFromSelector = typeof sel['distance'] === 'number'
|
|
168
|
+
? sel['distance']
|
|
169
|
+
: 0;
|
|
170
|
+
const distance = typeof distArg === 'number' && distArg > 0 ? distArg : distFromSelector || 50;
|
|
171
|
+
return (candidate) => {
|
|
172
|
+
const anchor = resolveAnchor(sel);
|
|
173
|
+
if (anchor === candidate)
|
|
174
|
+
return false;
|
|
175
|
+
const a = getClientRect(anchor);
|
|
176
|
+
const c = getClientRect(candidate);
|
|
177
|
+
const expanded = { left: a.left - distance, top: a.top - distance, width: a.width + distance * 2, height: a.height + distance * 2 };
|
|
178
|
+
return (c.left < expanded.left + expanded.width &&
|
|
179
|
+
c.left + c.width > expanded.left &&
|
|
180
|
+
c.top < expanded.top + expanded.height &&
|
|
181
|
+
c.top + c.height > expanded.top);
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
straightAbove: (sel) => makeProximityFilter(sel, (a, c) => c.left < a.left + a.width && c.left + c.width > a.left && c.top + c.height <= a.top),
|
|
185
|
+
straightBelow: (sel) => makeProximityFilter(sel, (a, c) => c.left < a.left + a.width && c.left + c.width > a.left && c.top >= a.top + a.height),
|
|
186
|
+
straightLeft: (sel) => makeProximityFilter(sel, (a, c) => c.top < a.top + a.height && c.top + c.height > a.top && c.left + c.width <= a.left),
|
|
187
|
+
straightRight: (sel) => makeProximityFilter(sel, (a, c) => c.top < a.top + a.height && c.top + c.height > a.top && c.left >= a.left + a.width),
|
|
188
|
+
};
|
|
189
|
+
function sortByProximity(anchor, elements) {
|
|
190
|
+
const ar = getClientRect(anchor);
|
|
191
|
+
const acx = ar.left + Math.max(1, ar.width) / 2;
|
|
192
|
+
const acy = ar.top + Math.max(1, ar.height) / 2;
|
|
193
|
+
return elements.slice().sort((a, b) => {
|
|
194
|
+
const ra = getClientRect(a);
|
|
195
|
+
const rb = getClientRect(b);
|
|
196
|
+
const da = Math.sqrt(Math.pow(acx - (ra.left + Math.max(1, ra.width) / 2), 2) + Math.pow(acy - (ra.top + Math.max(1, ra.height) / 2), 2));
|
|
197
|
+
const db = Math.sqrt(Math.pow(acx - (rb.left + Math.max(1, rb.width) / 2), 2) + Math.pow(acy - (rb.top + Math.max(1, rb.height) / 2), 2));
|
|
198
|
+
return da - db;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
function relativeMany(target, root) {
|
|
202
|
+
if (!Object.prototype.hasOwnProperty.call(target, 'root') || !Object.prototype.hasOwnProperty.call(target, 'filters')) {
|
|
203
|
+
throw botError(INVALID_ARGUMENT, 'Locator not suitable for relative locators: ' + JSON.stringify(target));
|
|
204
|
+
}
|
|
205
|
+
const filters = target['filters'];
|
|
206
|
+
if (!Array.isArray(filters)) {
|
|
207
|
+
throw botError(INVALID_ARGUMENT, 'Targets should be an array: ' + JSON.stringify(target));
|
|
208
|
+
}
|
|
209
|
+
let elements;
|
|
210
|
+
const rootTarget = target['root'];
|
|
211
|
+
if (rootTarget instanceof Element) {
|
|
212
|
+
elements = [rootTarget];
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
elements = findElements(rootTarget, root);
|
|
216
|
+
}
|
|
217
|
+
if (!elements.length) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
const matched = elements.filter(el => {
|
|
221
|
+
if (!el)
|
|
222
|
+
return false;
|
|
223
|
+
return filters.every(filter => {
|
|
224
|
+
const strategy = RELATIVE_STRATEGIES[filter.kind];
|
|
225
|
+
if (!strategy) {
|
|
226
|
+
throw botError(INVALID_ARGUMENT, 'Cannot find filter suitable for ' + filter.kind);
|
|
227
|
+
}
|
|
228
|
+
return strategy(...filter.args)(el);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
const finalFilter = filters[filters.length - 1];
|
|
232
|
+
if (!finalFilter || !RELATIVE_STRATEGIES[finalFilter.kind]) {
|
|
233
|
+
return matched;
|
|
234
|
+
}
|
|
235
|
+
const lastAnchor = resolveAnchor(finalFilter.args[0]);
|
|
236
|
+
return sortByProximity(lastAnchor, matched);
|
|
237
|
+
}
|
|
238
|
+
const actualRoot = root || document;
|
|
239
|
+
const keys = Object.keys(target).filter(k => Object.prototype.hasOwnProperty.call(target, k));
|
|
240
|
+
if (!keys.length) {
|
|
241
|
+
throw botError(INVALID_ARGUMENT, 'Unsupported locator strategy: (empty)');
|
|
242
|
+
}
|
|
243
|
+
const key = keys[0];
|
|
244
|
+
const value = target[key];
|
|
245
|
+
switch (key) {
|
|
246
|
+
case 'className':
|
|
247
|
+
case 'class name':
|
|
248
|
+
return classNameMany(value, actualRoot);
|
|
249
|
+
case 'css':
|
|
250
|
+
case 'css selector':
|
|
251
|
+
return cssMany(value, actualRoot);
|
|
252
|
+
case 'id':
|
|
253
|
+
return idMany(value, actualRoot);
|
|
254
|
+
case 'linkText':
|
|
255
|
+
case 'link text':
|
|
256
|
+
return linkTextMany(value, actualRoot, false);
|
|
257
|
+
case 'partialLinkText':
|
|
258
|
+
case 'partial link text':
|
|
259
|
+
return linkTextMany(value, actualRoot, true);
|
|
260
|
+
case 'name':
|
|
261
|
+
return nameMany(value, actualRoot);
|
|
262
|
+
case 'tagName':
|
|
263
|
+
case 'tag name':
|
|
264
|
+
return tagNameMany(value, actualRoot);
|
|
265
|
+
case 'xpath':
|
|
266
|
+
return xpathMany(value, actualRoot);
|
|
267
|
+
case 'relative':
|
|
268
|
+
return relativeMany(value, actualRoot);
|
|
269
|
+
default:
|
|
270
|
+
throw botError(INVALID_ARGUMENT, 'Unsupported locator strategy: ' + key);
|
|
271
|
+
}
|
|
272
|
+
})
|