From ae0afdcafabbb8164b3996627c7c3fc0f788eaf3 Mon Sep 17 00:00:00 2001 From: Mykola Mokhnach Date: Tue, 26 Sep 2023 09:07:46 +0200 Subject: [PATCH] feat: Add clearApp extension (#2031) --- docs/execute-methods.md | 26 +++++++++++++++++++++ lib/commands/app-management.js | 41 ++++++++++++++++++++++++++++++++++ lib/driver.js | 1 + lib/execute-method-map.ts | 6 +++++ 4 files changed, 74 insertions(+) diff --git a/docs/execute-methods.md b/docs/execute-methods.md index 5b32dd6b2..ed7a28fd8 100644 --- a/docs/execute-methods.md +++ b/docs/execute-methods.md @@ -216,6 +216,32 @@ applicationType | string | no | The type of applications to list. Either `System A list of apps, where each item is a map where keys are bundle identifiers and values are maps of platform-specific app properties. Having `UIFileSharingEnabled` set to `true` in the app properties map means this app supports files upload and download into its `documents` container. Read [Pushing/Pulling files](https://appium.io/docs/en/writing-running-appium/ios/ios-xctest-file-movement/) for more details. +### mobile: clearApp + +Deletes data files from the data container of an installed app, +so it could start from the clean state next time it is launched. +The destination app will be terminated if it is running when this API is invoked. +Sometimes it might also be necessary to invoke the following APIs +to fully reset the state of an installed app (make sure the app is not running while +calling them): +- [mobile: clearKeychains](#mobile-clearkeychains) +- [mobile: resetPermission](#mobile-resetpermission) + +This API might not be 100% reliable for some apps. The only reliable method to fully +reset an existing app that Apple supports is to [uninstall](#mobile-removeapp) it and then perform a fresh [install](#mobile-installapp) of the same app. + +This API only works on simulators. An exception is thrown if executed with real devices. + +#### Arguments + +Name | Type | Required | Description | Example +--- | --- | --- | --- | --- +bundleId | string | yes | The bundle identifier of the application to be cleared | com.mycompany.myapp + +#### Returned Result + +`true` if at least one item has been successfully deleted from the app data container. + ### mobile: startPerfRecord Starts performance profiling for the device under test. diff --git a/lib/commands/app-management.js b/lib/commands/app-management.js index 9a3f24ac3..b4e159d54 100644 --- a/lib/commands/app-management.js +++ b/lib/commands/app-management.js @@ -2,6 +2,8 @@ import _ from 'lodash'; import {fs, util} from 'appium/support'; import {errors} from 'appium/driver'; import {services} from 'appium-ios-device'; +import path from 'node:path'; +import B from 'bluebird'; export default { /** @@ -218,6 +220,45 @@ export default { } }, + /** + * Deletes application data files, so it could start from the clean state next time + * it is launched. + * This API only works on a Simulator. + * + * @param {string} bundleId Application bundle identifier + * @this {XCUITestDriver} + * @returns {Promise} true if any files from the app's data container have been deleted + */ + async mobileClearApp(bundleId) { + if (this.isRealDevice()) { + throw new errors.NotImplementedError( + `This extension is only supported on simulators. ` + + `The only known way to clear app data on real devices ` + + `would be to uninstall the app then perform a fresh install of it.` + ); + } + + // @ts-ignore This opt must exist + const simctl = this.opts.device.simctl; + const dataRoot = await simctl.getAppContainer(bundleId, 'data'); + this.log.debug(`Got the data container root of ${bundleId} at '${dataRoot}'`); + if (!await fs.exists(dataRoot)) { + return false; + } + + await this.mobileTerminateApp(bundleId); + const items = await fs.readdir(dataRoot); + if (!items.length) { + return false; + } + + await B.all(items.map((item) => fs.rimraf(path.join(dataRoot, item)))); + this.log.info( + `Cleaned up ${util.pluralize('item', items.length, true)} from ${bundleId}'s data container` + ); + return true; + }, + /** * Close app (simulate device home button). It is possible to restore * the app after the timeout or keep it minimized based on the parameter value. diff --git a/lib/driver.js b/lib/driver.js index 658a5418f..62d781ad8 100644 --- a/lib/driver.js +++ b/lib/driver.js @@ -1823,6 +1823,7 @@ class XCUITestDriver extends BaseDriver { terminateApp = commands.appManagementExtensions.terminateApp; queryAppState = commands.appManagementExtensions.queryAppState; mobileListApps = commands.appManagementExtensions.mobileListApps; + mobileClearApp = commands.appManagementExtensions.mobileClearApp; /*------------+ | APPEARANCE | diff --git a/lib/execute-method-map.ts b/lib/execute-method-map.ts index dac2f2cf4..fe7259839 100644 --- a/lib/execute-method-map.ts +++ b/lib/execute-method-map.ts @@ -195,6 +195,12 @@ export const executeMethodMap = { optional: ['applicationType'], }, }, + 'mobile: clearApp': { + command: 'mobileClearApp', + params: { + required: ['bundleId'], + }, + }, 'mobile: viewportScreenshot': { command: 'getViewportScreenshot', },