Instructor Dashboard management tools for Entrance Exams
Conflicts: lms/djangoapps/courseware/courses.py lms/djangoapps/instructor_task/api.py refined entrance exam student attempts reset added rescore, delete state and task history functionality
This commit is contained in:
@@ -102,7 +102,7 @@ class CourseFixture(XBlockContainerFixture):
|
||||
between tests, you should use unique course identifiers for each fixture.
|
||||
"""
|
||||
|
||||
def __init__(self, org, number, run, display_name, start_date=None, end_date=None):
|
||||
def __init__(self, org, number, run, display_name, start_date=None, end_date=None, settings=None):
|
||||
"""
|
||||
Configure the course fixture to create a course with
|
||||
|
||||
@@ -112,6 +112,8 @@ class CourseFixture(XBlockContainerFixture):
|
||||
The default is for the course to have started in the distant past, which is generally what
|
||||
we want for testing so students can enroll.
|
||||
|
||||
`settings` can be any additional course settings needs to be enabled. for example
|
||||
to enable entrance exam settings would be a dict like this {"entrance_exam_enabled": "true"}
|
||||
These have the same meaning as in the Studio restful API /course end-point.
|
||||
"""
|
||||
super(CourseFixture, self).__init__()
|
||||
@@ -134,6 +136,9 @@ class CourseFixture(XBlockContainerFixture):
|
||||
if end_date is not None:
|
||||
self._course_details['end_date'] = end_date.isoformat()
|
||||
|
||||
if settings is not None:
|
||||
self._course_details.update(settings)
|
||||
|
||||
self._updates = []
|
||||
self._handouts = []
|
||||
self._assets = []
|
||||
|
||||
@@ -37,6 +37,15 @@ class InstructorDashboardPage(CoursePage):
|
||||
data_download_section.wait_for_page()
|
||||
return data_download_section
|
||||
|
||||
def select_student_admin(self):
|
||||
"""
|
||||
Selects the student admin tab and returns the MembershipSection
|
||||
"""
|
||||
self.q(css='a[data-section=student_admin]').first.click()
|
||||
student_admin_section = StudentAdminPage(self.browser)
|
||||
student_admin_section.wait_for_page()
|
||||
return student_admin_section
|
||||
|
||||
@staticmethod
|
||||
def get_asset_path(file_name):
|
||||
"""
|
||||
@@ -460,3 +469,126 @@ class DataDownloadPage(PageObject):
|
||||
"""
|
||||
reports = self.q(css="#report-downloads-table .file-download-link>a").map(lambda el: el.text)
|
||||
return reports.results
|
||||
|
||||
|
||||
class StudentAdminPage(PageObject):
|
||||
"""
|
||||
Student admin section of the Instructor dashboard.
|
||||
"""
|
||||
url = None
|
||||
EE_CONTAINER = ".entrance-exam-grade-container"
|
||||
|
||||
def is_browser_on_page(self):
|
||||
"""
|
||||
Confirms student admin section is present
|
||||
"""
|
||||
return self.q(css='a[data-section=student_admin].active-section').present
|
||||
|
||||
@property
|
||||
def student_email_input(self):
|
||||
"""
|
||||
Returns email address/username input box.
|
||||
"""
|
||||
return self.q(css='{} input[name=entrance-exam-student-select-grade]'.format(self.EE_CONTAINER))
|
||||
|
||||
@property
|
||||
def reset_attempts_button(self):
|
||||
"""
|
||||
Returns reset student attempts button.
|
||||
"""
|
||||
return self.q(css='{} input[name=reset-entrance-exam-attempts]'.format(self.EE_CONTAINER))
|
||||
|
||||
@property
|
||||
def rescore_submission_button(self):
|
||||
"""
|
||||
Returns rescore student submission button.
|
||||
"""
|
||||
return self.q(css='{} input[name=rescore-entrance-exam]'.format(self.EE_CONTAINER))
|
||||
|
||||
@property
|
||||
def delete_student_state_button(self):
|
||||
"""
|
||||
Returns delete student state button.
|
||||
"""
|
||||
return self.q(css='{} input[name=delete-entrance-exam-state]'.format(self.EE_CONTAINER))
|
||||
|
||||
@property
|
||||
def background_task_history_button(self):
|
||||
"""
|
||||
Returns show background task history for student button.
|
||||
"""
|
||||
return self.q(css='{} input[name=entrance-exam-task-history]'.format(self.EE_CONTAINER))
|
||||
|
||||
@property
|
||||
def top_notification(self):
|
||||
"""
|
||||
Returns show background task history for student button.
|
||||
"""
|
||||
return self.q(css='{} .request-response-error'.format(self.EE_CONTAINER)).first
|
||||
|
||||
def is_student_email_input_visible(self):
|
||||
"""
|
||||
Returns True if student email address/username input box is present.
|
||||
"""
|
||||
return self.student_email_input.is_present()
|
||||
|
||||
def is_reset_attempts_button_visible(self):
|
||||
"""
|
||||
Returns True if reset student attempts button is present.
|
||||
"""
|
||||
return self.reset_attempts_button.is_present()
|
||||
|
||||
def is_rescore_submission_button_visible(self):
|
||||
"""
|
||||
Returns True if rescore student submission button is present.
|
||||
"""
|
||||
return self.rescore_submission_button.is_present()
|
||||
|
||||
def is_delete_student_state_button_visible(self):
|
||||
"""
|
||||
Returns True if delete student state for entrance exam button is present.
|
||||
"""
|
||||
return self.delete_student_state_button.is_present()
|
||||
|
||||
def is_background_task_history_button_visible(self):
|
||||
"""
|
||||
Returns True if show background task history for student button is present.
|
||||
"""
|
||||
return self.background_task_history_button.is_present()
|
||||
|
||||
def is_background_task_history_table_visible(self):
|
||||
"""
|
||||
Returns True if background task history table is present.
|
||||
"""
|
||||
return self.q(css='{} .entrance-exam-task-history-table'.format(self.EE_CONTAINER)).is_present()
|
||||
|
||||
def click_reset_attempts_button(self):
|
||||
"""
|
||||
clicks reset student attempts button.
|
||||
"""
|
||||
return self.reset_attempts_button.click()
|
||||
|
||||
def click_rescore_submissions_button(self):
|
||||
"""
|
||||
clicks rescore submissions button.
|
||||
"""
|
||||
return self.rescore_submission_button.click()
|
||||
|
||||
def click_delete_student_state_button(self):
|
||||
"""
|
||||
clicks delete student state button.
|
||||
"""
|
||||
return self.delete_student_state_button.click()
|
||||
|
||||
def click_task_history_button(self):
|
||||
"""
|
||||
clicks background task history button.
|
||||
"""
|
||||
return self.background_task_history_button.click()
|
||||
|
||||
def set_student_email(self, email_addres):
|
||||
"""
|
||||
Sets given email address as value of student email address/username input box.
|
||||
"""
|
||||
input_box = self.student_email_input.first.results[0]
|
||||
input_box.send_keys(email_addres)
|
||||
|
||||
@@ -13,6 +13,8 @@ from opaque_keys.edx.locator import CourseLocator
|
||||
from xmodule.partitions.partitions import UserPartition
|
||||
from xmodule.partitions.tests.test_partitions import MockUserPartitionScheme
|
||||
from selenium.webdriver.support.select import Select
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
|
||||
def skip_if_browser(browser):
|
||||
@@ -252,6 +254,15 @@ def assert_event_emitted_num_times(event_collection, event_name, event_time, eve
|
||||
)
|
||||
|
||||
|
||||
def get_modal_alert(browser):
|
||||
"""
|
||||
Returns instance of modal alert box shown in browser after waiting
|
||||
for 4 seconds
|
||||
"""
|
||||
WebDriverWait(browser, 4).until(EC.alert_is_present())
|
||||
return browser.switch_to.alert
|
||||
|
||||
|
||||
class UniqueCourseTest(WebAppTest):
|
||||
"""
|
||||
Test that provides a unique course ID.
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
End-to-end tests for the LMS Instructor Dashboard.
|
||||
"""
|
||||
|
||||
from ..helpers import UniqueCourseTest
|
||||
from ..helpers import UniqueCourseTest, get_modal_alert
|
||||
from ...pages.common.logout import LogoutPage
|
||||
from ...pages.lms.auto_auth import AutoAuthPage
|
||||
from ...pages.lms.instructor_dashboard import InstructorDashboardPage
|
||||
from ...fixtures.course import CourseFixture
|
||||
@@ -84,3 +85,166 @@ class AutoEnrollmentWithCSVTest(UniqueCourseTest):
|
||||
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), "Make sure that the file you upload is in CSV format with no extraneous characters or rows.")
|
||||
|
||||
|
||||
class EntranceExamGradeTest(UniqueCourseTest):
|
||||
"""
|
||||
Tests for Entrance exam specific student grading tasks.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(EntranceExamGradeTest, self).setUp()
|
||||
self.course_info.update({"settings": {"entrance_exam_enabled": "true"}})
|
||||
CourseFixture(**self.course_info).install()
|
||||
self.student_identifier = "johndoe_saee@example.com"
|
||||
# Create the user (automatically logs us in)
|
||||
AutoAuthPage(
|
||||
self.browser,
|
||||
username="johndoe_saee",
|
||||
email=self.student_identifier,
|
||||
course_id=self.course_id,
|
||||
staff=False
|
||||
).visit()
|
||||
|
||||
LogoutPage(self.browser).visit()
|
||||
|
||||
# login as an instructor
|
||||
AutoAuthPage(self.browser, course_id=self.course_id, staff=True).visit()
|
||||
|
||||
# go to the student admin page on the instructor dashboard
|
||||
instructor_dashboard_page = InstructorDashboardPage(self.browser, self.course_id)
|
||||
instructor_dashboard_page.visit()
|
||||
self.student_admin_section = instructor_dashboard_page.select_student_admin()
|
||||
|
||||
def test_input_text_and_buttons_are_visible(self):
|
||||
"""
|
||||
Scenario: On the Student admin tab of the Instructor Dashboard, Student Email input box,
|
||||
Reset Student Attempt, Rescore Student Submission, Delete Student State for entrance exam
|
||||
and Show Background Task History for Student buttons are visible
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
Then I see Student Email input box, Reset Student Attempt, Rescore Student Submission,
|
||||
Delete Student State for entrance exam and Show Background Task History for Student buttons
|
||||
"""
|
||||
self.assertTrue(self.student_admin_section.is_student_email_input_visible())
|
||||
self.assertTrue(self.student_admin_section.is_reset_attempts_button_visible())
|
||||
self.assertTrue(self.student_admin_section.is_rescore_submission_button_visible())
|
||||
self.assertTrue(self.student_admin_section.is_delete_student_state_button_visible())
|
||||
self.assertTrue(self.student_admin_section.is_background_task_history_button_visible())
|
||||
|
||||
def test_clicking_reset_student_attempts_button_without_email_shows_error(self):
|
||||
"""
|
||||
Scenario: Clicking on the Reset Student Attempts button without entering student email
|
||||
address or username results in error.
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
When I click the Reset Student Attempts Button under Entrance Exam Grade
|
||||
Adjustment without enter an email address
|
||||
Then I should be shown an Error Notification
|
||||
And The Notification message should read 'Please enter a student email address or username.'
|
||||
"""
|
||||
self.student_admin_section.click_reset_attempts_button()
|
||||
self.assertEqual(
|
||||
'Please enter a student email address or username.',
|
||||
self.student_admin_section.top_notification.text[0]
|
||||
)
|
||||
|
||||
def test_clicking_reset_student_attempts_button_with_success(self):
|
||||
"""
|
||||
Scenario: Clicking on the Reset Student Attempts button with valid student email
|
||||
address or username should result in success prompt.
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
When I click the Reset Student Attempts Button under Entrance Exam Grade
|
||||
Adjustment after entering a valid student
|
||||
email address or username
|
||||
Then I should be shown an alert with success message
|
||||
"""
|
||||
self.student_admin_section.set_student_email(self.student_identifier)
|
||||
self.student_admin_section.click_reset_attempts_button()
|
||||
alert = get_modal_alert(self.student_admin_section.browser)
|
||||
alert.dismiss()
|
||||
|
||||
def test_clicking_reset_student_attempts_button_with_error(self):
|
||||
"""
|
||||
Scenario: Clicking on the Reset Student Attempts button with email address or username
|
||||
of a non existing student should result in error message.
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
When I click the Reset Student Attempts Button under Entrance Exam Grade
|
||||
Adjustment after non existing student email address or username
|
||||
Then I should be shown an error message
|
||||
"""
|
||||
self.student_admin_section.set_student_email('non_existing@example.com')
|
||||
self.student_admin_section.click_reset_attempts_button()
|
||||
self.student_admin_section.wait_for_ajax()
|
||||
self.assertGreater(len(self.student_admin_section.top_notification.text[0]), 0)
|
||||
|
||||
def test_clicking_rescore_submission_button_with_success(self):
|
||||
"""
|
||||
Scenario: Clicking on the Rescore Student Submission button with valid student email
|
||||
address or username should result in success prompt.
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
When I click the Rescore Student Submission Button under Entrance Exam Grade
|
||||
Adjustment after entering a valid student email address or username
|
||||
Then I should be shown an alert with success message
|
||||
"""
|
||||
self.student_admin_section.set_student_email(self.student_identifier)
|
||||
self.student_admin_section.click_rescore_submissions_button()
|
||||
alert = get_modal_alert(self.student_admin_section.browser)
|
||||
alert.dismiss()
|
||||
|
||||
def test_clicking_rescore_submission_button_with_error(self):
|
||||
"""
|
||||
Scenario: Clicking on the Rescore Student Submission button with email address or username
|
||||
of a non existing student should result in error message.
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
When I click the Rescore Student Submission Button under Entrance Exam Grade
|
||||
Adjustment after non existing student email address or username
|
||||
Then I should be shown an error message
|
||||
"""
|
||||
self.student_admin_section.set_student_email('non_existing@example.com')
|
||||
self.student_admin_section.click_rescore_submissions_button()
|
||||
self.student_admin_section.wait_for_ajax()
|
||||
self.assertGreater(len(self.student_admin_section.top_notification.text[0]), 0)
|
||||
|
||||
def test_clicking_delete_student_attempts_button_with_success(self):
|
||||
"""
|
||||
Scenario: Clicking on the Delete Student State for entrance exam button
|
||||
with valid student email address or username should result in success prompt.
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
When I click the Delete Student State for entrance exam Button
|
||||
under Entrance Exam Grade Adjustment after entering a valid student
|
||||
email address or username
|
||||
Then I should be shown an alert with success message
|
||||
"""
|
||||
self.student_admin_section.set_student_email(self.student_identifier)
|
||||
self.student_admin_section.click_delete_student_state_button()
|
||||
alert = get_modal_alert(self.student_admin_section.browser)
|
||||
alert.dismiss()
|
||||
|
||||
def test_clicking_delete_student_attempts_button_with_error(self):
|
||||
"""
|
||||
Scenario: Clicking on the Delete Student State for entrance exam button
|
||||
with email address or username of a non existing student should result
|
||||
in error message.
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
When I click the Delete Student State for entrance exam Button
|
||||
under Entrance Exam Grade Adjustment after non existing student
|
||||
email address or username
|
||||
Then I should be shown an error message
|
||||
"""
|
||||
self.student_admin_section.set_student_email('non_existing@example.com')
|
||||
self.student_admin_section.click_delete_student_state_button()
|
||||
self.student_admin_section.wait_for_ajax()
|
||||
self.assertGreater(len(self.student_admin_section.top_notification.text[0]), 0)
|
||||
|
||||
def test_clicking_task_history_button_with_success(self):
|
||||
"""
|
||||
Scenario: Clicking on the Show Background Task History for Student
|
||||
with valid student email address or username should result in table of tasks.
|
||||
Given that I am on the Student Admin tab on the Instructor Dashboard
|
||||
When I click the Show Background Task History for Student Button
|
||||
under Entrance Exam Grade Adjustment after entering a valid student
|
||||
email address or username
|
||||
Then I should be shown an table listing all background tasks
|
||||
"""
|
||||
self.student_admin_section.set_student_email(self.student_identifier)
|
||||
self.student_admin_section.click_task_history_button()
|
||||
self.assertTrue(self.student_admin_section.is_background_task_history_table_visible())
|
||||
|
||||
Reference in New Issue
Block a user