Commit 8ae33679 by Calen Pennington

Merge remote-tracking branch 'origin/master' into feature/alex/poll-merged

parents 972fed2e d9b68e2d
...@@ -28,4 +28,5 @@ nosetests.xml ...@@ -28,4 +28,5 @@ nosetests.xml
cover_html/ cover_html/
.idea/ .idea/
.redcar/ .redcar/
chromedriver.log chromedriver.log
\ No newline at end of file ghostdriver.log
source :rubygems source 'https://rubygems.org'
gem 'rake', '~> 10.0.3' gem 'rake', '~> 10.0.3'
gem 'sass', '3.1.15' gem 'sass', '3.1.15'
gem 'bourbon', '~> 1.3.6' gem 'bourbon', '~> 1.3.6'
......
...@@ -7,6 +7,7 @@ Feature: Advanced (manual) course policy ...@@ -7,6 +7,7 @@ Feature: Advanced (manual) course policy
When I select the Advanced Settings When I select the Advanced Settings
Then I see only the display name Then I see only the display name
@skip-phantom
Scenario: Test if there are no policy settings without existing UI controls Scenario: Test if there are no policy settings without existing UI controls
Given I am on the Advanced Course Settings page in Studio Given I am on the Advanced Course Settings page in Studio
When I delete the display name When I delete the display name
...@@ -14,6 +15,7 @@ Feature: Advanced (manual) course policy ...@@ -14,6 +15,7 @@ Feature: Advanced (manual) course policy
And I reload the page And I reload the page
Then there are no advanced policy settings Then there are no advanced policy settings
@skip-phantom
Scenario: Test cancel editing key name Scenario: Test cancel editing key name
Given I am on the Advanced Course Settings page in Studio Given I am on the Advanced Course Settings page in Studio
When I edit the name of a policy key When I edit the name of a policy key
...@@ -32,6 +34,7 @@ Feature: Advanced (manual) course policy ...@@ -32,6 +34,7 @@ Feature: Advanced (manual) course policy
And I press the "Cancel" notification button And I press the "Cancel" notification button
Then the policy key value is unchanged Then the policy key value is unchanged
@skip-phantom
Scenario: Test editing key value Scenario: Test editing key value
Given I am on the Advanced Course Settings page in Studio Given I am on the Advanced Course Settings page in Studio
When I edit the value of a policy key When I edit the value of a policy key
......
from lettuce import world, step from lettuce import world, step
from common import * from common import *
import time import time
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.support import expected_conditions as EC
from nose.tools import assert_equal from nose.tools import assert_true, assert_false, assert_equal
from nose.tools import assert_true
""" """
http://selenium.googlecode.com/svn/trunk/docs/api/py/webdriver/selenium.webdriver.common.keys.html http://selenium.googlecode.com/svn/trunk/docs/api/py/webdriver/selenium.webdriver.common.keys.html
...@@ -19,6 +20,7 @@ def i_select_advanced_settings(step): ...@@ -19,6 +20,7 @@ def i_select_advanced_settings(step):
css_click(expand_icon_css) css_click(expand_icon_css)
link_css = 'li.nav-course-settings-advanced a' link_css = 'li.nav-course-settings-advanced a'
css_click(link_css) css_click(link_css)
# world.browser.click_link_by_text('Advanced Settings')
@step('I am on the Advanced Course Settings page in Studio$') @step('I am on the Advanced Course Settings page in Studio$')
...@@ -42,8 +44,20 @@ def edit_the_name_of_a_policy_key(step): ...@@ -42,8 +44,20 @@ def edit_the_name_of_a_policy_key(step):
@step(u'I press the "([^"]*)" notification button$') @step(u'I press the "([^"]*)" notification button$')
def press_the_notification_button(step, name): def press_the_notification_button(step, name):
world.browser.click_link_by_text(name) def is_visible(driver):
return EC.visibility_of_element_located((By.CSS_SELECTOR,css,))
def is_invisible(driver):
return EC.invisibility_of_element_located((By.CSS_SELECTOR,css,))
css = 'a.%s-button' % name.lower()
wait_for(is_visible)
try:
css_click_at(css)
wait_for(is_invisible)
except WebDriverException, e:
css_click_at(css)
wait_for(is_invisible)
@step(u'I edit the value of a policy key$') @step(u'I edit the value of a policy key$')
def edit_the_value_of_a_policy_key(step): def edit_the_value_of_a_policy_key(step):
...@@ -99,29 +113,29 @@ def it_is_formatted(step): ...@@ -99,29 +113,29 @@ def it_is_formatted(step):
@step(u'the policy key name is unchanged$') @step(u'the policy key name is unchanged$')
def the_policy_key_name_is_unchanged(step): def the_policy_key_name_is_unchanged(step):
policy_key_css = 'input.policy-key' policy_key_css = 'input.policy-key'
e = css_find(policy_key_css).first val = css_find(policy_key_css).first.value
assert_equal(e.value, 'display_name') assert_equal(val, 'display_name')
@step(u'the policy key name is changed$') @step(u'the policy key name is changed$')
def the_policy_key_name_is_changed(step): def the_policy_key_name_is_changed(step):
policy_key_css = 'input.policy-key' policy_key_css = 'input.policy-key'
e = css_find(policy_key_css).first val = css_find(policy_key_css).first.value
assert_equal(e.value, 'new') assert_equal(val, 'new')
@step(u'the policy key value is unchanged$') @step(u'the policy key value is unchanged$')
def the_policy_key_value_is_unchanged(step): def the_policy_key_value_is_unchanged(step):
policy_value_css = 'li.course-advanced-policy-list-item div.value textarea' policy_value_css = 'li.course-advanced-policy-list-item div.value textarea'
e = css_find(policy_value_css).first val = css_find(policy_value_css).first.value
assert_equal(e.value, '"Robot Super Course"') assert_equal(val, '"Robot Super Course"')
@step(u'the policy key value is changed$') @step(u'the policy key value is changed$')
def the_policy_key_value_is_unchanged(step): def the_policy_key_value_is_unchanged(step):
policy_value_css = 'li.course-advanced-policy-list-item div.value textarea' policy_value_css = 'li.course-advanced-policy-list-item div.value textarea'
e = css_find(policy_value_css).first val = css_find(policy_value_css).first.value
assert_equal(e.value, '"Robot Super Course X"') assert_equal(val, '"Robot Super Course X"')
############# HELPERS ############### ############# HELPERS ###############
...@@ -132,19 +146,23 @@ def create_entry(key, value): ...@@ -132,19 +146,23 @@ def create_entry(key, value):
new_key_css = 'div#__new_advanced_key__ input' new_key_css = 'div#__new_advanced_key__ input'
new_key_element = css_find(new_key_css).first new_key_element = css_find(new_key_css).first
new_key_element.fill(key) new_key_element.fill(key)
# For some reason have to get the instance for each command (get error that it is no longer attached to the DOM) # For some reason have to get the instance for each command
# Have to do all this because Selenium has a bug that fill does not remove existing text # (get error that it is no longer attached to the DOM)
# Have to do all this because Selenium fill does not remove existing text
new_value_css = 'div.CodeMirror textarea' new_value_css = 'div.CodeMirror textarea'
css_find(new_value_css).last.fill("") css_find(new_value_css).last.fill("")
css_find(new_value_css).last._element.send_keys(Keys.DELETE, Keys.DELETE) css_find(new_value_css).last._element.send_keys(Keys.DELETE, Keys.DELETE)
css_find(new_value_css).last.fill(value) css_find(new_value_css).last.fill(value)
# Add in a TAB key press because intermittently on ubuntu the
# last character of "value" above was not getting typed in
css_find(new_value_css).last._element.send_keys(Keys.TAB)
def delete_entry(index): def delete_entry(index):
""" """
Delete the nth entry where index is 0-based Delete the nth entry where index is 0-based
""" """
css = '.delete-button' css = 'a.delete-button'
assert_true(world.browser.is_element_present_by_css(css, 5)) assert_true(world.browser.is_element_present_by_css(css, 5))
delete_buttons = css_find(css) delete_buttons = css_find(css)
assert_true(len(delete_buttons) > index, "no delete button exists for entry " + str(index)) assert_true(len(delete_buttons) > index, "no delete button exists for entry " + str(index))
...@@ -165,16 +183,16 @@ def assert_entries(css, expected_values): ...@@ -165,16 +183,16 @@ def assert_entries(css, expected_values):
def click_save(): def click_save():
css = ".save-button" css = "a.save-button"
def is_shown(driver): # def is_shown(driver):
visible = css_find(css).first.visible # visible = css_find(css).first.visible
if visible: # if visible:
# Even when waiting for visible, this fails sporadically. Adding in a small wait. # # Even when waiting for visible, this fails sporadically. Adding in a small wait.
time.sleep(float(1)) # time.sleep(float(1))
return visible # return visible
wait_for(is_shown) # wait_for(is_shown)
css_click(css) css_click_at(css)
def fill_last_field(value): def fill_last_field(value):
......
...@@ -3,18 +3,20 @@ from lettuce.django import django_url ...@@ -3,18 +3,20 @@ from lettuce.django import django_url
from nose.tools import assert_true from nose.tools import assert_true
from nose.tools import assert_equal from nose.tools import assert_equal
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import WebDriverException, StaleElementReferenceException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from terrain.factories import UserFactory, RegistrationFactory, UserProfileFactory from terrain.factories import UserFactory, RegistrationFactory, UserProfileFactory
from terrain.factories import CourseFactory, GroupFactory from terrain.factories import CourseFactory, GroupFactory
import xmodule.modulestore.django from xmodule.modulestore.django import _MODULESTORES, modulestore
from xmodule.templates import update_templates
from auth.authz import get_user_by_email from auth.authz import get_user_by_email
from logging import getLogger from logging import getLogger
logger = getLogger(__name__) logger = getLogger(__name__)
########### STEP HELPERS ############## ########### STEP HELPERS ##############
@step('I (?:visit|access|open) the Studio homepage$') @step('I (?:visit|access|open) the Studio homepage$')
def i_visit_the_studio_homepage(step): def i_visit_the_studio_homepage(step):
# To make this go to port 8001, put # To make this go to port 8001, put
...@@ -52,9 +54,8 @@ def i_have_opened_a_new_course(step): ...@@ -52,9 +54,8 @@ def i_have_opened_a_new_course(step):
log_into_studio() log_into_studio()
create_a_course() create_a_course()
####### HELPER FUNCTIONS ##############
####### HELPER FUNCTIONS ##############
def create_studio_user( def create_studio_user(
uname='robot', uname='robot',
email='robot+studio@edx.org', email='robot+studio@edx.org',
...@@ -83,9 +84,9 @@ def flush_xmodule_store(): ...@@ -83,9 +84,9 @@ def flush_xmodule_store():
# (though it shouldn't), do this manually # (though it shouldn't), do this manually
# from the bash shell to drop it: # from the bash shell to drop it:
# $ mongo test_xmodule --eval "db.dropDatabase()" # $ mongo test_xmodule --eval "db.dropDatabase()"
xmodule.modulestore.django._MODULESTORES = {} _MODULESTORES = {}
xmodule.modulestore.django.modulestore().collection.drop() modulestore().collection.drop()
xmodule.templates.update_templates() update_templates()
def assert_css_with_text(css, text): def assert_css_with_text(css, text):
...@@ -94,8 +95,16 @@ def assert_css_with_text(css, text): ...@@ -94,8 +95,16 @@ def assert_css_with_text(css, text):
def css_click(css): def css_click(css):
assert_true(world.browser.is_element_present_by_css(css, 5)) '''
world.browser.find_by_css(css).first.click() First try to use the regular click method,
but if clicking in the middle of an element
doesn't work it might be that it thinks some other
element is on top of it there so click in the upper left
'''
try:
css_find(css).first.click()
except WebDriverException, e:
css_click_at(css)
def css_click_at(css, x=10, y=10): def css_click_at(css, x=10, y=10):
...@@ -103,8 +112,7 @@ def css_click_at(css, x=10, y=10): ...@@ -103,8 +112,7 @@ def css_click_at(css, x=10, y=10):
A method to click at x,y coordinates of the element A method to click at x,y coordinates of the element
rather than in the center of the element rather than in the center of the element
''' '''
assert_true(world.browser.is_element_present_by_css(css, 5)) e = css_find(css).first
e = world.browser.find_by_css(css).first
e.action_chains.move_to_element_with_offset(e._element, x, y) e.action_chains.move_to_element_with_offset(e._element, x, y)
e.action_chains.click() e.action_chains.click()
e.action_chains.perform() e.action_chains.perform()
...@@ -115,11 +123,16 @@ def css_fill(css, value): ...@@ -115,11 +123,16 @@ def css_fill(css, value):
def css_find(css): def css_find(css):
def is_visible(driver):
return EC.visibility_of_element_located((By.CSS_SELECTOR,css,))
assert_true(world.browser.is_element_present_by_css(css, 5))
wait_for(is_visible)
return world.browser.find_by_css(css) return world.browser.find_by_css(css)
def wait_for(func): def wait_for(func):
WebDriverWait(world.browser.driver, 10).until(func) WebDriverWait(world.browser.driver, 5).until(func)
def id_find(id): def id_find(id):
......
...@@ -26,9 +26,10 @@ Feature: Create Section ...@@ -26,9 +26,10 @@ Feature: Create Section
And I save a new section release date And I save a new section release date
Then the section release date is updated Then the section release date is updated
@skip-phantom
Scenario: Delete section Scenario: Delete section
Given I have opened a new course in Studio Given I have opened a new course in Studio
And I have added a new section And I have added a new section
When I press the "section" delete icon When I press the "section" delete icon
And I confirm the alert And I confirm the alert
Then the section does not exist Then the section does not exist
\ No newline at end of file
from lettuce import world, step from lettuce import world, step
from common import * from common import *
from nose.tools import assert_equal from nose.tools import assert_equal
from selenium.webdriver.common.keys import Keys
import time
############### ACTIONS #################### ############### ACTIONS ####################
...@@ -37,10 +39,14 @@ def i_save_a_new_section_release_date(step): ...@@ -37,10 +39,14 @@ def i_save_a_new_section_release_date(step):
date_css = 'input.start-date.date.hasDatepicker' date_css = 'input.start-date.date.hasDatepicker'
time_css = 'input.start-time.time.ui-timepicker-input' time_css = 'input.start-time.time.ui-timepicker-input'
css_fill(date_css, '12/25/2013') css_fill(date_css, '12/25/2013')
# click here to make the calendar go away # hit TAB to get to the time field
css_click(time_css) e = css_find(date_css).first
e._element.send_keys(Keys.TAB)
css_fill(time_css, '12:00am') css_fill(time_css, '12:00am')
css_click('a.save-button') e = css_find(time_css).first
e._element.send_keys(Keys.TAB)
time.sleep(float(1))
world.browser.click_link_by_text('Save')
############ ASSERTIONS ################### ############ ASSERTIONS ###################
...@@ -106,7 +112,7 @@ def the_section_release_date_picker_not_visible(step): ...@@ -106,7 +112,7 @@ def the_section_release_date_picker_not_visible(step):
def the_section_release_date_is_updated(step): def the_section_release_date_is_updated(step):
css = 'span.published-status' css = 'span.published-status'
status_text = world.browser.find_by_css(css).text status_text = world.browser.find_by_css(css).text
assert status_text == 'Will Release: 12/25/2013 at 12:00am' assert_equal(status_text,'Will Release: 12/25/2013 at 12:00am')
############ HELPER METHODS ################### ############ HELPER METHODS ###################
...@@ -120,4 +126,4 @@ def save_section_name(name): ...@@ -120,4 +126,4 @@ def save_section_name(name):
def see_my_section_on_the_courseware_page(name): def see_my_section_on_the_courseware_page(name):
section_css = 'span.section-name-span' section_css = 'span.section-name-span'
assert_css_with_text(section_css, name) assert_css_with_text(section_css, name)
\ No newline at end of file
from lettuce import world, step from lettuce import world, step
from common import *
@step('I fill in the registration form$') @step('I fill in the registration form$')
...@@ -13,10 +14,11 @@ def i_fill_in_the_registration_form(step): ...@@ -13,10 +14,11 @@ def i_fill_in_the_registration_form(step):
@step('I press the Create My Account button on the registration form$') @step('I press the Create My Account button on the registration form$')
def i_press_the_button_on_the_registration_form(step): def i_press_the_button_on_the_registration_form(step):
register_form = world.browser.find_by_css('form#register_form') submit_css = 'form#register_form button#submit'
submit_css = 'button#submit' # Workaround for click not working on ubuntu
register_form.find_by_css(submit_css).click() # for some unknown reason.
e = css_find(submit_css)
e.type(' ')
@step('I should see be on the studio home page$') @step('I should see be on the studio home page$')
def i_should_see_be_on_the_studio_home_page(step): def i_should_see_be_on_the_studio_home_page(step):
......
...@@ -21,6 +21,7 @@ Feature: Overview Toggle Section ...@@ -21,6 +21,7 @@ Feature: Overview Toggle Section
Then I see the "Collapse All Sections" link Then I see the "Collapse All Sections" link
And all sections are expanded And all sections are expanded
@skip-phantom
Scenario: Collapse link is not removed after last section of a course is deleted Scenario: Collapse link is not removed after last section of a course is deleted
Given I have a course with 1 section Given I have a course with 1 section
And I navigate to the course overview page And I navigate to the course overview page
......
...@@ -17,6 +17,7 @@ Feature: Create Subsection ...@@ -17,6 +17,7 @@ Feature: Create Subsection
And I click to edit the subsection name And I click to edit the subsection name
Then I see the complete subsection name with a quote in the editor Then I see the complete subsection name with a quote in the editor
@skip-phantom
Scenario: Delete a subsection Scenario: Delete a subsection
Given I have opened a new course section in Studio Given I have opened a new course section in Studio
And I have added a new subsection And I have added a new subsection
......
...@@ -62,3 +62,6 @@ AWS_SECRET_ACCESS_KEY = AUTH_TOKENS["AWS_SECRET_ACCESS_KEY"] ...@@ -62,3 +62,6 @@ AWS_SECRET_ACCESS_KEY = AUTH_TOKENS["AWS_SECRET_ACCESS_KEY"]
DATABASES = AUTH_TOKENS['DATABASES'] DATABASES = AUTH_TOKENS['DATABASES']
MODULESTORE = AUTH_TOKENS['MODULESTORE'] MODULESTORE = AUTH_TOKENS['MODULESTORE']
CONTENTSTORE = AUTH_TOKENS['CONTENTSTORE'] CONTENTSTORE = AUTH_TOKENS['CONTENTSTORE']
# Datadog for events!
DATADOG_API = AUTH_TOKENS.get("DATADOG_API")
\ No newline at end of file
from dogapi import dog_http_api, dog_stats_api
from django.conf import settings
if hasattr(settings, 'DATADOG_API'):
dog_http_api.api_key = settings.DATADOG_API
dog_stats_api.start(api_key=settings.DATADOG_API, statsd=True)
from django.conf import settings from django.conf import settings
from django.conf.urls import patterns, include, url from django.conf.urls import patterns, include, url
from . import one_time_startup
# Uncomment the next two lines to enable the admin: # Uncomment the next two lines to enable the admin:
# from django.contrib import admin # from django.contrib import admin
......
...@@ -2,8 +2,9 @@ import json ...@@ -2,8 +2,9 @@ import json
from datetime import datetime from datetime import datetime
from django.http import HttpResponse from django.http import HttpResponse
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from dogapi import dog_stats_api
@dog_stats_api.timed('edxapp.heartbeat')
def heartbeat(request): def heartbeat(request):
""" """
Simple view that a loadbalancer can check to verify that the app is up Simple view that a loadbalancer can check to verify that the app is up
......
...@@ -10,6 +10,7 @@ import paramiko ...@@ -10,6 +10,7 @@ import paramiko
import boto import boto
dog_http_api.api_key = settings.DATADOG_API dog_http_api.api_key = settings.DATADOG_API
dog_stats_api.start(api_key=settings.DATADOG_API, statsd=True)
class Command(BaseCommand): class Command(BaseCommand):
......
...@@ -13,6 +13,7 @@ from django.core.management import call_command ...@@ -13,6 +13,7 @@ from django.core.management import call_command
def initial_setup(server): def initial_setup(server):
# Launch the browser app (choose one of these below) # Launch the browser app (choose one of these below)
world.browser = Browser('chrome') world.browser = Browser('chrome')
# world.browser = Browser('phantomjs')
# world.browser = Browser('firefox') # world.browser = Browser('firefox')
......
...@@ -91,12 +91,12 @@ class CorrectMapTest(unittest.TestCase): ...@@ -91,12 +91,12 @@ class CorrectMapTest(unittest.TestCase):
npoints=0) npoints=0)
# Assert that we get the expected points # Assert that we get the expected points
# If points assigned and correct --> npoints # If points assigned --> npoints
# If no points assigned and correct --> 1 point # If no points assigned and correct --> 1 point
# Otherwise --> 0 points # If no points assigned and incorrect --> 0 points
self.assertEqual(self.cmap.get_npoints('1_2_1'), 5) self.assertEqual(self.cmap.get_npoints('1_2_1'), 5)
self.assertEqual(self.cmap.get_npoints('2_2_1'), 1) self.assertEqual(self.cmap.get_npoints('2_2_1'), 1)
self.assertEqual(self.cmap.get_npoints('3_2_1'), 0) self.assertEqual(self.cmap.get_npoints('3_2_1'), 5)
self.assertEqual(self.cmap.get_npoints('4_2_1'), 0) self.assertEqual(self.cmap.get_npoints('4_2_1'), 0)
self.assertEqual(self.cmap.get_npoints('5_2_1'), 0) self.assertEqual(self.cmap.get_npoints('5_2_1'), 0)
......
...@@ -3,4 +3,3 @@ ...@@ -3,4 +3,3 @@
-e git://github.com/MITx/django-pipeline.git#egg=django-pipeline -e git://github.com/MITx/django-pipeline.git#egg=django-pipeline
-e git://github.com/MITx/django-wiki.git@e2e84558#egg=django-wiki -e git://github.com/MITx/django-wiki.git@e2e84558#egg=django-wiki
-e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev -e git://github.com/dementrock/pystache_custom.git@776973740bdaad83a3b029f96e415a7d1e8bec2f#egg=pystache_custom-dev
-e git://github.com/MITx/dogapi.git@003a4fc9#egg=dogapi
from dogapi import dog_http_api, dog_stats_api
from django.conf import settings
if hasattr(settings, 'DATADOG_API'):
dog_http_api.api_key = settings.DATADOG_API
dog_stats_api.start(api_key=settings.DATADOG_API, statsd=True)
...@@ -3,6 +3,9 @@ from django.conf.urls import patterns, include, url ...@@ -3,6 +3,9 @@ from django.conf.urls import patterns, include, url
from django.contrib import admin from django.contrib import admin
from django.conf.urls.static import static from django.conf.urls.static import static
from django.views.generic import RedirectView from django.views.generic import RedirectView
from . import one_time_startup
import django.contrib.auth.views import django.contrib.auth.views
# Uncomment the next two lines to enable the admin: # Uncomment the next two lines to enable the admin:
......
...@@ -58,3 +58,4 @@ ipython==0.13.1 ...@@ -58,3 +58,4 @@ ipython==0.13.1
xmltodict==0.4.1 xmltodict==0.4.1
paramiko==1.9.0 paramiko==1.9.0
Pillow==1.7.8 Pillow==1.7.8
dogapi==1.2.1
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