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): ...@@ -58,6 +58,7 @@ class Recipe(GenericBaseRecipe):
"\npath_list = %s" % ",".join(software_path_list) "\npath_list = %s" % ",".join(software_path_list)
CONFIG['computer_id'] = self.buildout['slap-connection']['computer-id'] CONFIG['computer_id'] = self.buildout['slap-connection']['computer-id']
CONFIG['server_url'] = self.buildout['slap-connection']['server-url'] CONFIG['server_url'] = self.buildout['slap-connection']['server-url']
CONFIG['frontend_url'] = self.buildout['testnode-frontend']['connection-secure_access']
configuration_file = self.createFile( configuration_file = self.createFile(
self.options['configuration-file'], self.options['configuration-file'],
self.substituteTemplate( self.substituteTemplate(
......
...@@ -18,6 +18,7 @@ httpd_port = %(httpd_port)s ...@@ -18,6 +18,7 @@ httpd_port = %(httpd_port)s
httpd_software_access_port = %(httpd_software_access_port)s httpd_software_access_port = %(httpd_software_access_port)s
computer_id = %(computer_id)s computer_id = %(computer_id)s
server_url = %(server_url)s server_url = %(server_url)s
frontend_url = %(frontend_url)s
# Binaries # Binaries
git_binary = %(git_binary)s git_binary = %(git_binary)s
......
...@@ -76,8 +76,7 @@ SSLProxyEngine On ...@@ -76,8 +76,7 @@ SSLProxyEngine On
Listen [%(ip)s]:%(software_access_port)s Listen [%(ip)s]:%(software_access_port)s
<VirtualHost *:%(software_access_port)s> <VirtualHost *:%(software_access_port)s>
SSLEngine on SSLEngine on
RewriteRule (.*) http://[%(ip)s]:%(software_access_port)s/VirtualHostBase/https/[%(ip)s]:%(software_access_port)s/VirtualHostRoot/$1 [L,P] DocumentRoot "%(testnode_srv_directory)s"
DocumentRoot "%(testnode_software_directory)s"
<Directory /> <Directory />
Options FollowSymLinks Options FollowSymLinks
IndexOptions FancyIndexing IndexOptions FancyIndexing
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
parts = parts =
nginx-service nginx-service
runTestSuite-instance runTestSuite-instance
eggs-directory = ${buildout:eggs-directory} eggs-directory = ${buildout:eggs-directory}
develop-eggs-directory = ${buildout:develop-eggs-directory} develop-eggs-directory = ${buildout:develop-eggs-directory}
offline = true offline = true
...@@ -36,6 +35,7 @@ framebuffer = $${:srv}/framebuffer ...@@ -36,6 +35,7 @@ framebuffer = $${:srv}/framebuffer
recipe = slapos.recipe.template recipe = slapos.recipe.template
url = ${template-runTestSuite:output} url = ${template-runTestSuite:output}
output = $${directory:bin}/runTestSuite output = $${directory:bin}/runTestSuite
buildout-directory = $${buildout:directory}
mode = 0700 mode = 0700
[firefox-instance] [firefox-instance]
...@@ -76,7 +76,7 @@ output = $${directory:etc}/nginx.cfg ...@@ -76,7 +76,7 @@ output = $${directory:etc}/nginx.cfg
mode = 0600 mode = 0600
access_log = $${directory:log}/nginx-access.log access_log = $${directory:log}/nginx-access.log
error_log = $${directory:log}/nginx-error.log error_log = $${directory:log}/nginx-error.log
ip = $${instance-parameter:ipv6-random} ip = $${instance-parameters:ipv6-random}
port = 9443 port = 9443
ssl_key = $${directory:ssl}/nginx.key ssl_key = $${directory:ssl}/nginx.key
ssl_csr = $${directory:ssl}/nginx.csr ssl_csr = $${directory:ssl}/nginx.csr
...@@ -85,7 +85,7 @@ ssl_crt = $${directory:ssl}/nginx.crt ...@@ -85,7 +85,7 @@ ssl_crt = $${directory:ssl}/nginx.crt
################################# #################################
# SlapOS service # SlapOS service
################################# #################################
[instance-parameter] [instance-parameters]
recipe = slapos.cookbook:slapconfiguration recipe = slapos.cookbook:slapconfiguration
computer = $${slap_connection:computer_id} computer = $${slap_connection:computer_id}
partition = $${slap_connection:partition_id} partition = $${slap_connection:partition_id}
......
...@@ -31,9 +31,43 @@ def main(): ...@@ -31,9 +31,43 @@ def main():
parser.add_argument('--node_quantity', help='ignored', type=int) parser.add_argument('--node_quantity', help='ignored', type=int)
parser.add_argument('--master_url', parser.add_argument('--master_url',
help='The Url of Master controling many suites') 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() 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: try:
test_suite_title = args.test_suite_title or args.test_suite test_suite_title = args.test_suite_title or args.test_suite
test_suite = args.test_suite test_suite = args.test_suite
...@@ -51,8 +85,25 @@ def main(): ...@@ -51,8 +85,25 @@ def main():
########################## ##########################
# Run all tests # Run all tests
########################## ##########################
if args.target == 'firefox':
firefox_binary = webdriver.firefox.firefox_binary.FirefoxBinary(firefox_path=FIREFOX_EXECUTABLE) firefox_binary = webdriver.firefox.firefox_binary.FirefoxBinary(firefox_path=FIREFOX_EXECUTABLE)
browser = webdriver.Firefox(firefox_binary=firefox_binary) 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") agent = browser.execute_script("return navigator.userAgent")
print agent print agent
...@@ -65,9 +116,9 @@ def main(): ...@@ -65,9 +116,9 @@ def main():
html_parser = etree.HTMLParser(recover=True) html_parser = etree.HTMLParser(recover=True)
body = etree.fromstring(browser.page_source.encode('UTF-8'), html_parser) body = etree.fromstring(browser.page_source.encode('UTF-8'), html_parser)
browser.quit()
browser.title.encode('UTF-8') browser.title.encode('UTF-8')
browser.quit()
print ' '.join(body.xpath('//*[@id="qunit-testresult"]//text()')) print ' '.join(body.xpath('//*[@id="qunit-testresult"]//text()'))
...@@ -99,7 +150,7 @@ def main(): ...@@ -99,7 +150,7 @@ def main():
node_title = args.test_node_title, node_title = args.test_node_title,
test_title = test_suite_title, test_title = test_suite_title,
project_title = args.project_title) project_title = args.project_title)
if test_result is None: if test_result is None or not hasattr(args, 'master_url'):
return return
# report test results # report test results
while 1: while 1:
...@@ -123,8 +174,5 @@ def main(): ...@@ -123,8 +174,5 @@ def main():
# XXX: inform test node master of error # XXX: inform test node master of error
raise EnvironmentError(result) raise EnvironmentError(result)
finally:
browser.quit()
if __name__ == "__main__": if __name__ == "__main__":
main() main()
...@@ -25,7 +25,7 @@ parts = ...@@ -25,7 +25,7 @@ parts =
[instance] [instance]
recipe = slapos.recipe.template recipe = slapos.recipe.template
md5sum = 25a9c895fff279b71b0dbbad6647181b md5sum = 605c1f62f93bbb77bb5f3b485882d4f2
url = ${:_profile_base_location_}/instance.cfg.in url = ${:_profile_base_location_}/instance.cfg.in
output = ${buildout:directory}/instance.cfg output = ${buildout:directory}/instance.cfg
mode = 0644 mode = 0644
...@@ -107,7 +107,7 @@ mode = 0644 ...@@ -107,7 +107,7 @@ mode = 0644
[template-runTestSuite] [template-runTestSuite]
recipe = slapos.recipe.template recipe = slapos.recipe.template
url = ${:_profile_base_location_}/runTestSuite.in url = ${:_profile_base_location_}/runTestSuite.in
md5sum = 88e820d30b36ecca9b45242ce4a52039 md5sum = da89bddca28899023b67eb9757cb94f4
output = ${buildout:directory}/runTestSuite.in output = ${buildout:directory}/runTestSuite.in
mode = 0644 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