Skip to content

Commit

Permalink
[REF] tests: more stuffs - firefox partial support
Browse files Browse the repository at this point in the history
Firefox webdriver does not yet support browser console logs fetching:
mozilla/geckodriver#330
  • Loading branch information
d-fence committed Feb 27, 2018
1 parent 82c5fc2 commit 546d238
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 37 deletions.
1 change: 1 addition & 0 deletions odoo/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import common
from .common import *
from . import selenite
60 changes: 23 additions & 37 deletions odoo/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,14 @@
import re
import select
import subprocess
import tempfile
import threading
import time
import unittest
from contextlib import contextmanager
from datetime import datetime, timedelta, date
from pprint import pformat
try:
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
except ImportError:
webdriver = None
from . import selenite

import requests
from decorator import decorator
Expand Down Expand Up @@ -545,15 +542,9 @@ def setUp(self):

if not hasattr(self, 'logger'):
self.logger = _logger
if webdriver is None:
raise unittest.SkipTest("Selenium not installed")
if not odoo.tools.config['selenium_driver']:
raise unittest.SkipTest("Selenium not installed or missconfigured")
self.logger.info('Setting up Selenium test case')
self.chrome_bin_path = '/usr/bin/chromium-browser'
self.chrome_driver_path = '/usr/lib/chromium-browser/chromedriver'
if not os.path.exists(self.chrome_bin_path):
raise unittest.SkipTest("Chrome not found in '{}'".format(self.chrome_bin_path))
if not os.path.exists(self.chrome_driver_path):
raise unittest.SkipTest("Chrome driver not found in '{}'".format(self.chrome_driver_path))

if self.registry_test_mode:
self.registry.enter_test_mode()
Expand All @@ -567,22 +558,16 @@ def setUp(self):
self.opener = requests.Session()
self.opener.cookies['session_id'] = self.session_id

# Chrome headless setup
chrome_options = webdriver.ChromeOptions()
chrome_options.binary_location = self.chrome_bin_path
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--start-maximized')
chrome_options.add_argument('--window-size=1920,1080')
desire = DesiredCapabilities.CHROME
desire['loggingPrefs'] = {'browser': 'ALL'}
self.browser = webdriver.Chrome(self.chrome_driver_path, chrome_options=chrome_options, desired_capabilities=desire)
#desire = DesiredCapabilities.FIREFOX
#self.logger.info('DESIRE: {}'.format())
#desire['args'] = ['-headless',]
#self.browser = webdriver.Firefox(executable_path='/usr/lib/firefox/geckodriver', capabilities=desire)
self.browser.implicitly_wait(5)
self.addCleanup(self.browser.quit)
# configure the selenium browser driver
driver_select = selenite.DriverSelect(odoo.tools.config['selenium_driver'], driver_path=odoo.tools.config['selenium_driver_path'], browser_path=odoo.tools.config['selenium_browser_path'])
self.driver = driver_select()
self.driver.implicitly_wait(5)
self.addCleanup(self.driver.quit)

# configure get_screenshots
self.screenshot_path = odoo.tools.config['screenshot_path']
if not self.screenshot_path or not os.path.isdir(self.screenshot_path) or not os.access(self.screenshot_path, os.W_OK):
self.screenshot_path = tempfile.mkdtemp()

self.opener = requests.Session()
self.opener.cookies['session_id'] = self.session_id
Expand All @@ -599,7 +584,7 @@ def _build_url(self, url_path):
return "http://{}:{}{}".format(HOST, PORT, url_path)

def browser_get(self, url_path):
return self.browser.get(self._build_url(url_path))
return self.driver.get(self._build_url(url_path))

def authenticate(self, user, password):
self.logger.info('Pre authenticate session with user/password: {}/{}'.format(user, password))
Expand All @@ -626,7 +611,7 @@ def authenticate(self, user, password):
odoo.http.root.session_store.save(session)

self.browser_get('/')
self.browser.add_cookie({'domain': '127.0.0.1', 'name': 'session_id', 'value': self.session_id})
self.driver.add_cookie({'domain': '127.0.0.1', 'name': 'session_id', 'value': self.session_id})

