diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index f3875d5dbc..5dd72b3767 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -168,6 +168,18 @@ def log_into_studio( assert_in(uname, world.css_text('h2.title', timeout=10)) +def add_course_author(user, course): + """ + Add the user to the instructor group of the course + so they will have the permissions to see it in studio + """ + for role in ("staff", "instructor"): + groupname = get_course_groupname_for_role(course.location, role) + group, __ = Group.objects.get_or_create(name=groupname) + user.groups.add(group) + user.save() + + def create_a_course(): course = world.CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course') world.scenario_dict['COURSE'] = course @@ -176,13 +188,7 @@ def create_a_course(): if not user: user = get_user_by_email('robot+studio@edx.org') - # Add the user to the instructor group of the course - # so they will have the permissions to see it in studio - for role in ("staff", "instructor"): - groupname = get_course_groupname_for_role(course.location, role) - group, __ = Group.objects.get_or_create(name=groupname) - user.groups.add(group) - user.save() + add_course_author(user, course) # Navigate to the studio dashboard world.visit('/') diff --git a/cms/djangoapps/contentstore/features/component.feature b/cms/djangoapps/contentstore/features/component.feature index 0754049df0..3877cccc55 100644 --- a/cms/djangoapps/contentstore/features/component.feature +++ b/cms/djangoapps/contentstore/features/component.feature @@ -2,85 +2,87 @@ Feature: CMS.Component Adding As a course author, I want to be able to add a wide variety of components - #Scenario: I can add components - # Given I have opened a new course in studio - # And I am editing a new unit - # When I add the following components: - # | Component | - # | Discussion | - # | Blank HTML | - # | LaTex | - # | Blank Problem| - # | Dropdown | - # | Multi Choice | - # | Numerical | - # | Text Input | - # | Advanced | - # | Circuit | - # | Custom Python| - # | Image Mapped | - # | Math Input | - # | Problem LaTex| - # | Adaptive Hint| - # | Video | - # Then I see the following components: - # | Component | - # | Discussion | - # | Blank HTML | - # | LaTex | - # | Blank Problem| - # | Dropdown | - # | Multi Choice | - # | Numerical | - # | Text Input | - # | Advanced | - # | Circuit | - # | Custom Python| - # | Image Mapped | - # | Math Input | - # | Problem LaTex| - # | Adaptive Hint| - # | Video | + Scenario: I can add single step components + Given I am in Studio editing a new unit + When I add this type of single step component: + | Component | + | Discussion | + | Video | + Then I see this type of single step component: + | Component | + | Discussion | + | Video | - #Scenario: I can delete Components - # Given I have opened a new course in studio - # And I am editing a new unit - # And I add the following components: - # | Component | - # | Discussion | - # | Blank HTML | - # | LaTex | - # | Blank Problem| - # | Dropdown | - # | Multi Choice | - # | Numerical | - # | Text Input | - # | Advanced | - # | Circuit | - # | Custom Python| - # | Image Mapped | - # | Math Input | - # | Problem LaTex| - # | Adaptive Hint| - # | Video | - # When I will confirm all alerts - # And I delete all components - # Then I see no components + Scenario: I can add HTML components + Given I am in Studio editing a new unit + When I add this type of HTML component: + | Component | + | Text | + | Announcement | + | E-text Written in LaTeX | + Then I see HTML components in this order: + | Component | + | Text | + | Announcement | + | E-text Written in LaTeX | + + Scenario: I can add Common Problem components + Given I am in Studio editing a new unit + When I add this type of Problem component: + | Component | + | Blank Common Problem | + | Dropdown | + | Multiple Choice | + | Numerical Input | + | Text Input | + Then I see Problem components in this order: + | Component | + | Blank Common Problem | + | Dropdown | + | Multiple Choice | + | Numerical Input | + | Text Input | + + Scenario: I can add Advanced Problem components + Given I am in Studio editing a new unit + When I add this type of Advanced Problem component: + | Component | + | Blank Advanced Problem | + | Circuit Schematic Builder | + | Custom Python-Evaluated Input | + | Drag and Drop | + | Image Mapped Input | + | Math Expression Input | + | Problem Written in LaTeX | + | Problem with Adaptive Hint | + Then I see Problem components in this order: + | Component | + | Blank Advanced Problem | + | Circuit Schematic Builder | + | Custom Python-Evaluated Input | + | Drag and Drop | + | Image Mapped Input | + | Math Expression Input | + | Problem Written in LaTeX | + | Problem with Adaptive Hint | Scenario: I see a prompt on delete - Given I have opened a new course in studio - And I am editing a new unit - And I add the following components: - | Component | - | Discussion | - And I delete a component - Then I am shown a prompt + Given I am in Studio editing a new unit + And I add a "Discussion" "single step" component + And I delete a component + Then I am shown a prompt - Scenario: I see a notification on save - Given I have opened a new course in studio - And I am editing a new unit - And I add the following components: - | Component | - | Discussion | + Scenario: I can delete Components + Given I am in Studio editing a new unit + And I add a "Discussion" "single step" component + And I add a "Text" "HTML" component + And I add a "Blank Common Problem" "Problem" component + And I add a "Blank Advanced Problem" "Advanced Problem" component + And I delete all components + Then I see no components + + Scenario: I see a notification on save + Given I am in Studio editing a new unit + And I add a "Discussion" "single step" component And I edit and save a component Then I am shown a notification diff --git a/cms/djangoapps/contentstore/features/component.py b/cms/djangoapps/contentstore/features/component.py index d0c1fd59e7..4fd7b01d9d 100644 --- a/cms/djangoapps/contentstore/features/component.py +++ b/cms/djangoapps/contentstore/features/component.py @@ -2,38 +2,135 @@ #pylint: disable=W0621 from lettuce import world, step -from nose.tools import assert_true # pylint: disable=E0611 - -DATA_LOCATION = 'i4x://edx/templates' +from nose.tools import assert_true, assert_in, assert_equal # pylint: disable=E0611 +from common import create_studio_user, add_course_author, log_into_studio -@step(u'I am editing a new unit') +@step(u'I am in Studio editing a new unit$') def add_unit(step): - css_selectors = ['a.new-courseware-section-button', 'input.new-section-name-save', 'a.new-subsection-item', - 'input.new-subsection-name-save', 'div.section-item a.expand-collapse-icon', 'a.new-unit-item'] + world.clear_courses() + course = world.CourseFactory.create() + section = world.ItemFactory.create(parent_location=course.location) + world.ItemFactory.create( + parent_location=section.location, + category='sequential', + display_name='Subsection One',) + user = create_studio_user(is_staff=False) + add_course_author(user, course) + log_into_studio() + css_selectors = ['a.course-link', 'div.section-item a.expand-collapse-icon', 'a.new-unit-item'] for selector in css_selectors: world.css_click(selector) -@step(u'I add the following components:') -def add_components(step): - for component in [step_hash['Component'] for step_hash in step.hashes]: - assert component in COMPONENT_DICTIONARY - for css in COMPONENT_DICTIONARY[component]['steps']: - world.css_click(css) +@step(u'I add this type of single step component:$') +def add_a_single_step_component(step): + for step_hash in step.hashes: + component = step_hash['Component'] + assert_in(component, ['Discussion', 'Video']) + css_selector = 'a[data-type="{}"]'.format(component.lower()) + world.css_click(css_selector) -@step(u'I see the following components') -def check_components(step): - for component in [step_hash['Component'] for step_hash in step.hashes]: - assert component in COMPONENT_DICTIONARY - assert_true(COMPONENT_DICTIONARY[component]['found_func'](), "{} couldn't be found".format(component)) +@step(u'I see this type of single step component:$') +def see_a_single_step_component(step): + for step_hash in step.hashes: + component = step_hash['Component'] + assert_in(component, ['Discussion', 'Video']) + component_css = 'section.xmodule_{}Module'.format(component) + assert_true(world.is_css_present(component_css), + "{} couldn't be found".format(component)) -@step(u'I delete all components') +@step(u'I add this type of( Advanced)? (HTML|Problem) component:$') +def add_a_multi_step_component(step, is_advanced, category): + def click_advanced(): + css = 'ul.problem-type-tabs a[href="#tab2"]' + world.css_click(css) + my_css = 'ul.problem-type-tabs li.ui-state-active a[href="#tab2"]' + assert(world.css_find(my_css)) + + def find_matching_link(): + """ + Find the link with the specified text. There should be one and only one. + """ + # The tab shows links for the given category + links = world.css_find('div.new-component-{} a'.format(category)) + + # Find the link whose text matches what you're looking for + matched_links = [link for link in links if link.text == step_hash['Component']] + + # There should be one and only one + assert_equal(len(matched_links), 1) + return matched_links[0] + + def click_link(): + link.click() + + category = category.lower() + for step_hash in step.hashes: + css_selector = 'a[data-type="{}"]'.format(category) + world.css_click(css_selector) + world.wait_for_invisible(css_selector) + + if is_advanced: + # Sometimes this click does not work if you go too fast. + world.retry_on_exception(click_advanced, max_attempts=5, ignored_exceptions=AssertionError) + + # Retry this in case the list is empty because you tried too fast. + link = world.retry_on_exception(func=find_matching_link, ignored_exceptions=AssertionError) + + # Wait for the link to be clickable. If you go too fast it is not. + world.retry_on_exception(click_link) + + +@step(u'I see (HTML|Problem) components in this order:') +def see_a_multi_step_component(step, category): + components = world.css_find('li.component section.xmodule_display') + for idx, step_hash in enumerate(step.hashes): + if category == 'HTML': + html_matcher = { + 'Text': + '\n \n', + 'Announcement': + '
Words of encouragement! This is a short note that most students will read.
', + 'E-text Written in LaTeX': + '