From 21f75ff250a7a4a687d0bb0bd47d6ee71cb2cfc6 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Mon, 19 Aug 2013 09:08:02 -0400 Subject: [PATCH] Various stylistic and pylint fixes / changes Version numbers have very different ranges for different browsers so not having a dictionary of those. Fixed a whitespace issue Fixed pylint/pep8 violations Don't need django_url Spacing issues Changed how commenting works Forgot one Used wrong name Changed around importing Remove django_url Fixed function orderingn Made logic nicer for getting a new browser Modifying tests to run in opera Needed to increase time to account for slow sauce loading Now safari LMS works Forgot an assert statement Skipping a few tests for opera --- .../features/advanced-settings.feature | 7 +- .../contentstore/features/checklists.feature | 6 +- .../contentstore/features/course-team.py | 3 +- .../contentstore/features/grading.py | 9 +- .../features/video-editor.feature | 4 +- common/djangoapps/terrain/browser.py | 110 +++++++++--------- common/djangoapps/terrain/steps.py | 11 +- .../courseware/features/login.feature | 2 +- .../courseware/features/navigation.feature | 2 + .../courseware/features/signup.feature | 2 +- .../courseware/features/video.feature | 2 +- lms/envs/sauce.py | 13 ++- 12 files changed, 93 insertions(+), 78 deletions(-) diff --git a/cms/djangoapps/contentstore/features/advanced-settings.feature b/cms/djangoapps/contentstore/features/advanced-settings.feature index 767dafb796..b2941ac7a5 100644 --- a/cms/djangoapps/contentstore/features/advanced-settings.feature +++ b/cms/djangoapps/contentstore/features/advanced-settings.feature @@ -2,7 +2,6 @@ Feature: Advanced (manual) course policy In order to specify course policy settings for which no custom user interface exists I want to be able to manually enter JSON key /value pairs -#Sauce labs does not play nicely with CodeMirror Scenario: A course author sees default advanced settings Given I have opened a new course in Studio @@ -13,6 +12,7 @@ Feature: Advanced (manual) course policy Given I am on the Advanced Course Settings page in Studio Then the settings are alphabetized + # Sauce labs does not play nicely with CodeMirror @skip_sauce Scenario: Test cancel editing key value Given I am on the Advanced Course Settings page in Studio @@ -22,6 +22,7 @@ Feature: Advanced (manual) course policy And I reload the page Then the policy key value is unchanged + # Sauce labs does not play nicely with CodeMirror @skip_sauce Scenario: Test editing key value Given I am on the Advanced Course Settings page in Studio @@ -30,6 +31,7 @@ Feature: Advanced (manual) course policy And I reload the page Then the policy key value is changed + # Sauce labs does not play nicely with CodeMirror @skip_sauce Scenario: Test how multi-line input appears Given I am on the Advanced Course Settings page in Studio @@ -38,6 +40,7 @@ Feature: Advanced (manual) course policy And I reload the page Then it is displayed as formatted + # Sauce labs does not play nicely with CodeMirror @skip_sauce Scenario: Test error if value supplied is of the wrong type Given I am on the Advanced Course Settings page in Studio @@ -47,6 +50,7 @@ Feature: Advanced (manual) course policy Then the policy key value is unchanged # This feature will work in Firefox only when Firefox is the active window + # Sauce labs does not play nicely with CodeMirror @skip_sauce Scenario: Test automatic quoting of non-JSON values Given I am on the Advanced Course Settings page in Studio @@ -55,6 +59,7 @@ Feature: Advanced (manual) course policy And I reload the page Then it is displayed as a string + # Sauce labs does not play nicely with CodeMirror @skip_sauce Scenario: Confirmation is shown on save Given I am on the Advanced Course Settings page in Studio diff --git a/cms/djangoapps/contentstore/features/checklists.feature b/cms/djangoapps/contentstore/features/checklists.feature index 28a38b307e..1649cd0749 100644 --- a/cms/djangoapps/contentstore/features/checklists.feature +++ b/cms/djangoapps/contentstore/features/checklists.feature @@ -10,8 +10,9 @@ Feature: Course checklists Then I can check and uncheck tasks in a checklist And They are correctly selected after reloading the page - # CHROME ONLY, due to issues getting link to be active in firefox + # There are issues getting link to be active in browsers other than chrome @skip_firefox + @skip_opera Scenario: A task can link to a location within Studio Given I have opened Checklists When I select a link to the course outline @@ -19,8 +20,9 @@ Feature: Course checklists And I press the browser back button Then I am brought back to the course outline in the correct state - # CHROME ONLY, due to issues getting link to be active in firefox + # There are issues getting link to be active in browsers other than chrome @skip_firefox + @skip_opera Scenario: A task can link to a location outside Studio Given I have opened Checklists When I select a link to help page diff --git a/cms/djangoapps/contentstore/features/course-team.py b/cms/djangoapps/contentstore/features/course-team.py index ab68050866..8b31d325e5 100644 --- a/cms/djangoapps/contentstore/features/course-team.py +++ b/cms/djangoapps/contentstore/features/course-team.py @@ -2,7 +2,6 @@ #pylint: disable=W0621 from lettuce import world, step -from lettuce.django import django_url from common import create_studio_user from django.contrib.auth.models import Group from auth.authz import get_course_groupname_for_role, get_user_by_email @@ -92,7 +91,7 @@ def remove_course_team_admin(_step, outer_capture, name): @step(u'"([^"]*)" logs in$') def other_user_login(_step, name): - world.visit(django_url('logout')) + world.visit('logout') world.visit('/') signin_css = 'a.action-signin' diff --git a/cms/djangoapps/contentstore/features/grading.py b/cms/djangoapps/contentstore/features/grading.py index 719b3f7f7c..93e44b3893 100644 --- a/cms/djangoapps/contentstore/features/grading.py +++ b/cms/djangoapps/contentstore/features/grading.py @@ -112,10 +112,10 @@ def changes_not_persisted(step): @step(u'I see the assignment type "(.*)"$') def i_see_the_assignment_type(_step, name): - assignment_css = '#course-grading-assignment-name' - assignments = world.css_find(assignment_css) - types = [ele['value'] for ele in assignments] - assert name in types + assignment_css = '#course-grading-assignment-name' + assignments = world.css_find(assignment_css) + types = [ele['value'] for ele in assignments] + assert name in types @step(u'I change the highest grade range to "(.*)"$') @@ -144,6 +144,7 @@ def cannot_edit_fail(_step): pass # We should get this exception on failing to edit the element + @step(u'I change the grace period to "(.*)"$') def i_change_grace_period(_step, grace_period): grace_period_css = '#course-grading-graceperiod' diff --git a/cms/djangoapps/contentstore/features/video-editor.feature b/cms/djangoapps/contentstore/features/video-editor.feature index 6f5fbd48b9..7117926c60 100644 --- a/cms/djangoapps/contentstore/features/video-editor.feature +++ b/cms/djangoapps/contentstore/features/video-editor.feature @@ -1,8 +1,6 @@ Feature: Video Component Editor As a course author, I want to be able to create video components. - #Sauce Labs cannot delete cookies - Scenario: User can view Video metadata Given I have created a Video component And I edit the component @@ -14,12 +12,14 @@ Feature: Video Component Editor Then I can modify the display name And my video display name change is persisted on save + # Sauce Labs cannot delete cookies @skip_sauce Scenario: Captions are hidden when "show captions" is false Given I have created a Video component And I have set "show captions" to False Then when I view the video it does not show the captions + # Sauce Labs cannot delete cookies @skip_sauce Scenario: Captions are shown when "show captions" is true Given I have created a Video component diff --git a/common/djangoapps/terrain/browser.py b/common/djangoapps/terrain/browser.py index 80c47433b7..5820ad46f7 100644 --- a/common/djangoapps/terrain/browser.py +++ b/common/djangoapps/terrain/browser.py @@ -12,6 +12,9 @@ from django.core.management import call_command from django.conf import settings from selenium.common.exceptions import WebDriverException from selenium.webdriver.common.desired_capabilities import DesiredCapabilities +from requests import put +from base64 import encodestring +from json import dumps # Let the LMS and CMS do their one-time setup # For example, setting up mongo caches @@ -42,27 +45,32 @@ LOGGER.info("Loading the lettuce acceptance testing terrain file...") MAX_VALID_BROWSER_ATTEMPTS = 20 -# https://gist.github.com/santiycr/1644439 -import requests -import base64 -try: - import json -except ImportError: - import simplejson as json + +def get_username_and_key(): + """ + Returns the Sauce Labs username and access ID as set by environment variables + """ + return {"username": settings.SAUCE.get('USERNAME'), "access-key": settings.SAUCE.get('ACCESS_ID')} def set_job_status(jobid, passed=True): - body_content = json.dumps({"passed": passed}) + """ + Sets the job status on sauce labs + """ + body_content = dumps({"passed": passed}) config = get_username_and_key() - base64string = base64.encodestring('{}:{}'.format(config['username'], config['access-key']))[:-1] - result = requests.put('http://saucelabs.com/rest/v1/{}/jobs/{}'.format(config['username'], world.jobid), + base64string = encodestring('{}:{}'.format(config['username'], config['access-key']))[:-1] + result = put('http://saucelabs.com/rest/v1/{}/jobs/{}'.format(config['username'], world.jobid), data=body_content, headers={"Authorization": "Basic {}".format(base64string)}) return result.status_code == 200 def make_desired_capabilities(): - desired_capabilities = settings.SAUCE.get('BROWSER', DesiredCapabilities.CHROME) + """ + Returns a DesiredCapabilities object corresponding to the environment sauce parameters + """ + desired_capabilities = settings.SAUCE.get('BROWSER', DesiredCapabilities.CHROME) desired_capabilities['platform'] = settings.SAUCE.get('PLATFORM') desired_capabilities['version'] = settings.SAUCE.get('VERSION') desired_capabilities['device-type'] = settings.SAUCE.get('DEVICE') @@ -77,60 +85,54 @@ def make_desired_capabilities(): return desired_capabilities -def get_username_and_key(): - return {"username": settings.SAUCE.get('USERNAME'), "access-key": settings.SAUCE.get('ACCESS_ID')} - - @before.harvest def initial_setup(server): """ Launch the browser once before executing the tests. """ - world.absorb(settings.SAUCE.get('SAUCE_ENABLED'),'SAUCE_ENABLED') - browser_driver = getattr(settings, 'LETTUCE_BROWSER', 'chrome') + world.absorb(settings.SAUCE.get('SAUCE_ENABLED'), 'SAUCE_ENABLED') - # There is an issue with ChromeDriver2 r195627 on Ubuntu - # in which we sometimes get an invalid browser session. - # This is a work-around to ensure that we get a valid session. - success = False - num_attempts = 0 - while (not success) and num_attempts < MAX_VALID_BROWSER_ATTEMPTS: + if not world.SAUCE_ENABLED: + browser_driver = getattr(settings, 'LETTUCE_BROWSER', 'chrome') - # Get a browser session - if world.SAUCE_ENABLED: - config = get_username_and_key() - world.browser = Browser( - 'remote', - url="http://{}:{}@ondemand.saucelabs.com:80/wd/hub".format(config['username'], config['access-key']), - **make_desired_capabilities() - ) - world.absorb(world.browser.driver.session_id, 'jobid') - else: + # There is an issue with ChromeDriver2 r195627 on Ubuntu + # in which we sometimes get an invalid browser session. + # This is a work-around to ensure that we get a valid session. + success = False + num_attempts = 0 + while (not success) and num_attempts < MAX_VALID_BROWSER_ATTEMPTS: world.browser = Browser(browser_driver) + # Try to visit the main page + # If the browser session is invalid, this will + # raise a WebDriverException + try: + world.visit('/') + + except WebDriverException: + world.browser.quit() + num_attempts += 1 + + else: + success = True + + # If we were unable to get a valid session within the limit of attempts, + # then we cannot run the tests. + if not success: + raise IOError("Could not acquire valid {driver} browser session.".format(driver='remote')) + + world.browser.driver.set_window_size(1280, 1024) + + else: + config = get_username_and_key() + world.browser = Browser( + 'remote', + url="http://{}:{}@ondemand.saucelabs.com:80/wd/hub".format(config['username'], config['access-key']), + **make_desired_capabilities() + ) world.browser.driver.implicitly_wait(30) - # Try to visit the main page - # If the browser session is invalid, this will - # raise a WebDriverException - try: - world.visit('/') - - except WebDriverException: - world.browser.quit() - num_attempts += 1 - - else: - success = True - - # If we were unable to get a valid session within the limit of attempts, - # then we cannot run the tests. - if not success: - raise IOError("Could not acquire valid {driver} browser session.".format(driver='remote')) - - # Set the browser size to 1280x1024 - if not world.SAUCE_ENABLED: - world.browser.driver.set_window_size(1280, 1024) + world.absorb(world.browser.driver.session_id, 'jobid') @before.each_scenario diff --git a/common/djangoapps/terrain/steps.py b/common/djangoapps/terrain/steps.py index 6e11ed19ea..f13b3ff932 100644 --- a/common/djangoapps/terrain/steps.py +++ b/common/djangoapps/terrain/steps.py @@ -99,7 +99,7 @@ def i_am_logged_in_user(step): @step('I am not logged in$') def i_am_not_logged_in(step): - world.visit(django_url('logout')) + world.visit('logout') @step('I am staff for course "([^"]*)"$') @@ -138,10 +138,13 @@ def should_have_link_with_path_and_text(step, path, text): @step(r'should( not)? see "(.*)" (?:somewhere|anywhere) (?:in|on) (?:the|this) page') def should_see_in_the_page(step, doesnt_appear, text): + multiplier = 1 + if world.SAUCE_ENABLED: + multiplier = 2 if doesnt_appear: - assert world.browser.is_text_not_present(text, wait_time=5) + assert world.browser.is_text_not_present(text, wait_time=5*multiplier) else: - assert world.browser.is_text_present(text, wait_time=5) + assert world.browser.is_text_present(text, wait_time=5*multiplier) @step('I am logged in$') @@ -150,7 +153,7 @@ def i_am_logged_in(step): world.log_in(username='robot', password='test') world.browser.visit(django_url('/')) # You should not see the login link - world.is_css_not_present('a#login') + assert world.is_css_not_present('a#login') @step(u'I am an edX user$') diff --git a/lms/djangoapps/courseware/features/login.feature b/lms/djangoapps/courseware/features/login.feature index 5c777fd64f..4165a9bb9f 100644 --- a/lms/djangoapps/courseware/features/login.feature +++ b/lms/djangoapps/courseware/features/login.feature @@ -11,7 +11,7 @@ Feature: Login in as a registered user And I submit my credentials on the login form Then I should see the login error message "This account has not been activated" - # CHROME ONLY, firefox will not redirect properly + # firefox will not redirect properly when the whole suite is run @skip_firefox Scenario: Login to an activated account Given I am an edX user diff --git a/lms/djangoapps/courseware/features/navigation.feature b/lms/djangoapps/courseware/features/navigation.feature index 8fd8b54c1a..70cc93fd93 100644 --- a/lms/djangoapps/courseware/features/navigation.feature +++ b/lms/djangoapps/courseware/features/navigation.feature @@ -13,6 +13,8 @@ Feature: Navigate Course When I click on subsection "2" Then I should see the content of subsection "2" + # Clicking on the sequence link doesn't work on opera through sauce + @skip_opera Scenario: I can navigate to sequences Given I am viewing a section with multiple sequences When I click on sequence "2" diff --git a/lms/djangoapps/courseware/features/signup.feature b/lms/djangoapps/courseware/features/signup.feature index c1fce04b54..3c9f491f7d 100644 --- a/lms/djangoapps/courseware/features/signup.feature +++ b/lms/djangoapps/courseware/features/signup.feature @@ -3,7 +3,7 @@ Feature: Sign in As a new user I want to signup for a student account - # CHROME ONLY, firefox will not redirect properly + # firefox will not redirect properly @skip_firefox Scenario: Sign up from the homepage Given I visit the homepage diff --git a/lms/djangoapps/courseware/features/video.feature b/lms/djangoapps/courseware/features/video.feature index 260887290e..6c8299f2c5 100644 --- a/lms/djangoapps/courseware/features/video.feature +++ b/lms/djangoapps/courseware/features/video.feature @@ -11,7 +11,7 @@ Feature: Video component Given the course has a Video component in Youtube mode Then when I view the video it has rendered in Youtube mode - #Firefox doesn't have HTML5 + # Firefox doesn't have HTML5 @skip_firefox Scenario: Autoplay is enabled in LMS for a Video component Given the course has a Video component in HTML5 mode diff --git a/lms/envs/sauce.py b/lms/envs/sauce.py index 1704edd68b..e33d4dff62 100644 --- a/lms/envs/sauce.py +++ b/lms/envs/sauce.py @@ -27,25 +27,26 @@ DESIRED_CAPABILITIES = { 'android': DesiredCapabilities.ANDROID } +PLATFORMS = ['Linux', 'OS X 10.8', 'OS X 10.6', 'Windows 8', 'Windows 7', 'Windows XP'] #HACK #This needs to be done because Jenkins needs to satisfy URLs, JSON, BASH, SAUCE, and PYTHON -#This is the simplest way to adhere to all of these requirements and still be readible +#This is the simplest way to adhere to all of these requirements and still be readable DEFAULT_CONFIG = 'Linux-chrome--' SAUCE_INFO = os.environ.get('SAUCE_INFO', DEFAULT_CONFIG).split('-') -if len(SAUCE_INFO) !=4: - SAUCE_INFO = DEFAULT_CONFIG.split('-') +if len(SAUCE_INFO) != 4: + SAUCE_INFO = DEFAULT_CONFIG.split('-') # Information needed to utilize Sauce Labs. SAUCE = { 'SAUCE_ENABLED': os.environ.get('SAUCE_ENABLED'), 'USERNAME': os.environ.get('SAUCE_USER_NAME'), 'ACCESS_ID': os.environ.get('SAUCE_API_KEY'), - 'BROWSER': DESIRED_CAPABILITIES.get(SAUCE_INFO[0].lower(), DesiredCapabilities.CHROME), - 'PLATFORM': SAUCE_INFO[0], + 'PLATFORM': SAUCE_INFO[0] if SAUCE_INFO[0] in PLATFORMS else 'Linux', + 'BROWSER': DESIRED_CAPABILITIES.get(SAUCE_INFO[1].lower(), DesiredCapabilities.CHROME), 'VERSION': SAUCE_INFO[2], 'DEVICE': SAUCE_INFO[3], 'SESSION': 'Jenkins Acceptance Tests', - 'BUILD': os.environ.get('JOB_NAME', 'LETTUCE TESTS'), + 'BUILD': os.environ.get('BUILD_DISPLAY_NAME', 'LETTUCE TESTS'), }