Commit bc662696 authored by Jérome Perrin's avatar Jérome Perrin

JSTestNode on Selenium Server

Change jstestnode arguments to accept the [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities) as a JSON object, so that we can run jstestnode tests on any selenium server ( the target being nexedi/slapos!420 ), not only saucelabs.

This MR also introduces some JSON schema for instance parameter.

/reviewed-on nexedi/slapos!429
parents bf4aef15 dc50c648
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
[instance] [instance]
filename = instance.cfg.in filename = instance.cfg.in
md5sum = 74d4c0a5105fa3d74af894fa1afa9ba2 md5sum = 2a79bb6c4f593d7c4c7f4e0de97d9803
[template-nginx-service] [template-nginx-service]
filename = template-nginx-service.sh.in filename = template-nginx-service.sh.in
...@@ -27,4 +27,4 @@ md5sum = 9f22db89a2679534aa8fd37dbca86782 ...@@ -27,4 +27,4 @@ md5sum = 9f22db89a2679534aa8fd37dbca86782
[template-runTestSuite] [template-runTestSuite]
filename = runTestSuite.in filename = runTestSuite.in
md5sum = d26572727ef1679a8a9e51b021ece524 md5sum = bd9ff3543f0dfaf2702624e3ed74d334
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Parameters to instantiate JSTestNode",
"additionalProperties": false,
"properties": {
"test-suite": {
"description": "The test suite to run",
"type": "string",
"enum": [
"jio",
"renderjs"
]
},
"remote-access-url": {
"description": "URL that controlled browser must access to run tests",
"type": "string",
"format": "uri",
"default": "(the web server started by this instance)",
"example": "https://softinst1234.host.vifib.net/"
},
"oneOf": [
{
"title": "selenium server",
"description": "Configuration for running tests on selenium server",
"properties": {
"target": {
"description": "Target system",
"const": "selenium-server"
},
"server-url": {
"description": "URL of the selenium server",
"type": "string"
},
"verify-server-certificate": {
"description": "Verify the SSL/TLS Certificats of the selenium server when using HTTPS",
"type": "boolean",
"default": true
},
"server-ca-certificate": {
"description": "PEM encoded bundle of CA Certificates to verify the SSL/TLS Certificate of the selenium server when using HTTPS",
"type": "string",
"default": "root certificates from http://certifi.io/en/latest/"
},
"desired-capabilities": {
"description": "Desired browser capabilities",
"type": "object",
"properties": {
"browserName": {
"description": "Name of the browser being used, for example firefox, chrome",
"type": "string",
"required": true
},
"version": {
"description": "The browser version",
"type": "string"
}
},
"additionalProperties": true
}
}
},
{
"title": "firefox",
"description": "Configuration for running tests on local firefox process",
"properties": {
"target": {
"description": "Target system",
"const": "firefox",
"default": "firefox"
}
}
},
{
"title": "node",
"description": "Configuration for running tests on local nodejs",
"properties": {
"target": {
"description": "Target system",
"const": "node"
}
}
}
]
}
}
{
"$schema": "http://json-schema.org/draft-04/schema#",
"description": "Values returned by JSTestNode instantiation",
"additionalProperties": false,
"type": "object",
"nginx": {
"description": "Nginx web server serving html test runner",
"type": "string"
}
}
...@@ -2,12 +2,13 @@ ...@@ -2,12 +2,13 @@
parts = parts =
nginx-service nginx-service
runTestSuite-instance runTestSuite-instance
publish
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
[publish] [publish]
recipe = slapos.cookbook:publish recipe = slapos.cookbook:publish.serialised
nginx = http://[$${nginx-configuration:ip}]:$${nginx-configuration:port}/ nginx = http://[$${nginx-configuration:ip}]:$${nginx-configuration:port}/
[directory] [directory]
...@@ -28,9 +29,8 @@ ssl = $${:etc}/ssl ...@@ -28,9 +29,8 @@ ssl = $${:etc}/ssl
framebuffer = $${:srv}/framebuffer framebuffer = $${:srv}/framebuffer
################################# #################################
# Firefox # Test runner
################################# #################################
[runTestSuite-instance] [runTestSuite-instance]
recipe = slapos.recipe.template recipe = slapos.recipe.template
url = ${template-runTestSuite:output} url = ${template-runTestSuite:output}
...@@ -38,6 +38,15 @@ output = $${directory:bin}/runTestSuite ...@@ -38,6 +38,15 @@ output = $${directory:bin}/runTestSuite
buildout-directory = $${buildout:directory} buildout-directory = $${buildout:directory}
mode = 0700 mode = 0700
[runTestSuite-config-file]
recipe = slapos.recipe.template:jinja2
rendered = $${directory:etc}/$${:_buildout_section_name_}.json
template = inline:
$${instance-parameter:configuration._}
#################################
# Xvfb / Firefox
#################################
[xvfb-instance] [xvfb-instance]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
wrapper-path = $${directory:services}/$${:_buildout_section_name_} wrapper-path = $${directory:services}/$${:_buildout_section_name_}
......
...@@ -12,13 +12,17 @@ from selenium import webdriver ...@@ -12,13 +12,17 @@ from selenium import webdriver
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.remote.remote_connection import RemoteConnection
from subprocess import check_output from subprocess import check_output
import urllib3
import certifi
import json import json
os.environ['XORG_LOCK_DIR'] = '$${xvfb-instance:lock-dir}' os.environ['XORG_LOCK_DIR'] = '$${xvfb-instance:lock-dir}'
os.environ['DISPLAY'] = '$${xvfb-instance:display}' os.environ['DISPLAY'] = '$${xvfb-instance:display}'
BASE_URL = 'http://[$${nginx-configuration:ip}]:$${nginx-configuration:port}/' BASE_URL = 'http://[$${nginx-configuration:ip}]:$${nginx-configuration:port}/'
ETC_DIRECTORY = '$${directory:etc}'
def main(): def main():
parser = argparse.ArgumentParser(description='Run a test suite.') parser = argparse.ArgumentParser(description='Run a test suite.')
...@@ -33,40 +37,11 @@ def main(): ...@@ -33,40 +37,11 @@ def main():
help='The Url of Master controling many suites') help='The Url of Master controling many suites')
parser.add_argument('--frontend_url', parser.add_argument('--frontend_url',
help='The url of frontend of the test suite') 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.load(
parsed_parameters = json.loads('$${instance-parameter:configuration._}') open('$${runTestSuite-config-file:rendered}', 'rb'))
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')
is_browser_running = False is_browser_running = False
try: try:
...@@ -87,38 +62,8 @@ def main(): ...@@ -87,38 +62,8 @@ def main():
########################## ##########################
# Run all tests # Run all tests
########################## ##########################
target = parsed_parameters.get('target', 'firefox')
is_appium = False if target == 'node':
if args.target == 'firefox':
firefox_capabilities = webdriver.common.desired_capabilities.DesiredCapabilities.FIREFOX
firefox_capabilities['marionette'] = True
browser = webdriver.Firefox(capabilities=firefox_capabilities,
firefox_binary='${firefox-wrapper:location}',
executable_path='${geckodriver:location}')
elif args.target in ['iOS', 'Android']:
# parameters for mobile emulators have different names then parameters for
# desktop OSes
is_appium = True
capabilities = {
'platformName': args.target,
'platformVersion': args.target_version,
'deviceName': args.target_device,
'browserName': args.target_browser
}
elif 'Windows' in args.target or 'OS X' in args.target:
# parameters for mobile emulators have different names then parameters for
# desktop OSes
is_appium = True
capabilities = {
'browserName': args.target_browser,
'platform': args.target,
'version': args.target_version
}
if args.target == 'node':
# Execute NodeJS tests # Execute NodeJS tests
result_string = check_output(['${nodejs-output:node}', '${jio-repository.git:location}/test/node.js'], result_string = check_output(['${nodejs-output:node}', '${jio-repository.git:location}/test/node.js'],
cwd='${jio-repository.git:location}', cwd='${jio-repository.git:location}',
...@@ -139,21 +84,41 @@ def main(): ...@@ -139,21 +84,41 @@ def main():
else: else:
# Execute WebBrowser tests # Execute WebBrowser tests
if is_appium: if target == 'firefox':
if not args.appium_server_auth: firefox_capabilities = webdriver.common.desired_capabilities.DesiredCapabilities.FIREFOX
raise RuntimeError('--appium_server_auth is required.') firefox_capabilities['marionette'] = True
appium_url = "http://%s@ondemand.saucelabs.com/wd/hub" % (args.appium_server_auth) browser = webdriver.Firefox(
browser = webdriver.Remote(appium_url, capabilities) capabilities=firefox_capabilities,
firefox_binary='${firefox-wrapper:location}',
executable_path='${geckodriver:location}')
else:
assert target == 'selenium-server', "Unsupported target {}".format(parsed_parameters['target'])
# use a remote connection which verifies TLS certificate
# workaround for https://github.com/SeleniumHQ/selenium/issues/6534
executor = RemoteConnection(parsed_parameters['server-url'], keep_alive=True)
cert_reqs = 'CERT_REQUIRED'
ca_certs = certifi.where()
if not parsed_parameters.get('verify-server-certificate', True):
cert_reqs = 'CERT_NONE'
ca_certs = None
if parsed_parameters.get('server-ca-certificate'):
ca_certs = os.path.join(ETC_DIRECTORY, "cacerts.pem")
with open(ca_certs, 'w') as f:
f.write(parsed_parameters.get('server-ca-certificate'))
executor._conn = urllib3.PoolManager(cert_reqs=cert_reqs, ca_certs=ca_certs)
browser = webdriver.Remote(
command_executor=executor,
desired_capabilities=parsed_parameters['desired-capabilities'],
)
# adjust path for remote test url # adjust path for remote test url
remote_access_url = parsed_parameters.get('remote-access-url', None) remote_access_url = parsed_parameters.get('remote-access-url')
if remote_access_url: if remote_access_url:
if ('jio' in test_suite): if ('jio' in test_suite):
url = os.path.join(remote_access_url, 'jio/test/tests.html') url = '{}/jio/test/tests.html'.format(remote_access_url)
else:
url = os.path.join(remote_access_url, 'renderjs/test/')
else: else:
raise ValueError('remote-access-url is not defined in instance parameter') url = '{}/renderjs/test/'.format(remote_access_url)
is_browser_running = True is_browser_running = True
agent = browser.execute_script("return navigator.userAgent") agent = browser.execute_script("return navigator.userAgent")
......
...@@ -36,6 +36,7 @@ parts = ...@@ -36,6 +36,7 @@ parts =
recipe = zc.recipe.egg recipe = zc.recipe.egg
eggs = eggs =
erp5.util erp5.util
urllib3[secure]
selenium selenium
${lxml-python:egg} ${lxml-python:egg}
interpreter = pythonwitheggs interpreter = pythonwitheggs
...@@ -116,4 +117,6 @@ output = ${buildout:directory}/runTestSuite.in ...@@ -116,4 +117,6 @@ output = ${buildout:directory}/runTestSuite.in
[versions] [versions]
erp5.util = 0.4.51 erp5.util = 0.4.51
slapos.recipe.template = 4.3 slapos.recipe.template = 4.3
selenium = 3.8.0 selenium = 3.14.1
urllib3 = 1.24
certifi = 2018.10.15
\ No newline at end of file
{
"name": "JSTestNode",
"description": "Simple Test Runner for Javascrip projects",
"serialisation": "json-in-xml",
"software-type": {
"default": {
"title": "Default",
"request": "instance-jstestnode-input-schema.json",
"response": "instance-jstestnode-output-schema.json",
"index": 0
}
}
}
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