Commit 493b2cde authored by Douglas's avatar Douglas

jstestnode: added support for running tests with Selenium Remote and Appium

It uses Appium, which provides a Selenium WebDriver compatible API
to remotely control an iOS (or Android) simulator. This way we can
run tests in both mobile OSes without big changes to the current
test code and infrastructure.

This allows user to customize in the test suite module on which
system they want to run the tests (Firefox or iOS) through the
slapos parameters. In iOS, for example, it's possible to change
the iOS version and it's required that the user give the SauceLabs
credentials in form of user:apikey using the `appium_server_auth` parameter.

An example of parameters to use in a test suite:

```
{
  "mariadb": {
    "relaxed-writes": true,
    "mariadb-relaxed-writes": true,
    "test-database-amount": 30
  },
  "target": "iOS",
  "target-version": "9.3",
  "target-device": "iPhone Simulator",
  "target-browser": "Safari",
  "appium-server-auth": "username:auth_token",
  "test-suite" : "jio" ,
  "test-url": "jio-repository.git/test/tests.html"
}
```
parent cbcf4793
......@@ -58,6 +58,7 @@ class Recipe(GenericBaseRecipe):
"\npath_list = %s" % ",".join(software_path_list)
CONFIG['computer_id'] = self.buildout['slap-connection']['computer-id']
CONFIG['server_url'] = self.buildout['slap-connection']['server-url']
CONFIG['frontend_url'] = self.buildout['testnode-frontend']['connection-secure_access']
configuration_file = self.createFile(
self.options['configuration-file'],
self.substituteTemplate(
......
......@@ -18,6 +18,7 @@ httpd_port = %(httpd_port)s
httpd_software_access_port = %(httpd_software_access_port)s
computer_id = %(computer_id)s
server_url = %(server_url)s
frontend_url = %(frontend_url)s
# Binaries
git_binary = %(git_binary)s
......
......@@ -76,8 +76,7 @@ SSLProxyEngine On
Listen [%(ip)s]:%(software_access_port)s
<VirtualHost *:%(software_access_port)s>
SSLEngine on
RewriteRule (.*) http://[%(ip)s]:%(software_access_port)s/VirtualHostBase/https/[%(ip)s]:%(software_access_port)s/VirtualHostRoot/$1 [L,P]
DocumentRoot "%(testnode_software_directory)s"
DocumentRoot "%(testnode_srv_directory)s"
<Directory />
Options FollowSymLinks
IndexOptions FancyIndexing
......
......@@ -2,7 +2,6 @@
parts =
nginx-service
runTestSuite-instance
eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory}
offline = true
......@@ -36,6 +35,7 @@ framebuffer = $${:srv}/framebuffer
recipe = slapos.recipe.template
url = ${template-runTestSuite:output}
output = $${directory:bin}/runTestSuite
buildout-directory = $${buildout:directory}
mode = 0700
[firefox-instance]
......@@ -76,7 +76,7 @@ output = $${directory:etc}/nginx.cfg
mode = 0600
access_log = $${directory:log}/nginx-access.log
error_log = $${directory:log}/nginx-error.log
ip = $${instance-parameter:ipv6-random}
ip = $${instance-parameters:ipv6-random}
port = 9443
ssl_key = $${directory:ssl}/nginx.key
ssl_csr = $${directory:ssl}/nginx.csr
......@@ -85,7 +85,7 @@ ssl_crt = $${directory:ssl}/nginx.crt
#################################
# SlapOS service
#################################
[instance-parameter]
[instance-parameters]
recipe = slapos.cookbook:slapconfiguration
computer = $${slap_connection:computer_id}
partition = $${slap_connection:partition_id}
......
......@@ -31,9 +31,43 @@ def main():
parser.add_argument('--node_quantity', help='ignored', type=int)
parser.add_argument('--master_url',
help='The Url of Master controling many suites')
parser.add_argument('--frontend_url',
help='The url of frontend of the test suite')
parser.add_argument('--target',
help='Target OS to run tests on',
type=str)
parser.add_argument('--target_version',
help='Target OS version to use',
type=str,)
parser.add_argument('--target_browser',
help='The desired browser of the target OS to be used. Example: Firefox if target is Android.',
type=str,)
parser.add_argument('--target_device',
help='The desired device running the target OS. Example: iPad Simulator, if target is iOS.',
type=str,)
parser.add_argument('--appium_server_auth',
help='Combination of user and token to access SauceLabs service. (i.e. user:token)',
type=str)
args = parser.parse_args()
import json
parsed_parameters = json.loads('$${instance-parameters:configuration._}')
if not getattr(args, 'target', None):
args.target = parsed_parameters.get('target', 'firefox')
if not getattr(args, 'test_suite', None):
args.test_suite = parsed_parameters.get('test-suite')
if not getattr(args, 'target_version', None):
args.target_version = parsed_parameters.get('target-version')
if not getattr(args, 'appium_server_auth', None):
args.appium_server_auth = parsed_parameters.get('appium-server-auth')
if not getattr(args, 'target_browser', None):
args.target_browser = parsed_parameters.get('target-browser')
if not getattr(args, 'target_device', None):
args.target_device = parsed_parameters.get('target-device')
try:
test_suite_title = args.test_suite_title or args.test_suite
test_suite = args.test_suite
......@@ -51,8 +85,25 @@ def main():
##########################
# Run all tests
##########################
if args.target == 'firefox':
firefox_binary = webdriver.firefox.firefox_binary.FirefoxBinary(firefox_path=FIREFOX_EXECUTABLE)
browser = webdriver.Firefox(firefox_binary=firefox_binary)
else:
capabilities = {
'platformName': args.target,
'platformVersion': args.target_version,
'deviceName': args.target_device,
'browserName': args.target_browser
}
if not args.appium_server_auth:
raise RuntimeError('--appium_server_auth is required.')
appium_url = "http://%s@ondemand.saucelabs.com/wd/hub" % (args.appium_server_auth)
browser = webdriver.Remote(appium_url, capabilities)
full_path = '$${runTestSuite-instance:buildout-directory}/software_release/parts/%s' % parsed_parameters['test-url']
full_path = full_path.split('srv')[-1]
url = "%s%s" % (args.frontend_url, full_path)
agent = browser.execute_script("return navigator.userAgent")
print agent
......@@ -65,9 +116,9 @@ def main():
html_parser = etree.HTMLParser(recover=True)
body = etree.fromstring(browser.page_source.encode('UTF-8'), html_parser)
browser.quit()
browser.title.encode('UTF-8')
browser.quit()
print ' '.join(body.xpath('//*[@id="qunit-testresult"]//text()'))
......@@ -99,7 +150,7 @@ def main():
node_title = args.test_node_title,
test_title = test_suite_title,
project_title = args.project_title)
if test_result is None:
if test_result is None or not hasattr(args, 'master_url'):
return
# report test results
while 1:
......@@ -123,8 +174,5 @@ def main():
# XXX: inform test node master of error
raise EnvironmentError(result)
finally:
browser.quit()
if __name__ == "__main__":
main()
......@@ -25,7 +25,7 @@ parts =
[instance]
recipe = slapos.recipe.template
md5sum = 25a9c895fff279b71b0dbbad6647181b
md5sum = 605c1f62f93bbb77bb5f3b485882d4f2
url = ${:_profile_base_location_}/instance.cfg.in
output = ${buildout:directory}/instance.cfg
mode = 0644
......@@ -107,7 +107,7 @@ mode = 0644
[template-runTestSuite]
recipe = slapos.recipe.template
url = ${:_profile_base_location_}/runTestSuite.in
md5sum = 88e820d30b36ecca9b45242ce4a52039
md5sum = da89bddca28899023b67eb9757cb94f4
output = ${buildout:directory}/runTestSuite.in
mode = 0644
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment