UNPKG

playwright-core

Version:

A high-level API to automate web browsers

341 lines • 14 kB
"use strict"; /** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AndroidWebView = exports.AndroidInput = exports.AndroidSocket = exports.AndroidDevice = exports.Android = void 0; const fs_1 = __importDefault(require("fs")); const util = __importStar(require("util")); const utils_1 = require("../utils/utils"); const events_1 = require("./events"); const browserContext_1 = require("./browserContext"); const channelOwner_1 = require("./channelOwner"); const timeoutSettings_1 = require("../utils/timeoutSettings"); const waiter_1 = require("./waiter"); const events_2 = require("events"); class Android extends channelOwner_1.ChannelOwner { constructor(parent, type, guid, initializer) { super(parent, type, guid, initializer); this._timeoutSettings = new timeoutSettings_1.TimeoutSettings(); } static from(android) { return android._object; } setDefaultTimeout(timeout) { this._timeoutSettings.setDefaultTimeout(timeout); this._channel.setDefaultTimeoutNoReply({ timeout }); } async devices() { return this._wrapApiCall('android.devices', async (channel) => { const { devices } = await channel.devices(); return devices.map(d => AndroidDevice.from(d)); }); } } exports.Android = Android; class AndroidDevice extends channelOwner_1.ChannelOwner { constructor(parent, type, guid, initializer) { super(parent, type, guid, initializer); this._webViews = new Map(); this.input = new AndroidInput(this); this._timeoutSettings = new timeoutSettings_1.TimeoutSettings(parent._timeoutSettings); this._channel.on('webViewAdded', ({ webView }) => this._onWebViewAdded(webView)); this._channel.on('webViewRemoved', ({ pid }) => this._onWebViewRemoved(pid)); } static from(androidDevice) { return androidDevice._object; } _onWebViewAdded(webView) { const view = new AndroidWebView(this, webView); this._webViews.set(webView.pid, view); this.emit(events_1.Events.AndroidDevice.WebView, view); } _onWebViewRemoved(pid) { const view = this._webViews.get(pid); this._webViews.delete(pid); if (view) view.emit(events_1.Events.AndroidWebView.Close); } setDefaultTimeout(timeout) { this._timeoutSettings.setDefaultTimeout(timeout); this._channel.setDefaultTimeoutNoReply({ timeout }); } serial() { return this._initializer.serial; } model() { return this._initializer.model; } webViews() { return [...this._webViews.values()]; } async webView(selector, options) { const webView = [...this._webViews.values()].find(v => v.pkg() === selector.pkg); if (webView) return webView; return this.waitForEvent('webview', { ...options, predicate: (view) => view.pkg() === selector.pkg }); } async wait(selector, options) { await this._wrapApiCall('androidDevice.wait', async (channel) => { await channel.wait({ selector: toSelectorChannel(selector), ...options }); }); } async fill(selector, text, options) { await this._wrapApiCall('androidDevice.fill', async (channel) => { await channel.fill({ selector: toSelectorChannel(selector), text, ...options }); }); } async press(selector, key, options) { await this.tap(selector, options); await this.input.press(key); } async tap(selector, options) { await this._wrapApiCall('androidDevice.tap', async (channel) => { await channel.tap({ selector: toSelectorChannel(selector), ...options }); }); } async drag(selector, dest, options) { await this._wrapApiCall('androidDevice.drag', async (channel) => { await channel.drag({ selector: toSelectorChannel(selector), dest, ...options }); }); } async fling(selector, direction, options) { await this._wrapApiCall('androidDevice.fling', async (channel) => { await channel.fling({ selector: toSelectorChannel(selector), direction, ...options }); }); } async longTap(selector, options) { await this._wrapApiCall('androidDevice.longTap', async (channel) => { await channel.longTap({ selector: toSelectorChannel(selector), ...options }); }); } async pinchClose(selector, percent, options) { await this._wrapApiCall('androidDevice.pinchClose', async (channel) => { await channel.pinchClose({ selector: toSelectorChannel(selector), percent, ...options }); }); } async pinchOpen(selector, percent, options) { await this._wrapApiCall('androidDevice.pinchOpen', async (channel) => { await channel.pinchOpen({ selector: toSelectorChannel(selector), percent, ...options }); }); } async scroll(selector, direction, percent, options) { await this._wrapApiCall('androidDevice.scroll', async (channel) => { await channel.scroll({ selector: toSelectorChannel(selector), direction, percent, ...options }); }); } async swipe(selector, direction, percent, options) { await this._wrapApiCall('androidDevice.swipe', async (channel) => { await channel.swipe({ selector: toSelectorChannel(selector), direction, percent, ...options }); }); } async info(selector) { return await this._wrapApiCall('androidDevice.info', async (channel) => { return (await channel.info({ selector: toSelectorChannel(selector) })).info; }); } async screenshot(options = {}) { return await this._wrapApiCall('androidDevice.screenshot', async (channel) => { const { binary } = await channel.screenshot(); const buffer = Buffer.from(binary, 'base64'); if (options.path) await util.promisify(fs_1.default.writeFile)(options.path, buffer); return buffer; }); } async close() { return this._wrapApiCall('androidDevice.close', async (channel) => { await channel.close(); this.emit(events_1.Events.AndroidDevice.Close); }); } async shell(command) { return this._wrapApiCall('androidDevice.shell', async (channel) => { const { result } = await channel.shell({ command }); return Buffer.from(result, 'base64'); }); } async open(command) { return this._wrapApiCall('androidDevice.open', async (channel) => { return AndroidSocket.from((await channel.open({ command })).socket); }); } async installApk(file, options) { return this._wrapApiCall('androidDevice.installApk', async (channel) => { await channel.installApk({ file: await loadFile(file), args: options && options.args }); }); } async push(file, path, options) { return this._wrapApiCall('androidDevice.push', async (channel) => { await channel.push({ file: await loadFile(file), path, mode: options ? options.mode : undefined }); }); } async launchBrowser(options = {}) { return this._wrapApiCall('androidDevice.launchBrowser', async (channel) => { const contextOptions = await browserContext_1.prepareBrowserContextParams(options); const { context } = await channel.launchBrowser(contextOptions); return browserContext_1.BrowserContext.from(context); }); } async waitForEvent(event, optionsOrPredicate = {}) { const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const waiter = waiter_1.Waiter.createForEvent(this, 'androidDevice', event); waiter.rejectOnTimeout(timeout, `Timeout while waiting for event "${event}"`); if (event !== events_1.Events.AndroidDevice.Close) waiter.rejectOnEvent(this, events_1.Events.AndroidDevice.Close, new Error('Device closed')); const result = await waiter.waitForEvent(this, event, predicate); waiter.dispose(); return result; } } exports.AndroidDevice = AndroidDevice; class AndroidSocket extends channelOwner_1.ChannelOwner { static from(androidDevice) { return androidDevice._object; } constructor(parent, type, guid, initializer) { super(parent, type, guid, initializer); this._channel.on('data', ({ data }) => this.emit(events_1.Events.AndroidSocket.Data, Buffer.from(data, 'base64'))); this._channel.on('close', () => this.emit(events_1.Events.AndroidSocket.Close)); } async write(data) { return this._wrapApiCall('androidDevice.write', async (channel) => { await channel.write({ data: data.toString('base64') }); }); } async close() { return this._wrapApiCall('androidDevice.close', async (channel) => { await channel.close(); }); } } exports.AndroidSocket = AndroidSocket; async function loadFile(file) { if (utils_1.isString(file)) return (await util.promisify(fs_1.default.readFile)(file)).toString('base64'); return file.toString('base64'); } class AndroidInput { constructor(device) { this._device = device; } async type(text) { return this._device._wrapApiCall('androidDevice.inputType', async (channel) => { await channel.inputType({ text }); }); } async press(key) { return this._device._wrapApiCall('androidDevice.inputPress', async (channel) => { await channel.inputPress({ key }); }); } async tap(point) { return this._device._wrapApiCall('androidDevice.inputTap', async (channel) => { await channel.inputTap({ point }); }); } async swipe(from, segments, steps) { return this._device._wrapApiCall('androidDevice.inputSwipe', async (channel) => { await channel.inputSwipe({ segments, steps }); }); } async drag(from, to, steps) { return this._device._wrapApiCall('androidDevice.inputDragAndDrop', async (channel) => { await channel.inputDrag({ from, to, steps }); }); } } exports.AndroidInput = AndroidInput; function toSelectorChannel(selector) { const { checkable, checked, clazz, clickable, depth, desc, enabled, focusable, focused, hasChild, hasDescendant, longClickable, pkg, res, scrollable, selected, text, } = selector; const toRegex = (value) => { if (value === undefined) return undefined; if (value instanceof RegExp) return value.source; return '^' + value.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d') + '$'; }; return { checkable, checked, clazz: toRegex(clazz), pkg: toRegex(pkg), desc: toRegex(desc), res: toRegex(res), text: toRegex(text), clickable, depth, enabled, focusable, focused, hasChild: hasChild ? { selector: toSelectorChannel(hasChild.selector) } : undefined, hasDescendant: hasDescendant ? { selector: toSelectorChannel(hasDescendant.selector), maxDepth: hasDescendant.maxDepth } : undefined, longClickable, scrollable, selected, }; } class AndroidWebView extends events_2.EventEmitter { constructor(device, data) { super(); this._device = device; this._data = data; } pid() { return this._data.pid; } pkg() { return this._data.pkg; } async page() { if (!this._pagePromise) this._pagePromise = this._fetchPage(); return this._pagePromise; } async _fetchPage() { return this._device._wrapApiCall('androidWebView.page', async (channel) => { const { context } = await channel.connectToWebView({ pid: this._data.pid, sdkLanguage: 'javascript' }); return browserContext_1.BrowserContext.from(context).pages()[0]; }); } } exports.AndroidWebView = AndroidWebView; //# sourceMappingURL=android.js.map