Skip to content

Commit f8288d5

Browse files
author
Mykola Mokhnach
authored
Add applications management endpoint handlers (appium#204)
1 parent b75e067 commit f8288d5

6 files changed

Lines changed: 130 additions & 2 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env python
2+
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
class ApplicationState(object):
17+
NOT_INSTALLED = 0
18+
NOT_RUNNING = 1
19+
RUNNING_IN_BACKGROUND_SUSPENDED = 2
20+
RUNNING_IN_BACKGROUND = 3
21+
RUNNING_IN_FOREGROUND = 4

appium/webdriver/mobilecommand.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ class MobileCommand(object):
4545
IS_APP_INSTALLED = 'isAppInstalled'
4646
INSTALL_APP = 'installApp'
4747
REMOVE_APP = 'removeApp'
48+
TERMINATE_APP = 'terminateApp'
49+
ACTIVATE_APP = 'activateApp'
50+
QUERY_APP_STATE = 'queryAppState'
4851
LAUNCH_APP = 'launchApp'
4952
CLOSE_APP = 'closeApp'
5053
END_TEST_COVERAGE = 'endTestCoverage'

appium/webdriver/webdriver.py

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -708,27 +708,49 @@ def is_app_installed(self, bundle_id):
708708
}
709709
return self.execute(Command.IS_APP_INSTALLED, data)['value']
710710

711-
def install_app(self, app_path):
711+
def install_app(self, app_path, **options):
712712
"""Install the application found at `app_path` on the device.
713713
714714
:Args:
715715
- app_path - the local or remote path to the application to install
716+
- options - the possible installation options.
717+
The following options are available for Android:
718+
`replace`: whether to reinstall/upgrade the package if it is
719+
already present on the device under test. True by default
720+
`timeout`: how much time to wait for the installation to complete.
721+
60000ms by default.
722+
`allowTestPackages`: whether to allow installation of packages marked
723+
as test in the manifest. False by default
724+
`useSdcard`: whether to use the SD card to install the app. False
725+
by default
726+
`grantPermissions`: whether to automatically grant application permissions
727+
on Android 6+ after the installation completes. False by default
716728
"""
717729
data = {
718730
'appPath': app_path,
719731
}
732+
if options:
733+
data.update({'options': options})
720734
self.execute(Command.INSTALL_APP, data)
721735
return self
722736

723-
def remove_app(self, app_id):
737+
def remove_app(self, app_id, **options):
724738
"""Remove the specified application from the device.
725739
726740
:Args:
727741
- app_id - the application id to be removed
742+
- options - the possible removal options.
743+
The following options are available for Android:
744+
`keepData`: whether to keep application data and caches after it is uninstalled.
745+
False by default
746+
`timeout`: how much time to wait for the uninstall to complete.
747+
20000ms by default.
728748
"""
729749
data = {
730750
'appId': app_id,
731751
}
752+
if options:
753+
data.update({'options': options})
732754
self.execute(Command.REMOVE_APP, data)
733755
return self
734756

@@ -745,6 +767,54 @@ def close_app(self):
745767
self.execute(Command.CLOSE_APP)
746768
return self
747769

