diff --git a/README.md b/README.md index 67d4cda..1125754 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ The current Appium Flutter Driver is built on top of the `flutter_test` SDK, whi This driver is built using [Flutter Integration Test](https://docs.flutter.dev/cookbook/testing/integration/introduction). - :star: **⁠Strong Typing & Fluent APIs:** Ensures robust and easy-to-use interfaces. +:star: **⁠Strong Typing & Fluent APIs**: Ensures robust and easy-to-use interfaces. :star: **⁠Element Handling**: Automatically waits for elements to attach to the DOM before interacting. @@ -35,7 +35,7 @@ This driver is built using [Flutter Integration Test](https://docs.flutter.dev/c :star: **Simplified Powerful Gestures**: Supports powerful yet simplified gestures like LongPress, ScrollToElement, DragAndDrop, and DoubleClick. -:star:*⁠*Element Chaining**: Allows chaining of elements, enabling you to find child elements under a specific parent easily. +:star: **Element Chaining**: Allows chaining of elements, enabling you to find child elements under a specific parent easily. ## How to Use Appium Flutter Integration Driver @@ -60,7 +60,9 @@ This driver is built using [Flutter Integration Test](https://docs.flutter.dev/c initializeTest(app: const MyApp()); } ``` + If you are in need to configure certain prerequists before the testing app is loaded, you can try the following code: + ```dart import 'package:appium_testing_app/main.dart'; as app; void main() { @@ -91,11 +93,11 @@ Bingo! You are ready to run your tests using Appium Flutter Integration Driver. Check if your Flutter app is running on the device or emulator. For Android -``` + 1. Run adb command `adb logcat | grep flutter` to check if the Flutter app is running. 2. Open the application in the device or emulator manually. 3. Verify the logs in the console. -``` + ``` 06-17 17:02:13.246 32697 32743 I flutter : The Dart VM service is listening on http://127.0.0.1:33339/E2REX61NaiI=/ 06-17 17:02:13.584 32697 32735 I flutter : 00:00 +0: appium flutter server @@ -108,11 +110,12 @@ For Android For iOS Simulator: -```xcrun simctl spawn booted log stream | grep flutter``` +`xcrun simctl spawn booted log stream | grep flutter` Real Device: Check xcode device logs. 2. Open the application in the device or emulator manually. + ``` 06-17 17:02:13.246 32697 32743 I flutter : The Dart VM service is listening on http://127.0.0.1:33339/E2REX61NaiI=/ 06-17 17:02:13.584 32697 32735 I flutter : 00:00 +0: appium flutter server @@ -120,6 +123,7 @@ Real Device: Check xcode device logs. 06-17 17:02:14.814 32697 32735 I flutter : [APPIUM FLUTTER] Appium flutter server is listening on port 9000 06-17 17:02:14.866 32697 32735 I flutter : [APPIUM FLUTTER] New Request [GET] http://127.0.0.1:10000/status 06-17 +``` ## Install the Flutter Integration Driver @@ -140,30 +144,30 @@ For more details, refer to the documentation for each driver: ## Capabilities for Appium Flutter Integration Driver -| Capability | Description | Required | -|----------------------------------|-------------------------------------------------------------------------|----------| -| appium:flutterServerLaunchTimeout | Time in ms to wait for flutter server to be pingable. Default is 5000ms | No | -| appium:flutterSystemPort | The number of the port on the host machine used for the Flutter server. By default the first free port from 10000..11000 range is selected. It is recommended to set this value if you are running parallel tests on the same machine.| No | - +| Capability | Description | Required | +| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | +| appium:flutterServerLaunchTimeout | Time in ms to wait for flutter server to be pingable. Default is 5000ms | No | +| appium:flutterSystemPort | The number of the port on the host machine used for the Flutter server. By default the first free port from 10000..11000 range is selected. It is recommended to set this value if you are running parallel tests on the same machine. | No | +| appium:address | Address to connect with flutter server | No | ## Locating Elements You can use the following locators to find elements in your Flutter app. Custom finders are built for WDIO. Refer to the [wdio-flutter-by-service](https://www.npmjs.com/package/wdio-flutter-by-service?activeTab=readme). -| Locator | Description | -|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------| -| `flutterByValueKey(value: string): Flutter.Locator` | Locate by value key | -| `flutterByValueKey$(value: string): WebdriverIO.Element` | Locate single element by value key | -| `flutterByValueKey$$(value: string): WebdriverIO.Element[]` | Locate multiple elements by value key | -| `flutterBySemanticsLabel(label: string): Flutter.Locator` | Locate by semantics label | -| `flutterBySemanticsLabel$(label: string): WebdriverIO.Element` | Locate single element by semantics label | -| `flutterBySemanticsLabel$$(label: string): WebdriverIO.Element[]` | Locate multiple elements by semantics label | -| `flutterByText(text: string): Flutter.Locator` | Locate by text | -| `flutterByText$(text: string): WebdriverIO.Element` | Locate single element by text | -| `flutterByType$(text: string): WebdriverIO.Element` | Locate single element by Type(Checkbox, RadioButton, ListView) | -| `flutterByType$$(text: string): WebdriverIO.Element[]` | Locate multiple elements by text(Checkbox, RadioButton, ListView)| -| `flutterDoubleClick(element: WebdriverIO.Element): WebdriverIO.Element` | Double click on an element | -| `flutterWaitForAbsent(options: { element: WebdriverIO.Element; locator: Flutter.Locator; }): void` | Wait for an element to be absent | -| `flutterScrollTillVisible(options: { finder: WebdriverIO.Element; scrollView?: WebdriverIO.Element; scrollDirection?: 'up','right','down','left'; delta?: number; maxScrolls?: number; settleBetweenScrollsTimeout?: number; dragDuration?: number; }): Promise` | Scroll until an element is visible | +| Locator | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `flutterByValueKey(value: string): Flutter.Locator` | Locate by value key | +| `flutterByValueKey$(value: string): WebdriverIO.Element` | Locate single element by value key | +| `flutterByValueKey$$(value: string): WebdriverIO.Element[]` | Locate multiple elements by value key | +| `flutterBySemanticsLabel(label: string): Flutter.Locator` | Locate by semantics label | +| `flutterBySemanticsLabel$(label: string): WebdriverIO.Element` | Locate single element by semantics label | +| `flutterBySemanticsLabel$$(label: string): WebdriverIO.Element[]` | Locate multiple elements by semantics label | +| `flutterByText(text: string): Flutter.Locator` | Locate by text | +| `flutterByText$(text: string): WebdriverIO.Element` | Locate single element by text | +| `flutterByType$(text: string): WebdriverIO.Element` | Locate single element by Type(Checkbox, RadioButton, ListView) | +| `flutterByType$$(text: string): WebdriverIO.Element[]` | Locate multiple elements by text(Checkbox, RadioButton, ListView) | +| `flutterDoubleClick(element: WebdriverIO.Element): WebdriverIO.Element` | Double click on an element | +| `flutterWaitForAbsent(options: { element: WebdriverIO.Element; locator: Flutter.Locator; }): void` | Wait for an element to be absent | +| `flutterScrollTillVisible(options: { finder: WebdriverIO.Element; scrollView?: WebdriverIO.Element; scrollDirection?: 'up','right','down','left'; delta?: number; maxScrolls?: number; settleBetweenScrollsTimeout?: number; dragDuration?: number; }): Promise` | Scroll until an element is visible | For more examples, see the [test file](https://github.com/AppiumTestDistribution/appium-flutter-integration-driver/blob/main/test/specs/test.e2e.js). diff --git a/package-lock.json b/package-lock.json index 76616f7..fe09758 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "appium-flutter-integration-driver", - "version": "1.0.0-beta.14", + "version": "1.0.0-beta.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "appium-flutter-integration-driver", - "version": "1.0.0-beta.14", + "version": "1.0.0-beta.15", "license": "MIT License", "dependencies": { "@appium/base-driver": "^9.6.0", diff --git a/package.json b/package.json index 882cbd6..c75e4b2 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "appium", "flutter" ], - "version": "1.0.0-beta.14", + "version": "1.0.0-beta.15", "author": "", "license": "MIT License", "repository": { diff --git a/src/desiredCaps.ts b/src/desiredCaps.ts index 200099d..ff196ee 100644 --- a/src/desiredCaps.ts +++ b/src/desiredCaps.ts @@ -22,5 +22,8 @@ export const desiredCapConstraints = { }, flutterSystemPort: { isNumber: true, - } + }, + address: { + isString: true, + }, } as const; diff --git a/src/driver.ts b/src/driver.ts index 6c98185..ae7b9f0 100644 --- a/src/driver.ts +++ b/src/driver.ts @@ -114,9 +114,9 @@ export class AppiumFlutterDriver extends BaseDriver { 'flutter: dragAndDrop': { command: 'dragAndDrop', params: { - required: ['source', 'target'] - } - } + required: ['source', 'target'], + }, + }, }; async doubleClick(origin: any, offset: any) { @@ -246,28 +246,39 @@ export class AppiumFlutterDriver extends BaseDriver { : this.proxydriver.opts.bundleId!; const portcallbacks: { - portForwardCallback?: PortForwardCallback, - portReleaseCallback?: PortReleaseCallback, + portForwardCallback?: PortForwardCallback; + portReleaseCallback?: PortReleaseCallback; } = {}; if (this.proxydriver instanceof AndroidUiautomator2Driver) { - portcallbacks.portForwardCallback = async (_: string, systemPort: number, devicePort: number) => await androidPortForward( - // @ts-ignore ADB instance is ok - (this.proxydriver as AndroidUiautomator2Driver).adb, - systemPort, - devicePort - ); - portcallbacks.portReleaseCallback = async (_: string, systemPort: number) => await androidRemovePortForward( - // @ts-ignore ADB instance is ok - (this.proxydriver as AndroidUiautomator2Driver).adb, - systemPort - ); + portcallbacks.portForwardCallback = async ( + _: string, + systemPort: number, + devicePort: number, + ) => + await androidPortForward( + // @ts-ignore ADB instance is ok + (this.proxydriver as AndroidUiautomator2Driver).adb, + systemPort, + devicePort, + ); + portcallbacks.portReleaseCallback = async ( + _: string, + systemPort: number, + ) => + await androidRemovePortForward( + // @ts-ignore ADB instance is ok + (this.proxydriver as AndroidUiautomator2Driver).adb, + systemPort, + ); } else if (this.proxydriver.isRealDevice()) { portcallbacks.portForwardCallback = iosPortForward; portcallbacks.portReleaseCallback = iosRemovePortForward; } const flutterCaps: DriverCaps = { - flutterServerLaunchTimeout: this.internalCaps.flutterServerLaunchTimeout || 5000, - flutterSystemPort: this.internalCaps.flutterSystemPort || await getFreePort(), + flutterServerLaunchTimeout: + this.internalCaps.flutterServerLaunchTimeout || 5000, + flutterSystemPort: + this.internalCaps.flutterSystemPort || (await getFreePort()), } as DriverCaps; const systemPort = this.proxydriver instanceof XCUITestDriver && @@ -281,7 +292,7 @@ export class AppiumFlutterDriver extends BaseDriver { packageName, ...portcallbacks, systemPort, - flutterCaps + flutterCaps, }); if (!this.flutterPort) { @@ -294,7 +305,7 @@ export class AppiumFlutterDriver extends BaseDriver { // @ts-ignore this.proxy = new JWProxy({ - server: '127.0.0.1', + server: this.internalCaps.address || '127.0.0.1', port: this.flutterPort, }); diff --git a/src/utils.ts b/src/utils.ts index 3dda795..b2f9771 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -58,7 +58,11 @@ export async function getFreePort() { return await findAPortNotInUse(start, end); } -async function waitForFlutterServer(port: number, packageName: string, flutterCaps: DriverCaps) { +async function waitForFlutterServer( + port: number, + packageName: string, + flutterCaps: DriverCaps, +) { const proxy = new JWProxy({ server: '127.0.0.1', port: port, @@ -71,12 +75,15 @@ async function waitForFlutterServer(port: number, packageName: string, flutterCa return false; } if (response?.appInfo?.packageName === packageName) { - log.info(`Flutter server version the application is build with ${response.serverVersion}`); + log.info( + `Flutter server version the application is build with ${response.serverVersion}`, + ); return true; } else { - throw new Error( + log.info( `Looking for flutter server with package ${packageName}. But found ${response.appInfo?.packageName}`, ); + return false; } } catch (err: any) { log.info(`FlutterServer not reachable on port ${port}, Retrying..`); @@ -96,7 +103,7 @@ export async function fetchFlutterServerPort({ portForwardCallback, portReleaseCallback, packageName, - flutterCaps + flutterCaps, }: { udid: string; systemPort?: number | null;