diff --git a/README.md b/README.md index 0260ca80..cd925e07 100644 --- a/README.md +++ b/README.md @@ -394,6 +394,8 @@ These Appium commands can work across context - `getContexts` - `activateApp('appId')`/`mobile:activateApp` - `mobile:activateApp` has `skipAttachObservatoryUrl` key to not try to attach to an observatory url. e.g. `driver.execute_script 'mobile:activateApp', {skipAttachObservatoryUrl: true, appId: 'com.android.chrome'}` +- `mobile:launchApp` (iOS) + - Relaunches the app with `arguments`/`environment` via XCUITest, then attaches to the observatory url (`skipAttachObservatoryUrl` is honored as well). Use it instead of `activateApp` when the app needs launch arguments again, e.g. with `appium:dartVmServicePort`: `driver.execute_script 'mobile:launchApp', {bundleId: 'com.example.bundleId', arguments: ['--vm-service-port=8283', '--disable-service-auth-codes']}` - `terminateApp('appId')`/`mobile:terminateApp` - `installApp(appPath, options)` - `getClipboard` diff --git a/driver/lib/driver.ts b/driver/lib/driver.ts index 5503ad75..1a869e05 100644 --- a/driver/lib/driver.ts +++ b/driver/lib/driver.ts @@ -236,7 +236,7 @@ class FlutterDriver extends BaseDriver { } public async executeCommand(cmd: string, ...args: [string, [{skipAttachObservatoryUrl: string; any: any}]]) { - if (new RegExp(/^[\s]*mobile:[\s]*activateApp$/).test(args[0])) { + if (new RegExp(/^[\s]*mobile:[\s]*(activateApp|launchApp)$/).test(args[0])) { const {skipAttachObservatoryUrl = false} = args[1][0]; await this.proxydriver?.executeCommand(cmd, ...args); if (skipAttachObservatoryUrl) { diff --git a/driver/test/mobile-launch-app.test.mjs b/driver/test/mobile-launch-app.test.mjs new file mode 100644 index 00000000..5e3e0a7e --- /dev/null +++ b/driver/test/mobile-launch-app.test.mjs @@ -0,0 +1,38 @@ +import assert from 'node:assert/strict'; +import {it, mock} from 'node:test'; + +import {FlutterDriver} from '../build/lib/driver.js'; + +const noopLog = {info() {}, debug() {}, warn() {}, error() {}, errorWithException() {}}; + +// The reconnect that follows the launch fails right away here because no device-log monitor +// was started, which shows that the driver tried to re-attach to the Dart VM. +it('forwards mobile: launchApp to XCUITest from the FLUTTER context and reconnects', async () => { + const proxydriver = {executeCommand: mock.fn(async () => {})}; + const params = {bundleId: 'com.example.app', arguments: ['--vm-service-port=9123']}; + + await assert.rejects( + FlutterDriver.prototype.executeCommand.call( + {log: noopLog, currentContext: 'FLUTTER', internalCaps: {platformName: 'iOS'}, proxydriver}, + 'execute', + 'mobile: launchApp', + [params], + ), + /mandatory syslog service/, + ); + + assert.deepEqual(proxydriver.executeCommand.mock.calls[0].arguments, ['execute', 'mobile: launchApp', [params]]); +}); + +it('skips the reconnect for mobile: launchApp when skipAttachObservatoryUrl is set', async () => { + const proxydriver = {executeCommand: mock.fn(async () => {})}; + + await FlutterDriver.prototype.executeCommand.call( + {log: noopLog, currentContext: 'FLUTTER', internalCaps: {platformName: 'iOS'}, proxydriver}, + 'execute', + 'mobile: launchApp', + [{bundleId: 'com.example.app', skipAttachObservatoryUrl: true}], + ); + + assert.equal(proxydriver.executeCommand.mock.callCount(), 1); +});