Commit 4a3700c1 by chrisndodge

Merge pull request #5969 from edx/muhhshoaib/WL-124-front-end-tests-for-auto-enrollment

muhhshoaib/wl 124 add front end tests for autoenrollment
parents ec552cf1 97b45cc2
......@@ -5,6 +5,7 @@ Instructor (2) dashboard page.
from bok_choy.page_object import PageObject
from .course_page import CoursePage
import os
class InstructorDashboardPage(CoursePage):
......@@ -12,7 +13,6 @@ class InstructorDashboardPage(CoursePage):
Instructor dashboard, where course staff can manage a course.
"""
url_path = "instructor"
def is_browser_on_page(self):
return self.q(css='div.instructor-dashboard-wrapper-2').present
......@@ -31,10 +31,15 @@ class MembershipPage(PageObject):
Membership section of the Instructor dashboard.
"""
url = None
def is_browser_on_page(self):
return self.q(css='a[data-section=membership].active-section').present
def select_auto_enroll_section(self):
"""
returns the MembershipPageAutoEnrollSection
"""
return MembershipPageAutoEnrollSection(self.browser)
def _get_cohort_options(self):
"""
Returns the available options in the cohort dropdown, including the initial "Select a cohort group".
......@@ -154,6 +159,106 @@ class MembershipPage(PageObject):
self.q(css="a.link-cross-reference[data-section=data_download]").first.click()
class MembershipPageAutoEnrollSection(PageObject):
"""
CSV Auto Enroll section of the Membership tab of the Instructor dashboard.
"""
url = None
auto_enroll_browse_button_selector = '.auto_enroll_csv .file-browse input.file_field#browseBtn'
auto_enroll_upload_button_selector = '.auto_enroll_csv button[name="enrollment_signup_button"]'
NOTIFICATION_ERROR = 'error'
NOTIFICATION_WARNING = 'warning'
NOTIFICATION_SUCCESS = 'confirmation'
def is_browser_on_page(self):
return self.q(css=self.auto_enroll_browse_button_selector).present
def is_file_attachment_browse_button_visible(self):
"""
Returns True if the Auto-Enroll Browse button is present.
"""
return self.q(css=self.auto_enroll_browse_button_selector).is_present()
def is_upload_button_visible(self):
"""
Returns True if the Auto-Enroll Upload button is present.
"""
return self.q(css=self.auto_enroll_upload_button_selector).is_present()
def click_upload_file_button(self):
"""
Clicks the Auto-Enroll Upload Button.
"""
self.q(css=self.auto_enroll_upload_button_selector).click()
def is_notification_displayed(self, section_type):
"""
Valid inputs for section_type: MembershipPageAutoEnrollSection.NOTIFICATION_SUCCESS /
MembershipPageAutoEnrollSection.NOTIFICATION_WARNING /
MembershipPageAutoEnrollSection.NOTIFICATION_ERROR
Returns True if a {section_type} notification is displayed.
"""
notification_selector = '.auto_enroll_csv .results .message-%s' % section_type
self.wait_for_element_presence(notification_selector, "%s Notification" % section_type.title())
return self.q(css=notification_selector).is_present()
def first_notification_message(self, section_type):
"""
Valid inputs for section_type: MembershipPageAutoEnrollSection.NOTIFICATION_WARNING /
MembershipPageAutoEnrollSection.NOTIFICATION_ERROR
Returns the first message from the list of messages in the {section_type} section.
"""
error_message_selector = '.auto_enroll_csv .results .message-%s li.summary-item' % section_type
self.wait_for_element_presence(error_message_selector, "%s message" % section_type.title())
return self.q(css=error_message_selector).text[0]
def get_asset_path(self, file_name):
"""
Returns the full path of the file to upload.
These files have been placed in edx-platform/common/test/data/uploads/
"""
# Separate the list of folders in the path reaching to the current file,
# e.g. '... common/test/acceptance/pages/lms/instructor_dashboard.py' will result in
# [..., 'common', 'test', 'acceptance', 'pages', 'lms', 'instructor_dashboard.py']
folders_list_in_path = __file__.split(os.sep)
# Get rid of the last 4 elements: 'acceptance', 'pages', 'lms', and 'instructor_dashboard.py'
# to point to the 'test' folder, a shared point in the path's tree.
folders_list_in_path = folders_list_in_path[:-4]
# Append the folders in the asset's path
folders_list_in_path.extend(['data', 'uploads', file_name])
# Return the joined path of the required asset.
return os.sep.join(folders_list_in_path)
def upload_correct_csv_file(self):
"""
Selects the correct file and clicks the upload button.
"""
correct_files_path = self.get_asset_path('auto_reg_enrollment.csv')
self.q(css=self.auto_enroll_browse_button_selector).results[0].send_keys(correct_files_path)
self.click_upload_file_button()
def upload_csv_file_with_errors_warnings(self):
"""
Selects the file which will generate errors and warnings and clicks the upload button.
"""
errors_warnings_files_path = self.get_asset_path('auto_reg_enrollment_errors_warnings.csv')
self.q(css=self.auto_enroll_browse_button_selector).results[0].send_keys(errors_warnings_files_path)
self.click_upload_file_button()
def upload_non_csv_file(self):
"""
Selects an image file and clicks the upload button.
"""
errors_warnings_files_path = self.get_asset_path('image.jpg')
self.q(css=self.auto_enroll_browse_button_selector).results[0].send_keys(errors_warnings_files_path)
self.click_upload_file_button()
class DataDownloadPage(PageObject):
"""
Data Download section of the Instructor dashboard.
......
# -*- coding: utf-8 -*-
"""
End-to-end tests for the LMS Instructor Dashboard.
"""
from ..helpers import UniqueCourseTest
from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.instructor_dashboard import InstructorDashboardPage
from ...fixtures.course import CourseFixture
class AutoEnrollmentWithCSVTest(UniqueCourseTest):
"""
End-to-end tests for Auto-Registration and enrollment functionality via CSV file.
"""
def setUp(self):
super(AutoEnrollmentWithCSVTest, self).setUp()
self.course_fixture = CourseFixture(**self.course_info).install()
# login as an instructor
AutoAuthPage(self.browser, course_id=self.course_id, staff=True).visit()
# go to the membership page on the instructor dashboard
instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
instructor_dashboard_page.visit()
self.auto_enroll_section = instructor_dashboard_page.select_membership().select_auto_enroll_section()
def test_browse_and_upload_buttons_are_visible(self):
"""
Scenario: On the Membership tab of the Instructor Dashboard, Auto-Enroll Browse and Upload buttons are visible.
Given that I am on the Membership tab on the Instructor Dashboard
Then I see the 'REGISTER/ENROLL STUDENTS' section on the page with the 'Browse' and 'Upload' buttons
"""
self.assertTrue(self.auto_enroll_section.is_file_attachment_browse_button_visible())
self.assertTrue(self.auto_enroll_section.is_upload_button_visible())
def test_clicking_file_upload_button_without_file_shows_error(self):
"""
Scenario: Clicking on the upload button without specifying a CSV file results in error.
Given that I am on the Membership tab on the Instructor Dashboard
When I click the Upload Button without specifying a CSV file
Then I should be shown an Error Notification
And The Notification message should read 'File is not attached.'
"""
self.auto_enroll_section.click_upload_file_button()
self.assertTrue(self.auto_enroll_section.is_notification_displayed(section_type=self.auto_enroll_section.NOTIFICATION_ERROR))
self.assertEqual(self.auto_enroll_section.first_notification_message(section_type=self.auto_enroll_section.NOTIFICATION_ERROR), "File is not attached.")
def test_uploading_correct_csv_file_results_in_success(self):
"""
Scenario: Uploading a CSV with correct data results in Success.
Given that I am on the Membership tab on the Instructor Dashboard
When I select a csv file with correct data and click the Upload Button
Then I should be shown a Success Notification.
"""
self.auto_enroll_section.upload_correct_csv_file()
self.assertTrue(self.auto_enroll_section.is_notification_displayed(section_type=self.auto_enroll_section.NOTIFICATION_SUCCESS))
def test_uploading_csv_file_with_bad_data_results_in_errors_and_warnings(self):
"""
Scenario: Uploading a CSV with incorrect data results in error and warnings.
Given that I am on the Membership tab on the Instructor Dashboard
When I select a csv file with incorrect data and click the Upload Button
Then I should be shown an Error Notification
And a corresponding Error Message.
And I should be shown a Warning Notification
And a corresponding Warning Message.
"""
self.auto_enroll_section.upload_csv_file_with_errors_warnings()
self.assertTrue(self.auto_enroll_section.is_notification_displayed(section_type=self.auto_enroll_section.NOTIFICATION_ERROR))
self.assertEqual(self.auto_enroll_section.first_notification_message(section_type=self.auto_enroll_section.NOTIFICATION_ERROR), "Data in row #2 must have exactly four columns: email, username, full name, and country")
self.assertTrue(self.auto_enroll_section.is_notification_displayed(section_type=self.auto_enroll_section.NOTIFICATION_WARNING))
self.assertEqual(self.auto_enroll_section.first_notification_message(section_type=self.auto_enroll_section.NOTIFICATION_WARNING), "ename (d@a.com): (An account with email d@a.com exists but the provided username ename is different. Enrolling anyway with d@a.com.)")
def test_uploading_non_csv_file_results_in_error(self):
"""
Scenario: Uploading an image file for auto-enrollment results in error.
Given that I am on the Membership tab on the Instructor Dashboard
When I select an image file (a non-csv file) and click the Upload Button
Then I should be shown an Error Notification
And The Notification message should read 'Could not read uploaded file.'
"""
self.auto_enroll_section.upload_non_csv_file()
self.assertTrue(self.auto_enroll_section.is_notification_displayed(section_type=self.auto_enroll_section.NOTIFICATION_ERROR))
self.assertEqual(self.auto_enroll_section.first_notification_message(section_type=self.auto_enroll_section.NOTIFICATION_ERROR), "Could not read uploaded file.")
a@a.com,aname,aname,PK
b@a.com,bname,bname,PK
c@a.com,cname,cname,PK
d@a.com,dname,dname,PK
a@a.com,missing_data
d@a.com,ename,ename,PK
e@a.com,dname,dname,PK
......@@ -72,7 +72,8 @@
"ENABLE_COMBINED_LOGIN_REGISTRATION": true,
"PREVIEW_LMS_BASE": "localhost:8003",
"SUBDOMAIN_BRANDING": false,
"SUBDOMAIN_COURSE_LISTINGS": false
"SUBDOMAIN_COURSE_LISTINGS": false,
"ALLOW_AUTOMATED_SIGNUPS": true
},
"FEEDBACK_SUBMISSION_EMAIL": "",
"GITHUB_REPO_ROOT": "** OVERRIDDEN **",
......
<div class="auto_enroll auto_enroll_csv">
<h2> ${_("Register/Enroll Students")} </h2>
<p>
${_("To register and enroll a list of users in this course, choose a CSV file that contains the following columns in this exact order: email, username, name, and country. Please include one student per row and do not include any headers, footers, or blank lines.")}
</p>
<form id="student-auto-enroll-form">
<div class="customBrowseBtn">
<input disabled="disabled" id="browseFile" placeholder="choose file"/>
<div class="file-browse btn btn-primary">
<span class="browse"> Browse </span>
<input class="file_field" id="browseBtn" name="students_list" type="file" accept=".csv"/>
</div>
</div>
<button type="submit" name="enrollment_signup_button">${_("Upload CSV")}</button>
</form>
<div class="results"></div>
</div>
\ No newline at end of file
describe 'AutoEnrollment', ->
beforeEach ->
loadFixtures 'coffee/fixtures/autoenrollment.html'
@autoenrollment = new AutoEnrollmentViaCsv $('.auto_enroll_csv')
it 'binds to the enrollment_signup_button on click event', ->
expect(@autoenrollment.$enrollment_signup_button).toHandle 'click'
it 'binds to the browse button on change event', ->
expect(@autoenrollment.$browse_button).toHandle 'change'
it 'binds the ajax call and the result will be success', ->
spyOn($, "ajax").andCallFake((params) =>
params.success({row_errors: [], general_errors: [], warnings: []})
{always: ->}
)
# mock the render_notification_view which returns the html (since we are only using the existing notification model)
@autoenrollment.render_notification_view = jasmine.createSpy("render_notification_view(type, title, message, details) spy").andCallFake =>
return '<div><div class="message message-confirmation"><h3 class="message-title">Success</h3><div class="message-copy"><p>All accounts were created successfully.</p></div></div><div>'
submitCallback = jasmine.createSpy().andReturn()
@autoenrollment.$student_enrollment_form.submit(submitCallback)
@autoenrollment.$enrollment_signup_button.click()
expect($('.results .message-copy').text()).toEqual('All accounts were created successfully.')
expect(submitCallback).toHaveBeenCalled()
it 'binds the ajax call and the result will be error', ->
spyOn($, "ajax").andCallFake((params) =>
params.success({
row_errors: [{
'username': 'testuser1',
'email': 'testemail1@email.com',
'response': 'Username already exists'
}],
general_errors: [{
'response': 'cannot read the line 2'
}],
warnings: []
})
{always: ->}
)
# mock the render_notification_view which returns the html (since we are only using the existing notification model)
@autoenrollment.render_notification_view = jasmine.createSpy("render_notification_view(type, title, message, details) spy").andCallFake =>
return '<div><div class="message message-error"><h3 class="message-title">Errors</h3><div class="message-copy"><p>The following errors were generated:</p><ul class="list-summary summary-items"><li class="summary-item">cannot read the line 2</li><li class="summary-item">testuser1 (testemail1@email.com): (Username already exists)</li></ul></div></div></div>'
submitCallback = jasmine.createSpy().andReturn()
@autoenrollment.$student_enrollment_form.submit(submitCallback)
@autoenrollment.$enrollment_signup_button.click()
expect($('.results .list-summary').text()).toEqual('cannot read the line 2testuser1 (testemail1@email.com): (Username already exists)');
expect(submitCallback).toHaveBeenCalled()
it 'binds the ajax call and the result will be warnings', ->
spyOn($, "ajax").andCallFake((params) =>
params.success({
row_errors: [],
general_errors: [],
warnings: [{
'username': 'user1',
'email': 'user1email',
'response': 'email is in valid'
}]
})
{always: ->}
)
# mock the render_notification_view which returns the html (since we are only using the existing notification model)
@autoenrollment.render_notification_view = jasmine.createSpy("render_notification_view(type, title, message, details) spy").andCallFake =>
return '<div><div class="message message-warning"><h3 class="message-title">Warnings</h3><div class="message-copy"><p>The following warnings were generated:</p><ul class="list-summary summary-items"><li class="summary-item">user1 (user1email): (email is in valid)</li></ul></div></div></div>'
submitCallback = jasmine.createSpy().andReturn()
@autoenrollment.$student_enrollment_form.submit(submitCallback)
@autoenrollment.$enrollment_signup_button.click()
expect($('.results .list-summary').text()).toEqual('user1 (user1email): (email is in valid)')
expect(submitCallback).toHaveBeenCalled()
\ No newline at end of file
......@@ -174,7 +174,7 @@ class AuthListWidget extends MemberListWidget
else
@reload_list()
class AutoEnrollmentViaCsv
class @AutoEnrollmentViaCsv
constructor: (@$container) ->
# Wrapper for the AutoEnrollmentViaCsv subsection.
# This object handles buttons, success and failure reporting,
......@@ -220,7 +220,6 @@ class AutoEnrollmentViaCsv
@$results.empty()
errors = []
warnings = []
result_from_server_is_success = true
if data_from_server.general_errors.length
......@@ -241,41 +240,35 @@ class AutoEnrollmentViaCsv
warning['is_general_error'] = false
warnings.push warning
render_response = (label, type, student_results) =>
if type is 'success'
task_res_section = $ '<div/>', class: 'message message-confirmation'
message_title = $ '<h3/>', class: 'message-title', text: label
task_res_section.append message_title
@$results.append task_res_section
return
if type is 'error'
task_res_section = $ '<div/>', class: 'message message-error'
if type is 'warning'
task_res_section = $ '<div/>', class: 'message message-warning'
message_title = $ '<h3/>', class: 'message-title', text: label
task_res_section. append message_title
messages_copy = $ '<div/>', class: 'message-copy'
task_res_section. append messages_copy
messages_summary = $ '<ul/>', class: 'list-summary summary-items'
messages_copy.append messages_summary
render_response = (title, message, type, student_results) =>
details = []
for student_result in student_results
if student_result.is_general_error
response_message = student_result.response
details.push student_result.response
else
response_message = student_result.username + ' ('+ student_result.email + '): ' + ' (' + student_result.response + ')'
messages_summary.append $ '<li/>', class: 'summary-item', text: response_message
details.push response_message
@$results.append task_res_section
@$results.append @render_notification_view type, title, message, details
if errors.length
render_response gettext("The following errors were generated:"), 'error', errors
render_response gettext('Errors'), gettext("The following errors were generated:"), 'error', errors
if warnings.length
render_response gettext("The following warnings were generated:"), 'warning', warnings
render_response gettext('Warnings'), gettext("The following warnings were generated:"), 'warning', warnings
if result_from_server_is_success
render_response gettext("All accounts were created successfully."), 'success', []
render_response gettext('Success'), gettext("All accounts were created successfully."), 'confirmation', []
render_notification_view: (type, title, message, details) ->
notification_model = new NotificationModel()
notification_model.set({
'type': type,
'title': title,
'message': message,
'details': details,
});
view = new NotificationView(model:notification_model);
view.render()
return view.$el.html()
class BetaTesterBulkAddition
constructor: (@$container) ->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment