Commit 193f315f by Muddasser

Converted all lettuce tests from cms/advanced_settings.feature to bokchoy

parent b1313671
@shard_1
Feature: CMS.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
Scenario: A course author sees default advanced settings
Given I have opened a new course in Studio
When I select the Advanced Settings
Then I see default advanced settings
Scenario: Add new entries, and they appear alphabetically after save
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
When I edit the value of a policy key
And I press the "Cancel" notification button
Then the policy key value is unchanged
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
When I edit the value of a policy key and save
Then the policy key value is changed
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
When I create a JSON object as a value for "Discussion Topic Mapping"
Then it is displayed as formatted
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
When I create a JSON object as a value for "Course Display Name"
Then I get an error on save
And I reload the page
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
When I create a non-JSON value not in quotes
Then it is displayed as a string
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
When I edit the value of a policy key
And I press the "Save" notification button
Then I see a confirmation that my changes have been saved
Scenario: Deprecated Settings are not shown by default
Given I am on the Advanced Course Settings page in Studio
Then deprecated settings are not shown
Scenario: Deprecated Settings can be toggled
Given I am on the Advanced Course Settings page in Studio
When I toggle the display of deprecated settings
Then deprecated settings are then shown
And I toggle the display of deprecated settings
Then deprecated settings are not shown
# pylint: disable=missing-docstring
# pylint: disable=redefined-outer-name
from lettuce import world, step
from nose.tools import assert_false, assert_equal, assert_regexp_matches
from common import type_in_codemirror, press_the_notification_button, get_codemirror_value
KEY_CSS = '.key h3.title'
DISPLAY_NAME_KEY = "Course Display Name"
DISPLAY_NAME_VALUE = '"Robot Super Course"'
ADVANCED_MODULES_KEY = "Advanced Module List"
# A few deprecated settings for testing toggling functionality.
DEPRECATED_SETTINGS = ["CSS Class for Course Reruns", "Hide Progress Tab", "XQA Key"]
@step('I select the Advanced Settings$')
def i_select_advanced_settings(step):
world.click_course_settings()
# The click handlers are set up so that if you click <body>
# the menu disappears. This means that if we're even a *little*
# bit off on the last item ('Advanced Settings'), the menu
# will close and the test will fail.
# For this reason, we retrieve the link and visit it directly
# This is what the browser *should* be doing, since it's just a native
# link with no JavaScript involved.
link_css = 'li.nav-course-settings-advanced a'
world.wait_for_visible(link_css)
link = world.css_find(link_css).first['href']
world.visit(link)
@step('I am on the Advanced Course Settings page in Studio$')
def i_am_on_advanced_course_settings(step):
step.given('I have opened a new course in Studio')
step.given('I select the Advanced Settings')
@step(u'I edit the value of a policy key$')
def edit_the_value_of_a_policy_key(step):
type_in_codemirror(get_index_of(DISPLAY_NAME_KEY), 'X')
@step(u'I edit the value of a policy key and save$')
def edit_the_value_of_a_policy_key_and_save(step):
change_display_name_value(step, '"foo"')
@step('I create a JSON object as a value for "(.*)"$')
def create_JSON_object(step, key):
change_value(step, key, '{"key": "value", "key_2": "value_2"}')
@step('I create a non-JSON value not in quotes$')
def create_value_not_in_quotes(step):
change_display_name_value(step, 'quote me')
@step('I see default advanced settings$')
def i_see_default_advanced_settings(step):
# Test only a few of the existing properties (there are around 34 of them)
assert_policy_entries(
[ADVANCED_MODULES_KEY, DISPLAY_NAME_KEY, "Show Calculator"], ["[]", DISPLAY_NAME_VALUE, "false"])
@step('the settings are alphabetized$')
def they_are_alphabetized(step):
key_elements = world.css_find(KEY_CSS)
all_keys = []
for key in key_elements:
all_keys.append(key.value)
assert_equal(sorted(all_keys), all_keys, "policy keys were not sorted")
@step('it is displayed as formatted$')
def it_is_formatted(step):
assert_policy_entries(['Discussion Topic Mapping'], ['{\n "key": "value",\n "key_2": "value_2"\n}'])
@step('I get an error on save$')
def error_on_save(step):
assert_regexp_matches(
world.css_text('.error-item-message'),
"Value stored in a .* must be .*, found .*"
)
@step('it is displayed as a string')
def it_is_displayed_as_string(step):
assert_policy_entries([DISPLAY_NAME_KEY], ['"quote me"'])
@step(u'the policy key value is unchanged$')
def the_policy_key_value_is_unchanged(step):
assert_equal(get_display_name_value(), DISPLAY_NAME_VALUE)
@step(u'the policy key value is changed$')
def the_policy_key_value_is_changed(step):
assert_equal(get_display_name_value(), '"foo"')
@step(u'deprecated settings are (then|not) shown$')
def verify_deprecated_settings_shown(_step, expected):
for setting in DEPRECATED_SETTINGS:
if expected == "not":
assert_equal(-1, get_index_of(setting))
else:
world.wait_for(lambda _: get_index_of(setting) != -1)
@step(u'I toggle the display of deprecated settings$')
def toggle_deprecated_settings(_step):
world.css_click(".deprecated-settings-label")
def assert_policy_entries(expected_keys, expected_values):
for key, value in zip(expected_keys, expected_values):
index = get_index_of(key)
assert_false(index == -1, "Could not find key: {key}".format(key=key))
found_value = get_codemirror_value(index)
assert_equal(
value, found_value,
"Expected {} to have value {} but found {}".format(key, value, found_value)
)
def get_index_of(expected_key):
for i, element in enumerate(world.css_find(KEY_CSS)):
# Sometimes get stale reference if I hold on to the array of elements
key = world.css_value(KEY_CSS, index=i)
if key == expected_key:
return i
return -1
def get_display_name_value():
index = get_index_of(DISPLAY_NAME_KEY)
return get_codemirror_value(index)
def change_display_name_value(step, new_value):
change_value(step, DISPLAY_NAME_KEY, new_value)
def change_value(step, key, new_value):
index = get_index_of(key)
type_in_codemirror(index, new_value)
press_the_notification_button(step, "Save")
world.wait_for_ajax_complete()
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
import json import json
from lettuce import world, step from lettuce import world, step
from nose.tools import assert_equal, assert_true from nose.tools import assert_equal, assert_true
from common import type_in_codemirror, open_new_course from common import type_in_codemirror, open_new_course, press_the_notification_button
from advanced_settings import change_value, ADVANCED_MODULES_KEY
from course_import import import_file from course_import import import_file
KEY_CSS = '.key h3.title'
DISPLAY_NAME = "Display Name" DISPLAY_NAME = "Display Name"
MAXIMUM_ATTEMPTS = "Maximum Attempts" MAXIMUM_ATTEMPTS = "Maximum Attempts"
PROBLEM_WEIGHT = "Problem Weight" PROBLEM_WEIGHT = "Problem Weight"
...@@ -16,6 +16,7 @@ SHOW_ANSWER = "Show Answer" ...@@ -16,6 +16,7 @@ SHOW_ANSWER = "Show Answer"
SHOW_RESET_BUTTON = "Show Reset Button" SHOW_RESET_BUTTON = "Show Reset Button"
TIMER_BETWEEN_ATTEMPTS = "Timer Between Attempts" TIMER_BETWEEN_ATTEMPTS = "Timer Between Attempts"
MATLAB_API_KEY = "Matlab API key" MATLAB_API_KEY = "Matlab API key"
ADVANCED_MODULES_KEY = "Advanced Module List"
@step('I have created a Blank Common Problem$') @step('I have created a Blank Common Problem$')
...@@ -380,6 +381,23 @@ def set_weight(weight): ...@@ -380,6 +381,23 @@ def set_weight(weight):
world.set_field_value(index, weight) world.set_field_value(index, weight)
def get_index_of(expected_key):
for i, element in enumerate(world.css_find(KEY_CSS)):
# Sometimes get stale reference if I hold on to the array of elements
key = world.css_value(KEY_CSS, index=i)
if key == expected_key:
return i
return -1
def change_value(step, key, new_value):
index = get_index_of(key)
type_in_codemirror(index, new_value)
press_the_notification_button(step, "Save")
world.wait_for_ajax_complete()
def open_high_level_source(): def open_high_level_source():
world.edit_component() world.edit_component()
world.css_click('.launch-latex-compiler > a') world.css_click('.launch-latex-compiler > a')
...@@ -107,6 +107,12 @@ class AdvancedSettingsPage(CoursePage): ...@@ -107,6 +107,12 @@ class AdvancedSettingsPage(CoursePage):
return -1 return -1
def get_index_of(self, key):
"""
Returns the index of a setting passed as key.
"""
return self._get_index_of(key)
def save(self): def save(self):
press_the_notification_button(self, "Save") press_the_notification_button(self, "Save")
...@@ -118,6 +124,19 @@ class AdvancedSettingsPage(CoursePage): ...@@ -118,6 +124,19 @@ class AdvancedSettingsPage(CoursePage):
type_in_codemirror(self, index, new_value) type_in_codemirror(self, index, new_value)
self.save() self.save()
def set_value_without_saving(self, key, new_value):
"""
Set value without saving it.
"""
index = self._get_index_of(key)
type_in_codemirror(self, index, new_value)
def click_toggle_button(self):
"""
Click toggle button with css class deprecated-settings-label.
"""
self.q(css='.deprecated-settings-label').click()
def get(self, key): def get(self, key):
index = self._get_index_of(key) index = self._get_index_of(key)
return get_codemirror_value(self, index) return get_codemirror_value(self, index)
...@@ -222,3 +241,10 @@ class AdvancedSettingsPage(CoursePage): ...@@ -222,3 +241,10 @@ class AdvancedSettingsPage(CoursePage):
'learning_info', 'learning_info',
'instructor_info' 'instructor_info'
] ]
def get_all_displayed_settings(self):
"""
Get names of all displayed settings.
"""
query = self.q(css=SETTINGS_NAME_SELECTOR)
return query.text
...@@ -571,3 +571,225 @@ class StudioSubsectionSettingsA11yTest(StudioCourseTest): ...@@ -571,3 +571,225 @@ class StudioSubsectionSettingsA11yTest(StudioCourseTest):
include=['section.edit-settings-timed-examination'] include=['section.edit-settings-timed-examination']
) )
self.course_outline.a11y_audit.check_for_accessibility_errors() self.course_outline.a11y_audit.check_for_accessibility_errors()
@attr('shard_8')
class AdvancedSettingsTest(StudioCourseTest):
"""
Tests for Advanced Settings tab in studio.
"""
DEPRECATED_SETTINGS = ["CSS Class for Course Reruns", "Hide Progress Tab", "XQA Key"]
DISPLAY_NAME_KEY = "Course Banner Image"
DISPLAY_NAME_VALUE = '"images_course_image.jpg"'
ADVANCED_MODULES_KEY = "Advanced Module List"
def setUp(self, is_staff=False, test_xss=True):
super(AdvancedSettingsTest, self).setUp()
self.advanced_settings = AdvancedSettingsPage(
self.browser,
self.course_info['org'],
self.course_info['number'],
self.course_info['run']
)
self.type_fields = ['Course Display Name', 'Advanced Module List', 'Discussion Topic Mapping',
'Maximum Attempts', 'Course Announcement Date']
# Before every test, make sure to visit the page first
self.advanced_settings.visit()
def verify_deprecated_settings_shown(self, expected):
"""
Verify deprecated settings are displayed/not display based on the
argument. If argument passed has the value not, then it is assumed that
deprecated settings should not be shown.
"""
for setting in self.DEPRECATED_SETTINGS:
if expected == "not":
self.assertEquals(-1, self.advanced_settings.get_index_of(setting))
else:
self.assertNotEquals(-1, self.advanced_settings.get_index_of(setting))
def assert_policy_entries(self, expected_keys, expected_values):
"""
Ensure keys in expected_keys has corresponding displayed values
in expected_values.
"""
for key, value in zip(expected_keys, expected_values):
index = self.advanced_settings.get_index_of(key)
self.assertNotEquals(index == -1, "Could not find key: {key}".format(key=key))
found_value = self.advanced_settings.get(key)
self.assertEquals(
value, found_value,
"Expected {} to have value {} but found {}".format(key, value, found_value)
)
def test_author_sees_default_advanced_settings(self):
"""
Scenario: A course author sees default advanced settings
Given I have opened a new course in Studio
When I select the Advanced Settings
Then I see default advanced settings
"""
# Verify some of the advanced settings.
self.assert_policy_entries(
[self.ADVANCED_MODULES_KEY, self.DISPLAY_NAME_KEY, "Show Calculator"],
["[]", self.DISPLAY_NAME_VALUE, "false"])
def test_new_entries_appear_alphabetically(self):
"""
Scenario: Add new entries, and they appear alphabetically after save
Given I am on the Advanced Course Settings page in Studio
Then the settings are alphabetized
"""
# Get all keys displayed on the page.
all_keys = self.advanced_settings.get_all_displayed_settings()
# Returned keys should match its sorted version.
self.assertEquals(sorted(all_keys), all_keys, "Settings are alphabetically ordered")
def test_cancel_edit_key_value(self):
"""
Scenario: Test cancel editing key value
Given I am on the Advanced Course Settings page in Studio
When I edit the value of a policy key
And I press the "Cancel" notification button
Then the policy key value is unchanged
And I reload the page
Then the policy key value is unchanged
"""
default_course_name = self.advanced_settings.get('Course Display Name')
# Change the value of 'Course Display Name'.
self.advanced_settings.set_value_without_saving('Course Display Name', 1)
# Cancel and don't save the value when asked in notification.
self.advanced_settings.cancel()
self.assertEquals(self.advanced_settings.get('Course Display Name'), default_course_name)
# Refresh the page.
self.advanced_settings.refresh_and_wait_for_load()
# 'Course Display Name' should be same as before.
self.assertEquals(self.advanced_settings.get('Course Display Name'), default_course_name)
def test_edit_key_value(self):
"""
Scenario: Test editing key value
Given I am on the Advanced Course Settings page in Studio
When I edit the value of a policy key and save
Then the policy key value is changed
And I reload the page
Then the policy key value is changed
"""
course_name_to_set = '"New Course Name"'
# Change the value of 'Course Display Name' and save it.
self.advanced_settings.set('Course Display Name', course_name_to_set)
new_course_name = self.advanced_settings.get('Course Display Name')
# Course name has been changed.
self.assertEquals(new_course_name, course_name_to_set)
# Refresh the page.
self.advanced_settings.refresh_and_wait_for_load()
new_course_name = self.advanced_settings.get('Course Display Name')
# Course name has been changed.
self.assertEquals(new_course_name, course_name_to_set)
def test_multi_lines_input_appearance(self):
"""
Scenario: Test how multi-line input appears
Given I am on the Advanced Course Settings page in Studio
When I create a JSON object as a value for "Discussion Topic Mapping"
Then it is displayed as formatted
And I reload the page
Then it is displayed as formatted
"""
new_json_value_to_set = '{"key": "value", "key_2": "value_2"}'
expected_json_value = '{\n "key": "value",\n "key_2": "value_2"\n}'
# Set value of Discussion Topic Mapping.
self.advanced_settings.set('Discussion Topic Mapping', new_json_value_to_set)
# Value should be properly formatted.
self.assertEquals(self.advanced_settings.get('Discussion Topic Mapping'), expected_json_value)
# Refresh the page.
self.advanced_settings.refresh_and_wait_for_load()
# Value should still be well formatted.
self.assertEquals(self.advanced_settings.get('Discussion Topic Mapping'), expected_json_value)
def test_error_on_wrong_input(self):
"""
Scenario: Test error if value supplied is of the wrong type
Given I am on the Advanced Course Settings page in Studio
When I create a JSON object as a value for "Course Display Name"
Then I get an error on save
And I reload the page
Then the policy key value is unchanged
"""
# Get current Course Display Name value.
default_course_name = self.advanced_settings.get('Course Display Name')
new_json_value_to_set = '{"key": "value", "key_2": "value_2"}'
# Set value of Course Display Name.
self.advanced_settings.set('Course Display Name', new_json_value_to_set)
# Make sure error message is shown successfully.
self.advanced_settings.wait_for(
lambda: self.advanced_settings.q(css='.error-item-message').present,
"Error message is shown successfully on wrong input."
)
# Refresh the page.
self.advanced_settings.refresh_and_wait_for_load()
# New value shouldn't been saved.
self.assertEquals(default_course_name, self.advanced_settings.get('Course Display Name'))
def test_automatic_quoting_on_non_json_value(self):
"""
Scenario: Test automatic quoting of non-JSON values
Given I am on the Advanced Course Settings page in Studio
When I create a non-JSON value not in quotes
Then it is displayed as a string
And I reload the page
Then it is displayed as a string
"""
value_to_set = 'quote me'
self.advanced_settings.set('Course Display Name', value_to_set)
expected_value = '"quote me"'
# New value should be in quotes.
self.assertEquals(self.advanced_settings.get('Course Display Name'), expected_value)
# Refresh the page.
self.advanced_settings.refresh_and_wait_for_load()
# New value should still be in quotes.
self.assertEquals(self.advanced_settings.get('Course Display Name'), expected_value)
def test_confirmation_on_save(self):
"""
Scenario: Confirmation is shown on save
Given I am on the Advanced Course Settings page in Studio
When I edit the value of a policy key
And I press the "Save" notification button
Then I see a confirmation that my changes have been saved
"""
# Set the Course Display Name to a new value.
self.advanced_settings.set('Course Display Name', 'New Course Name')
# Make sure we get save confirmation is shown.
self.advanced_settings.wait_for(
lambda: self.advanced_settings.q(css='#alert-confirmation').present,
"Confirmation of save has been shown."
)
def test_deprecated_settings_not_shown(self):
"""
Scenario: Deprecated Settings are not shown by default
Given I am on the Advanced Course Settings page in Studio
Then deprecated settings are not shown
"""
self.verify_deprecated_settings_shown("not")
def test_deprecated_settings_toggle(self):
"""
Scenario: Deprecated Settings can be toggled
Given I am on the Advanced Course Settings page in Studio
When I toggle the display of deprecated settings
Then deprecated settings are then shown
And I toggle the display of deprecated settings
Then deprecated settings are not shown
"""
# Click toggle button to toggle the display of deprecated settings.
self.advanced_settings.click_toggle_button()
# Verify that deprecated settings are shown.
self.verify_deprecated_settings_shown("yes")
# Click toggle button to toggle the display of deprecated settings.
self.advanced_settings.click_toggle_button()
# Verify that deprecated settings are not shown.
self.verify_deprecated_settings_shown("not")
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