From 271de4fbc931d686885559fedfbfa72ea96f24a7 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Mon, 10 Jun 2013 14:38:52 -0400 Subject: [PATCH 01/19] Added in testing for course team Also modified the create_course method in common to allow for the possibility to access the course name and etc. from other files. --- .../contentstore/features/common.py | 14 ++-- .../contentstore/features/course-team.feature | 34 ++++++++ .../contentstore/features/course-team.py | 77 +++++++++++++++++++ 3 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 cms/djangoapps/contentstore/features/course-team.feature create mode 100644 cms/djangoapps/contentstore/features/course-team.py diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index 494192ad06..a80fbcbb8f 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -13,6 +13,10 @@ import time from logging import getLogger logger = getLogger(__name__) +COURSE_NAME = 'Robot Super Course' +COURSE_NUM = '999' +COURSE_ORG = 'MITx' + ########### STEP HELPERS ############## @step('I (?:visit|access|open) the Studio homepage$') @@ -75,9 +79,9 @@ def create_studio_user( def fill_in_course_info( - name='Robot Super Course', - org='MITx', - num='101'): + name=COURSE_NAME, + org=COURSE_ORG, + num=COURSE_NUM): world.css_fill('.new-course-name', name) world.css_fill('.new-course-org', org) world.css_fill('.new-course-number', num) @@ -107,11 +111,11 @@ def log_into_studio( def create_a_course(): - c = world.CourseFactory.create(org='MITx', course='999', display_name='Robot Super Course') + c = world.CourseFactory.create(org=COURSE_ORG, course=COURSE_NUM, display_name=COURSE_NAME) # Add the user to the instructor group of the course # so they will have the permissions to see it in studio - g = world.GroupFactory.create(name='instructor_MITx/999/Robot_Super_Course') + g = world.GroupFactory.create(name='instructor_MITx/%s/%s' % (COURSE_NUM, COURSE_NAME.replace(" ", "_"),)) u = get_user_by_email('robot+studio@edx.org') u.groups.add(g) u.save() diff --git a/cms/djangoapps/contentstore/features/course-team.feature b/cms/djangoapps/contentstore/features/course-team.feature new file mode 100644 index 0000000000..502321c49b --- /dev/null +++ b/cms/djangoapps/contentstore/features/course-team.feature @@ -0,0 +1,34 @@ +Feature: Course Team + As a course author, I want to be able to add others to my team + + Scenario: Users can add other users + Given I have opened a new course in Studio + And The user "abcd" exists + And I am viewing the course team settings + When I add "abcd" to the course team + And "abcd" logs in + Then He does see the course on his page + + Scenario: Added users cannot delete or add other users + Given I have opened a new course in Studio + And The user "abcd" exists + And I am viewing the course team settings + When I add "abcd" to the course team + And "abcd" logs in + Then He cannot delete users + And He cannot add users + + Scenario: Users can delete other users + Given I have opened a new course in Studio + And The user "abcd" exists + And I am viewing the course team settings + When I add "abcd" to the course team + And I delete "abcd" from the course team + And "abcd" logs in + Then He does not see the course on his page + + Scenario: Users cannot add users that do not exist + Given I have opened a new course in Studio + And I am viewing the course team settings + When I add "abcd" to the course team + Then I should see "Could not find user by email address" somewhere on the page diff --git a/cms/djangoapps/contentstore/features/course-team.py b/cms/djangoapps/contentstore/features/course-team.py new file mode 100644 index 0000000000..7bf538157d --- /dev/null +++ b/cms/djangoapps/contentstore/features/course-team.py @@ -0,0 +1,77 @@ +#pylint: disable=C0111 +#pylint: disable=W0621 + +from lettuce import world, step +from common import create_studio_user, COURSE_NAME + +PASS = 'test' +EXTENSION = '@edx.org' + + +@step(u'I am viewing the course team settings') +def view_grading_settings(step): + world.click_course_settings() + link_css = 'li.nav-course-settings-team a' + world.css_click(link_css) + + +@step(u'The user "([^"]*)" exists$') +def create_other_user(step, name): + create_studio_user(uname=name, password=PASS, email=(name + EXTENSION)) + + +@step(u'I add "([^"]*)" to the course team') +def add_other_user(step, name): + new_user_css = '.new-user-button' + world.css_find(new_user_css).click() + + email_css = '.email-input' + f = world.css_find(email_css) + f._element.send_keys(name, EXTENSION) + + confirm_css = '#add_user' + world.css_find(confirm_css).click() + + +@step(u'I delete "([^"]*)" from the course team') +def delete_other_user(step, name): + to_delete_css = '.remove-user[data-id="%s%s"]' % (name, EXTENSION,) + world.css_find(to_delete_css).click() + + +@step(u'"([^"]*)" logs in$') +def other_user_login(step, name): + world.browser.cookies.delete() + world.visit('/') + + signin_css = 'a.action-signin' + world.is_css_present(signin_css) + world.css_click(signin_css) + + login_form = world.browser.find_by_css('form#login_form') + login_form.find_by_name('email').fill(name + EXTENSION) + login_form.find_by_name('password').fill(PASS) + login_form.find_by_name('submit').click() + + +@step(u'He does( not)? see the course on his page') +def see_course(step, doesnt): + class_css = '.class-name' + all_courses = world.css_find(class_css) + all_names = [item.html for item in all_courses] + if doesnt: + assert not COURSE_NAME in all_names + else: + assert COURSE_NAME in all_names + + +@step(u'He cannot delete users') +def cannot_delete(step): + to_delete_css = '.remove-user' + assert world.is_css_not_present(to_delete_css) + + +@step(u'He cannot add users') +def cannot_add(step): + add_css = '.new-user' + assert world.is_css_not_present(add_css) From b7674679d9782fdbb4686857a6f32dffd2f69efe Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Tue, 11 Jun 2013 09:18:16 -0400 Subject: [PATCH 02/19] Tests now use their own contentstore database test.py was changed such that the database being used is test_xcontent course_helpers.clear_courses now calls contentstore().fs_files.drop() such that all content is removed and the databse is started afresh in the same manner as the modulestore --- cms/envs/test.py | 2 +- common/djangoapps/terrain/course_helpers.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cms/envs/test.py b/cms/envs/test.py index 8a3f9ba158..667b9f3555 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -70,7 +70,7 @@ CONTENTSTORE = { 'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore', 'OPTIONS': { 'host': 'localhost', - 'db': 'xcontent', + 'db': 'test_xcontent', } } diff --git a/common/djangoapps/terrain/course_helpers.py b/common/djangoapps/terrain/course_helpers.py index cc1f770217..23f932bc3b 100644 --- a/common/djangoapps/terrain/course_helpers.py +++ b/common/djangoapps/terrain/course_helpers.py @@ -11,6 +11,7 @@ from django.contrib.auth.middleware import AuthenticationMiddleware from django.contrib.sessions.middleware import SessionMiddleware from student.models import CourseEnrollment from xmodule.modulestore.django import _MODULESTORES, modulestore +from xmodule.contentstore.django import contentstore from xmodule.templates import update_templates from bs4 import BeautifulSoup import os.path @@ -133,3 +134,4 @@ def clear_courses(): _MODULESTORES = {} modulestore().collection.drop() update_templates(modulestore('direct')) + contentstore().fs_files.drop() From d471bcb72316a51d7761318a0c8211f87dcac90f Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Tue, 11 Jun 2013 10:00:52 -0400 Subject: [PATCH 03/19] Added tests for uploading files TODO: When deleting is available, test deleting uploaded files` --- .../contentstore/features/upload.feature | 15 +++++ .../contentstore/features/upload.py | 57 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 cms/djangoapps/contentstore/features/upload.feature create mode 100644 cms/djangoapps/contentstore/features/upload.py diff --git a/cms/djangoapps/contentstore/features/upload.feature b/cms/djangoapps/contentstore/features/upload.feature new file mode 100644 index 0000000000..5717cfb907 --- /dev/null +++ b/cms/djangoapps/contentstore/features/upload.feature @@ -0,0 +1,15 @@ +Feature: Upload Files + As a course author, I want to be able to upload files for my students + + Scenario: Users can upload files + Given I have opened a new course in Studio + And I go to the files and uploads page + When I upload the file "upload.feature" + Then I see the file "upload.feature" was uploaded + + Scenario: Users can update files + Given I have opened a new course in studio + And I go to the files and uploads page + When I upload the file "upload.feature" + And I upload the file "upload.feature" + Then I see only one "upload.feature" diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py new file mode 100644 index 0000000000..6c53741dcc --- /dev/null +++ b/cms/djangoapps/contentstore/features/upload.py @@ -0,0 +1,57 @@ +#pylint: disable=C0111 +#pylint: disable=W0621 + +from lettuce import world, step +import os + + +@step(u'I go to the files and uploads page') +def go_to_uploads(step): + menu_css = 'li.nav-course-courseware' + uploads_css = '.nav-course-courseware-uploads' + world.css_find(menu_css).click() + world.css_find(uploads_css).click() + + +@step(u'I upload the file "([^"]*)"$') +def upload_file(step, file_name): + upload_css = '.upload-button' + world.css_find(upload_css).click() + + file_css = '.file-input' + upload = world.css_find(file_css) + upload._element.send_keys(os.getcwd() + '/' + file_name) + + close_css = '.close-button' + world.css_find(close_css).click() + + +@step(u'I see the file "([^"]*)" was uploaded') +def check_upload(step, file_name): + index = get_index(file_name) + assert index != -1 + + +@step(u'I see only one "([^"]*)"$') +def no_duplicate(step, file_name): + names_css = '.name-col > a.filename' + all_names = world.css_find(names_css) + only_one = False + for i in range(len(all_names)): + if file_name == all_names[i].html: + only_one = not only_one + assert only_one + + +@step(u'I PAUSE') +def pause(step): + from pdb import set_trace; set_trace() + + +def get_index(file_name): + names_css = '.name-col > a.filename' + all_names = world.css_find(names_css) + for i in range(len(all_names)): + if file_name == all_names[i].html: + return i + return -1 From c74d76a36b1609e0a2c7b5ed16718fb9cc66d477 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Tue, 11 Jun 2013 10:07:13 -0400 Subject: [PATCH 04/19] Removed test breakpoint --- cms/djangoapps/contentstore/features/upload.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index 6c53741dcc..d7a6eba999 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -43,11 +43,6 @@ def no_duplicate(step, file_name): assert only_one -@step(u'I PAUSE') -def pause(step): - from pdb import set_trace; set_trace() - - def get_index(file_name): names_css = '.name-col > a.filename' all_names = world.css_find(names_css) From 81b06d5c702cf280368a7f6bc0fd7fcaf15ba3d8 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Tue, 11 Jun 2013 14:34:18 -0400 Subject: [PATCH 05/19] Added tests for static pages Also added in drag and drop helper. Please be careful using this method as it is truly a hack to obtain the visual effects. As of this commit, selenium does not support drag and drop for jQuery sortable items --- .../features/static-pages.feature | 34 +++++++++ .../contentstore/features/static-pages.py | 76 +++++++++++++++++++ common/djangoapps/terrain/ui_helpers.py | 12 +++ 3 files changed, 122 insertions(+) create mode 100644 cms/djangoapps/contentstore/features/static-pages.feature create mode 100644 cms/djangoapps/contentstore/features/static-pages.py diff --git a/cms/djangoapps/contentstore/features/static-pages.feature b/cms/djangoapps/contentstore/features/static-pages.feature new file mode 100644 index 0000000000..99a4e802dd --- /dev/null +++ b/cms/djangoapps/contentstore/features/static-pages.feature @@ -0,0 +1,34 @@ +Feature: Static Pages + As a course author, I want to be able to add static pages + + Scenario: Users can add static pages + Given I have opened a new course in Studio + And I go to the static pages page + When I add a new page + Then I should see a "Empty" static page + + Scenario: Users can delete static pages + Given I have opened a new course in Studio + And I go to the static pages page + And I add a new page + When I will confirm all alerts + And I "delete" the "Empty" page + Then I should not see a "Empty" static page + + Scenario: Users can edit static pages + Given I have opened a new course in Studio + And I go to the static pages page + And I add a new page + When I "edit" the "Empty" page + And I change the name to "New" + Then I should see a "New" static page + + Scenario: Users can reorder static pages + Given I have opened a new course in Studio + And I go to the static pages page + And I add a new page + And I "edit" the "Empty" page + And I change the name to "New" + And I add a new page + When I move "New" after "Empty" + Then I see the order is "Empty New" diff --git a/cms/djangoapps/contentstore/features/static-pages.py b/cms/djangoapps/contentstore/features/static-pages.py new file mode 100644 index 0000000000..2a1110de40 --- /dev/null +++ b/cms/djangoapps/contentstore/features/static-pages.py @@ -0,0 +1,76 @@ +#pylint: disable=C0111 +#pylint: disable=W0621 + +from lettuce import world, step +from selenium.webdriver.common.keys import Keys + + +@step(u'I go to the static pages page') +def go_to_uploads(step): + menu_css = 'li.nav-course-courseware' + uploads_css = '.nav-course-courseware-pages' + world.css_find(menu_css).click() + world.css_find(uploads_css).click() + + +@step(u'I add a new page') +def add_page(step): + button_css = '.new-button' + world.css_find(button_css).click() + + +@step(u'I should( not)? see a "([^"]*)" static page$') +def see_page(step, doesnt, page): + index = get_index(page) + if doesnt: + assert index == -1 + else: + assert index != -1 + + +@step(u'I "([^"]*)" the "([^"]*)" page$') +def click_edit_delete(step, edit_delete, page): + button_css = '.%s-button' % edit_delete + index = get_index(page) + assert index != -1 + world.css_find(button_css)[index].click() + + +@step(u'I change the name to "([^"]*)"$') +def change_name(step, new_name): + settings_css = '#settings-mode' + world.css_find(settings_css).click() + input_css = '.setting-input' + name_input = world.css_find(input_css) + old_name = name_input.value + for count in range(len(old_name)): + name_input._element.send_keys(Keys.END, Keys.BACK_SPACE) + name_input._element.send_keys(new_name) + save_button = '.save-button' + world.css_find(save_button).click() + + +@step(u'I move "([^"]*)" after "([^"]*)"$') +def change_list(step, item1, item2): + index1 = get_index(item1) + index2 = get_index(item2) + assert index1 != -1 and index2 != -1 + world.drag_sortable_after(".component", index1, index2, ".ui-sortable") + + +@step(u'I see the order is "([^"]*)"$') +def check_order(step, items): + items = items.split(' ') + name_css = 'section[data-type="HTMLModule"]' + all_elements = world.css_find(name_css) + for i in range(len(items)): + assert all_elements[i].html == '\n %s\n' % items[i] + + +def get_index(name): + page_name_css = 'section[data-type="HTMLModule"]' + all_pages = world.css_find(page_name_css) + for i in range(len(all_pages)): + if all_pages[i].html == '\n %s\n' % name: + return i + return -1 diff --git a/common/djangoapps/terrain/ui_helpers.py b/common/djangoapps/terrain/ui_helpers.py index ecd43eb719..c59d7030ff 100644 --- a/common/djangoapps/terrain/ui_helpers.py +++ b/common/djangoapps/terrain/ui_helpers.py @@ -158,3 +158,15 @@ def click_tools(): tools_css = 'li.nav-course-tools' if world.browser.is_element_present_by_css(tools_css): world.css_click(tools_css) + + +@world.absorb +def drag_sortable_after(item_css, index1, index2, sortable_css): + """ + This is a hack in order to simulate jQuery's sortable list dragging as Selenium cannot currently do it with action_chains + Please note that this is very finnicky with keeping the changes after refreshing the page so be careful when testing persistant changes + Also note that this is mainly for visualization of the sortable list and the true sortable drag and drop fires many events + """ + world.browser.execute_script('$("%(item_css)s:eq(%(index_one)s)").insertAfter($("%(item_css)s:eq(%(index_two)s)"));\ + $("%(sortable_css)s").trigger("sortupdate")' % + {'item_css': item_css, 'index_one': index1, 'index_two': index2, 'sortable_css': sortable_css}) From eb14a07814db2b4ca9f2a240afc9ceac019141b1 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Tue, 11 Jun 2013 16:15:49 -0400 Subject: [PATCH 06/19] Added tests for course updates and handouts --- .../features/course-updates.feature | 44 ++++++++++ .../contentstore/features/course-updates.py | 82 +++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 cms/djangoapps/contentstore/features/course-updates.feature create mode 100644 cms/djangoapps/contentstore/features/course-updates.py diff --git a/cms/djangoapps/contentstore/features/course-updates.feature b/cms/djangoapps/contentstore/features/course-updates.feature new file mode 100644 index 0000000000..7266e4f9a6 --- /dev/null +++ b/cms/djangoapps/contentstore/features/course-updates.feature @@ -0,0 +1,44 @@ +Feature: Course updates + As a course author, I want to be able to provide updates to my students + + Scenario: Users can add updates + Given I have opened a new course in Studio + And I go to the course updates page + When I add a new update + And I make the text "Hello" + Then I should see the update "Hello" + + Scenario: Users can edit updates + Given I have opened a new course in Studio + And I go to the course updates page + When I add a new update + And I make the text "Hello" + And I click the "edit" button + And I make the text "Goodbye" + Then I should see the update "Goodbye" + + Scenario: Users can delete updates + Given I have opened a new course in Studio + And I go to the course updates page + And I add a new update + And I make the text "Hello" + When I will confirm all alerts + And I click the "delete" button + Then I should not see the update "Hello" + + + Scenario: Users can edit update dates + Given I have opened a new course in Studio + And I go to the course updates page + And I add a new update + And I make the text "Hello" + When I click the "edit" button + And I make the date "June 1, 2013" + Then I should see the date "June 1, 2013" + + Scenario: Users can change handouts + Given I have opened a new course in Studio + And I go to the course updates page + When I edit the handouts + And I make the text "
    Test
" + Then I see the handout "Test" diff --git a/cms/djangoapps/contentstore/features/course-updates.py b/cms/djangoapps/contentstore/features/course-updates.py new file mode 100644 index 0000000000..4d0b5ef174 --- /dev/null +++ b/cms/djangoapps/contentstore/features/course-updates.py @@ -0,0 +1,82 @@ +#pylint: disable=C0111 +#pylint: disable=W0621 + +from lettuce import world, step +from selenium.webdriver.common.keys import Keys + + +@step(u'I go to the course updates page') +def go_to_uploads(step): + menu_css = 'li.nav-course-courseware' + uploads_css = '.nav-course-courseware-updates' + world.css_find(menu_css).click() + world.css_find(uploads_css).click() + + +@step(u'I add a new update') +def add_update(step): + update_css = '.new-update-button' + world.css_find(update_css).click() + + +@step(u'I make the text "([^"]*)"$') +def change_text(step, text): + text_css = 'div.CodeMirror > div > textarea' + prev_css = 'div.CodeMirror-lines > div > div:last-child > pre' + all_lines = world.css_find(prev_css) + all_text = '' + for i in range(len(all_lines)): + all_text = all_lines[i].html + text_area = world.css_find(text_css) + for i in range(len(all_text)): + text_area._element.send_keys(Keys.END, Keys.BACK_SPACE) + text_area._element.send_keys(text) + save_css = '.save-button' + world.css_find(save_css).click() + + +@step(u'I should( not)? see the update "([^"]*)"$') +def check_update(step, doesnt, text): + update_css = '.update-contents' + update = world.css_find(update_css) + if doesnt: + assert len(update) == 0 or not text in update.html + else: + assert text in update.html + + +@step(u'I click the "([^"]*)" button$') +def click_button(step, edit_delete): + button_css = '.post-preview .%s-button' % edit_delete + world.css_find(button_css).click() + + +@step(u'I make the date "([^"]*)"$') +def change_date(step, new_date): + date_css = 'input.date' + date = world.css_find(date_css) + for i in range(len(date.value)): + date._element.send_keys(Keys.END, Keys.BACK_SPACE) + date._element.send_keys(new_date) + save_css = '.save-button' + world.css_find(save_css).click() + + +@step(u'I should see the date "([^"]*)"$') +def check_date(step, date): + date_css = '.date-display' + date_html = world.css_find(date_css) + assert date == date_html.html + + +@step(u'I edit the handouts') +def edit_handouts(step): + edit_css = '.course-handouts > .edit-button' + world.css_find(edit_css).click() + + +@step(u'I see the handout "([^"]*)"$') +def check_handout(step, handout): + handout_css = '.handouts-content' + handouts = world.css_find(handout_css) + assert handout in handouts.html From 1b5050c9ac3abab8aaf49223199ebac55de03831 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Wed, 12 Jun 2013 13:25:33 -0400 Subject: [PATCH 07/19] Changed the following: % to format css_find().click() to its actual function A few name changes as well as step changes Removal of drag testing A refactoring of login and create user. Login will no longer create the specified user --- .../contentstore/features/common.py | 6 +- .../contentstore/features/course-team.py | 38 ++---- .../features/course-updates.feature | 23 ++-- .../contentstore/features/course-updates.py | 120 ++++++++++-------- .../contentstore/features/courses.py | 1 + .../features/static-pages.feature | 10 -- .../contentstore/features/static-pages.py | 19 +-- .../features/studio-overview-togglesection.py | 3 +- .../contentstore/features/upload.py | 1 + common/djangoapps/terrain/ui_helpers.py | 12 -- 10 files changed, 94 insertions(+), 139 deletions(-) diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index a80fbcbb8f..aa4ed56a16 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -58,6 +58,7 @@ def i_have_opened_a_new_course(step): ####### HELPER FUNCTIONS ############## def open_new_course(): world.clear_courses() + create_studio_user() log_into_studio() create_a_course() @@ -90,10 +91,7 @@ def fill_in_course_info( def log_into_studio( uname='robot', email='robot+studio@edx.org', - password='test', - is_staff=False): - - create_studio_user(uname=uname, email=email, is_staff=is_staff) + password='test'): world.browser.cookies.delete() world.visit('/') diff --git a/cms/djangoapps/contentstore/features/course-team.py b/cms/djangoapps/contentstore/features/course-team.py index 7bf538157d..cfcd0e053d 100644 --- a/cms/djangoapps/contentstore/features/course-team.py +++ b/cms/djangoapps/contentstore/features/course-team.py @@ -2,10 +2,10 @@ #pylint: disable=W0621 from lettuce import world, step -from common import create_studio_user, COURSE_NAME +from common import create_studio_user, log_into_studio, COURSE_NAME -PASS = 'test' -EXTENSION = '@edx.org' +PASSWORD = 'test' +EMAIL_EXTENSION = '@edx.org' @step(u'I am viewing the course team settings') @@ -17,49 +17,39 @@ def view_grading_settings(step): @step(u'The user "([^"]*)" exists$') def create_other_user(step, name): - create_studio_user(uname=name, password=PASS, email=(name + EXTENSION)) + create_studio_user(uname=name, password=PASSWORD, email=(name + EMAIL_EXTENSION)) @step(u'I add "([^"]*)" to the course team') def add_other_user(step, name): - new_user_css = '.new-user-button' - world.css_find(new_user_css).click() + new_user_css = 'a.new-user-button' + world.css_click(new_user_css) - email_css = '.email-input' + email_css = 'input.email-input' f = world.css_find(email_css) - f._element.send_keys(name, EXTENSION) + f._element.send_keys(name, EMAIL_EXTENSION) confirm_css = '#add_user' - world.css_find(confirm_css).click() + world.css_click(confirm_css) @step(u'I delete "([^"]*)" from the course team') def delete_other_user(step, name): - to_delete_css = '.remove-user[data-id="%s%s"]' % (name, EXTENSION,) - world.css_find(to_delete_css).click() + to_delete_css = '.remove-user[data-id="{name}{extension}"]'.format(name=name, extension=EMAIL_EXTENSION,) + world.css_click(to_delete_css) @step(u'"([^"]*)" logs in$') def other_user_login(step, name): - world.browser.cookies.delete() - world.visit('/') - - signin_css = 'a.action-signin' - world.is_css_present(signin_css) - world.css_click(signin_css) - - login_form = world.browser.find_by_css('form#login_form') - login_form.find_by_name('email').fill(name + EXTENSION) - login_form.find_by_name('password').fill(PASS) - login_form.find_by_name('submit').click() + log_into_studio(uname=name, password=PASSWORD, email=name + EMAIL_EXTENSION) @step(u'He does( not)? see the course on his page') -def see_course(step, doesnt): +def see_course(step, doesnt_see_course): class_css = '.class-name' all_courses = world.css_find(class_css) all_names = [item.html for item in all_courses] - if doesnt: + if doesnt_see_course: assert not COURSE_NAME in all_names else: assert COURSE_NAME in all_names diff --git a/cms/djangoapps/contentstore/features/course-updates.feature b/cms/djangoapps/contentstore/features/course-updates.feature index 7266e4f9a6..81714c43ae 100644 --- a/cms/djangoapps/contentstore/features/course-updates.feature +++ b/cms/djangoapps/contentstore/features/course-updates.feature @@ -4,41 +4,34 @@ Feature: Course updates Scenario: Users can add updates Given I have opened a new course in Studio And I go to the course updates page - When I add a new update - And I make the text "Hello" + When I add a new update with the text "Hello" Then I should see the update "Hello" Scenario: Users can edit updates Given I have opened a new course in Studio And I go to the course updates page - When I add a new update - And I make the text "Hello" - And I click the "edit" button - And I make the text "Goodbye" + When I add a new update with the text "Hello" + And I modify the text to "Goodbye" Then I should see the update "Goodbye" Scenario: Users can delete updates Given I have opened a new course in Studio And I go to the course updates page - And I add a new update - And I make the text "Hello" + And I add a new update with the text "Hello" When I will confirm all alerts - And I click the "delete" button + And I delete the update Then I should not see the update "Hello" Scenario: Users can edit update dates Given I have opened a new course in Studio And I go to the course updates page - And I add a new update - And I make the text "Hello" - When I click the "edit" button - And I make the date "June 1, 2013" + And I add a new update with the text "Hello" + When I edit the date to "June 1, 2013" Then I should see the date "June 1, 2013" Scenario: Users can change handouts Given I have opened a new course in Studio And I go to the course updates page - When I edit the handouts - And I make the text "
    Test
" + When I modify the handout to "
    Test
" Then I see the handout "Test" diff --git a/cms/djangoapps/contentstore/features/course-updates.py b/cms/djangoapps/contentstore/features/course-updates.py index 4d0b5ef174..dbb9cbc958 100644 --- a/cms/djangoapps/contentstore/features/course-updates.py +++ b/cms/djangoapps/contentstore/features/course-updates.py @@ -9,18 +9,75 @@ from selenium.webdriver.common.keys import Keys def go_to_uploads(step): menu_css = 'li.nav-course-courseware' uploads_css = '.nav-course-courseware-updates' - world.css_find(menu_css).click() - world.css_find(uploads_css).click() + world.css_click(menu_css) + world.css_click(uploads_css) -@step(u'I add a new update') -def add_update(step): +@step(u'I add a new update with the text "([^"]*)"$') +def add_update(step, text): update_css = '.new-update-button' - world.css_find(update_css).click() + world.css_click(update_css) + change_text(text) -@step(u'I make the text "([^"]*)"$') -def change_text(step, text): +@step(u'I should( not)? see the update "([^"]*)"$') +def check_update(step, doesnt_see_update, text): + update_css = '.update-contents' + update = world.css_find(update_css) + if doesnt_see_update: + assert len(update) == 0 or not text in update.html + else: + assert text in update.html + + +@step(u'I modify the text to "([^"]*)"$') +def modify_update(step, text): + button_css = '.post-preview .edit-button' + world.css_click(button_css) + change_text(text) + + +@step(u'I delete the update$') +def click_button(step): + button_css = '.post-preview .delete-button' + world.css_click(button_css) + + +@step(u'I edit the date to "([^"]*)"$') +def change_date(step, new_date): + button_css = '.post-preview .edit-button' + world.css_click(button_css) + date_css = 'input.date' + date = world.css_find(date_css) + for i in range(len(date.value)): + date._element.send_keys(Keys.END, Keys.BACK_SPACE) + date._element.send_keys(new_date) + save_css = '.save-button' + world.css_click(save_css) + + +@step(u'I should see the date "([^"]*)"$') +def check_date(step, date): + date_css = '.date-display' + date_html = world.css_find(date_css) + assert date == date_html.html + + +@step(u'I modify the handout to "([^"]*)"$') +def edit_handouts(step, text): + edit_css = '.course-handouts > .edit-button' + world.css_click(edit_css) + change_text(text) + + +@step(u'I see the handout "([^"]*)"$') +def check_handout(step, handout): + handout_css = '.handouts-content' + handouts = world.css_find(handout_css) + assert handout in handouts.html + + +def change_text(text): text_css = 'div.CodeMirror > div > textarea' prev_css = 'div.CodeMirror-lines > div > div:last-child > pre' all_lines = world.css_find(prev_css) @@ -32,51 +89,4 @@ def change_text(step, text): text_area._element.send_keys(Keys.END, Keys.BACK_SPACE) text_area._element.send_keys(text) save_css = '.save-button' - world.css_find(save_css).click() - - -@step(u'I should( not)? see the update "([^"]*)"$') -def check_update(step, doesnt, text): - update_css = '.update-contents' - update = world.css_find(update_css) - if doesnt: - assert len(update) == 0 or not text in update.html - else: - assert text in update.html - - -@step(u'I click the "([^"]*)" button$') -def click_button(step, edit_delete): - button_css = '.post-preview .%s-button' % edit_delete - world.css_find(button_css).click() - - -@step(u'I make the date "([^"]*)"$') -def change_date(step, new_date): - date_css = 'input.date' - date = world.css_find(date_css) - for i in range(len(date.value)): - date._element.send_keys(Keys.END, Keys.BACK_SPACE) - date._element.send_keys(new_date) - save_css = '.save-button' - world.css_find(save_css).click() - - -@step(u'I should see the date "([^"]*)"$') -def check_date(step, date): - date_css = '.date-display' - date_html = world.css_find(date_css) - assert date == date_html.html - - -@step(u'I edit the handouts') -def edit_handouts(step): - edit_css = '.course-handouts > .edit-button' - world.css_find(edit_css).click() - - -@step(u'I see the handout "([^"]*)"$') -def check_handout(step, handout): - handout_css = '.handouts-content' - handouts = world.css_find(handout_css) - assert handout in handouts.html + world.css_click(save_css) diff --git a/cms/djangoapps/contentstore/features/courses.py b/cms/djangoapps/contentstore/features/courses.py index a3e838a9d1..5b279d402f 100644 --- a/cms/djangoapps/contentstore/features/courses.py +++ b/cms/djangoapps/contentstore/features/courses.py @@ -10,6 +10,7 @@ from common import * @step('There are no courses$') def no_courses(step): world.clear_courses() + create_studio_user() @step('I click the New Course button$') diff --git a/cms/djangoapps/contentstore/features/static-pages.feature b/cms/djangoapps/contentstore/features/static-pages.feature index 99a4e802dd..9997df69f0 100644 --- a/cms/djangoapps/contentstore/features/static-pages.feature +++ b/cms/djangoapps/contentstore/features/static-pages.feature @@ -22,13 +22,3 @@ Feature: Static Pages When I "edit" the "Empty" page And I change the name to "New" Then I should see a "New" static page - - Scenario: Users can reorder static pages - Given I have opened a new course in Studio - And I go to the static pages page - And I add a new page - And I "edit" the "Empty" page - And I change the name to "New" - And I add a new page - When I move "New" after "Empty" - Then I see the order is "Empty New" diff --git a/cms/djangoapps/contentstore/features/static-pages.py b/cms/djangoapps/contentstore/features/static-pages.py index 2a1110de40..56d12f18aa 100644 --- a/cms/djangoapps/contentstore/features/static-pages.py +++ b/cms/djangoapps/contentstore/features/static-pages.py @@ -50,27 +50,10 @@ def change_name(step, new_name): world.css_find(save_button).click() -@step(u'I move "([^"]*)" after "([^"]*)"$') -def change_list(step, item1, item2): - index1 = get_index(item1) - index2 = get_index(item2) - assert index1 != -1 and index2 != -1 - world.drag_sortable_after(".component", index1, index2, ".ui-sortable") - - -@step(u'I see the order is "([^"]*)"$') -def check_order(step, items): - items = items.split(' ') - name_css = 'section[data-type="HTMLModule"]' - all_elements = world.css_find(name_css) - for i in range(len(items)): - assert all_elements[i].html == '\n %s\n' % items[i] - - def get_index(name): page_name_css = 'section[data-type="HTMLModule"]' all_pages = world.css_find(page_name_css) for i in range(len(all_pages)): - if all_pages[i].html == '\n %s\n' % name: + if all_pages[i].html == '\n {name}\n'.format(name=name): return i return -1 diff --git a/cms/djangoapps/contentstore/features/studio-overview-togglesection.py b/cms/djangoapps/contentstore/features/studio-overview-togglesection.py index 3a39f3cc15..3aca2aee92 100644 --- a/cms/djangoapps/contentstore/features/studio-overview-togglesection.py +++ b/cms/djangoapps/contentstore/features/studio-overview-togglesection.py @@ -50,7 +50,8 @@ def have_a_course_with_two_sections(step): @step(u'I navigate to the course overview page$') def navigate_to_the_course_overview_page(step): - log_into_studio(is_staff=True) + create_studio_user(is_staff=True) + log_into_studio() course_locator = '.class-name' world.css_click(course_locator) diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index d7a6eba999..16e38e3ff7 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -20,6 +20,7 @@ def upload_file(step, file_name): file_css = '.file-input' upload = world.css_find(file_css) + #uploading the file itself upload._element.send_keys(os.getcwd() + '/' + file_name) close_css = '.close-button' diff --git a/common/djangoapps/terrain/ui_helpers.py b/common/djangoapps/terrain/ui_helpers.py index c59d7030ff..ecd43eb719 100644 --- a/common/djangoapps/terrain/ui_helpers.py +++ b/common/djangoapps/terrain/ui_helpers.py @@ -158,15 +158,3 @@ def click_tools(): tools_css = 'li.nav-course-tools' if world.browser.is_element_present_by_css(tools_css): world.css_click(tools_css) - - -@world.absorb -def drag_sortable_after(item_css, index1, index2, sortable_css): - """ - This is a hack in order to simulate jQuery's sortable list dragging as Selenium cannot currently do it with action_chains - Please note that this is very finnicky with keeping the changes after refreshing the page so be careful when testing persistant changes - Also note that this is mainly for visualization of the sortable list and the true sortable drag and drop fires many events - """ - world.browser.execute_script('$("%(item_css)s:eq(%(index_one)s)").insertAfter($("%(item_css)s:eq(%(index_two)s)"));\ - $("%(sortable_css)s").trigger("sortupdate")' % - {'item_css': item_css, 'index_one': index1, 'index_two': index2, 'sortable_css': sortable_css}) From 6d686dd9f61582c4e8cfa898d0fc28cf15c87719 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Wed, 12 Jun 2013 13:32:46 -0400 Subject: [PATCH 08/19] Changed the way the file path was obtained --- cms/djangoapps/contentstore/features/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index 16e38e3ff7..47aa58d71b 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -21,7 +21,7 @@ def upload_file(step, file_name): file_css = '.file-input' upload = world.css_find(file_css) #uploading the file itself - upload._element.send_keys(os.getcwd() + '/' + file_name) + upload._element.send_keys(os.path.dirname(__file__) + '/' + file_name) close_css = '.close-button' world.css_find(close_css).click() From 3c43dbb26fec2cb3a8e6d647518867dbc46fc727 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Wed, 12 Jun 2013 13:36:46 -0400 Subject: [PATCH 09/19] Changed the other % to format --- cms/djangoapps/contentstore/features/common.py | 2 +- cms/djangoapps/contentstore/features/course-team.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index aa4ed56a16..8146c4ab99 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -113,7 +113,7 @@ def create_a_course(): # Add the user to the instructor group of the course # so they will have the permissions to see it in studio - g = world.GroupFactory.create(name='instructor_MITx/%s/%s' % (COURSE_NUM, COURSE_NAME.replace(" ", "_"),)) + g = world.GroupFactory.create(name='instructor_MITx/{course_num}/{course_name}'.format(course_num=COURSE_NUM, course_name=COURSE_NAME.replace(" ", "_"))) u = get_user_by_email('robot+studio@edx.org') u.groups.add(g) u.save() diff --git a/cms/djangoapps/contentstore/features/course-team.py b/cms/djangoapps/contentstore/features/course-team.py index cfcd0e053d..439eccb265 100644 --- a/cms/djangoapps/contentstore/features/course-team.py +++ b/cms/djangoapps/contentstore/features/course-team.py @@ -35,7 +35,7 @@ def add_other_user(step, name): @step(u'I delete "([^"]*)" from the course team') def delete_other_user(step, name): - to_delete_css = '.remove-user[data-id="{name}{extension}"]'.format(name=name, extension=EMAIL_EXTENSION,) + to_delete_css = '.remove-user[data-id="{name}{extension}"]'.format(name=name, extension=EMAIL_EXTENSION) world.css_click(to_delete_css) From 40bf2514abce3d995326329332d21e4d0245c959 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Thu, 13 Jun 2013 11:00:45 -0400 Subject: [PATCH 10/19] Added tests for downloading files A few notes: 1. Downloads are done through direct requests. This is due to the difficulty of downloading a file to the correct place 2. Modifiying a file will just change the file to a random 10 character string. 3. The page is reloaded in between uploads. This is due to a current caching bug that is in the process of being looked into and will be updated once it is fixed. --- .../contentstore/features/upload.feature | 26 ++++++++--- .../contentstore/features/upload.py | 45 +++++++++++++++++-- common/test/data/uploads/test | 1 + 3 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 common/test/data/uploads/test diff --git a/cms/djangoapps/contentstore/features/upload.feature b/cms/djangoapps/contentstore/features/upload.feature index 5717cfb907..71b754a3e7 100644 --- a/cms/djangoapps/contentstore/features/upload.feature +++ b/cms/djangoapps/contentstore/features/upload.feature @@ -4,12 +4,28 @@ Feature: Upload Files Scenario: Users can upload files Given I have opened a new course in Studio And I go to the files and uploads page - When I upload the file "upload.feature" - Then I see the file "upload.feature" was uploaded + When I upload the file "test" + Then I see the file "test" was uploaded + And The url for the file "test" is valid Scenario: Users can update files Given I have opened a new course in studio And I go to the files and uploads page - When I upload the file "upload.feature" - And I upload the file "upload.feature" - Then I see only one "upload.feature" + When I upload the file "test" + And I upload the file "test" + Then I see only one "test" + + Scenario: Users can download files + Given I have opened a new course in studio + And I go to the files and uploads page + When I upload the file "test" + Then I can download the correct "test" file + + Scenario: Users can download updated files + Given I have opened a new course in studio + And I go to the files and uploads page + When I upload the file "test" + And I modify "test" + And I reload the page + And I upload the file "test" + Then I can download the correct "test" file diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index 47aa58d71b..acc889ac8a 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -2,7 +2,13 @@ #pylint: disable=W0621 from lettuce import world, step -import os +from django.conf import settings +import requests +import string +import random + +TEST_ROOT = settings.COMMON_TEST_DATA_ROOT +HTTP_PREFIX = "http://localhost:8001" @step(u'I go to the files and uploads page') @@ -21,18 +27,24 @@ def upload_file(step, file_name): file_css = '.file-input' upload = world.css_find(file_css) #uploading the file itself - upload._element.send_keys(os.path.dirname(__file__) + '/' + file_name) + upload._element.send_keys(TEST_ROOT + '/uploads/' + file_name) close_css = '.close-button' world.css_find(close_css).click() -@step(u'I see the file "([^"]*)" was uploaded') +@step(u'I see the file "([^"]*)" was uploaded$') def check_upload(step, file_name): index = get_index(file_name) assert index != -1 +@step(u'The url for the file "([^"]*)" is valid$') +def check_url(step, file_name): + r = get_file(file_name) + assert r.status_code == 200 + + @step(u'I see only one "([^"]*)"$') def no_duplicate(step, file_name): names_css = '.name-col > a.filename' @@ -44,6 +56,24 @@ def no_duplicate(step, file_name): assert only_one +@step(u'I can download the correct "([^"]*)" file$') +def check_download(step, file_name): + cur_file = open(TEST_ROOT + '/uploads/' + file_name, 'r') + cur_text = cur_file.read() + r = get_file(file_name) + downloaded_text = r.text + assert cur_text == downloaded_text + cur_file.close() + + +@step(u'I modify "([^"]*)"$') +def modify_upload(step, file_name): + new_text = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10)) + cur_file = open(TEST_ROOT + '/uploads/' + file_name, 'w') + cur_file.write(new_text) + cur_file.close() + + def get_index(file_name): names_css = '.name-col > a.filename' all_names = world.css_find(names_css) @@ -51,3 +81,12 @@ def get_index(file_name): if file_name == all_names[i].html: return i return -1 + + +def get_file(file_name): + index = get_index(file_name) + assert index != -1 + + url_css = 'input.embeddable-xml-input' + url = world.css_find(url_css)[index].value + return requests.get(HTTP_PREFIX + url) diff --git a/common/test/data/uploads/test b/common/test/data/uploads/test new file mode 100644 index 0000000000..27bb8ecaac --- /dev/null +++ b/common/test/data/uploads/test @@ -0,0 +1 @@ +R22VMJ2M \ No newline at end of file From ef1f523c5576ce421b26b84eba6768a60b5840d5 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Mon, 17 Jun 2013 14:12:42 -0400 Subject: [PATCH 11/19] Removed _MODULESTORE reference that was doing nothing --- common/djangoapps/terrain/course_helpers.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/djangoapps/terrain/course_helpers.py b/common/djangoapps/terrain/course_helpers.py index 23f932bc3b..b843d47934 100644 --- a/common/djangoapps/terrain/course_helpers.py +++ b/common/djangoapps/terrain/course_helpers.py @@ -10,7 +10,7 @@ from django.contrib.auth import authenticate, login from django.contrib.auth.middleware import AuthenticationMiddleware from django.contrib.sessions.middleware import SessionMiddleware from student.models import CourseEnrollment -from xmodule.modulestore.django import _MODULESTORES, modulestore +from xmodule.modulestore.django import modulestore from xmodule.contentstore.django import contentstore from xmodule.templates import update_templates from bs4 import BeautifulSoup @@ -112,7 +112,6 @@ def save_the_course_content(path='/tmp'): u = world.browser.url section_url = u[u.find('courseware/') + 11:] - if not os.path.exists(path): os.makedirs(path) @@ -131,7 +130,6 @@ def clear_courses(): # (though it shouldn't), do this manually # from the bash shell to drop it: # $ mongo test_xmodule --eval "db.dropDatabase()" - _MODULESTORES = {} modulestore().collection.drop() update_templates(modulestore('direct')) contentstore().fs_files.drop() From 25fe8e8070abf0bf1351fd9eedf5b6f0e870bf30 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Mon, 17 Jun 2013 15:54:10 -0400 Subject: [PATCH 12/19] Now using context files and os.path --- .../contentstore/features/upload.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index acc889ac8a..386fc81040 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -6,6 +6,7 @@ from django.conf import settings import requests import string import random +import os TEST_ROOT = settings.COMMON_TEST_DATA_ROOT HTTP_PREFIX = "http://localhost:8001" @@ -27,7 +28,8 @@ def upload_file(step, file_name): file_css = '.file-input' upload = world.css_find(file_css) #uploading the file itself - upload._element.send_keys(TEST_ROOT + '/uploads/' + file_name) + path = os.path.join(TEST_ROOT, 'uploads/', file_name) + upload._element.send_keys(os.path.abspath(path)) close_css = '.close-button' world.css_find(close_css).click() @@ -58,20 +60,20 @@ def no_duplicate(step, file_name): @step(u'I can download the correct "([^"]*)" file$') def check_download(step, file_name): - cur_file = open(TEST_ROOT + '/uploads/' + file_name, 'r') - cur_text = cur_file.read() - r = get_file(file_name) - downloaded_text = r.text - assert cur_text == downloaded_text - cur_file.close() + path = os.path.join(TEST_ROOT, 'uploads/', file_name) + with open(os.path.abspath(path), 'r') as cur_file: + cur_text = cur_file.read() + r = get_file(file_name) + downloaded_text = r.text + assert cur_text == downloaded_text @step(u'I modify "([^"]*)"$') def modify_upload(step, file_name): new_text = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10)) - cur_file = open(TEST_ROOT + '/uploads/' + file_name, 'w') - cur_file.write(new_text) - cur_file.close() + path = os.path.join(TEST_ROOT, 'uploads/', file_name) + with open(os.path.abspath(path), 'w') as cur_file: + cur_file.write(new_text) def get_index(file_name): From 15facf8fc0efb1c2cf4224a0e2504418f053d755 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Mon, 17 Jun 2013 16:48:37 -0400 Subject: [PATCH 13/19] Changed change_text to implement new type_in_codemirror function --- .../contentstore/features/course-updates.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/cms/djangoapps/contentstore/features/course-updates.py b/cms/djangoapps/contentstore/features/course-updates.py index dbb9cbc958..3bbdd75d26 100644 --- a/cms/djangoapps/contentstore/features/course-updates.py +++ b/cms/djangoapps/contentstore/features/course-updates.py @@ -3,6 +3,7 @@ from lettuce import world, step from selenium.webdriver.common.keys import Keys +from common import type_in_codemirror @step(u'I go to the course updates page') @@ -78,15 +79,6 @@ def check_handout(step, handout): def change_text(text): - text_css = 'div.CodeMirror > div > textarea' - prev_css = 'div.CodeMirror-lines > div > div:last-child > pre' - all_lines = world.css_find(prev_css) - all_text = '' - for i in range(len(all_lines)): - all_text = all_lines[i].html - text_area = world.css_find(text_css) - for i in range(len(all_text)): - text_area._element.send_keys(Keys.END, Keys.BACK_SPACE) - text_area._element.send_keys(text) + type_in_codemirror(0, text) save_css = '.save-button' world.css_click(save_css) From 650a2ba423907f099492027fd3ef75e4ca641338 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Tue, 18 Jun 2013 11:20:23 -0400 Subject: [PATCH 14/19] Added deletion tests --- .../contentstore/features/upload.feature | 11 ++++++++-- .../contentstore/features/upload.py | 22 +++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/cms/djangoapps/contentstore/features/upload.feature b/cms/djangoapps/contentstore/features/upload.feature index 71b754a3e7..b3c1fc2ce3 100644 --- a/cms/djangoapps/contentstore/features/upload.feature +++ b/cms/djangoapps/contentstore/features/upload.feature @@ -5,7 +5,7 @@ Feature: Upload Files Given I have opened a new course in Studio And I go to the files and uploads page When I upload the file "test" - Then I see the file "test" was uploaded + Then I should see the file "test" was uploaded And The url for the file "test" is valid Scenario: Users can update files @@ -13,7 +13,14 @@ Feature: Upload Files And I go to the files and uploads page When I upload the file "test" And I upload the file "test" - Then I see only one "test" + Then I should see only one "test" + + Scenario: Users can delete uploaded files + Given I have opened a new course in studio + And I go to the files and uploads page + When I upload the file "test" + And I delete the file "test" + Then I should not see the file "test" was uploaded Scenario: Users can download files Given I have opened a new course in studio diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index 386fc81040..9b049ccc78 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -35,10 +35,13 @@ def upload_file(step, file_name): world.css_find(close_css).click() -@step(u'I see the file "([^"]*)" was uploaded$') -def check_upload(step, file_name): +@step(u'I should( not)? see the file "([^"]*)" was uploaded$') +def check_upload(step, do_not_see_file, file_name): index = get_index(file_name) - assert index != -1 + if do_not_see_file: + assert index == -1 + else: + assert index != -1 @step(u'The url for the file "([^"]*)" is valid$') @@ -47,7 +50,18 @@ def check_url(step, file_name): assert r.status_code == 200 -@step(u'I see only one "([^"]*)"$') +@step(u'I delete the file "([^"]*)"$') +def delete_file(step, file_name): + index = get_index(file_name) + assert index != -1 + delete_css = ".remove-asset-button" + world.css_click(delete_css, index=index) + + prompt_confirm_css = 'li.nav-item > a.action-primary' + world.css_click(prompt_confirm_css) + + +@step(u'I should see only one "([^"]*)"$') def no_duplicate(step, file_name): names_css = '.name-col > a.filename' all_names = world.css_find(names_css) From 9dc126271989441a9612f9b506af2652b7290749 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Tue, 18 Jun 2013 13:58:58 -0400 Subject: [PATCH 15/19] This was causing some failures in the unit tests for some reason --- cms/envs/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cms/envs/test.py b/cms/envs/test.py index 89813dd937..954a553e10 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -70,7 +70,7 @@ CONTENTSTORE = { 'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore', 'OPTIONS': { 'host': 'localhost', - 'db': 'test_xcontent', + 'db': 'test_xmodule', }, # allow for additional options that can be keyed on a name, e.g. 'trashcan' 'ADDITIONAL_OPTIONS': { From b9d79aea605722d177dd3d9b56d2bf23b56ec7f1 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Thu, 20 Jun 2013 15:55:53 -0400 Subject: [PATCH 16/19] A bunch of pylint fixes and explicit use case for uploads/test --- .../contentstore/features/common.py | 25 ++++++++++--------- .../contentstore/features/course-team.py | 16 ++++++------ .../contentstore/features/course-updates.py | 18 ++++++------- .../contentstore/features/static-pages.py | 10 ++++---- .../contentstore/features/upload.py | 16 ++++++------ common/test/data/uploads/test | 2 +- 6 files changed, 44 insertions(+), 43 deletions(-) diff --git a/cms/djangoapps/contentstore/features/common.py b/cms/djangoapps/contentstore/features/common.py index 0b7cb11d2a..e126b746c5 100644 --- a/cms/djangoapps/contentstore/features/common.py +++ b/cms/djangoapps/contentstore/features/common.py @@ -3,7 +3,6 @@ from lettuce import world, step from nose.tools import assert_true -from nose.tools import assert_equal from auth.authz import get_user_by_email @@ -13,12 +12,13 @@ import time from logging import getLogger logger = getLogger(__name__) -COURSE_NAME = 'Robot Super Course' -COURSE_NUM = '999' -COURSE_ORG = 'MITx' +_COURSE_NAME = 'Robot Super Course' +_COURSE_NUM = '999' +_COURSE_ORG = 'MITx' ########### STEP HELPERS ############## + @step('I (?:visit|access|open) the Studio homepage$') def i_visit_the_studio_homepage(_step): # To make this go to port 8001, put @@ -78,10 +78,11 @@ def create_studio_user( registration.register(studio_user) registration.activate() + def fill_in_course_info( - name=COURSE_NAME, - org=COURSE_ORG, - num=COURSE_NUM): + name=_COURSE_NAME, + org=_COURSE_ORG, + num=_COURSE_NUM): world.css_fill('.new-course-name', name) world.css_fill('.new-course-org', org) world.css_fill('.new-course-number', num) @@ -108,14 +109,14 @@ def log_into_studio( def create_a_course(): - c = world.CourseFactory.create(org=COURSE_ORG, course=COURSE_NUM, display_name=COURSE_NAME) + world.CourseFactory.create(org=_COURSE_ORG, course=_COURSE_NUM, display_name=_COURSE_NAME) # Add the user to the instructor group of the course # so they will have the permissions to see it in studio - g = world.GroupFactory.create(name='instructor_MITx/{course_num}/{course_name}'.format(course_num=COURSE_NUM, course_name=COURSE_NAME.replace(" ", "_"))) - u = get_user_by_email('robot+studio@edx.org') - u.groups.add(g) - u.save() + course = world.GroupFactory.create(name='instructor_MITx/{course_num}/{course_name}'.format(course_num=_COURSE_NUM, course_name=_COURSE_NAME.replace(" ", "_"))) + user = get_user_by_email('robot+studio@edx.org') + user.groups.add(course) + user.save() world.browser.reload() course_link_css = 'span.class-name' diff --git a/cms/djangoapps/contentstore/features/course-team.py b/cms/djangoapps/contentstore/features/course-team.py index 439eccb265..15c9e5169d 100644 --- a/cms/djangoapps/contentstore/features/course-team.py +++ b/cms/djangoapps/contentstore/features/course-team.py @@ -9,19 +9,19 @@ EMAIL_EXTENSION = '@edx.org' @step(u'I am viewing the course team settings') -def view_grading_settings(step): +def view_grading_settings(_step): world.click_course_settings() link_css = 'li.nav-course-settings-team a' world.css_click(link_css) @step(u'The user "([^"]*)" exists$') -def create_other_user(step, name): +def create_other_user(_step, name): create_studio_user(uname=name, password=PASSWORD, email=(name + EMAIL_EXTENSION)) @step(u'I add "([^"]*)" to the course team') -def add_other_user(step, name): +def add_other_user(_step, name): new_user_css = 'a.new-user-button' world.css_click(new_user_css) @@ -34,18 +34,18 @@ def add_other_user(step, name): @step(u'I delete "([^"]*)" from the course team') -def delete_other_user(step, name): +def delete_other_user(_step, name): to_delete_css = '.remove-user[data-id="{name}{extension}"]'.format(name=name, extension=EMAIL_EXTENSION) world.css_click(to_delete_css) @step(u'"([^"]*)" logs in$') -def other_user_login(step, name): +def other_user_login(_step, name): log_into_studio(uname=name, password=PASSWORD, email=name + EMAIL_EXTENSION) @step(u'He does( not)? see the course on his page') -def see_course(step, doesnt_see_course): +def see_course(_step, doesnt_see_course): class_css = '.class-name' all_courses = world.css_find(class_css) all_names = [item.html for item in all_courses] @@ -56,12 +56,12 @@ def see_course(step, doesnt_see_course): @step(u'He cannot delete users') -def cannot_delete(step): +def cannot_delete(_step): to_delete_css = '.remove-user' assert world.is_css_not_present(to_delete_css) @step(u'He cannot add users') -def cannot_add(step): +def cannot_add(_step): add_css = '.new-user' assert world.is_css_not_present(add_css) diff --git a/cms/djangoapps/contentstore/features/course-updates.py b/cms/djangoapps/contentstore/features/course-updates.py index 3bbdd75d26..e742b6a40c 100644 --- a/cms/djangoapps/contentstore/features/course-updates.py +++ b/cms/djangoapps/contentstore/features/course-updates.py @@ -7,7 +7,7 @@ from common import type_in_codemirror @step(u'I go to the course updates page') -def go_to_uploads(step): +def go_to_uploads(_step): menu_css = 'li.nav-course-courseware' uploads_css = '.nav-course-courseware-updates' world.css_click(menu_css) @@ -15,14 +15,14 @@ def go_to_uploads(step): @step(u'I add a new update with the text "([^"]*)"$') -def add_update(step, text): +def add_update(_step, text): update_css = '.new-update-button' world.css_click(update_css) change_text(text) @step(u'I should( not)? see the update "([^"]*)"$') -def check_update(step, doesnt_see_update, text): +def check_update(_step, doesnt_see_update, text): update_css = '.update-contents' update = world.css_find(update_css) if doesnt_see_update: @@ -32,20 +32,20 @@ def check_update(step, doesnt_see_update, text): @step(u'I modify the text to "([^"]*)"$') -def modify_update(step, text): +def modify_update(_step, text): button_css = '.post-preview .edit-button' world.css_click(button_css) change_text(text) @step(u'I delete the update$') -def click_button(step): +def click_button(_step): button_css = '.post-preview .delete-button' world.css_click(button_css) @step(u'I edit the date to "([^"]*)"$') -def change_date(step, new_date): +def change_date(_step, new_date): button_css = '.post-preview .edit-button' world.css_click(button_css) date_css = 'input.date' @@ -58,21 +58,21 @@ def change_date(step, new_date): @step(u'I should see the date "([^"]*)"$') -def check_date(step, date): +def check_date(_step, date): date_css = '.date-display' date_html = world.css_find(date_css) assert date == date_html.html @step(u'I modify the handout to "([^"]*)"$') -def edit_handouts(step, text): +def edit_handouts(_step, text): edit_css = '.course-handouts > .edit-button' world.css_click(edit_css) change_text(text) @step(u'I see the handout "([^"]*)"$') -def check_handout(step, handout): +def check_handout(_step, handout): handout_css = '.handouts-content' handouts = world.css_find(handout_css) assert handout in handouts.html diff --git a/cms/djangoapps/contentstore/features/static-pages.py b/cms/djangoapps/contentstore/features/static-pages.py index 56d12f18aa..23656690fc 100644 --- a/cms/djangoapps/contentstore/features/static-pages.py +++ b/cms/djangoapps/contentstore/features/static-pages.py @@ -6,7 +6,7 @@ from selenium.webdriver.common.keys import Keys @step(u'I go to the static pages page') -def go_to_uploads(step): +def go_to_uploads(_step): menu_css = 'li.nav-course-courseware' uploads_css = '.nav-course-courseware-pages' world.css_find(menu_css).click() @@ -14,13 +14,13 @@ def go_to_uploads(step): @step(u'I add a new page') -def add_page(step): +def add_page(_step): button_css = '.new-button' world.css_find(button_css).click() @step(u'I should( not)? see a "([^"]*)" static page$') -def see_page(step, doesnt, page): +def see_page(_step, doesnt, page): index = get_index(page) if doesnt: assert index == -1 @@ -29,7 +29,7 @@ def see_page(step, doesnt, page): @step(u'I "([^"]*)" the "([^"]*)" page$') -def click_edit_delete(step, edit_delete, page): +def click_edit_delete(_step, edit_delete, page): button_css = '.%s-button' % edit_delete index = get_index(page) assert index != -1 @@ -37,7 +37,7 @@ def click_edit_delete(step, edit_delete, page): @step(u'I change the name to "([^"]*)"$') -def change_name(step, new_name): +def change_name(_step, new_name): settings_css = '#settings-mode' world.css_find(settings_css).click() input_css = '.setting-input' diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index 9b049ccc78..7ef782ea13 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -13,7 +13,7 @@ HTTP_PREFIX = "http://localhost:8001" @step(u'I go to the files and uploads page') -def go_to_uploads(step): +def go_to_uploads(_step): menu_css = 'li.nav-course-courseware' uploads_css = '.nav-course-courseware-uploads' world.css_find(menu_css).click() @@ -21,7 +21,7 @@ def go_to_uploads(step): @step(u'I upload the file "([^"]*)"$') -def upload_file(step, file_name): +def upload_file(_step, file_name): upload_css = '.upload-button' world.css_find(upload_css).click() @@ -36,7 +36,7 @@ def upload_file(step, file_name): @step(u'I should( not)? see the file "([^"]*)" was uploaded$') -def check_upload(step, do_not_see_file, file_name): +def check_upload(_step, do_not_see_file, file_name): index = get_index(file_name) if do_not_see_file: assert index == -1 @@ -45,13 +45,13 @@ def check_upload(step, do_not_see_file, file_name): @step(u'The url for the file "([^"]*)" is valid$') -def check_url(step, file_name): +def check_url(_step, file_name): r = get_file(file_name) assert r.status_code == 200 @step(u'I delete the file "([^"]*)"$') -def delete_file(step, file_name): +def delete_file(_step, file_name): index = get_index(file_name) assert index != -1 delete_css = ".remove-asset-button" @@ -62,7 +62,7 @@ def delete_file(step, file_name): @step(u'I should see only one "([^"]*)"$') -def no_duplicate(step, file_name): +def no_duplicate(_step, file_name): names_css = '.name-col > a.filename' all_names = world.css_find(names_css) only_one = False @@ -73,7 +73,7 @@ def no_duplicate(step, file_name): @step(u'I can download the correct "([^"]*)" file$') -def check_download(step, file_name): +def check_download(_step, file_name): path = os.path.join(TEST_ROOT, 'uploads/', file_name) with open(os.path.abspath(path), 'r') as cur_file: cur_text = cur_file.read() @@ -83,7 +83,7 @@ def check_download(step, file_name): @step(u'I modify "([^"]*)"$') -def modify_upload(step, file_name): +def modify_upload(_step, file_name): new_text = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in range(10)) path = os.path.join(TEST_ROOT, 'uploads/', file_name) with open(os.path.abspath(path), 'w') as cur_file: diff --git a/common/test/data/uploads/test b/common/test/data/uploads/test index 27bb8ecaac..0424951e34 100644 --- a/common/test/data/uploads/test +++ b/common/test/data/uploads/test @@ -1 +1 @@ -R22VMJ2M \ No newline at end of file +This is an arbitrary file for testing uploads From 70d48e2e9fd20d7eac0289fccb24210d68c8af06 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Thu, 20 Jun 2013 16:40:13 -0400 Subject: [PATCH 17/19] Now referencing css element --- .../contentstore/features/course-team.py | 14 +++++----- .../contentstore/features/course-updates.py | 26 +++++++++---------- .../contentstore/features/static-pages.py | 14 +++++----- .../contentstore/features/upload.py | 15 ++++++----- common/test/data/uploads/test | 2 +- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/cms/djangoapps/contentstore/features/course-team.py b/cms/djangoapps/contentstore/features/course-team.py index 15c9e5169d..4303d5066c 100644 --- a/cms/djangoapps/contentstore/features/course-team.py +++ b/cms/djangoapps/contentstore/features/course-team.py @@ -2,7 +2,7 @@ #pylint: disable=W0621 from lettuce import world, step -from common import create_studio_user, log_into_studio, COURSE_NAME +from common import create_studio_user, log_into_studio, _COURSE_NAME PASSWORD = 'test' EMAIL_EXTENSION = '@edx.org' @@ -35,7 +35,7 @@ def add_other_user(_step, name): @step(u'I delete "([^"]*)" from the course team') def delete_other_user(_step, name): - to_delete_css = '.remove-user[data-id="{name}{extension}"]'.format(name=name, extension=EMAIL_EXTENSION) + to_delete_css = 'a.remove-user[data-id="{name}{extension}"]'.format(name=name, extension=EMAIL_EXTENSION) world.css_click(to_delete_css) @@ -46,22 +46,22 @@ def other_user_login(_step, name): @step(u'He does( not)? see the course on his page') def see_course(_step, doesnt_see_course): - class_css = '.class-name' + class_css = 'span.class-name' all_courses = world.css_find(class_css) all_names = [item.html for item in all_courses] if doesnt_see_course: - assert not COURSE_NAME in all_names + assert not _COURSE_NAME in all_names else: - assert COURSE_NAME in all_names + assert _COURSE_NAME in all_names @step(u'He cannot delete users') def cannot_delete(_step): - to_delete_css = '.remove-user' + to_delete_css = 'a.remove-user' assert world.is_css_not_present(to_delete_css) @step(u'He cannot add users') def cannot_add(_step): - add_css = '.new-user' + add_css = 'a.new-user' assert world.is_css_not_present(add_css) diff --git a/cms/djangoapps/contentstore/features/course-updates.py b/cms/djangoapps/contentstore/features/course-updates.py index e742b6a40c..d838061698 100644 --- a/cms/djangoapps/contentstore/features/course-updates.py +++ b/cms/djangoapps/contentstore/features/course-updates.py @@ -7,23 +7,23 @@ from common import type_in_codemirror @step(u'I go to the course updates page') -def go_to_uploads(_step): +def go_to_updates(_step): menu_css = 'li.nav-course-courseware' - uploads_css = '.nav-course-courseware-updates' + updates_css = 'li.nav-course-courseware-updates' world.css_click(menu_css) - world.css_click(uploads_css) + world.css_click(updates_css) @step(u'I add a new update with the text "([^"]*)"$') def add_update(_step, text): - update_css = '.new-update-button' + update_css = 'a.new-update-button' world.css_click(update_css) change_text(text) @step(u'I should( not)? see the update "([^"]*)"$') def check_update(_step, doesnt_see_update, text): - update_css = '.update-contents' + update_css = 'div.update-contents' update = world.css_find(update_css) if doesnt_see_update: assert len(update) == 0 or not text in update.html @@ -33,52 +33,52 @@ def check_update(_step, doesnt_see_update, text): @step(u'I modify the text to "([^"]*)"$') def modify_update(_step, text): - button_css = '.post-preview .edit-button' + button_css = 'div.post-preview a.edit-button' world.css_click(button_css) change_text(text) @step(u'I delete the update$') def click_button(_step): - button_css = '.post-preview .delete-button' + button_css = 'div.post-preview a.delete-button' world.css_click(button_css) @step(u'I edit the date to "([^"]*)"$') def change_date(_step, new_date): - button_css = '.post-preview .edit-button' + button_css = 'div.post-preview a.edit-button' world.css_click(button_css) date_css = 'input.date' date = world.css_find(date_css) for i in range(len(date.value)): date._element.send_keys(Keys.END, Keys.BACK_SPACE) date._element.send_keys(new_date) - save_css = '.save-button' + save_css = 'a.save-button' world.css_click(save_css) @step(u'I should see the date "([^"]*)"$') def check_date(_step, date): - date_css = '.date-display' + date_css = 'span.date-display' date_html = world.css_find(date_css) assert date == date_html.html @step(u'I modify the handout to "([^"]*)"$') def edit_handouts(_step, text): - edit_css = '.course-handouts > .edit-button' + edit_css = 'div.course-handouts > a.edit-button' world.css_click(edit_css) change_text(text) @step(u'I see the handout "([^"]*)"$') def check_handout(_step, handout): - handout_css = '.handouts-content' + handout_css = 'div.handouts-content' handouts = world.css_find(handout_css) assert handout in handouts.html def change_text(text): type_in_codemirror(0, text) - save_css = '.save-button' + save_css = 'a.save-button' world.css_click(save_css) diff --git a/cms/djangoapps/contentstore/features/static-pages.py b/cms/djangoapps/contentstore/features/static-pages.py index 23656690fc..a16a3246da 100644 --- a/cms/djangoapps/contentstore/features/static-pages.py +++ b/cms/djangoapps/contentstore/features/static-pages.py @@ -6,16 +6,16 @@ from selenium.webdriver.common.keys import Keys @step(u'I go to the static pages page') -def go_to_uploads(_step): +def go_to_static(_step): menu_css = 'li.nav-course-courseware' - uploads_css = '.nav-course-courseware-pages' + static_css = 'li.nav-course-courseware-pages' world.css_find(menu_css).click() - world.css_find(uploads_css).click() + world.css_find(static_css).click() @step(u'I add a new page') def add_page(_step): - button_css = '.new-button' + button_css = 'a.new-button' world.css_find(button_css).click() @@ -30,7 +30,7 @@ def see_page(_step, doesnt, page): @step(u'I "([^"]*)" the "([^"]*)" page$') def click_edit_delete(_step, edit_delete, page): - button_css = '.%s-button' % edit_delete + button_css = 'a.%s-button' % edit_delete index = get_index(page) assert index != -1 world.css_find(button_css)[index].click() @@ -40,13 +40,13 @@ def click_edit_delete(_step, edit_delete, page): def change_name(_step, new_name): settings_css = '#settings-mode' world.css_find(settings_css).click() - input_css = '.setting-input' + input_css = 'input.setting-input' name_input = world.css_find(input_css) old_name = name_input.value for count in range(len(old_name)): name_input._element.send_keys(Keys.END, Keys.BACK_SPACE) name_input._element.send_keys(new_name) - save_button = '.save-button' + save_button = 'a.save-button' world.css_find(save_button).click() diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index 7ef782ea13..5bf082c774 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -15,23 +15,23 @@ HTTP_PREFIX = "http://localhost:8001" @step(u'I go to the files and uploads page') def go_to_uploads(_step): menu_css = 'li.nav-course-courseware' - uploads_css = '.nav-course-courseware-uploads' + uploads_css = 'li.nav-course-courseware-uploads' world.css_find(menu_css).click() world.css_find(uploads_css).click() @step(u'I upload the file "([^"]*)"$') def upload_file(_step, file_name): - upload_css = '.upload-button' + upload_css = 'a.upload-button' world.css_find(upload_css).click() - file_css = '.file-input' + file_css = 'input.file-input' upload = world.css_find(file_css) #uploading the file itself path = os.path.join(TEST_ROOT, 'uploads/', file_name) upload._element.send_keys(os.path.abspath(path)) - close_css = '.close-button' + close_css = 'a.close-button' world.css_find(close_css).click() @@ -54,7 +54,8 @@ def check_url(_step, file_name): def delete_file(_step, file_name): index = get_index(file_name) assert index != -1 - delete_css = ".remove-asset-button" + from pdb import set_trace; set_trace() + delete_css = "a.remove-asset-button" world.css_click(delete_css, index=index) prompt_confirm_css = 'li.nav-item > a.action-primary' @@ -63,7 +64,7 @@ def delete_file(_step, file_name): @step(u'I should see only one "([^"]*)"$') def no_duplicate(_step, file_name): - names_css = '.name-col > a.filename' + names_css = 'td.name-col > a.filename' all_names = world.css_find(names_css) only_one = False for i in range(len(all_names)): @@ -91,7 +92,7 @@ def modify_upload(_step, file_name): def get_index(file_name): - names_css = '.name-col > a.filename' + names_css = 'td.name-col > a.filename' all_names = world.css_find(names_css) for i in range(len(all_names)): if file_name == all_names[i].html: diff --git a/common/test/data/uploads/test b/common/test/data/uploads/test index 0424951e34..f019db7176 100644 --- a/common/test/data/uploads/test +++ b/common/test/data/uploads/test @@ -1 +1 @@ -This is an arbitrary file for testing uploads +R2FUIGM88K \ No newline at end of file From a52d0beb41a75b493ed5d6d3110790a9edd0c505 Mon Sep 17 00:00:00 2001 From: JonahStanley Date: Thu, 20 Jun 2013 17:00:46 -0400 Subject: [PATCH 18/19] Got rid of leftover set_trace --- cms/djangoapps/contentstore/features/upload.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index 5bf082c774..258fc5ebcf 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -54,7 +54,6 @@ def check_url(_step, file_name): def delete_file(_step, file_name): index = get_index(file_name) assert index != -1 - from pdb import set_trace; set_trace() delete_css = "a.remove-asset-button" world.css_click(delete_css, index=index) From d632ffe9cd8ca16bb6ddf5e34cef3f7ed97a477f Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Fri, 21 Jun 2013 14:52:27 -0400 Subject: [PATCH 19/19] Make Course Team lettuce tests gender-neutral Because it bothers me, although I don't expect anyone else to care. --- .../contentstore/features/course-team.feature | 30 +++++++++---------- .../contentstore/features/course-team.py | 10 +++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/cms/djangoapps/contentstore/features/course-team.feature b/cms/djangoapps/contentstore/features/course-team.feature index 502321c49b..fc1212f398 100644 --- a/cms/djangoapps/contentstore/features/course-team.feature +++ b/cms/djangoapps/contentstore/features/course-team.feature @@ -3,32 +3,32 @@ Feature: Course Team Scenario: Users can add other users Given I have opened a new course in Studio - And The user "abcd" exists + And the user "alice" exists And I am viewing the course team settings - When I add "abcd" to the course team - And "abcd" logs in - Then He does see the course on his page + When I add "alice" to the course team + And "alice" logs in + Then she does see the course on her page Scenario: Added users cannot delete or add other users Given I have opened a new course in Studio - And The user "abcd" exists + And the user "bob" exists And I am viewing the course team settings - When I add "abcd" to the course team - And "abcd" logs in - Then He cannot delete users - And He cannot add users + When I add "bob" to the course team + And "bob" logs in + Then he cannot delete users + And he cannot add users Scenario: Users can delete other users Given I have opened a new course in Studio - And The user "abcd" exists + And the user "carol" exists And I am viewing the course team settings - When I add "abcd" to the course team - And I delete "abcd" from the course team - And "abcd" logs in - Then He does not see the course on his page + When I add "carol" to the course team + And I delete "carol" from the course team + And "carol" logs in + Then she does not see the course on her page Scenario: Users cannot add users that do not exist Given I have opened a new course in Studio And I am viewing the course team settings - When I add "abcd" to the course team + When I add "dennis" to the course team Then I should see "Could not find user by email address" somewhere on the page diff --git a/cms/djangoapps/contentstore/features/course-team.py b/cms/djangoapps/contentstore/features/course-team.py index 4303d5066c..c126773db6 100644 --- a/cms/djangoapps/contentstore/features/course-team.py +++ b/cms/djangoapps/contentstore/features/course-team.py @@ -15,7 +15,7 @@ def view_grading_settings(_step): world.css_click(link_css) -@step(u'The user "([^"]*)" exists$') +@step(u'the user "([^"]*)" exists$') def create_other_user(_step, name): create_studio_user(uname=name, password=PASSWORD, email=(name + EMAIL_EXTENSION)) @@ -44,8 +44,8 @@ def other_user_login(_step, name): log_into_studio(uname=name, password=PASSWORD, email=name + EMAIL_EXTENSION) -@step(u'He does( not)? see the course on his page') -def see_course(_step, doesnt_see_course): +@step(u's?he does( not)? see the course on (his|her) page') +def see_course(_step, doesnt_see_course, gender): class_css = 'span.class-name' all_courses = world.css_find(class_css) all_names = [item.html for item in all_courses] @@ -55,13 +55,13 @@ def see_course(_step, doesnt_see_course): assert _COURSE_NAME in all_names -@step(u'He cannot delete users') +@step(u's?he cannot delete users') def cannot_delete(_step): to_delete_css = 'a.remove-user' assert world.is_css_not_present(to_delete_css) -@step(u'He cannot add users') +@step(u's?he cannot add users') def cannot_add(_step): add_css = 'a.new-user' assert world.is_css_not_present(add_css)