Skip to content

Commit e380546

Browse files
author
Feroz Khan
committed
feat: Added support for macOS
1 parent 792fe63 commit e380546

File tree

10 files changed

+152
-26
lines changed

10 files changed

+152
-26
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ Get the latest version from `https://pub.dev/packages/appium_flutter_server/inst
9696
flutter build ipa --release integration_test/appium_test.dart
9797
```
9898
99+
7. Build the MacOS app:
100+
```bash
101+
flutter build macos --release integration_test/appium_test.dart
102+
```
103+
99104
Bingo! You are ready to run your tests using Appium Flutter Integration Driver.
100105
101106
Check if your Flutter app is running on the device or emulator.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"appium",
66
"flutter"
77
],
8-
"version": "1.4.1",
8+
"version": "1.5.0",
99
"author": "",
1010
"license": "MIT License",
1111
"repository": {
@@ -29,7 +29,8 @@
2929
"automationName": "FlutterIntegration",
3030
"platformNames": [
3131
"Android",
32-
"iOS"
32+
"iOS",
33+
"Mac"
3334
],
3435
"mainClass": "AppiumFlutterDriver",
3536
"flutterServerVersion": ">=0.0.18 <1.0.0"
@@ -98,6 +99,7 @@
9899
"appium-ios-device": "^2.7.20",
99100
"appium-uiautomator2-driver": "^4.1.5",
100101
"appium-xcuitest-driver": "9.1.2",
102+
"appium-mac2-driver": "^2.2.2",
101103
"async-retry": "^1.3.3",
102104
"asyncbox": "^3.0.0",
103105
"bluebird": "^3.7.2",

src/commands/element.ts

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import _ from 'lodash';
2-
import { getProxyDriver } from '../utils';
2+
import { getProxyDriver, FLUTTER_LOCATORS } from '../utils';
33
import { JWProxy } from 'appium/driver';
44
import { AndroidUiautomator2Driver } from 'appium-uiautomator2-driver';
5+
// @ts-ignore
6+
import { XCUITestDriver } from 'appium-xcuitest-driver';
7+
// @ts-ignore
8+
import { Mac2Driver } from 'appium-mac2-driver';
59
import { W3C_ELEMENT_KEY } from 'appium/driver';
610
import type { AppiumFlutterDriver } from '../driver';
711

@@ -15,28 +19,37 @@ export async function findElOrEls(
1519
): Promise<any> {
1620
const driver = await getProxyDriver.bind(this)(strategy);
1721
let elementBody;
18-
if (
19-
!(driver instanceof JWProxy) &&
20-
!(this.proxydriver instanceof AndroidUiautomator2Driver)
22+
function constructFindElementPayload(
23+
strategy: string,
24+
selector: string,
25+
proxyDriver: XCUITestDriver | AndroidUiautomator2Driver | Mac2Driver,
2126
) {
22-
elementBody = {
23-
using: strategy,
24-
value: selector,
25-
context, //this needs be validated
26-
};
27-
} else {
28-
elementBody = {
29-
strategy,
30-
selector: ['-flutter descendant', '-flutter ancestor'].includes(
31-
strategy,
32-
)
33-
? _.isString(selector)
34-
? JSON.parse(selector)
35-
: selector
36-
: selector,
37-
context,
38-
};
27+
const isFlutterLocator =
28+
strategy.startsWith('-flutter') || FLUTTER_LOCATORS.includes(strategy);
29+
30+
const parsedSelector =
31+
['-flutter descendant', '-flutter ancestor'].includes(strategy) &&
32+
_.isString(selector)
33+
? JSON.parse(selector)
34+
: selector; // Special case
35+
36+
if (
37+
proxyDriver instanceof AndroidUiautomator2Driver ||
38+
proxyDriver instanceof XCUITestDriver
39+
) {
40+
return { strategy, selector, context };
41+
} else if (isFlutterLocator) {
42+
return { using: strategy, selector: parsedSelector, context };
43+
} else {
44+
return { using: strategy, value: parsedSelector, context };
45+
}
3946
}
47+
48+
elementBody = constructFindElementPayload(
49+
strategy,
50+
selector,
51+
this.proxydriver,
52+
);
4053
if (mult) {
4154
const response = await driver.command('/elements', 'POST', elementBody);
4255
response.forEach((element: any) => {

src/desiredCaps.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export const desiredCapConstraints = {
77
presence: true,
88
},
99
platformName: {
10-
inclusionCaseInsensitive: ['iOS', 'Android'],
10+
inclusionCaseInsensitive: ['iOS', 'Android', 'Mac'],
1111
isString: true,
1212
presence: true,
1313
},

src/driver.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ type FlutterDriverConstraints = typeof desiredCapConstraints;
1010
// @ts-ignore
1111
import { XCUITestDriver } from 'appium-xcuitest-driver';
1212
import { AndroidUiautomator2Driver } from 'appium-uiautomator2-driver';
13+
// @ts-ignore
14+
import { Mac2Driver } from 'appium-mac2-driver';
1315
import { createSession as createSessionMixin } from './session';
1416
import {
1517
findElOrEls,
@@ -55,7 +57,7 @@ const WEBVIEW_NO_PROXY = [
5557

5658
export class AppiumFlutterDriver extends BaseDriver<FlutterDriverConstraints> {
5759
// @ts-ignore
58-
public proxydriver: XCUITestDriver | AndroidUiautomator2Driver;
60+
public proxydriver: XCUITestDriver | AndroidUiautomator2Driver | Mac2Driver;
5961
public flutterPort: number | null | undefined;
6062
private internalCaps: DriverCaps<FlutterDriverConstraints> | undefined;
6163
public proxy: JWProxy | undefined;
@@ -220,6 +222,9 @@ export class AppiumFlutterDriver extends BaseDriver<FlutterDriverConstraints> {
220222
this.currentContext === this.NATIVE_CONTEXT_NAME &&
221223
isFlutterDriverCommand(command)
222224
) {
225+
this.log.debug(
226+
`executeCommand: command ${command} is flutter command using flutter driver`,
227+
);
223228
return await super.executeCommand(command, ...args);
224229
} else {
225230
this.log.info(

src/macOS.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { AppiumFlutterDriver } from './driver';
2+
// @ts-ignore
3+
import { Mac2Driver } from 'appium-mac2-driver';
4+
import type { InitialOpts } from '@appium/types';
5+
import { DEVICE_CONNECTIONS_FACTORY } from './iProxy';
6+
7+
export async function startMacOsSession(
8+
this: AppiumFlutterDriver,
9+
...args: any[]
10+
): Promise<Mac2Driver> {
11+
this.log.info(`Starting an MacOs proxy session`);
12+
const macOsDriver = new Mac2Driver({} as InitialOpts);
13+
await macOsDriver.createSession(...args);
14+
return macOsDriver;
15+
}
16+
17+
export async function macOsPortForward(
18+
udid: string,
19+
systemPort: number,
20+
devicePort: number,
21+
) {
22+
await DEVICE_CONNECTIONS_FACTORY.requestConnection(udid, systemPort, {
23+
usePortForwarding: true,
24+
devicePort: devicePort,
25+
});
26+
}
27+
28+
export function macOsRemovePortForward(udid: string, systemPort: number) {
29+
DEVICE_CONNECTIONS_FACTORY.releaseConnection(udid, systemPort);
30+
}

src/platform.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export const PLATFORM = {
22
IOS: 'ios',
33
ANDROID: 'android',
4+
MAC: 'mac',
45
} as const;

src/session.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import _ from 'lodash';
33
import { PLATFORM } from './platform';
44
import { startAndroidSession } from './android';
55
import { startIOSSession } from './iOS';
6+
import { startMacOsSession } from './macOS';
67
import type { DefaultCreateSessionResult } from '@appium/types';
78

89
export async function createSession(
@@ -28,6 +29,13 @@ export async function createSession(
2829
this.proxydriver.denyInsecure = this.denyInsecure;
2930
this.proxydriver.allowInsecure = this.allowInsecure;
3031
break;
32+
case PLATFORM.MAC:
33+
this.proxydriver = await startMacOsSession.bind(this)(...args);
34+
this.proxydriver.relaxedSecurityEnabled =
35+
this.relaxedSecurityEnabled;
36+
this.proxydriver.denyInsecure = this.denyInsecure;
37+
this.proxydriver.allowInsecure = this.allowInsecure;
38+
break;
3139
default:
3240
this.log.errorWithException(
3341
`Unsupported platformName: ${caps.platformName}. ` +

src/utils.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { AndroidUiautomator2Driver } from 'appium-uiautomator2-driver';
2+
// @ts-ignore
3+
import { XCUITestDriver } from 'appium-xcuitest-driver';
4+
// @ts-ignore
5+
import { Mac2Driver } from 'appium-mac2-driver';
26
import { findAPortNotInUse } from 'portscanner';
37
import { waitForCondition } from 'asyncbox';
48
import { JWProxy } from '@appium/base-driver';
@@ -29,13 +33,28 @@ export async function getProxyDriver(
2933
strategy: string,
3034
): Promise<JWProxy | undefined> {
3135
if (strategy.startsWith('-flutter') || FLUTTER_LOCATORS.includes(strategy)) {
36+
this.log.debug(
37+
`getProxyDriver: using flutter driver, strategy: ${strategy}`,
38+
);
3239
return this.proxy;
3340
} else if (this.proxydriver instanceof AndroidUiautomator2Driver) {
41+
this.log.debug(
42+
'getProxyDriver: using AndroidUiautomator2Driver driver for Android',
43+
);
3444
// @ts-ignore Proxy instance is OK
3545
return this.proxydriver.uiautomator2.jwproxy;
36-
} else {
46+
} else if (this.proxydriver instanceof XCUITestDriver) {
47+
this.log.debug('getProxyDriver: using XCUITestDriver driver for iOS');
3748
// @ts-ignore Proxy instance is OK
3849
return this.proxydriver.wda.jwproxy;
50+
} else if (this.proxydriver instanceof Mac2Driver) {
51+
this.log.debug('getProxyDriver: using Mac2Driver driver for mac');
52+
// @ts-ignore Proxy instance is OK
53+
return this.proxydriver.wda.proxy;
54+
} else {
55+
throw new Error(
56+
`proxydriver is unknown type (${typeof this.proxydriver})`,
57+
);
3958
}
4059
}
4160

test/unit/element.specs.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import sinon from 'sinon';
33
import * as utils from '../../src/utils';
44
import { AndroidUiautomator2Driver } from 'appium-uiautomator2-driver';
5+
// @ts-ignore
6+
import { XCUITestDriver } from 'appium-xcuitest-driver';
7+
// @ts-ignore
8+
import { Mac2Driver } from 'appium-mac2-driver';
59
import { W3C_ELEMENT_KEY } from 'appium/driver';
610
import {
711
ELEMENT_CACHE,
@@ -137,6 +141,45 @@ describe('Element Interaction Functions', () => {
137141
}),
138142
).to.be.true;
139143
});
144+
145+
it('should use different element body for XCUITestDriver', async () => {
146+
mockAppiumFlutterDriver.proxydriver = new XCUITestDriver();
147+
148+
await findElOrEls.call(
149+
mockAppiumFlutterDriver,
150+
'strategy',
151+
'selector',
152+
false,
153+
'context',
154+
);
155+
156+
expect(
157+
mockDriver.command.calledWith('/element', 'POST', {
158+
strategy: 'strategy',
159+
selector: 'selector',
160+
context: 'context',
161+
}),
162+
).to.be.true;
163+
});
164+
165+
it('should use different element body for Mac2Driver', async () => {
166+
mockAppiumFlutterDriver.proxydriver = new Mac2Driver();
167+
168+
await findElOrEls.call(
169+
mockAppiumFlutterDriver,
170+
'strategy',
171+
'selector',
172+
false,
173+
'context',
174+
);
175+
expect(
176+
mockDriver.command.calledWith('/element', 'POST', {
177+
using: 'strategy',
178+
value: 'selector',
179+
context: 'context',
180+
}),
181+
).to.be.true;
182+
});
140183
});
141184

142185
describe('click', () => {

0 commit comments

Comments
 (0)