1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", {
|
4 | value: true
|
5 | });
|
6 | exports.getBrowserObject = getBrowserObject;
|
7 | exports.transformToCharString = transformToCharString;
|
8 | exports.parseCSS = parseCSS;
|
9 | exports.checkUnicode = checkUnicode;
|
10 | exports.findElement = findElement;
|
11 | exports.findElements = findElements;
|
12 | exports.verifyArgsAndStripIfElement = verifyArgsAndStripIfElement;
|
13 | exports.getElementRect = getElementRect;
|
14 | exports.getAbsoluteFilepath = getAbsoluteFilepath;
|
15 | exports.assertDirectoryExists = assertDirectoryExists;
|
16 | exports.validateUrl = validateUrl;
|
17 | exports.getScrollPosition = getScrollPosition;
|
18 | exports.hasElementId = hasElementId;
|
19 | exports.addLocatorStrategyHandler = addLocatorStrategyHandler;
|
20 | exports.getAutomationProtocol = exports.isStub = exports.enhanceElementsArray = exports.getElementFromResponse = exports.getPrototype = void 0;
|
21 |
|
22 | var _fs = _interopRequireDefault(require("fs"));
|
23 |
|
24 | var _http = _interopRequireDefault(require("http"));
|
25 |
|
26 | var _path = _interopRequireDefault(require("path"));
|
27 |
|
28 | var _cssValue = _interopRequireDefault(require("css-value"));
|
29 |
|
30 | var _rgb2hex = _interopRequireDefault(require("rgb2hex"));
|
31 |
|
32 | var _graphemeSplitter = _interopRequireDefault(require("grapheme-splitter"));
|
33 |
|
34 | var _logger = _interopRequireDefault(require("@wdio/logger"));
|
35 |
|
36 | var _lodash = _interopRequireDefault(require("lodash.isobject"));
|
37 |
|
38 | var _url = require("url");
|
39 |
|
40 | var _devtools = require("devtools");
|
41 |
|
42 | var _lodash2 = _interopRequireDefault(require("lodash.isplainobject"));
|
43 |
|
44 | var _constants = require("./constants");
|
45 |
|
46 | var _findStrategy = require("./utils/findStrategy");
|
47 |
|
48 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
49 |
|
50 | const browserCommands = require('./commands/browser');
|
51 |
|
52 | const elementCommands = require('./commands/element');
|
53 |
|
54 | const log = (0, _logger.default)('webdriverio');
|
55 | const INVALID_SELECTOR_ERROR = 'selector needs to be typeof `string` or `function`';
|
56 | const scopes = {
|
57 | browser: browserCommands,
|
58 | element: elementCommands
|
59 | };
|
60 |
|
61 | const applyScopePrototype = (prototype, scope) => {
|
62 | Object.entries(scopes[scope]).forEach(([commandName, command]) => {
|
63 | prototype[commandName] = {
|
64 | value: command
|
65 | };
|
66 | });
|
67 | };
|
68 |
|
69 | const getPrototype = scope => {
|
70 | const prototype = {};
|
71 | applyScopePrototype(prototype, scope);
|
72 | prototype.strategies = {
|
73 | value: new Map()
|
74 | };
|
75 | return prototype;
|
76 | };
|
77 |
|
78 | exports.getPrototype = getPrototype;
|
79 |
|
80 | const getElementFromResponse = res => {
|
81 | if (!res) {
|
82 | return null;
|
83 | }
|
84 |
|
85 | if (res.ELEMENT) {
|
86 | return res.ELEMENT;
|
87 | }
|
88 |
|
89 | if (res[_constants.ELEMENT_KEY]) {
|
90 | return res[_constants.ELEMENT_KEY];
|
91 | }
|
92 |
|
93 | return null;
|
94 | };
|
95 |
|
96 | exports.getElementFromResponse = getElementFromResponse;
|
97 |
|
98 | function getBrowserObject(elem) {
|
99 | return elem.parent ? getBrowserObject(elem.parent) : elem;
|
100 | }
|
101 |
|
102 | function transformToCharString(value) {
|
103 | const ret = [];
|
104 |
|
105 | if (!Array.isArray(value)) {
|
106 | value = [value];
|
107 | }
|
108 |
|
109 | for (const val of value) {
|
110 | if (typeof val === 'string') {
|
111 | ret.push(...checkUnicode(val));
|
112 | } else if (typeof val === 'number') {
|
113 | const entry = `${val}`.split('');
|
114 | ret.push(...entry);
|
115 | } else if (val && typeof val === 'object') {
|
116 | try {
|
117 | ret.push(...JSON.stringify(val).split(''));
|
118 | } catch (e) {}
|
119 | } else if (typeof val === 'boolean') {
|
120 | const entry = val ? 'true'.split('') : 'false'.split('');
|
121 | ret.push(...entry);
|
122 | }
|
123 | }
|
124 |
|
125 | return ret;
|
126 | }
|
127 |
|
128 | function sanitizeCSS(value) {
|
129 | if (!value) {
|
130 | return value;
|
131 | }
|
132 |
|
133 | return value.trim().replace(/'/g, '').replace(/"/g, '').toLowerCase();
|
134 | }
|
135 |
|
136 | function parseCSS(cssPropertyValue, cssProperty) {
|
137 | if (!cssPropertyValue) {
|
138 | return null;
|
139 | }
|
140 |
|
141 | let parsedValue = {
|
142 | property: cssProperty,
|
143 | value: cssPropertyValue.toLowerCase().trim()
|
144 | };
|
145 |
|
146 | if (parsedValue.value.indexOf('rgb') === 0) {
|
147 | parsedValue.value = parsedValue.value.replace(/\s/g, '');
|
148 | let color = parsedValue.value;
|
149 | parsedValue.parsed = (0, _rgb2hex.default)(parsedValue.value);
|
150 | parsedValue.parsed.type = 'color';
|
151 | parsedValue.parsed[/[rgba]+/g.exec(color)[0]] = color;
|
152 | } else if (parsedValue.property === 'font-family') {
|
153 | let font = (0, _cssValue.default)(cssPropertyValue);
|
154 | let string = parsedValue.value;
|
155 | let value = cssPropertyValue.split(/,/).map(sanitizeCSS);
|
156 | parsedValue.value = sanitizeCSS(font[0].value || font[0].string);
|
157 | parsedValue.parsed = {
|
158 | value,
|
159 | type: 'font',
|
160 | string
|
161 | };
|
162 | } else {
|
163 | try {
|
164 | parsedValue.parsed = (0, _cssValue.default)(cssPropertyValue);
|
165 |
|
166 | if (parsedValue.parsed.length === 1) {
|
167 | parsedValue.parsed = parsedValue.parsed[0];
|
168 | }
|
169 |
|
170 | if (parsedValue.parsed.type && parsedValue.parsed.type === 'number' && parsedValue.parsed.unit === '') {
|
171 | parsedValue.value = parsedValue.parsed.value;
|
172 | }
|
173 | } catch (e) {}
|
174 | }
|
175 |
|
176 | return parsedValue;
|
177 | }
|
178 |
|
179 | function checkUnicode(value, isDevTools) {
|
180 | return Object.prototype.hasOwnProperty.call(_constants.UNICODE_CHARACTERS, value) ? isDevTools ? [value] : [_constants.UNICODE_CHARACTERS[value]] : new _graphemeSplitter.default().splitGraphemes(value);
|
181 | }
|
182 |
|
183 | function fetchElementByJSFunction(selector, scope) {
|
184 | if (!scope.elementId) {
|
185 | return scope.execute(selector);
|
186 | }
|
187 |
|
188 | const script = function (elem) {
|
189 | return selector.call(elem);
|
190 | }.toString().replace('selector', `(${selector.toString()})`);
|
191 |
|
192 | return getBrowserObject(scope).execute(`return (${script}).apply(null, arguments)`, scope);
|
193 | }
|
194 |
|
195 | async function findElement(selector) {
|
196 | if (typeof selector === 'string' || (0, _lodash2.default)(selector)) {
|
197 | const {
|
198 | using,
|
199 | value
|
200 | } = (0, _findStrategy.findStrategy)(selector, this.isW3C, this.isMobile);
|
201 | return this.elementId ? this.findElementFromElement(this.elementId, using, value) : this.findElement(using, value);
|
202 | }
|
203 |
|
204 | if (typeof selector === 'function') {
|
205 | const notFoundError = new Error(`Function selector "${selector.toString()}" did not return an HTMLElement`);
|
206 | let elem = await fetchElementByJSFunction(selector, this);
|
207 | elem = Array.isArray(elem) ? elem[0] : elem;
|
208 | return getElementFromResponse(elem) ? elem : notFoundError;
|
209 | }
|
210 |
|
211 | throw new Error(INVALID_SELECTOR_ERROR);
|
212 | }
|
213 |
|
214 | async function findElements(selector) {
|
215 | if (typeof selector === 'string' || (0, _lodash2.default)(selector)) {
|
216 | const {
|
217 | using,
|
218 | value
|
219 | } = (0, _findStrategy.findStrategy)(selector, this.isW3C, this.isMobile);
|
220 | return this.elementId ? this.findElementsFromElement(this.elementId, using, value) : this.findElements(using, value);
|
221 | }
|
222 |
|
223 | if (typeof selector === 'function') {
|
224 | let elems = await fetchElementByJSFunction(selector, this);
|
225 | elems = Array.isArray(elems) ? elems : [elems];
|
226 | return elems.filter(elem => elem && getElementFromResponse(elem));
|
227 | }
|
228 |
|
229 | throw new Error(INVALID_SELECTOR_ERROR);
|
230 | }
|
231 |
|
232 | function verifyArgsAndStripIfElement(args) {
|
233 | function verify(arg) {
|
234 | if ((0, _lodash.default)(arg) && arg.constructor.name === 'Element') {
|
235 | if (!arg.elementId) {
|
236 | throw new Error(`The element with selector "${arg.selector}" you trying to pass into the execute method wasn't found`);
|
237 | }
|
238 |
|
239 | return {
|
240 | [_constants.ELEMENT_KEY]: arg.elementId,
|
241 | ELEMENT: arg.elementId
|
242 | };
|
243 | }
|
244 |
|
245 | return arg;
|
246 | }
|
247 |
|
248 | return !Array.isArray(args) ? verify(args) : args.map(verify);
|
249 | }
|
250 |
|
251 | async function getElementRect(scope) {
|
252 | const rect = await scope.getElementRect(scope.elementId);
|
253 | let defaults = {
|
254 | x: 0,
|
255 | y: 0,
|
256 | width: 0,
|
257 | height: 0
|
258 | };
|
259 |
|
260 | if (Object.keys(defaults).some(key => rect[key] == null)) {
|
261 | const rectJs = await getBrowserObject(scope).execute(function (el) {
|
262 | if (!el || !el.getBoundingClientRect) {
|
263 | return;
|
264 | }
|
265 |
|
266 | const {
|
267 | left,
|
268 | top,
|
269 | width,
|
270 | height
|
271 | } = el.getBoundingClientRect();
|
272 | return {
|
273 | x: left + this.scrollX,
|
274 | y: top + this.scrollY,
|
275 | width,
|
276 | height
|
277 | };
|
278 | }, scope);
|
279 | Object.keys(defaults).forEach(key => {
|
280 | if (rect[key] != null) {
|
281 | return;
|
282 | }
|
283 |
|
284 | if (typeof rectJs[key] === 'number') {
|
285 | rect[key] = Math.floor(rectJs[key]);
|
286 | } else {
|
287 | log.error('getElementRect', {
|
288 | rect,
|
289 | rectJs,
|
290 | key
|
291 | });
|
292 | throw new Error('Failed to receive element rects via execute command');
|
293 | }
|
294 | });
|
295 | }
|
296 |
|
297 | return rect;
|
298 | }
|
299 |
|
300 | function getAbsoluteFilepath(filepath) {
|
301 | return filepath.startsWith('/') || filepath.startsWith('\\') || filepath.match(/^[a-zA-Z]:\\/) ? filepath : _path.default.join(process.cwd(), filepath);
|
302 | }
|
303 |
|
304 | function assertDirectoryExists(filepath) {
|
305 | if (!_fs.default.existsSync(_path.default.dirname(filepath))) {
|
306 | throw new Error(`directory (${_path.default.dirname(filepath)}) doesn't exist`);
|
307 | }
|
308 | }
|
309 |
|
310 | function validateUrl(url, origError) {
|
311 | try {
|
312 | const urlObject = new _url.URL(url);
|
313 | return urlObject.href;
|
314 | } catch (e) {
|
315 | if (origError) {
|
316 | throw origError;
|
317 | }
|
318 |
|
319 | return validateUrl(`http://${url}`, e);
|
320 | }
|
321 | }
|
322 |
|
323 | function getScrollPosition(scope) {
|
324 | return getBrowserObject(scope).execute('return { scrollX: this.pageXOffset, scrollY: this.pageYOffset };');
|
325 | }
|
326 |
|
327 | async function hasElementId(element) {
|
328 | if (!element.elementId) {
|
329 | const method = element.isReactElement ? 'react$' : '$';
|
330 | element.elementId = (await element.parent[method](element.selector)).elementId;
|
331 | }
|
332 |
|
333 | if (!element.elementId) {
|
334 | return false;
|
335 | }
|
336 |
|
337 | return true;
|
338 | }
|
339 |
|
340 | function addLocatorStrategyHandler(scope) {
|
341 | return (name, script) => {
|
342 | if (scope.strategies.get(name)) {
|
343 | throw new Error(`Strategy ${name} already exists`);
|
344 | }
|
345 |
|
346 | scope.strategies.set(name, script);
|
347 | };
|
348 | }
|
349 |
|
350 | const enhanceElementsArray = (elements, parent, selector, foundWith = '$$', props = []) => {
|
351 | elements.parent = parent;
|
352 | elements.selector = selector;
|
353 | elements.foundWith = foundWith;
|
354 | elements.props = props;
|
355 | return elements;
|
356 | };
|
357 |
|
358 | exports.enhanceElementsArray = enhanceElementsArray;
|
359 |
|
360 | const isStub = automationProtocol => automationProtocol === './protocol-stub';
|
361 |
|
362 | exports.isStub = isStub;
|
363 |
|
364 | const getAutomationProtocol = async config => {
|
365 | if (config.automationProtocol) {
|
366 | return config.automationProtocol;
|
367 | }
|
368 |
|
369 | if (config.hostname || config.port || config.path || config.user && config.key) {
|
370 | return 'webdriver';
|
371 | }
|
372 |
|
373 | if (config.capabilities && typeof config.capabilities.browserName === 'string' && !_devtools.SUPPORTED_BROWSER.includes(config.capabilities.browserName.toLowerCase())) {
|
374 | return 'webdriver';
|
375 | }
|
376 |
|
377 | const driverEndpointHeaders = await new Promise(resolve => {
|
378 | const req = _http.default.request(_constants.DRIVER_DEFAULT_ENDPOINT, resolve);
|
379 |
|
380 | req.on('error', error => resolve({
|
381 | error
|
382 | }));
|
383 | req.end();
|
384 | });
|
385 |
|
386 | if (driverEndpointHeaders && parseInt(driverEndpointHeaders.statusCode, 10) === 200) {
|
387 | return 'webdriver';
|
388 | }
|
389 |
|
390 | return 'devtools';
|
391 | };
|
392 |
|
393 | exports.getAutomationProtocol = getAutomationProtocol; |
\ | No newline at end of file |