diff --git a/cms/djangoapps/contentstore/features/upload.feature b/cms/djangoapps/contentstore/features/upload.feature index e01bcf8fed..2e73c11c0c 100644 --- a/cms/djangoapps/contentstore/features/upload.feature +++ b/cms/djangoapps/contentstore/features/upload.feature @@ -5,17 +5,16 @@ Feature: CMS.Upload Files # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Users can upload files - Given I have opened a new course in Studio - And I go to the files and uploads page + Given I am at the files and upload page of a Studio course When I upload the file "test" Then I should see the file "test" was uploaded And The url for the file "test" is valid + # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Users can upload multiple files - Given I have opened a new course in studio - And I go to the files and uploads page - When I upload the files "test","test2" + Given I am at the files and upload page of a Studio course + When I upload the files "test,test2" Then I should see the file "test" was uploaded And I should see the file "test2" was uploaded And The url for the file "test2" is valid @@ -24,8 +23,7 @@ Feature: CMS.Upload Files # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Users can update files - Given I have opened a new course in studio - And I go to the files and uploads page + Given I am at the files and upload page of a Studio course When I upload the file "test" And I upload the file "test" Then I should see only one "test" @@ -33,8 +31,7 @@ Feature: CMS.Upload Files # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Users can delete uploaded files - Given I have opened a new course in studio - And I go to the files and uploads page + Given I am at the files and upload page of a Studio course When I upload the file "test" And I delete the file "test" Then I should not see the file "test" was uploaded @@ -43,16 +40,14 @@ Feature: CMS.Upload Files # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Users can download files - Given I have opened a new course in studio - And I go to the files and uploads page + Given I am at the files and upload page of a Studio course When I upload the file "test" Then I can download the correct "test" file # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Users can download updated files - Given I have opened a new course in studio - And I go to the files and uploads page + Given I am at the files and upload page of a Studio course When I upload the file "test" And I modify "test" And I reload the page @@ -62,57 +57,59 @@ Feature: CMS.Upload Files # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Users can lock assets through asset index - 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 lock "test" - Then "test" is locked + Given I am at the files and upload page of a Studio course + When I upload an asset + And I lock the asset + Then the asset is locked And I see a "saving" notification And I reload the page - Then "test" is locked + Then the asset is locked # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Users can unlock assets through asset index - Given I have opened a course with a locked asset "test" - And I unlock "test" - Then "test" is unlocked + Given I have created a course with a locked asset + When I unlock the asset + Then the asset is unlocked And I see a "saving" notification And I reload the page - Then "test" is unlocked + Then the asset is unlocked # Uploading isn't working on safari with sauce labs - # TODO: work with Jay -# @skip_safari -# Scenario: Locked assets can't be viewed if logged in as unregistered user -# Given I have opened a course with a locked asset "locked.html" -# Then the asset "locked.html" can be clicked from the asset index -# And the user "bob" exists -# And "bob" logs in -# Then the asset "locked.html" is protected + @skip_safari + Scenario: Locked assets can't be viewed if logged in as an unregistered user + Given I have created a course with a locked asset + And the user "bob" exists + When "bob" logs in + Then the asset is protected + + # Uploading isn't working on safari with sauce labs + @skip_safari + Scenario: Locked assets can be viewed if logged in as a registered user + Given I have created a course with a locked asset + And the user "bob" exists + And the user "bob" is enrolled in the course + When "bob" logs in + Then the asset is viewable # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Locked assets can't be viewed if logged out - Given I have opened a course with a locked asset "locked.html" - # Note that logging out doesn't really matter at the moment- - # the asset will be protected because the user sent to middleware is the anonymous user. - # Need to work with Jay. - And I log out - Then the asset "locked.html" is protected + Given I have created a course with a locked asset + When I log out + Then the asset is protected # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Locked assets can be viewed with is_staff account - Given I have opened a course with a locked asset "locked.html" + Given I have created a course with a locked asset And the user "staff" exists as a course is_staff - And "staff" logs in - Then the asset "locked.html" can be clicked from the asset index + When "staff" logs in + Then the asset is viewable # Uploading isn't working on safari with sauce labs @skip_safari Scenario: Unlocked assets can be viewed by anyone - Given I have opened a course with a unlocked asset "unlocked.html" - Then the asset "unlocked.html" can be clicked from the asset index - And I log out - Then the asset "unlocked.html" is viewable + Given I have created a course with a unlocked asset + When I log out + Then the asset is viewable diff --git a/cms/djangoapps/contentstore/features/upload.py b/cms/djangoapps/contentstore/features/upload.py index b94ccd114a..25e33f7e5e 100644 --- a/cms/djangoapps/contentstore/features/upload.py +++ b/cms/djangoapps/contentstore/features/upload.py @@ -2,14 +2,17 @@ #pylint: disable=W0621 from lettuce import world, step +from lettuce.django import django_url from django.conf import settings import requests import string import random import os +from django.contrib.auth.models import User +from student.models import CourseEnrollment +from splinter.request_handler.status_code import HttpResponseError from nose.tools import assert_equal, assert_not_equal # pylint: disable=E0611 - TEST_ROOT = settings.COMMON_TEST_DATA_ROOT ASSET_NAMES_CSS = 'td.name-col > span.title > a.filename' @@ -26,7 +29,10 @@ def go_to_uploads(_step): def upload_file(_step, file_name): upload_css = 'a.upload-button' world.css_click(upload_css) - #uploading the file itself + + _write_test_file(file_name, "test file") + + # uploading the file itself path = os.path.join(TEST_ROOT, 'uploads/', file_name) world.browser.execute_script("$('input.file-input').css('display', 'block')") world.browser.attach_file('file', os.path.abspath(path)) @@ -34,19 +40,20 @@ def upload_file(_step, file_name): world.css_click(close_css) -@step(u'I upload the files (".*")$') +@step(u'I upload the files "([^"]*)"$') def upload_files(_step, files_string): - # Turn files_string to a list of file names + # files_string should be comma separated with no spaces. files = files_string.split(",") - files = map(lambda x: string.strip(x, ' "\''), files) - upload_css = 'a.upload-button' world.css_click(upload_css) - #uploading the files - for f in files: - path = os.path.join(TEST_ROOT, 'uploads/', f) + + # uploading the files + for filename in files: + _write_test_file(filename, "test file") + path = os.path.join(TEST_ROOT, 'uploads/', filename) world.browser.execute_script("$('input.file-input').css('display', 'block')") world.browser.attach_file('file', os.path.abspath(path)) + close_css = 'a.close-button' world.css_click(close_css) @@ -104,13 +111,13 @@ def check_download(_step, file_name): r = get_file(file_name) downloaded_text = r.text assert cur_text == downloaded_text - #resetting the file back to its original state + # resetting the file back to its original state _write_test_file(file_name, "This is an arbitrary file for testing uploads") def _write_test_file(file_name, text): path = os.path.join(TEST_ROOT, 'uploads/', file_name) - #resetting the file back to its original state + # resetting the file back to its original state with open(os.path.abspath(path), 'w') as cur_file: cur_file.write(text) @@ -121,68 +128,68 @@ def modify_upload(_step, file_name): _write_test_file(file_name, new_text) -@step(u'I (lock|unlock) "([^"]*)"$') -def lock_unlock_file(_step, _lock_state, file_name): - index = get_index(file_name) - assert index != -1 +@step(u'I upload an asset$') +def upload_an_asset(step): + step.given('I upload the file "asset.html"') + + +@step(u'I (lock|unlock) the asset$') +def lock_unlock_file(_step, _lock_state): + index = get_index('asset.html') + assert index != -1, 'Expected to find an asset but could not.' + + # Warning: this is a misnomer, it really only toggles the + # lock state. TODO: fix it. lock_css = "input.lock-checkbox" world.css_find(lock_css)[index].click() -@step(u'Then "([^"]*)" is (locked|unlocked)$') -def verify_lock_unlock_file(_step, file_name, lock_state): - index = get_index(file_name) - assert index != -1 +@step(u'the user "([^"]*)" is enrolled in the course$') +def user_foo_is_enrolled_in_the_course(step, name): + world.create_user(name, 'test') + user = User.objects.get(username=name) + + course_id = world.scenario_dict['COURSE'].location.course_id + CourseEnrollment.enroll(user, course_id) + + +@step(u'Then the asset is (locked|unlocked)$') +def verify_lock_unlock_file(_step, lock_state): + index = get_index('asset.html') + assert index != -1, 'Expected to find an asset but could not.' lock_css = "input.lock-checkbox" checked = world.css_find(lock_css)[index]._element.get_attribute('checked') assert_equal(lock_state == "locked", bool(checked)) -@step(u'I have opened a course with a (locked|unlocked) asset "([^"]*)"$') -def open_course_with_locked(step, lock_state, file_name): +@step(u'I am at the files and upload page of a Studio course') +def at_upload_page(step): step.given('I have opened a new course in studio') step.given('I go to the files and uploads page') - _write_test_file(file_name, "test file") - step.given('I upload the file "' + file_name + '"') + + +@step(u'I have created a course with a (locked|unlocked) asset$') +def open_course_with_locked(step, lock_state): + step.given('I am at the files and upload page of a Studio course') + step.given('I upload the file "asset.html"') + if lock_state == "locked": - step.given('I lock "' + file_name + '"') + step.given('I lock the asset') step.given('I reload the page') -@step(u'Then the asset "([^"]*)" is (viewable|protected)$') -def view_asset(_step, file_name, status): - url = '/c4x/MITx/999/asset/' + file_name +@step(u'Then the asset is (viewable|protected)$') +def view_asset(_step, status): + url = django_url('/c4x/MITx/999/asset/asset.html') if status == 'viewable': - world.visit(url) - _verify_body_text() + expected_text = 'test file' else: - error_thrown = False - try: - world.visit(url) - except Exception as e: - assert e.status_code == 403 - error_thrown = True - assert error_thrown + expected_text = 'Unauthorized' - -@step(u'Then the asset "([^"]*)" can be clicked from the asset index$') -def click_asset_from_index(step, file_name): - # This is not ideal, but I'm having trouble with the middleware not having - # the same user in the request when I hit the URL directly. - course_link_css = 'a.course-link' - world.css_click(course_link_css) - step.given("I go to the files and uploads page") - index = get_index(file_name) - assert index != -1 - world.css_click('a.filename', index=index) - _verify_body_text() - - -def _verify_body_text(): - def verify_text(driver): - return world.css_text('body') == 'test file' - - world.wait_for(verify_text) + # Note that world.visit would trigger a 403 error instead of displaying "Unauthorized" + # Instead, we can drop back into the selenium driver get command. + world.browser.driver.get(url) + assert_equal(world.css_text('body'),expected_text) @step('I see a confirmation that the file was deleted$') diff --git a/common/djangoapps/terrain/course_helpers.py b/common/djangoapps/terrain/course_helpers.py index fc01d25d66..22222d30a4 100644 --- a/common/djangoapps/terrain/course_helpers.py +++ b/common/djangoapps/terrain/course_helpers.py @@ -2,17 +2,10 @@ # pylint: disable=W0621 from lettuce import world -from .factories import * -from django.conf import settings -from django.http import HttpRequest from django.contrib.auth.models import User -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 editable_modulestore from xmodule.contentstore.django import contentstore -from urllib import quote_plus @world.absorb @@ -22,7 +15,7 @@ def create_user(uname, password): if len(User.objects.filter(username=uname)) > 0: return - portal_user = UserFactory.build(username=uname, email=uname + '@edx.org') + portal_user = world.UserFactory.build(username=uname, email=uname + '@edx.org') portal_user.set_password(password) portal_user.save() @@ -30,7 +23,7 @@ def create_user(uname, password): registration.register(portal_user) registration.activate() - user_profile = world.UserProfileFactory(user=portal_user) + world.UserProfileFactory(user=portal_user) @world.absorb diff --git a/common/test/data/uploads/.gitignore b/common/test/data/uploads/.gitignore new file mode 100644 index 0000000000..a85ef7b7f3 --- /dev/null +++ b/common/test/data/uploads/.gitignore @@ -0,0 +1,3 @@ +test +test2 +asset.html diff --git a/common/test/data/uploads/test b/common/test/data/uploads/test deleted file mode 100644 index 588e9fb125..0000000000 --- a/common/test/data/uploads/test +++ /dev/null @@ -1 +0,0 @@ -This is an arbitrary file for testing uploads \ No newline at end of file