Skip to content

Commit

Permalink
Merge branch 'master' into tests-cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
KazuCocoa authored Sep 23, 2024
2 parents 991d57e + 2ffa930 commit f0349fd
Show file tree
Hide file tree
Showing 26 changed files with 1,467 additions and 0 deletions.
119 changes: 119 additions & 0 deletions .github/workflows/functional-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,122 @@ jobs:
with:
name: appium-android-${{matrix.test_targets.name}}.log
path: appium.log

flutter_e2e_test:
# These flutter integration driver tests are maintained by: MummanaSubramanya
strategy:
fail-fast: false
matrix:
include:
- platform: macos-14
e2e-tests: flutter-ios
- platform: ubuntu-latest
e2e-tests: flutter-android

runs-on: ${{ matrix.platform }}

env:
API_LEVEL: 28
ARCH: x86
CI: true
XCODE_VERSION: 15.4
IOS_VERSION: 17.5
IPHONE_MODEL: iPhone 15
FLUTTER_ANDROID_APP: "https://github.com/AppiumTestDistribution/appium-flutter-server/releases/latest/download/app-debug.apk"
FLUTTER_IOS_APP: "https://github.com/AppiumTestDistribution/appium-flutter-server/releases/latest/download/ios.zip"

steps:

- uses: actions/checkout@v4

- uses: actions/setup-java@v4
if: matrix.e2e-tests == 'flutter-android'
with:
distribution: 'zulu'
java-version: '17'

- name: Enable KVM group perms
if: matrix.e2e-tests == 'flutter-android'
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Set up Python 3.12
uses: actions/setup-python@v3
with:
python-version: 3.12

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 'lts/*'

- name: Install Appium
run: npm install --location=global appium

