UNPKG

appium-remote-debugger

Version:
221 lines 8.49 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const logger_1 = __importDefault(require("../logger")); const utils_1 = require("../utils"); const events_1 = __importDefault(require("./events")); const support_1 = require("@appium/support"); const lodash_1 = __importDefault(require("lodash")); const bluebird_1 = __importDefault(require("bluebird")); /** * @typedef {(() => Promise<any>|void)|undefined} TPageLoadVerifyHook */ /** * @this {import('../remote-debugger').RemoteDebugger} * @returns {void} */ function frameDetached() { this.emit(events_1.default.EVENT_FRAMES_DETACHED); } /** * @this {import('../remote-debugger').RemoteDebugger} * @param {timing.Timer?} startPageLoadTimer * @param {TPageLoadVerifyHook} [pageLoadVerifyHook] * @returns {Promise<void>} */ async function pageLoad(startPageLoadTimer, pageLoadVerifyHook = lodash_1.default.noop) { const timeoutMs = 500; if (!lodash_1.default.isFunction(startPageLoadTimer?.getDuration)) { logger_1.default.debug(`Page load timer not a timer. Creating new timer`); startPageLoadTimer = new support_1.timing.Timer().start(); } logger_1.default.debug('Page loaded, verifying whether ready'); this.pageLoading = true; const verify = async () => { this.pageLoadDelay = support_1.util.cancellableDelay(timeoutMs); try { await this.pageLoadDelay; } catch (err) { if (err instanceof bluebird_1.default.CancellationError) { // if the promise has been cancelled // we want to skip checking the readiness return; } } // we can get this called in the middle of trying to find a new app if (!this.appIdKey) { logger_1.default.debug('Not connected to an application. Ignoring page load'); return; } if (lodash_1.default.isFunction(pageLoadVerifyHook)) { await pageLoadVerifyHook(); } // if we are ready, or we've spend too much time on this // @ts-ignore startPageLoadTimer is defined here const elapsedMs = startPageLoadTimer.getDuration().asMilliSeconds; if (await this.checkPageIsReady() || (this.pageLoadMs > 0 && elapsedMs > this.pageLoadMs)) { logger_1.default.debug('Page is ready'); this.pageLoading = false; } else { logger_1.default.debug('Page was not ready, retrying'); await verify(); } }; try { await verify(); } finally { // @ts-ignore startPageLoadTimer is defined here logger_1.default.debug(`Page load completed in ${startPageLoadTimer.getDuration().asMilliSeconds.toFixed(0)}ms`); this.pageLoading = false; } } /** * @this {import('../remote-debugger').RemoteDebugger} * @returns {void} */ function cancelPageLoad() { logger_1.default.debug('Unregistering from page readiness notifications'); this.pageLoading = false; if (this.pageLoadDelay) { this.pageLoadDelay.cancel(); } } /** * @this {import('../remote-debugger').RemoteDebugger} * @returns {Promise<void>} */ async function pageUnload() { logger_1.default.debug('Page unloading'); await this.waitForDom(); } /** * @this {import('../remote-debugger').RemoteDebugger} * @param {timing.Timer|null|undefined} startPageLoadTimer * @param {TPageLoadVerifyHook} [pageLoadVerifyHook] * @returns {Promise<void>} */ async function waitForDom(startPageLoadTimer, pageLoadVerifyHook) { logger_1.default.debug('Waiting for dom...'); await this.pageLoad(startPageLoadTimer, pageLoadVerifyHook); } /** * @this {import('../remote-debugger').RemoteDebugger} * @returns {Promise<boolean>} */ async function checkPageIsReady() { (0, utils_1.checkParams)({ appIdKey: this.appIdKey }); logger_1.default.debug('Checking document readyState'); const readyCmd = 'document.readyState;'; let readyState = 'loading'; try { readyState = await bluebird_1.default.resolve(this.execute(readyCmd, true)).timeout(this.pageReadyTimeout); } catch (err) { if (!(err instanceof bluebird_1.default.TimeoutError)) { throw err; } logger_1.default.debug(`Page readiness check timed out after ${this.pageReadyTimeout}ms`); return false; } logger_1.default.debug(`Document readyState is '${readyState}'`); return readyState === 'complete'; } /** * @this {import('../remote-debugger').RemoteDebugger} * @param {string} url * @param {TPageLoadVerifyHook} [pageLoadVerifyHook] * @returns {Promise<void>} */ async function navToUrl(url, pageLoadVerifyHook) { (0, utils_1.checkParams)({ appIdKey: this.appIdKey, pageIdKey: this.pageIdKey }); if (!this.rpcClient) { throw new Error('rpcClient is undefined. Is the debugger connected?'); } this._navigatingToPage = true; try { logger_1.default.debug(`Navigating to new URL: '${url}'`); // begin listening for frame navigation event, which will be waited for later const waitForFramePromise = this.waitForFrameNavigated(); await this.rpcClient.send('Page.navigate', { url, appIdKey: this.appIdKey, pageIdKey: this.pageIdKey, }); if (!this.useNewSafari) { // a small pause for the browser to catch up await bluebird_1.default.delay(1000); } // wait until the page has been navigated await waitForFramePromise; await this.waitForDom(new support_1.timing.Timer().start(), pageLoadVerifyHook); // enable console logging, so we get the events (otherwise we only // get notified when navigating to a local page await this.rpcClient.send('Console.enable', { appIdKey: this.appIdKey, pageIdKey: this.pageIdKey, }); } finally { this._navigatingToPage = false; } } /** * @this {import('../remote-debugger').RemoteDebugger} * @returns {Promise<any>} */ async function waitForFrameNavigated() { let navEventListener; return await new bluebird_1.default(async (resolve) => { logger_1.default.debug('Waiting for frame navigated message...'); if (!this.rpcClient) { throw new Error('rpcClient is undefined. Is the debugger connected?'); } const start = new support_1.timing.Timer().start(); // add a handler for the `Page.frameNavigated` message // from the remote debugger navEventListener = (err, value) => { logger_1.default.debug(`Frame navigated in ${start.getDuration().asMilliSeconds.toFixed(0)}ms from: ${value}`); if (!this.allowNavigationWithoutReload && !this.pageLoading) { resolve(value); } else { logger_1.default.debug('Frame navigated but we were warned about it, not ' + 'considering page state unloaded'); this.allowNavigationWithoutReload = false; } if (this.navigationDelay) { this.navigationDelay.cancel(); } }; this.rpcClient.once('Page.frameNavigated', navEventListener); // timeout, in case remote debugger doesn't respond, // or takes a long time if (!this.useNewSafari || this.pageLoadMs >= 0) { // use pageLoadMs, or a small amount of time const timeout = this.useNewSafari ? this.pageLoadMs : 500; this.navigationDelay = support_1.util.cancellableDelay(timeout); try { await this.navigationDelay; navEventListener(null, `${timeout}ms timeout`); } catch (err) { // nothing to do: we only get here if the remote debugger // already notified of frame navigation, and the delay // was cancelled } } }).finally(() => { if (navEventListener) { this.rpcClient?.off('Page.frameNavigated', navEventListener); } }); } exports.default = { frameDetached, pageLoad, cancelPageLoad, pageUnload, waitForDom, checkPageIsReady, navToUrl, waitForFrameNavigated }; //# sourceMappingURL=navigate.js.map