def _wait_ready(self, ready_js_code, max_tries=10):
"""Selenium should wait for the page to be ready but this is a safeguard."""
Expand All @@ -637,7 +622,7 @@ def _wait_ready(self, ready_js_code, max_tries=10):
if tries > max_tries:
break
time.sleep(0.1 * tries)
res = self.browser.execute_script("return {}".format(ready_js_code))
res = self.driver.execute_script("return {}".format(ready_js_code))
if res:
return
tries += 1
Expand All @@ -648,18 +633,18 @@ def selenium_run(self, url_path, js_code, ready='window', login=None, max_tries=
"""Runs a js_code javascript test in Chrome headless"""
self.authenticate(login, login)
self.browser_get(url_path)
self.browser.execute_script('localStorage.clear();')
self.driver.execute_script('localStorage.clear();')
self._wait_ready(ready) if ready else self.logger.info('No Ready code, running directly')
self.logger.info("Running JS test code: '{}'".format(js_code))
self.browser.execute_script('return eval({})'.format(js_code))
self.driver.execute_script('return eval({})'.format(js_code))

tries = 1
start_time = time.time()
while True:
if tries > max_tries:
break
time.sleep(0.1 * tries)
for log_line in self.browser.get_log('browser'):
for log_line in self.driver.get_log('browser'):
if log_line.get('message', '').lower().endswith('"ok"'):
waiting_time = time.time() - start_time
self.logger.info('JS Test succesfully passed (tries: {} - waiting time: {})'.format(tries, waiting_time))
Expand All @@ -678,8 +663,9 @@ def selenium_run(self, url_path, js_code, ready='window', login=None, max_tries=

def take_screenshot(self):
filename = "selenium-shot_{}.png".format(time.strftime("%Y-%m-%d_%H-%M-%S"))
filepath = os.path.join('/tmp', filename)
self.browser.get_screenshot_as_file(filepath)
filepath = os.path.join(self.screenshot_path, filename)
if self.driver.get_screenshot_as_file(filepath):
_logger.info("Screenshot written in '{}'".format(filepath))

def _wait_remaining_requests(self):
t0 = int(time.time())
Expand Down
80 changes: 80 additions & 0 deletions odoo/tests/selenite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import os
import logging
import sys

from odoo.tools.which import which

_logger = logging.getLogger(__name__)

try:
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
from selenium.webdriver.firefox.options import Options
except ImportError:
_logger.warning('Selenium not installed, Http tests will be skipped')
webdriver = None

SELENIUM_DRIVERS = ('chrome', 'chromium', 'firefox')


class SeleniteError(Exception):
pass


class DriverSelect():
"""A callable object that returns a selenium driver"""

def __init__(self, driver_name, driver_path=None, browser_path=None):
self.driver_name = driver_name
self.driver_path = driver_path
self.browser_path = browser_path
self._check_pathes()

def __call__(self):
if self.driver_name == 'chrome':
return self._get_chrome()
elif self.driver_name == 'chromium':
return self._get_chrome(browser_name='chromium')
elif self.driver_name == 'firefox':
return self._get_firefox()

def _get_chrome(self, browser_name=None):
chrome_options = webdriver.ChromeOptions()
if self.browser_path is not None:
chrome_options.binary_location = self.browser_path
chrome_options.set_headless()
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--start-maximized')
chrome_options.add_argument('--window-size=1920,1080')
desire = DesiredCapabilities.CHROME
if browser_name:
desire['browserName'] = browser_name
desire['loggingPrefs'] = {'browser': 'ALL'}
if self.driver_path:
return webdriver.Chrome(self.driver_path, chrome_options=chrome_options, desired_capabilities=desire)
else:
return webdriver.Chrome(chrome_options=chrome_options, desired_capabilities=desire)

def _get_firefox(self):
__logger.warning("Firefox doesn't allow to fetch console log. Your tests are willing to fail.")
firefox_capabilities = DesiredCapabilities.FIREFOX
if not self.browser_path:
self.browser_path = which('firefox')
desire = DesiredCapabilities.FIREFOX
options = Options()
options.set_headless()
binary = FirefoxBinary(self.browser_path)
if self.driver_path:
return webdriver.Firefox(executable_path=self.driver_path, firefox_binary=binary, firefox_options=options, log_path='/dev/null')
else:
return webdriver.Firefox(firefox_binary=binary, firefox_options=options)

def _check_pathes(self):
if self.driver_path and not os.path.exists(self.driver_path):
raise SeleniteError("Driver not found")
if self.browser_path and not os.path.exists(self.browser_path):
raise SeleniteError("Browser not found")
14 changes: 14 additions & 0 deletions odoo/tools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
import os
import sys
import odoo

from .. import release, conf, loglevels
from . import appdirs, pycompat

SELENIUM_DRIVERS = ('chrome', 'chromium', 'firefox')

from passlib.context import CryptContext
crypt_context = CryptContext(schemes=['pbkdf2_sha512', 'plaintext'],
deprecated=['plaintext'])
Expand Down Expand Up @@ -153,6 +156,16 @@ def __init__(self, fname=None):

# Testing Group
group = optparse.OptionGroup(parser, "Testing Configuration")
group.add_option("--selenium-driver", dest="selenium_driver", my_default="chrome",
type="choice",
choices=SELENIUM_DRIVERS,
help="Selenium driver/browser to use. Supported: {}".format('/'.join(SELENIUM_DRIVERS)))
group.add_option("--driver-path", dest="selenium_driver_path", my_default='',
help="Path to the selenium driver (default auto discover)")
group.add_option("--browser-path", dest="selenium_browser_path", my_default='',
help="Path to the browser used by selenium driver (default auto discover)")
group.add_option("--screenshot-path", dest="screenshot_path", my_default='',
help="Where to put screenshots when a selenium test fails")
group.add_option("--test-file", dest="test_file", my_default=False,
help="Launch a python test file.")
group.add_option("--test-enable", action="store_true", dest="test_enable",
Expand Down Expand Up @@ -426,6 +439,7 @@ def die(cond, msg):
'stop_after_init', 'logrotate', 'without_demo', 'http_enable', 'syslog',
'list_db', 'proxy_mode',
'test_file', 'test_enable', 'test_tags',
'selenium_driver', 'selenium_driver_path', 'selenium_browser_path', 'screenshot_path',
'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent',
'data_dir',
'server_wide_modules',
Expand Down

0 comments on commit 546d238

Please sign in to comment.