diff --git a/lib/commands/device/common.js b/lib/commands/device/common.js index 7485d468..2c2d0f3c 100644 --- a/lib/commands/device/common.js +++ b/lib/commands/device/common.js @@ -10,6 +10,7 @@ import { validatePackageActivityNames, pushSettingsApp, } from './utils'; +import {adjustTimeZone} from '../time'; /** * @this {AndroidDriver} @@ -194,6 +195,7 @@ export async function initDevice() { skipLogcatCapture, logcatFormat, logcatFilterSpecs, + timeZone, } = this.opts; if (skipDeviceInitialization) { @@ -222,10 +224,9 @@ export async function initDevice() { } /** @type {Promise[]} */ - const promises = []; - + const setupPromises = []; if (!this.isEmulator()) { - promises.push((async () => { + setupPromises.push((async () => { if (mockLocationApp || _.isUndefined(mockLocationApp)) { await setMockLocationApp.bind(this)(mockLocationApp || SETTINGS_HELPER_ID); } else { @@ -234,17 +235,17 @@ export async function initDevice() { })()); } if (language && locale) { - promises.push(this.ensureDeviceLocale(language, locale, localeScript)); + setupPromises.push(this.ensureDeviceLocale(language, locale, localeScript)); } if (skipLogcatCapture) { this.log.info(`'skipLogcatCapture' is set. Skipping starting logcat capture.`); } else { - promises.push(this.adb.startLogcat({ + setupPromises.push(this.adb.startLogcat({ format: logcatFormat, filterSpecs: logcatFilterSpecs, })); } - promises.push((async () => { + setupPromises.push((async () => { if (hideKeyboard) { await hideKeyboardCompletely.bind(this)(); } else if (hideKeyboard === false) { @@ -252,7 +253,7 @@ export async function initDevice() { } })()); if (unicodeKeyboard) { - promises.push((async () => { + setupPromises.push((async () => { this.log.warn( `The 'unicodeKeyboard' capability has been deprecated and will be removed. ` + `Set the 'hideKeyboard' capability to 'true' in order to make the on-screen keyboard invisible.`, @@ -260,8 +261,11 @@ export async function initDevice() { await initUnicodeKeyboard.bind(this)(); })()); } + if (timeZone) { + setupPromises.push(adjustTimeZone.bind(this)(timeZone)); + } - await B.all(promises); + await B.all(setupPromises); } /** diff --git a/lib/commands/time.js b/lib/commands/time.js index 44b4ed87..a4db94c1 100644 --- a/lib/commands/time.js +++ b/lib/commands/time.js @@ -1,9 +1,9 @@ -import moment from 'moment'; +import moment from 'moment-timezone'; const MOMENT_FORMAT_ISO8601 = 'YYYY-MM-DDTHH:mm:ssZ'; /** - * @this {import('../driver').AndroidDriver} + * @this {AndroidDriver} * @param {string} [format=MOMENT_FORMAT_ISO8601] * @returns {Promise} */ @@ -23,7 +23,7 @@ export async function getDeviceTime(format = MOMENT_FORMAT_ISO8601) { } /** - * @this {import('../driver').AndroidDriver} + * @this {AndroidDriver} * @param {import('./types').DeviceTimeOpts} [opts={}] * @returns {Promise} */ @@ -31,6 +31,28 @@ export async function mobileGetDeviceTime(opts = {}) { return await this.getDeviceTime(opts.format); } +/** + * @this {AndroidDriver} + * @param {string} zoneName + * @returns {Promise} + */ +export async function adjustTimeZone(zoneName) { + if (!moment.tz.names().includes(zoneName)) { + throw new Error( + `The provided time zone identifier '${zoneName}' is not known. ` + + `Please choose a valid TZ identifier from https://en.wikipedia.org/wiki/List_of_tz_database_time_zones` + ); + } + this.log.info(`Setting the device time zone to '${zoneName}'`); + // The magic number '3' depends on the actual ordering of methods in + // the IAlarmManager interface and might be a subject of change between + // different Android API versions. + // See, for example, + // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/apex/jobscheduler/framework/java/android/app/IAlarmManager.aidl;l=1?q=IAlarmManager + await this.adb.shell(['service', 'call', 'alarm', '3', 's16', zoneName]); +} + /** * @typedef {import('appium-adb').ADB} ADB + * @typedef {import('../driver').AndroidDriver} AndroidDriver */ diff --git a/lib/constraints.ts b/lib/constraints.ts index d63b20b6..44f13366 100644 --- a/lib/constraints.ts +++ b/lib/constraints.ts @@ -263,6 +263,9 @@ export const ANDROID_DRIVER_CONSTRAINTS = { appWaitForLaunch: { isBoolean: true, }, + timeZone: { + isString: true, + }, } as const satisfies Constraints; export default ANDROID_DRIVER_CONSTRAINTS; diff --git a/package.json b/package.json index 19322404..832d0a09 100644 --- a/package.json +++ b/package.json @@ -87,8 +87,8 @@ "@types/source-map-support": "^0.5.6", "@types/teen_process": "^2.0.0", "@types/ws": "^8.5.4", - "@typescript-eslint/eslint-plugin": "^6.9.0", - "@typescript-eslint/parser": "^6.9.0", + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", "@xmldom/xmldom": "^0.x", "android-apidemos": "^4.1.0", "appium": "^2.0.0-rc.5", @@ -111,7 +111,7 @@ "xpath": "^0.x" }, "peerDependencies": { - "appium": "^2.0.0-beta.40" + "appium": "^2.0.0" }, "engines": { "node": ">=14",