770+
def terminate_app(self, app_id, **options):
771+
"""Terminates the application if it is running.
772+
773+
:Args:
774+
- app_id - the application id to be terminates
775+
- options - the possible termination options.
776+
The following options are available for Android:
777+
`timeout`: how much time to wait for the uninstall to complete.
778+
500ms by default.
779+
780+
:return: True if the app has been successfully terminated
781+
"""
782+
data = {
783+
'appId': app_id,
784+
}
785+
if options:
786+
data.update({'options': options})
787+
return self.execute(Command.TERMINATE_APP, data)['value']
788+
789+
def activate_app(self, app_id):
790+
"""Activates the application if it is not running
791+
or is running in the background.
792+
793+
:Args:
794+
- app_id - the application id to be activated
795+
796+
:return: self instance for chaining
797+
"""
798+
data = {
799+
'appId': app_id,
800+
}
801+
self.execute(Command.ACTIVATE_APP, data)
802+
return self
803+
804+
def query_app_state(self, app_id):
805+
"""Queries the state of the application.
806+
807+
:Args:
808+
- app_id - the application id to be queried
809+
810+
:return: One of possible application state constants. See ApplicationState
811+
class for more details.
812+
"""
813+
data = {
814+
'appId': app_id,
815+
}
816+
return self.execute(Command.QUERY_APP_STATE, data)['value']
817+
748818
def start_activity(self, app_package, app_activity, **opts):
749819
"""Opens an arbitrary activity during a test. If the activity belongs to
750820
another application, that application is started and the activity is opened.
@@ -1094,6 +1164,12 @@ def _addCommands(self):
10941164
('POST', '/session/$sessionId/appium/device/install_app')
10951165
self.command_executor._commands[Command.REMOVE_APP] = \
10961166
('POST', '/session/$sessionId/appium/device/remove_app')
1167+
self.command_executor._commands[Command.TERMINATE_APP] = \
1168+
('POST', '/session/$sessionId/appium/device/terminate_app')
1169+
self.command_executor._commands[Command.ACTIVATE_APP] = \
1170+
('POST', '/session/$sessionId/appium/device/activate_app')
1171+
self.command_executor._commands[Command.QUERY_APP_STATE] = \
1172+
('GET', '/session/$sessionId/appium/device/app_state')
10971173
self.command_executor._commands[Command.START_ACTIVITY] = \
10981174
('POST', '/session/$sessionId/appium/device/start_activity')
10991175
self.command_executor._commands[Command.LAUNCH_APP] = \

test/functional/android/appium_tests.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from time import sleep
2222
from dateutil.parser import parse
2323

24+
from webdriver.applicationstate import ApplicationState
2425
from selenium.common.exceptions import NoSuchElementException
2526

2627
from appium import webdriver
@@ -49,6 +50,17 @@ def test_screen_record(self):
4950
result = self.driver.stop_recording_screen()
5051
self.assertTrue(len(result) > 0)
5152

53+
def test_app_management(self):
54+
app_id = self.driver.current_package
55+
self.assertEqual(self.driver.query_app_state(app_id),
56+
ApplicationState.RUNNING_IN_FOREGROUND)
57+
self.driver.background_app(-1)
58+
self.assertTrue(self.driver.query_app_state(app_id) <
59+
ApplicationState.RUNNING_IN_FOREGROUND)
60+
self.driver.activate_app(app_id)
61+
self.assertEqual(self.driver.query_app_state(app_id),
62+
ApplicationState.RUNNING_IN_FOREGROUND)
63+
5264
def test_lock(self):
5365
self.driver.lock(-1)
5466
try:

test/functional/ios/appium_tests.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import unittest
1616
from time import sleep
1717

18+
from webdriver.applicationstate import ApplicationState
1819
from appium import webdriver
1920
import desired_capabilities
2021

@@ -41,6 +42,20 @@ def test_screen_record(self):
4142
result = self.driver.stop_recording_screen()
4243
self.assertTrue(len(result) > 0)
4344

45+
def test_app_management(self):
46+
# this only works in Xcode9+
47+
if float(desired_capabilities.get_desired_capabilities(
48+
desired_capabilities.BUNDLE_ID)['platformVersion']) < 11:
49+
return
50+
self.assertEqual(self.driver.query_app_state(desired_capabilities.BUNDLE_ID),
51+
ApplicationState.RUNNING_IN_FOREGROUND)
52+
self.driver.background_app(-1)
53+
self.assertTrue(self.driver.query_app_state(desired_capabilities.BUNDLE_ID) <
54+
ApplicationState.RUNNING_IN_FOREGROUND)
55+
self.driver.activate_app(desired_capabilities.BUNDLE_ID)
56+
self.assertEqual(self.driver.query_app_state(desired_capabilities.BUNDLE_ID),
57+
ApplicationState.RUNNING_IN_FOREGROUND)
58+
4459
def test_shake(self):
4560
# what can we assert about this?
4661
self.driver.shake()

test/functional/ios/desired_capabilities.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
os.path.join(os.path.dirname(__file__), p)
1919
)
2020

21+
BUNDLE_ID = 'com.example.apple-samplecode.UICatalog'
2122

2223
def get_desired_capabilities(app):
2324
desired_caps = {

0 commit comments

Comments
 (0)