Fix merge conflict.
This commit is contained in:
@@ -27,3 +27,59 @@ def click_component_from_menu(instance_id, expected_css):
|
||||
assert_equal(1, len(world.css_find(elem_css)))
|
||||
world.css_click(elem_css)
|
||||
assert_equal(1, len(world.css_find(expected_css)))
|
||||
|
||||
@world.absorb
|
||||
def edit_component_and_select_settings():
|
||||
world.css_click('a.edit-button')
|
||||
world.css_click('#settings-mode')
|
||||
|
||||
|
||||
@world.absorb
|
||||
def verify_setting_entry(setting, display_name, value, explicitly_set):
|
||||
assert_equal(display_name, setting.find_by_css('.setting-label')[0].value)
|
||||
assert_equal(value, setting.find_by_css('.setting-input')[0].value)
|
||||
settingClearButton = setting.find_by_css('.setting-clear')[0]
|
||||
assert_equal(explicitly_set, settingClearButton.has_class('active'))
|
||||
assert_equal(not explicitly_set, settingClearButton.has_class('inactive'))
|
||||
|
||||
|
||||
@world.absorb
|
||||
def verify_all_setting_entries(expected_entries):
|
||||
settings = world.browser.find_by_css('.wrapper-comp-setting')
|
||||
assert_equal(len(expected_entries), len(settings))
|
||||
for (counter, setting) in enumerate(settings):
|
||||
world.verify_setting_entry(
|
||||
setting, expected_entries[counter][0],
|
||||
expected_entries[counter][1], expected_entries[counter][2]
|
||||
)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def save_component_and_reopen(step):
|
||||
world.css_click("a.save-button")
|
||||
# We have a known issue that modifications are still shown within the edit window after cancel (though)
|
||||
# they are not persisted. Refresh the browser to make sure the changes WERE persisted after Save.
|
||||
reload_the_page(step)
|
||||
edit_component_and_select_settings()
|
||||
|
||||
|
||||
@world.absorb
|
||||
def cancel_component(step):
|
||||
world.css_click("a.cancel-button")
|
||||
# We have a known issue that modifications are still shown within the edit window after cancel (though)
|
||||
# they are not persisted. Refresh the browser to make sure the changes were not persisted.
|
||||
reload_the_page(step)
|
||||
|
||||
|
||||
@world.absorb
|
||||
def revert_setting_entry(label):
|
||||
get_setting_entry(label).find_by_css('.setting-clear')[0].click()
|
||||
|
||||
|
||||
@world.absorb
|
||||
def get_setting_entry(label):
|
||||
settings = world.browser.find_by_css('.wrapper-comp-setting')
|
||||
for setting in settings:
|
||||
if setting.find_by_css('.setting-label')[0].value == label:
|
||||
return setting
|
||||
return None
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
Feature: Discussion Component Editor
|
||||
As a course author, I want to be able to create discussion components.
|
||||
|
||||
Scenario: User can view metadata
|
||||
Given I have created a Discussion Tag
|
||||
And I edit and select Settings
|
||||
Then I see three alphabetized settings and their expected values
|
||||
|
||||
Scenario: User can modify display name
|
||||
Given I have created a Discussion Tag
|
||||
And I edit and select Settings
|
||||
Then I can modify the display name
|
||||
And my display name change is persisted on save
|
||||
23
cms/djangoapps/contentstore/features/discussion-editor.py
Normal file
23
cms/djangoapps/contentstore/features/discussion-editor.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# disable missing docstring
|
||||
#pylint: disable=C0111
|
||||
|
||||
from lettuce import world, step
|
||||
|
||||
|
||||
@step('I have created a Discussion Tag$')
|
||||
def i_created_discussion_tag(step):
|
||||
world.create_component_instance(
|
||||
step, '.large-discussion-icon',
|
||||
'i4x://edx/templates/discussion/Discussion_Tag',
|
||||
'.xmodule_DiscussionModule'
|
||||
)
|
||||
|
||||
|
||||
@step('I see three alphabetized settings and their expected values$')
|
||||
def i_see_only_the_settings_and_values(step):
|
||||
world.verify_all_setting_entries(
|
||||
[
|
||||
['Category', "Week 1", True],
|
||||
['Display Name', "Discussion Tag", True],
|
||||
['Subcategory', "Topic-Level Student-Visible Label", True]
|
||||
])
|
||||
13
cms/djangoapps/contentstore/features/html-editor.feature
Normal file
13
cms/djangoapps/contentstore/features/html-editor.feature
Normal file
@@ -0,0 +1,13 @@
|
||||
Feature: HTML Editor
|
||||
As a course author, I want to be able to create HTML blocks.
|
||||
|
||||
Scenario: User can view metadata
|
||||
Given I have created a Blank HTML Page
|
||||
And I edit and select Settings
|
||||
Then I see only the HTML display name setting
|
||||
|
||||
Scenario: User can modify display name
|
||||
Given I have created a Blank HTML Page
|
||||
And I edit and select Settings
|
||||
Then I can modify the display name
|
||||
And my display name change is persisted on save
|
||||
17
cms/djangoapps/contentstore/features/html-editor.py
Normal file
17
cms/djangoapps/contentstore/features/html-editor.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# disable missing docstring
|
||||
#pylint: disable=C0111
|
||||
|
||||
from lettuce import world, step
|
||||
|
||||
|
||||
@step('I have created a Blank HTML Page$')
|
||||
def i_created_blank_html_page(step):
|
||||
world.create_component_instance(
|
||||
step, '.large-html-icon', 'i4x://edx/templates/html/Blank_HTML_Page',
|
||||
'.xmodule_HtmlModule'
|
||||
)
|
||||
|
||||
|
||||
@step('I see only the HTML display name setting$')
|
||||
def i_see_only_the_html_display_name(step):
|
||||
world.verify_all_setting_entries([['Display Name', "Blank HTML Page", True]])
|
||||
67
cms/djangoapps/contentstore/features/problem-editor.feature
Normal file
67
cms/djangoapps/contentstore/features/problem-editor.feature
Normal file
@@ -0,0 +1,67 @@
|
||||
Feature: Problem Editor
|
||||
As a course author, I want to be able to create problems and edit their settings.
|
||||
|
||||
Scenario: User can view metadata
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then I see five alphabetized settings and their expected values
|
||||
And Edit High Level Source is not visible
|
||||
|
||||
Scenario: User can modify String values
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then I can modify the display name
|
||||
And my display name change is persisted on save
|
||||
|
||||
Scenario: User can specify special characters in String values
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then I can specify special characters in the display name
|
||||
And my special characters and persisted on save
|
||||
|
||||
Scenario: User can revert display name to unset
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then I can revert the display name to unset
|
||||
And my display name is unset on save
|
||||
|
||||
Scenario: User can select values in a Select
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then I can select Per Student for Randomization
|
||||
And my change to randomization is persisted
|
||||
And I can revert to the default value for randomization
|
||||
|
||||
Scenario: User can modify float input values
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then I can set the weight to "3.5"
|
||||
And my change to weight is persisted
|
||||
And I can revert to the default value of unset for weight
|
||||
|
||||
Scenario: User cannot type letters in float number field
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then if I set the weight to "abc", it remains unset
|
||||
|
||||
Scenario: User cannot type decimal values integer number field
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then if I set the max attempts to "2.34", it displays initially as "234", and is persisted as "234"
|
||||
|
||||
Scenario: User cannot type out of range values in an integer number field
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then if I set the max attempts to "-3", it displays initially as "-3", and is persisted as "1"
|
||||
|
||||
Scenario: Settings changes are not saved on Cancel
|
||||
Given I have created a Blank Common Problem
|
||||
And I edit and select Settings
|
||||
Then I can set the weight to "3.5"
|
||||
And I can modify the display name
|
||||
Then If I press Cancel my changes are not persisted
|
||||
|
||||
Scenario: Edit High Level source is available for LaTeX problem
|
||||
Given I have created a LaTeX Problem
|
||||
And I edit and select Settings
|
||||
Then Edit High Level Source is visible
|
||||
187
cms/djangoapps/contentstore/features/problem-editor.py
Normal file
187
cms/djangoapps/contentstore/features/problem-editor.py
Normal file
@@ -0,0 +1,187 @@
|
||||
# disable missing docstring
|
||||
#pylint: disable=C0111
|
||||
|
||||
from lettuce import world, step
|
||||
from nose.tools import assert_equal
|
||||
|
||||
DISPLAY_NAME = "Display Name"
|
||||
MAXIMUM_ATTEMPTS = "Maximum Attempts"
|
||||
PROBLEM_WEIGHT = "Problem Weight"
|
||||
RANDOMIZATION = 'Randomization'
|
||||
SHOW_ANSWER = "Show Answer"
|
||||
|
||||
|
||||
############### ACTIONS ####################
|
||||
@step('I have created a Blank Common Problem$')
|
||||
def i_created_blank_common_problem(step):
|
||||
world.create_component_instance(
|
||||
step,
|
||||
'.large-problem-icon',
|
||||
'i4x://edx/templates/problem/Blank_Common_Problem',
|
||||
'.xmodule_CapaModule'
|
||||
)
|
||||
|
||||
|
||||
@step('I edit and select Settings$')
|
||||
def i_edit_and_select_settings(step):
|
||||
world.edit_component_and_select_settings()
|
||||
|
||||
|
||||
@step('I see five alphabetized settings and their expected values$')
|
||||
def i_see_five_settings_with_values(step):
|
||||
world.verify_all_setting_entries(
|
||||
[
|
||||
[DISPLAY_NAME, "Blank Common Problem", True],
|
||||
[MAXIMUM_ATTEMPTS, "", False],
|
||||
[PROBLEM_WEIGHT, "", False],
|
||||
[RANDOMIZATION, "Never", True],
|
||||
[SHOW_ANSWER, "Finished", True]
|
||||
])
|
||||
|
||||
|
||||
@step('I can modify the display name')
|
||||
def i_can_modify_the_display_name(step):
|
||||
world.get_setting_entry(DISPLAY_NAME).find_by_css('.setting-input')[0].fill('modified')
|
||||
verify_modified_display_name()
|
||||
|
||||
|
||||
@step('my display name change is persisted on save')
|
||||
def my_display_name_change_is_persisted_on_save(step):
|
||||
world.save_component_and_reopen(step)
|
||||
verify_modified_display_name()
|
||||
|
||||
|
||||
@step('I can specify special characters in the display name')
|
||||
def i_can_modify_the_display_name_with_special_chars(step):
|
||||
world.get_setting_entry(DISPLAY_NAME).find_by_css('.setting-input')[0].fill("updated ' \" &")
|
||||
verify_modified_display_name_with_special_chars()
|
||||
|
||||
|
||||
@step('my special characters and persisted on save')
|
||||
def special_chars_persisted_on_save(step):
|
||||
world.save_component_and_reopen(step)
|
||||
verify_modified_display_name_with_special_chars()
|
||||
|
||||
|
||||
@step('I can revert the display name to unset')
|
||||
def can_revert_display_name_to_unset(step):
|
||||
world.revert_setting_entry(DISPLAY_NAME)
|
||||
verify_unset_display_name()
|
||||
|
||||
|
||||
@step('my display name is unset on save')
|
||||
def my_display_name_is_persisted_on_save(step):
|
||||
world.save_component_and_reopen(step)
|
||||
verify_unset_display_name()
|
||||
|
||||
|
||||
@step('I can select Per Student for Randomization')
|
||||
def i_can_select_per_student_for_randomization(step):
|
||||
world.browser.select(RANDOMIZATION, "Per Student")
|
||||
verify_modified_randomization()
|
||||
|
||||
|
||||
@step('my change to randomization is persisted')
|
||||
def my_change_to_randomization_is_persisted(step):
|
||||
world.save_component_and_reopen(step)
|
||||
verify_modified_randomization()
|
||||
|
||||
|
||||
@step('I can revert to the default value for randomization')
|
||||
def i_can_revert_to_default_for_randomization(step):
|
||||
world.revert_setting_entry(RANDOMIZATION)
|
||||
world.save_component_and_reopen(step)
|
||||
world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Always", False)
|
||||
|
||||
|
||||
@step('I can set the weight to "(.*)"?')
|
||||
def i_can_set_weight(step, weight):
|
||||
set_weight(weight)
|
||||
verify_modified_weight()
|
||||
|
||||
|
||||
@step('my change to weight is persisted')
|
||||
def my_change_to_weight_is_persisted(step):
|
||||
world.save_component_and_reopen(step)
|
||||
verify_modified_weight()
|
||||
|
||||
|
||||
@step('I can revert to the default value of unset for weight')
|
||||
def i_can_revert_to_default_for_unset_weight(step):
|
||||
world.revert_setting_entry(PROBLEM_WEIGHT)
|
||||
world.save_component_and_reopen(step)
|
||||
world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False)
|
||||
|
||||
|
||||
@step('if I set the weight to "(.*)", it remains unset')
|
||||
def set_the_weight_to_abc(step, bad_weight):
|
||||
set_weight(bad_weight)
|
||||
# We show the clear button immediately on type, hence the "True" here.
|
||||
world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", True)
|
||||
world.save_component_and_reopen(step)
|
||||
# But no change was actually ever sent to the model, so on reopen, explicitly_set is False
|
||||
world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "", False)
|
||||
|
||||
|
||||
@step('if I set the max attempts to "(.*)", it displays initially as "(.*)", and is persisted as "(.*)"')
|
||||
def set_the_max_attempts(step, max_attempts_set, max_attempts_displayed, max_attempts_persisted):
|
||||
world.get_setting_entry(MAXIMUM_ATTEMPTS).find_by_css('.setting-input')[0].fill(max_attempts_set)
|
||||
world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, max_attempts_displayed, True)
|
||||
world.save_component_and_reopen(step)
|
||||
world.verify_setting_entry(world.get_setting_entry(MAXIMUM_ATTEMPTS), MAXIMUM_ATTEMPTS, max_attempts_persisted, True)
|
||||
|
||||
|
||||
@step('Edit High Level Source is not visible')
|
||||
def edit_high_level_source_not_visible(step):
|
||||
verify_high_level_source(step, False)
|
||||
|
||||
|
||||
@step('Edit High Level Source is visible')
|
||||
def edit_high_level_source_visible(step):
|
||||
verify_high_level_source(step, True)
|
||||
|
||||
|
||||
@step('If I press Cancel my changes are not persisted')
|
||||
def cancel_does_not_save_changes(step):
|
||||
world.cancel_component(step)
|
||||
step.given("I edit and select Settings")
|
||||
step.given("I see five alphabetized settings and their expected values")
|
||||
|
||||
|
||||
@step('I have created a LaTeX Problem')
|
||||
def create_latex_problem(step):
|
||||
world.click_new_component_button(step, '.large-problem-icon')
|
||||
# Go to advanced tab (waiting for the tab to be visible)
|
||||
world.css_find('#ui-id-2')
|
||||
world.css_click('#ui-id-2')
|
||||
world.click_component_from_menu("i4x://edx/templates/problem/Problem_Written_in_LaTeX", '.xmodule_CapaModule')
|
||||
|
||||
|
||||
def verify_high_level_source(step, visible):
|
||||
assert_equal(visible, world.is_css_present('.launch-latex-compiler'))
|
||||
world.cancel_component(step)
|
||||
assert_equal(visible, world.is_css_present('.upload-button'))
|
||||
|
||||
|
||||
def verify_modified_weight():
|
||||
world.verify_setting_entry(world.get_setting_entry(PROBLEM_WEIGHT), PROBLEM_WEIGHT, "3.5", True)
|
||||
|
||||
|
||||
def verify_modified_randomization():
|
||||
world.verify_setting_entry(world.get_setting_entry(RANDOMIZATION), RANDOMIZATION, "Per Student", True)
|
||||
|
||||
|
||||
def verify_modified_display_name():
|
||||
world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, 'modified', True)
|
||||
|
||||
|
||||
def verify_modified_display_name_with_special_chars():
|
||||
world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, "updated ' \" &", True)
|
||||
|
||||
|
||||
def verify_unset_display_name():
|
||||
world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, '', False)
|
||||
|
||||
|
||||
def set_weight(weight):
|
||||
world.get_setting_entry(PROBLEM_WEIGHT).find_by_css('.setting-input')[0].fill(weight)
|
||||
@@ -10,9 +10,7 @@ from nose.tools import assert_equal
|
||||
|
||||
@step('I have opened a new course section in Studio$')
|
||||
def i_have_opened_a_new_course_section(step):
|
||||
world.clear_courses()
|
||||
log_into_studio()
|
||||
create_a_course()
|
||||
open_new_course()
|
||||
add_section()
|
||||
|
||||
|
||||
|
||||
13
cms/djangoapps/contentstore/features/video-editor.feature
Normal file
13
cms/djangoapps/contentstore/features/video-editor.feature
Normal file
@@ -0,0 +1,13 @@
|
||||
Feature: Video Component Editor
|
||||
As a course author, I want to be able to create video components.
|
||||
|
||||
Scenario: User can view metadata
|
||||
Given I have created a Video component
|
||||
And I edit and select Settings
|
||||
Then I see only the Video display name setting
|
||||
|
||||
Scenario: User can modify display name
|
||||
Given I have created a Video component
|
||||
And I edit and select Settings
|
||||
Then I can modify the display name
|
||||
And my display name change is persisted on save
|
||||
9
cms/djangoapps/contentstore/features/video-editor.py
Normal file
9
cms/djangoapps/contentstore/features/video-editor.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# disable missing docstring
|
||||
#pylint: disable=C0111
|
||||
|
||||
from lettuce import world, step
|
||||
|
||||
|
||||
@step('I see only the video display name setting$')
|
||||
def i_see_only_the_video_display_name(step):
|
||||
world.verify_all_setting_entries([['Display Name', "default", True]])
|
||||
@@ -34,6 +34,8 @@ from xmodule.course_module import CourseDescriptor
|
||||
from xmodule.seq_module import SequenceDescriptor
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
|
||||
from contentstore.views.component import ADVANCED_COMPONENT_TYPES
|
||||
|
||||
from django_comment_common.utils import are_permissions_roles_seeded
|
||||
|
||||
TEST_DATA_MODULESTORE = copy.deepcopy(settings.MODULESTORE)
|
||||
@@ -75,6 +77,31 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
self.client = Client()
|
||||
self.client.login(username=uname, password=password)
|
||||
|
||||
def test_advanced_components_in_edit_unit(self):
|
||||
store = modulestore('direct')
|
||||
import_from_xml(store, 'common/test/data/', ['simple'])
|
||||
|
||||
course = store.get_item(Location(['i4x', 'edX', 'simple',
|
||||
'course', '2012_Fall', None]), depth=None)
|
||||
|
||||
course.advanced_modules = ADVANCED_COMPONENT_TYPES
|
||||
|
||||
store.update_metadata(course.location, own_metadata(course))
|
||||
|
||||
# just pick one vertical
|
||||
descriptor = store.get_items(Location('i4x', 'edX', 'simple', 'vertical', None, None))[0]
|
||||
|
||||
resp = self.client.get(reverse('edit_unit', kwargs={'location': descriptor.location.url()}))
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
# This could be made better, but for now let's just assert that we see the advanced modules mentioned in the page
|
||||
# response HTML
|
||||
self.assertIn('Video Alpha', resp.content)
|
||||
self.assertIn('Word cloud', resp.content)
|
||||
self.assertIn('Annotation', resp.content)
|
||||
self.assertIn('Open Ended Response', resp.content)
|
||||
self.assertIn('Peer Grading Interface', resp.content)
|
||||
|
||||
def check_edit_unit(self, test_course_name):
|
||||
import_from_xml(modulestore('direct'), 'common/test/data/', [test_course_name])
|
||||
|
||||
|
||||
@@ -111,6 +111,18 @@ class AuthTestCase(ContentStoreTestCase):
|
||||
# Now login should work
|
||||
self.login(self.email, self.pw)
|
||||
|
||||
def test_login_link_on_activation_age(self):
|
||||
self.create_account(self.username, self.email, self.pw)
|
||||
# we want to test the rendering of the activation page when the user isn't logged in
|
||||
self.client.logout()
|
||||
resp = self._activate_user(self.email)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
# check the the HTML has links to the right login page. Note that this is merely a content
|
||||
# check and thus could be fragile should the wording change on this page
|
||||
expected = 'You can now <a href="' + reverse('login') + '">login</a>.'
|
||||
self.assertIn(expected, resp.content)
|
||||
|
||||
def test_private_pages_auth(self):
|
||||
"""Make sure pages that do require login work."""
|
||||
auth_pages = (
|
||||
|
||||
@@ -42,7 +42,7 @@ COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video']
|
||||
|
||||
OPEN_ENDED_COMPONENT_TYPES = ["combinedopenended", "peergrading"]
|
||||
NOTE_COMPONENT_TYPES = ['notes']
|
||||
ADVANCED_COMPONENT_TYPES = ['annotatable', 'word_cloud'] + OPEN_ENDED_COMPONENT_TYPES + NOTE_COMPONENT_TYPES
|
||||
ADVANCED_COMPONENT_TYPES = ['annotatable', 'word_cloud', 'videoalpha'] + OPEN_ENDED_COMPONENT_TYPES + NOTE_COMPONENT_TYPES
|
||||
ADVANCED_COMPONENT_CATEGORY = 'advanced'
|
||||
ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules'
|
||||
|
||||
@@ -149,8 +149,7 @@ def edit_unit(request, location):
|
||||
component_templates[category].append((
|
||||
template.display_name_with_default,
|
||||
template.location.url(),
|
||||
hasattr(template, 'markdown') and template.markdown is not None,
|
||||
template.cms.empty,
|
||||
hasattr(template, 'markdown') and template.markdown is not None
|
||||
))
|
||||
|
||||
components = [
|
||||
|
||||
Reference in New Issue
Block a user