UNPKG

appium-xcuitest-driver

Version:

Appium driver for iOS using XCUITest for backend

122 lines 6.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getGeoLocation = getGeoLocation; exports.setGeoLocation = setGeoLocation; exports.mobileResetLocationService = mobileResetLocationService; const driver_1 = require("appium/driver"); const simulate_location_client_1 = require("../device/simulate-location-client"); const support_1 = require("appium/support"); const enum_1 = require("./enum"); const utils_1 = require("../utils"); /** * Returns the geographic location of the device under test. * * Location Services for WDA must be set to 'Always' for reliable readings from the device * (`/wda/device/location`). Latitude, longitude, and altitude may still be zero briefly after * enabling Always, while the device updates its fix. * * On iOS 17 and newer, if `mobile:setSimulatedLocation` was used earlier in the session, this * command may return that simulated position via `mobile:getSimulatedLocation` before falling * back to the device endpoint above. * * @returns Coordinates with altitude * @throws {Error} If WDA returns an error (for example, tvOS may report unsupported). */ async function getGeoLocation() { if ((0, utils_1.isIos17OrNewer)(this.opts)) { const { latitude, longitude } = await this.mobileGetSimulatedLocation(); if (support_1.util.hasValue(latitude) && support_1.util.hasValue(longitude)) { this.log.debug('Returning the geolocation that has been previously set by mobile:setSimulatedLocation. ' + 'mobile:resetSimulatedLocation can reset the location configuration.'); return { latitude, longitude, altitude: 0 }; } this.log.warn(`No location was set by mobile:setSimulatedLocation. Trying to return the location from the device.`); } // Prefer `/wda/device/location` over `/wda/simulatedLocation` for reads: they can disagree // until a simulated location is applied; `/wda/simulatedLocation` may be null until then. const { authorizationStatus, latitude, longitude, altitude } = (await this.proxyCommand('/wda/device/location', 'GET')); // `3` === kCLAuthorizationStatusAuthorizedAlways (CLAuthorizationStatus) // https://developer.apple.com/documentation/corelocation/clauthorizationstatus if (authorizationStatus !== enum_1.AuthorizationStatus.authorizedAlways) { throw this.log.errorWithException(`Location service must be set to 'Always' in order to ` + `retrieve the current geolocation data. Please set it up manually via ` + `'Settings > Privacy > Location Services -> WebDriverAgentRunner-Runner'. ` + `Or please use 'mobile:getSimulatedLocation'/'mobile:setSimulatedLocation' commands ` + `to simulate locations instead.`); } return { latitude, longitude, altitude }; } /** * Sets the geographic location of the device under test. * * On a simulator, coordinates are passed to the simulator API. On a real device running * iOS 17 or newer, this uses `mobile:setSimulatedLocation` (XCTest session simulated location). * On older real devices, it uses the legacy lockdown simulate-location service. * * @param location - Must include `latitude` and `longitude` (each coerced with `Number()`). */ async function setGeoLocation(location) { for (const name of ['latitude', 'longitude']) { if (!support_1.util.hasValue(location[name])) { throw new driver_1.errors.InvalidArgumentError(`${name} should be set`); } if (!Number.isFinite(Number(location[name]))) { throw new driver_1.errors.InvalidArgumentError(`${name} must be a number, got '${location[name]}' instead`); } } const [latitudeNumber, longitudeNumber] = [Number(location.latitude), Number(location.longitude)]; if (this.isSimulator()) { await this.device.setGeolocation(`${latitudeNumber}`, `${longitudeNumber}`); return { latitude: latitudeNumber, longitude: longitudeNumber, altitude: 0 }; } if ((0, utils_1.isIos17OrNewer)(this.opts)) { this.log.info(`Proxying to mobile:setSimulatedLocation method for iOS 17+`); await this.mobileSetSimulatedLocation(latitudeNumber, longitudeNumber); } else { await withLegacySimulateLocationSession(this, 'Device UDID is required to set geolocation on a real device', (session) => session.setLocation(latitudeNumber, longitudeNumber), (udid, msg) => `Can't set the location on device '${udid}'. Original error: ${msg}`); } return { latitude: latitudeNumber, longitude: longitudeNumber, altitude: 0 }; } /** * Resets simulated or legacy location state. * * - iOS 17 and newer: `mobile:resetSimulatedLocation` (simulator or real device). * - Real device, older iOS: legacy simulate-location session over lockdown (UDID required). * - Simulator, older iOS: not supported. * * @throws {errors.NotImplementedError} When the target is a simulator on iOS < 17. * @throws {errors.InvalidArgumentError} When the legacy path runs without a UDID. * @throws {Error} When the underlying reset fails. */ async function mobileResetLocationService() { if ((0, utils_1.isIos17OrNewer)(this.opts)) { this.log.info(`Proxying to mobile:resetSimulatedLocation method for iOS 17+`); await this.mobileResetSimulatedLocation(); return; } if (this.isSimulator()) { throw new driver_1.errors.NotImplementedError(); } await withLegacySimulateLocationSession(this, 'Device UDID is required to reset location on a real device', (session) => session.resetLocation(), (udid, msg) => `Failed to reset location on device '${udid}'. Original error: ${msg}`); } /** * Opens a legacy simulate-location session, runs `run`, closes the session, and maps errors. */ async function withLegacySimulateLocationSession(driver, udidRequiredMessage, run, formatError) { const { udid } = driver.opts; if (!udid) { throw new driver_1.errors.InvalidArgumentError(udidRequiredMessage); } const session = await simulate_location_client_1.SimulateLocationClient.startSession(udid); try { await run(session); } catch (e) { throw driver.log.errorWithException(formatError(udid, e.message)); } finally { session.close(); } } //# sourceMappingURL=location.js.map