appium-safari-driver
Version:
Appium driver for Safari browser
181 lines • 6.71 kB
JavaScript
;
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 node_os_1 = __importDefault(require("node:os"));
const node_path_1 = __importDefault(require("node: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 node_child_process_1 = require("node: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(node_path_1.default.resolve(node_os_1.default.tmpdir(), 'safari_server_guard.lock'), { timeout: 5, tryRecovery: true });
class SafariProxy extends driver_1.JWProxy {
didProcessExit;
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 {
port = null;
proc = null;
log;
constructor() {
this.log = support_1.logger.getLogger('SafariDriverProcess');
}
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', String(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, node_child_process_1.execSync)(`kill ${SAFARI_DRIVER_PROCESS.proc?.pid}`);
}
catch { }
}
});
class SafariDriverServer {
_proxy = null;
log;
constructor(log) {
this.log = log;
}
get proxy() {
if (!this._proxy) {
throw new Error('Safari driver proxy is not initialized');
}
return this._proxy;
}
get isRunning() {
return !!SAFARI_DRIVER_PROCESS.isRunning;
}
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, { cause: err });
}
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`, { cause: e });
}
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;
//# sourceMappingURL=safari.js.map