- name: Install Android drivers and Run Appium
if: matrix.e2e-tests == 'flutter-android'
run: |
appium driver install uiautomator2
appium driver install appium-flutter-integration-driver --source npm
nohup appium --allow-insecure=adb_shell --relaxed-security --log-timestamp --log-no-colors 2>&1 > appium_flutter_android.log &
- name: Run Android tests
if: matrix.e2e-tests == 'flutter-android'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ env.API_LEVEL }}
script: |
pip install --upgrade pip
pip install --upgrade pipenv
pipenv lock --clear
pipenv install -d --system
export PLATFORM=android
pytest test/functional/flutter_integration/*_test.py --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html
target: default
disable-spellchecker: true
disable-animations: true

- name: Save server output
if: always() && matrix.e2e-tests == 'flutter-android'
uses: actions/upload-artifact@master
with:
name: appium-flutter-android.log
path: appium_flutter_android.log

- name: Select Xcode
if: matrix.e2e-tests == 'flutter-ios'
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ env.XCODE_VERSION }}

- uses: futureware-tech/simulator-action@v3
if: matrix.e2e-tests == 'flutter-ios'
with:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md
model: ${{ env.IPHONE_MODEL }}
os_version: ${{ env.IOS_VERSION }}

- name: install dependencies
if: matrix.e2e-tests == 'flutter-ios'
run: brew install ffmpeg

- name: Install IOS drivers and Run Appium
if: matrix.e2e-tests == 'flutter-ios'
run: |
appium driver install xcuitest
appium driver install appium-flutter-integration-driver --source npm
appium driver run xcuitest build-wda
nohup appium --allow-insecure=adb_shell --relaxed-security --log-timestamp --log-no-colors 2>&1 > appium_ios.log &
- name: Run IOS tests
if: matrix.e2e-tests == 'flutter-ios'
run: |
# Separate 'run' creates differnet pipenv env. Does them in one run for now.
pip install --upgrade pip
pip install --upgrade pipenv
pipenv lock --clear
pipenv install -d --system
export PLATFORM=ios
pytest test/functional/flutter_integration/*_test.py --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html
7 changes: 7 additions & 0 deletions appium/common/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import base64
from typing import Any, Dict

from appium import version as appium_version
Expand All @@ -33,3 +34,9 @@ def library_version() -> str:
"""Return a version of this python library"""

return appium_version.version


def encode_file_to_base64(file_path: str) -> str:
"""Return base64 encoded string for given file"""
with open(file_path, 'rb') as file:
return base64.b64encode(file.read()).decode('utf-8')
1 change: 1 addition & 0 deletions appium/options/flutter_integration/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .base import FlutterOptions
40 changes: 40 additions & 0 deletions appium/options/flutter_integration/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from typing import Dict

from appium.options.common.automation_name_option import AUTOMATION_NAME
from appium.options.common.base import AppiumOptions
from appium.options.flutter_integration.flutter_element_wait_timeout_option import FlutterElementWaitTimeOutOption
from appium.options.flutter_integration.flutter_enable_mock_camera_option import FlutterEnableMockCameraOption
from appium.options.flutter_integration.flutter_server_launch_timeout_option import FlutterServerLaunchTimeOutOption
from appium.options.flutter_integration.flutter_system_port_option import FlutterSystemPortOption


class FlutterOptions(
AppiumOptions,
FlutterElementWaitTimeOutOption,
FlutterEnableMockCameraOption,
FlutterServerLaunchTimeOutOption,
FlutterSystemPortOption,
):

@property
def default_capabilities(self) -> Dict:
return {
AUTOMATION_NAME: 'FlutterIntegration',
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from datetime import timedelta
from typing import Optional, Union

from appium.options.common.supports_capabilities import SupportsCapabilities

FLUTTER_ELEMENT_WAIT_TIMEOUT = 'flutterElementWaitTimeout'


class FlutterElementWaitTimeOutOption(SupportsCapabilities):

@property
def flutter_element_wait_timeout(self) -> Optional[timedelta]:
"""
Maximum timeout to wait for element for Flutter integration test
Returns:
Optional[timedelta]: The timeout value as a `timedelta` object if set, or `None` if the timeout is not defined.
"""
return self.get_capability(FLUTTER_ELEMENT_WAIT_TIMEOUT)

@flutter_element_wait_timeout.setter
def flutter_element_wait_timeout(self, value: Union[timedelta, int]) -> None:
"""
Sets the maximum timeout to wait for a Flutter element in an integration test.
Default timeout is 5000ms
Args:
value (Union[timedelta, int]): The timeout value, either as a `timedelta` object or an integer in milliseconds.
If provided as a `timedelta`, it will be converted to milliseconds.
"""
self.set_capability(
FLUTTER_ELEMENT_WAIT_TIMEOUT,
(int(value.total_seconds() * 1000) if isinstance(value, timedelta) else value),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from typing import Optional

from appium.options.common.supports_capabilities import SupportsCapabilities

FLUTTER_ENABLE_MOCK_CAMERA = 'flutterEnableMockCamera'


class FlutterEnableMockCameraOption(SupportsCapabilities):

@property
def flutter_enable_mock_camera(self) -> bool:
"""
Get state of the mock camera for Flutter integration test
Returns:
bool: A boolean indicating whether the mock camera is enabled (True) or disabled (False).
"""
return self.get_capability(FLUTTER_ENABLE_MOCK_CAMERA)

@flutter_enable_mock_camera.setter
def flutter_enable_mock_camera(self, value: bool) -> None:
"""
Setter method enable or disable the mock camera for Flutter integration test
Default state is `False`
Args:
value (bool): A boolean value indicating whether to enable (True) or disable (False) the mock camera.
"""
self.set_capability(FLUTTER_ENABLE_MOCK_CAMERA, value)
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from datetime import timedelta
from typing import Optional, Union

from appium.options.common.supports_capabilities import SupportsCapabilities

FLUTTER_SERVER_LAUNCH_TIMEOUT = 'flutterServerLaunchTimeout'


class FlutterServerLaunchTimeOutOption(SupportsCapabilities):

@property
def flutter_server_launch_timeout(self) -> Optional[timedelta]:
"""
Gets the current timeout for launching the Flutter server in a Flutter application.
Returns:
Optional[timedelta]: The timeout value as a `timedelta` object if set, or `None` if the timeout is not defined.
"""
return self.get_capability(FLUTTER_SERVER_LAUNCH_TIMEOUT)

@flutter_server_launch_timeout.setter
def flutter_server_launch_timeout(self, value: Union[timedelta, int]) -> None:
"""
Sets the timeout for launching the Flutter server in Flutter application.
Default timeout is 5000ms
Args:
value (Union[timedelta, int]): The timeout value, either as a `timedelta` object or an integer in milliseconds.
If provided as a `timedelta`, it will be converted to milliseconds.
"""
self.set_capability(
FLUTTER_SERVER_LAUNCH_TIMEOUT,
(int(value.total_seconds() * 1000) if isinstance(value, timedelta) else value),
)
46 changes: 46 additions & 0 deletions appium/options/flutter_integration/flutter_system_port_option.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Licensed to the Software Freedom Conservancy (SFC) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The SFC licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from typing import Optional

from appium.options.common.supports_capabilities import SupportsCapabilities

FLUTTER_SYSTEM_PORT = 'flutterSystemPort'


class FlutterSystemPortOption(SupportsCapabilities):

@property
def flutter_system_port(self) -> Optional[int]:
"""
Get flutter system port for Flutter integration tests.
Returns:
int: returns the port number
"""
return self.get_capability(FLUTTER_SYSTEM_PORT)

@flutter_system_port.setter
def flutter_system_port(self, value: int) -> None:
"""
Sets the system port for Flutter integration tests.
By default the first free port from 10000..11000 range is selected
Args:
value (int): The port number to be used for the Flutter server.
"""
self.set_capability(FLUTTER_SYSTEM_PORT, value)
7 changes: 7 additions & 0 deletions appium/webdriver/common/appiumby.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ class AppiumBy(By):
ACCESSIBILITY_ID = 'accessibility id'
IMAGE = '-image'
CUSTOM = '-custom'

# For Flutter integration usage https://github.com/AppiumTestDistribution/appium-flutter-integration-driver/tree/main
FLUTTER_INTEGRATION_SEMANTICS_LABEL = '-flutter semantics label'
FLUTTER_INTEGRATION_TYPE = '-flutter type'
FLUTTER_INTEGRATION_KEY = '-flutter key'
FLUTTER_INTEGRATION_TEXT = '-flutter text'
FLUTTER_INTEGRATION_TEXT_CONTAINING = '-flutter text containing'
Loading

0 comments on commit f0349fd

Please sign in to comment.