UNPKG

appium-safari-driver

Version:
185 lines 6.76 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SafariDriverServer = void 0; const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const driver_1 = require("appium/driver"); const support_1 = require("appium/support"); const teen_process_1 = require("teen_process"); const asyncbox_1 = require("asyncbox"); const portscanner_1 = require("portscanner"); const child_process_1 = require("child_process"); const SD_BINARY = 'safaridriver'; const STARTUP_TIMEOUT = 10000; // seconds const SAFARI_PORT_RANGE = [5100, 5200]; // This guard is needed to make sure // we never run multiple Safari driver processes for the same Appium process const SAFARI_SERVER_GUARD = support_1.util.getLockFileGuard(path_1.default.resolve(os_1.default.tmpdir(), 'safari_server_guard.lock'), { timeout: 5, tryRecovery: true }); class SafariProxy extends driver_1.JWProxy { async proxyCommand(url, method, body = null) { if (this.didProcessExit) { throw new driver_1.errors.InvalidContextError(`'${method} ${url}' cannot be proxied to Safari Driver server because ` + 'the process is not running (probably crashed). Check the server log for more details'); } return await super.proxyCommand(url, method, body); } } class SafariDriverProcess { constructor() { this.log = support_1.logger.getLogger('SafariDriverProcess'); this.port = null; this.proc = null; } get isRunning() { return !!(this.proc?.isRunning); } async init() { await SAFARI_SERVER_GUARD(async () => { if (this.isRunning) { return; } const [startPort, endPort] = SAFARI_PORT_RANGE; try { this.port = await (0, portscanner_1.findAPortNotInUse)(startPort, endPort); } catch { throw new Error(`Cannot find any free port in range ${startPort}..${endPort}. ` + `Double check the processes that are locking ports within this range and terminate ` + `these which are not needed anymore`); } let safariBin; try { safariBin = await support_1.fs.which(SD_BINARY); } catch { throw new Error(`${SD_BINARY} binary cannot be found in PATH. ` + `Please make sure it is present on your system`); } this.proc = new teen_process_1.SubProcess(safariBin, ['-p', this.port, '--diagnose']); this.proc.on('output', (stdout, stderr) => { const line = stdout || stderr; this.log.debug(`[${SD_BINARY}] ${line}`); }); this.proc.on('exit', (code, signal) => { this.log.info(`${SD_BINARY} has exited with code ${code}, signal ${signal}`); }); this.log.info(`Starting '${safariBin}' on port ${this.port}`); await this.proc.start(0); }); } async kill() { if (this.isRunning) { try { await this.proc?.stop('SIGKILL'); } catch { } } } } // Single server process per Appium instance const SAFARI_DRIVER_PROCESS = new SafariDriverProcess(); process.once('exit', () => { if (SAFARI_DRIVER_PROCESS.isRunning) { try { (0, child_process_1.execSync)(`kill ${SAFARI_DRIVER_PROCESS.proc?.pid}`); } catch { } } }); class SafariDriverServer { /** * @param {import('@appium/types').AppiumLogger} log */ constructor(log) { this.log = log; // @ts-ignore That's ok this.proxy = null; } get isRunning() { return !!(SAFARI_DRIVER_PROCESS.isRunning); } /** * * @param {import('@appium/types').StringRecord} caps * @param {SessionOptions} [opts={}] */ async start(caps, opts = {}) { await SAFARI_DRIVER_PROCESS.init(); const proxyOptions = { server: '127.0.0.1', port: SAFARI_DRIVER_PROCESS.port, base: '', log: this.log, keepAlive: true, }; if (opts.reqBasePath) { proxyOptions.reqBasePath = opts.reqBasePath; } this.proxy = new SafariProxy(proxyOptions); this.proxy.didProcessExit = false; SAFARI_DRIVER_PROCESS.proc?.on('exit', () => { if (this.proxy) { this.proxy.didProcessExit = true; } }); try { await (0, asyncbox_1.waitForCondition)(async () => { try { await this.proxy?.command('/status', 'GET'); return true; } catch (err) { if (this.proxy?.didProcessExit) { throw new Error(err.message); } return false; } }, { waitMs: STARTUP_TIMEOUT, intervalMs: 1000, }); } catch (e) { if (/Condition unmet/.test(e.message)) { if (SAFARI_DRIVER_PROCESS.isRunning) { // avoid "frozen" processes, await SAFARI_DRIVER_PROCESS.kill(); } throw new Error(`Safari Driver server is not listening within ${STARTUP_TIMEOUT}ms timeout. ` + `Make sure it has been executed manually at least once with '--enable' command line argument. ` + `Check the server log for more details`); } throw e; } await this.proxy.command('/session', 'POST', { capabilities: { firstMatch: [{}], alwaysMatch: caps, } }); } async stop() { if (!this.isRunning) { this.log.info(`${SD_BINARY} session cannot be stopped, because the server is not running`); return; } if (this.proxy?.sessionId) { try { await this.proxy.command(`/session/${this.proxy.sessionId}`, 'DELETE'); } catch (e) { this.log.info(`${SD_BINARY} session cannot be deleted. Original error: ${e.message}`); } } } } exports.SafariDriverServer = SafariDriverServer; exports.default = SafariDriverServer; /** * @typedef {Object} SessionOptions * @property {string} [reqBasePath] */ //# sourceMappingURL=safari.js.map