Merge pull request #9069 from edx/cdodge/proctoring-lms-integration
Integration of Proctoring into the LMS
This commit is contained in:
@@ -104,6 +104,36 @@ class CoursewarePage(CoursePage):
|
||||
"""
|
||||
return self.q(css='.chapter ul li.active a').attrs('href')[0]
|
||||
|
||||
@property
|
||||
def can_start_proctored_exam(self):
|
||||
"""
|
||||
Returns True if the timed/proctored exam timer bar is visible on the courseware.
|
||||
"""
|
||||
return self.q(css='button.start-timed-exam[data-start-immediately="false"]').is_present()
|
||||
|
||||
def start_timed_exam(self):
|
||||
"""
|
||||
clicks the start this timed exam link
|
||||
"""
|
||||
self.q(css=".xblock-student_view .timed-exam .start-timed-exam").first.click()
|
||||
self.wait_for_element_presence(".proctored_exam_status .exam-timer", "Timer bar")
|
||||
|
||||
def start_proctored_exam(self):
|
||||
"""
|
||||
clicks the start this timed exam link
|
||||
"""
|
||||
self.q(css='button.start-timed-exam[data-start-immediately="false"]').first.click()
|
||||
|
||||
# Wait for the unique exam code to appear.
|
||||
# elf.wait_for_element_presence(".proctored-exam-code", "unique exam code")
|
||||
|
||||
@property
|
||||
def is_timer_bar_present(self):
|
||||
"""
|
||||
Returns True if the timed/proctored exam timer bar is visible on the courseware.
|
||||
"""
|
||||
return self.q(css=".proctored_exam_status .exam-timer").is_present()
|
||||
|
||||
|
||||
class CoursewareSequentialTabPage(CoursePage):
|
||||
"""
|
||||
|
||||
@@ -66,6 +66,15 @@ class InstructorDashboardPage(CoursePage):
|
||||
certificates_section.wait_for_page()
|
||||
return certificates_section
|
||||
|
||||
def select_proctoring(self):
|
||||
"""
|
||||
Selects the proctoring tab and returns the ProctoringSection
|
||||
"""
|
||||
self.q(css='a[data-section=proctoring]').first.click()
|
||||
proctoring_section = ProctoringPage(self.browser)
|
||||
proctoring_section.wait_for_ajax()
|
||||
return proctoring_section
|
||||
|
||||
@staticmethod
|
||||
def get_asset_path(file_name):
|
||||
"""
|
||||
@@ -105,6 +114,40 @@ class MembershipPage(PageObject):
|
||||
return MembershipPageAutoEnrollSection(self.browser)
|
||||
|
||||
|
||||
class ProctoringPage(PageObject):
|
||||
"""
|
||||
Proctoring section of the Instructor dashboard.
|
||||
"""
|
||||
url = None
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='a[data-section=proctoring].active-section').present
|
||||
|
||||
def select_allowance_section(self):
|
||||
"""
|
||||
Expand the allowance section
|
||||
"""
|
||||
allowance_section = ProctoringPageAllowanceSection(self.browser)
|
||||
if not self.q(css="div.wrap #ui-accordion-proctoring-accordion-header-0[aria-selected=true]").present:
|
||||
self.q(css="div.wrap #ui-accordion-proctoring-accordion-header-0").click()
|
||||
self.wait_for_element_presence("div.wrap #ui-accordion-proctoring-accordion-header-0[aria-selected=true]",
|
||||
"Allowance Section")
|
||||
allowance_section.wait_for_page()
|
||||
return allowance_section
|
||||
|
||||
def select_exam_attempts_section(self):
|
||||
"""
|
||||
Expand the Student Attempts Section
|
||||
"""
|
||||
exam_attempts_section = ProctoringPageAttemptsSection(self.browser)
|
||||
if not self.q(css="div.wrap #ui-accordion-proctoring-accordion-header-1[aria-selected=true]").present:
|
||||
self.q(css="div.wrap #ui-accordion-proctoring-accordion-header-1").click()
|
||||
self.wait_for_element_presence("div.wrap #ui-accordion-proctoring-accordion-header-1[aria-selected=true]",
|
||||
"Attempts Section")
|
||||
exam_attempts_section.wait_for_page()
|
||||
return exam_attempts_section
|
||||
|
||||
|
||||
class CohortManagementSection(PageObject):
|
||||
"""
|
||||
The Cohort Management section of the Instructor dashboard.
|
||||
@@ -707,6 +750,55 @@ class MembershipPageAutoEnrollSection(PageObject):
|
||||
self.click_upload_file_button()
|
||||
|
||||
|
||||
class ProctoringPageAllowanceSection(PageObject):
|
||||
"""
|
||||
Allowance section of the Instructor dashboard's Proctoring tab.
|
||||
"""
|
||||
url = None
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css="div.wrap #ui-accordion-proctoring-accordion-header-0[aria-selected=true]").present
|
||||
|
||||
@property
|
||||
def is_add_allowance_button_visible(self):
|
||||
"""
|
||||
Returns True if the Add Allowance button is present.
|
||||
"""
|
||||
return self.q(css="a#add-allowance").present
|
||||
|
||||
|
||||
class ProctoringPageAttemptsSection(PageObject):
|
||||
"""
|
||||
Exam Attempts section of the Instructor dashboard's Proctoring tab.
|
||||
"""
|
||||
url = None
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css="div.wrap #ui-accordion-proctoring-accordion-header-1[aria-selected=true]").present
|
||||
|
||||
@property
|
||||
def is_search_text_field_visible(self):
|
||||
"""
|
||||
Returns True if the search field is present
|
||||
"""
|
||||
return self.q(css="#search_attempt_id").present
|
||||
|
||||
@property
|
||||
def is_student_attempt_visible(self):
|
||||
"""
|
||||
Returns True if a row with the Student's attempt is present
|
||||
"""
|
||||
return self.q(css="a.remove-attempt").present
|
||||
|
||||
def remove_student_attempt(self):
|
||||
"""
|
||||
Clicks the "x" to remove the Student's attempt.
|
||||
"""
|
||||
with self.handle_alert(confirm=True):
|
||||
self.q(css="a.remove-attempt").first.click()
|
||||
self.wait_for_element_absence("a.remove-attempt", "exam attempt")
|
||||
|
||||
|
||||
class DataDownloadPage(PageObject):
|
||||
"""
|
||||
Data Download section of the Instructor dashboard.
|
||||
|
||||
@@ -513,6 +513,60 @@ class CourseOutlinePage(CoursePage, CourseOutlineContainer):
|
||||
"""
|
||||
self.reindex_button.click()
|
||||
|
||||
def open_exam_settings_dialog(self):
|
||||
"""
|
||||
clicks on the settings button of subsection.
|
||||
"""
|
||||
self.q(css=".subsection-header-actions .configure-button").first.click()
|
||||
|
||||
def change_problem_release_date_in_studio(self):
|
||||
"""
|
||||
Sets a new start date
|
||||
"""
|
||||
self.q(css=".subsection-header-actions .configure-button").first.click()
|
||||
self.q(css="#start_date").fill("01/01/2030")
|
||||
self.q(css=".action-save").first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def make_exam_proctored(self):
|
||||
"""
|
||||
Makes a Proctored exam.
|
||||
"""
|
||||
self.q(css="#id_timed_examination").first.click()
|
||||
self.q(css="#id_exam_proctoring").first.click()
|
||||
self.q(css=".action-save").first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def make_exam_timed(self):
|
||||
"""
|
||||
Makes a timed exam.
|
||||
"""
|
||||
self.q(css="#id_timed_examination").first.click()
|
||||
self.q(css=".action-save").first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def proctoring_items_are_displayed(self):
|
||||
"""
|
||||
Returns True if all the items are found.
|
||||
"""
|
||||
# The Timed exam checkbox
|
||||
if not self.q(css="#id_timed_examination").present:
|
||||
return False
|
||||
|
||||
# The time limit field
|
||||
if not self.q(css="#id_time_limit").present:
|
||||
return False
|
||||
|
||||
# The Practice exam checkbox
|
||||
if not self.q(css="#id_practice_exam").present:
|
||||
return False
|
||||
|
||||
# The Proctored exam checkbox
|
||||
if not self.q(css="#id_exam_proctoring").present:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def bottom_add_section_button(self):
|
||||
"""
|
||||
|
||||
@@ -6,11 +6,15 @@ import time
|
||||
|
||||
from ..helpers import UniqueCourseTest
|
||||
from ...pages.studio.auto_auth import AutoAuthPage
|
||||
from ...pages.lms.create_mode import ModeCreationPage
|
||||
from ...pages.studio.overview import CourseOutlinePage
|
||||
from ...pages.lms.courseware import CoursewarePage, CoursewareSequentialTabPage
|
||||
from ...pages.lms.course_nav import CourseNavPage
|
||||
from ...pages.lms.problem import ProblemPage
|
||||
from ...pages.common.logout import LogoutPage
|
||||
from ...pages.lms.track_selection import TrackSelectionPage
|
||||
from ...pages.lms.pay_and_verify import PaymentAndVerificationFlow, FakePaymentPage
|
||||
from ...pages.lms.dashboard import DashboardPage
|
||||
from ...fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
|
||||
|
||||
@@ -63,14 +67,6 @@ class CoursewareTest(UniqueCourseTest):
|
||||
self.problem_page = ProblemPage(self.browser)
|
||||
self.assertEqual(self.problem_page.problem_name, 'TEST PROBLEM 1')
|
||||
|
||||
def _change_problem_release_date_in_studio(self):
|
||||
"""
|
||||
|
||||
"""
|
||||
self.course_outline.q(css=".subsection-header-actions .configure-button").first.click()
|
||||
self.course_outline.q(css="#start_date").fill("01/01/2030")
|
||||
self.course_outline.q(css=".action-save").first.click()
|
||||
|
||||
def _auto_auth(self, username, email, staff):
|
||||
"""
|
||||
Logout and login with given credentials.
|
||||
@@ -94,10 +90,7 @@ class CoursewareTest(UniqueCourseTest):
|
||||
self.course_outline.visit()
|
||||
|
||||
# Set release date for subsection in future.
|
||||
self._change_problem_release_date_in_studio()
|
||||
|
||||
# Wait for 2 seconds to save new date.
|
||||
time.sleep(2)
|
||||
self.course_outline.change_problem_release_date_in_studio()
|
||||
|
||||
# Logout and login as a student.
|
||||
LogoutPage(self.browser).visit()
|
||||
@@ -109,6 +102,158 @@ class CoursewareTest(UniqueCourseTest):
|
||||
self.assertEqual(self.problem_page.problem_name, 'TEST PROBLEM 2')
|
||||
|
||||
|
||||
class ProctoredExamTest(UniqueCourseTest):
|
||||
"""
|
||||
Test courseware.
|
||||
"""
|
||||
USERNAME = "STUDENT_TESTER"
|
||||
EMAIL = "student101@example.com"
|
||||
|
||||
def setUp(self):
|
||||
super(ProctoredExamTest, self).setUp()
|
||||
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
|
||||
self.course_outline = CourseOutlinePage(
|
||||
self.browser,
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
self.course_info['run']
|
||||
)
|
||||
|
||||
# Install a course with sections/problems, tabs, updates, and handouts
|
||||
course_fix = CourseFixture(
|
||||
self.course_info['org'], self.course_info['number'],
|
||||
self.course_info['run'], self.course_info['display_name']
|
||||
)
|
||||
course_fix.add_advanced_settings({
|
||||
"enable_proctored_exams": {"value": "true"}
|
||||
})
|
||||
|
||||
course_fix.add_children(
|
||||
XBlockFixtureDesc('chapter', 'Test Section 1').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection 1').add_children(
|
||||
XBlockFixtureDesc('problem', 'Test Problem 1')
|
||||
)
|
||||
)
|
||||
).install()
|
||||
|
||||
self.track_selection_page = TrackSelectionPage(self.browser, self.course_id)
|
||||
self.payment_and_verification_flow = PaymentAndVerificationFlow(self.browser, self.course_id)
|
||||
self.immediate_verification_page = PaymentAndVerificationFlow(
|
||||
self.browser, self.course_id, entry_point='verify-now'
|
||||
)
|
||||
self.upgrade_page = PaymentAndVerificationFlow(self.browser, self.course_id, entry_point='upgrade')
|
||||
self.fake_payment_page = FakePaymentPage(self.browser, self.course_id)
|
||||
self.dashboard_page = DashboardPage(self.browser)
|
||||
self.problem_page = ProblemPage(self.browser)
|
||||
|
||||
# Add a verified mode to the course
|
||||
ModeCreationPage(
|
||||
self.browser, self.course_id, mode_slug=u'verified', mode_display_name=u'Verified Certificate',
|
||||
min_price=10, suggested_prices='10,20'
|
||||
).visit()
|
||||
|
||||
# Auto-auth register for the course.
|
||||
self._auto_auth(self.USERNAME, self.EMAIL, False)
|
||||
|
||||
def _auto_auth(self, username, email, staff):
|
||||
"""
|
||||
Logout and login with given credentials.
|
||||
"""
|
||||
AutoAuthPage(self.browser, username=username, email=email,
|
||||
course_id=self.course_id, staff=staff).visit()
|
||||
|
||||
def _login_as_a_verified_user(self):
|
||||
"""
|
||||
login as a verififed user
|
||||
"""
|
||||
|
||||
self._auto_auth(self.USERNAME, self.EMAIL, False)
|
||||
|
||||
# the track selection page cannot be visited. see the other tests to see if any prereq is there.
|
||||
# Navigate to the track selection page
|
||||
self.track_selection_page.visit()
|
||||
|
||||
# Enter the payment and verification flow by choosing to enroll as verified
|
||||
self.track_selection_page.enroll('verified')
|
||||
|
||||
# Proceed to the fake payment page
|
||||
self.payment_and_verification_flow.proceed_to_payment()
|
||||
|
||||
# Submit payment
|
||||
self.fake_payment_page.submit_payment()
|
||||
|
||||
def test_can_create_proctored_exam_in_studio(self):
|
||||
"""
|
||||
Test that Proctored exam settings are visible in Studio.
|
||||
"""
|
||||
|
||||
# Given that I am a staff member
|
||||
LogoutPage(self.browser).visit()
|
||||
self._auto_auth("STAFF_TESTER", "staff101@example.com", True)
|
||||
|
||||
# When I visit the course outline page in studio.
|
||||
self.course_outline.visit()
|
||||
|
||||
# And open the subsection edit dialog
|
||||
self.course_outline.open_exam_settings_dialog()
|
||||
|
||||
# Then I can view all settings related to Proctored and timed exams
|
||||
self.assertTrue(self.course_outline.proctoring_items_are_displayed())
|
||||
|
||||
def test_proctored_exam_flow(self):
|
||||
"""
|
||||
Test that staff can create a proctored exam.
|
||||
"""
|
||||
|
||||
# Given that I am a staff member on the exam settings section
|
||||
LogoutPage(self.browser).visit()
|
||||
self._auto_auth("STAFF_TESTER", "staff101@example.com", True)
|
||||
self.course_outline.visit()
|
||||
self.course_outline.open_exam_settings_dialog()
|
||||
|
||||
# When I Make the exam proctored.
|
||||
self.course_outline.make_exam_proctored()
|
||||
|
||||
# And I login as a verified student.
|
||||
LogoutPage(self.browser).visit()
|
||||
self._login_as_a_verified_user()
|
||||
|
||||
# And visit the courseware as a verified student.
|
||||
self.courseware_page.visit()
|
||||
|
||||
# Then I can see an option to take the exam as a proctored exam.
|
||||
self.assertTrue(self.courseware_page.can_start_proctored_exam)
|
||||
|
||||
def test_timed_exam_flow(self):
|
||||
"""
|
||||
Test that staff can create a timed exam.
|
||||
"""
|
||||
|
||||
# Given that I am a staff member on the exam settings section
|
||||
LogoutPage(self.browser).visit()
|
||||
self._auto_auth("STAFF_TESTER", "staff101@example.com", True)
|
||||
self.course_outline.visit()
|
||||
self.course_outline.open_exam_settings_dialog()
|
||||
|
||||
# When I Make the exam timed.
|
||||
self.course_outline.make_exam_timed()
|
||||
|
||||
# And I login as a verified student.
|
||||
LogoutPage(self.browser).visit()
|
||||
self._login_as_a_verified_user()
|
||||
|
||||
# And visit the courseware as a verified student.
|
||||
self.courseware_page.visit()
|
||||
|
||||
# And I start the timed exam
|
||||
self.courseware_page.start_timed_exam()
|
||||
|
||||
# Then I am taken to the exam with a timer bar showing
|
||||
self.assertTrue(self.courseware_page.is_timer_bar_present)
|
||||
|
||||
|
||||
class CoursewareMultipleVerticalsTest(UniqueCourseTest):
|
||||
"""
|
||||
Test courseware with multiple verticals
|
||||
|
||||
@@ -3,14 +3,23 @@
|
||||
End-to-end tests for the LMS Instructor Dashboard.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
from nose.plugins.attrib import attr
|
||||
from bok_choy.promise import EmptyPromise
|
||||
|
||||
from ..helpers import UniqueCourseTest, get_modal_alert, EventsTestMixin
|
||||
from ...pages.common.logout import LogoutPage
|
||||
from ...pages.lms.auto_auth import AutoAuthPage
|
||||
from ...pages.studio.overview import CourseOutlinePage
|
||||
from ...pages.lms.create_mode import ModeCreationPage
|
||||
from ...pages.lms.courseware import CoursewarePage
|
||||
from ...pages.lms.instructor_dashboard import InstructorDashboardPage
|
||||
from ...fixtures.course import CourseFixture
|
||||
from ...fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
from ...pages.lms.dashboard import DashboardPage
|
||||
from ...pages.lms.problem import ProblemPage
|
||||
from ...pages.lms.track_selection import TrackSelectionPage
|
||||
from ...pages.lms.pay_and_verify import PaymentAndVerificationFlow, FakePaymentPage
|
||||
|
||||
|
||||
class BaseInstructorDashboardTest(EventsTestMixin, UniqueCourseTest):
|
||||
@@ -107,6 +116,175 @@ class AutoEnrollmentWithCSVTest(BaseInstructorDashboardTest):
|
||||
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 ProctoredExamsTest(BaseInstructorDashboardTest):
|
||||
"""
|
||||
End-to-end tests for Proctoring Sections of the Instructor Dashboard.
|
||||
"""
|
||||
|
||||
USERNAME = "STUDENT_TESTER"
|
||||
EMAIL = "student101@example.com"
|
||||
|
||||
def setUp(self):
|
||||
super(ProctoredExamsTest, self).setUp()
|
||||
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
|
||||
self.course_outline = CourseOutlinePage(
|
||||
self.browser,
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
self.course_info['run']
|
||||
)
|
||||
|
||||
course_fixture = CourseFixture(**self.course_info)
|
||||
course_fixture.add_advanced_settings({
|
||||
"enable_proctored_exams": {"value": "true"}
|
||||
})
|
||||
|
||||
course_fixture.add_children(
|
||||
XBlockFixtureDesc('chapter', 'Test Section 1').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection 1').add_children(
|
||||
XBlockFixtureDesc('problem', 'Test Problem 1')
|
||||
)
|
||||
)
|
||||
).install()
|
||||
|
||||
self.track_selection_page = TrackSelectionPage(self.browser, self.course_id)
|
||||
self.payment_and_verification_flow = PaymentAndVerificationFlow(self.browser, self.course_id)
|
||||
self.immediate_verification_page = PaymentAndVerificationFlow(
|
||||
self.browser, self.course_id, entry_point='verify-now'
|
||||
)
|
||||
self.upgrade_page = PaymentAndVerificationFlow(self.browser, self.course_id, entry_point='upgrade')
|
||||
self.fake_payment_page = FakePaymentPage(self.browser, self.course_id)
|
||||
self.dashboard_page = DashboardPage(self.browser)
|
||||
self.problem_page = ProblemPage(self.browser)
|
||||
|
||||
# Add a verified mode to the course
|
||||
ModeCreationPage(
|
||||
self.browser, self.course_id, mode_slug=u'verified', mode_display_name=u'Verified Certificate',
|
||||
min_price=10, suggested_prices='10,20'
|
||||
).visit()
|
||||
|
||||
# Auto-auth register for the course.
|
||||
self._auto_auth(self.USERNAME, self.EMAIL, False)
|
||||
|
||||
def _auto_auth(self, username, email, staff):
|
||||
"""
|
||||
Logout and login with given credentials.
|
||||
"""
|
||||
AutoAuthPage(self.browser, username=username, email=email,
|
||||
course_id=self.course_id, staff=staff).visit()
|
||||
|
||||
def _login_as_a_verified_user(self):
|
||||
"""
|
||||
login as a verififed user
|
||||
"""
|
||||
|
||||
self._auto_auth(self.USERNAME, self.EMAIL, False)
|
||||
|
||||
# the track selection page cannot be visited. see the other tests to see if any prereq is there.
|
||||
# Navigate to the track selection page
|
||||
self.track_selection_page.visit()
|
||||
|
||||
# Enter the payment and verification flow by choosing to enroll as verified
|
||||
self.track_selection_page.enroll('verified')
|
||||
|
||||
# Proceed to the fake payment page
|
||||
self.payment_and_verification_flow.proceed_to_payment()
|
||||
|
||||
# Submit payment
|
||||
self.fake_payment_page.submit_payment()
|
||||
|
||||
def _create_a_proctored_exam_and_attempt(self):
|
||||
"""
|
||||
Creates a proctored exam and makes the student attempt it so that
|
||||
the associated allowance and attempts are visible on the Instructor Dashboard.
|
||||
"""
|
||||
# Visit the course outline page in studio
|
||||
LogoutPage(self.browser).visit()
|
||||
self._auto_auth("STAFF_TESTER", "staff101@example.com", True)
|
||||
self.course_outline.visit()
|
||||
|
||||
#open the exam settings to make it a proctored exam.
|
||||
self.course_outline.open_exam_settings_dialog()
|
||||
self.course_outline.make_exam_proctored()
|
||||
time.sleep(2) # Wait for 2 seconds to save the settings.
|
||||
|
||||
# login as a verified student and visit the courseware.
|
||||
LogoutPage(self.browser).visit()
|
||||
self._login_as_a_verified_user()
|
||||
self.courseware_page.visit()
|
||||
|
||||
# Start the proctored exam.
|
||||
self.courseware_page.start_proctored_exam()
|
||||
|
||||
def _create_a_timed_exam_and_attempt(self):
|
||||
"""
|
||||
Creates a timed exam and makes the student attempt it so that
|
||||
the associated allowance and attempts are visible on the Instructor Dashboard.
|
||||
"""
|
||||
# Visit the course outline page in studio
|
||||
LogoutPage(self.browser).visit()
|
||||
self._auto_auth("STAFF_TESTER", "staff101@example.com", True)
|
||||
self.course_outline.visit()
|
||||
|
||||
#open the exam settings to make it a proctored exam.
|
||||
self.course_outline.open_exam_settings_dialog()
|
||||
self.course_outline.make_exam_timed()
|
||||
time.sleep(2) # Wait for 2 seconds to save the settings.
|
||||
|
||||
# login as a verified student and visit the courseware.
|
||||
LogoutPage(self.browser).visit()
|
||||
self._login_as_a_verified_user()
|
||||
self.courseware_page.visit()
|
||||
|
||||
# Start the proctored exam.
|
||||
self.courseware_page.start_timed_exam()
|
||||
|
||||
def test_can_add_remove_allowance(self):
|
||||
"""
|
||||
Make sure that allowances can be added and removed.
|
||||
"""
|
||||
|
||||
# Given that an exam has been configured to be a proctored exam.
|
||||
self._create_a_proctored_exam_and_attempt()
|
||||
|
||||
# When I log in as an instructor,
|
||||
self.log_in_as_instructor()
|
||||
|
||||
# And visit the Allowance Section of Instructor Dashboard's Proctoring tab
|
||||
instructor_dashboard_page = self.visit_instructor_dashboard()
|
||||
allowance_section = instructor_dashboard_page.select_proctoring().select_allowance_section()
|
||||
|
||||
# Then I can add Allowance to that exam for a student
|
||||
self.assertTrue(allowance_section.is_add_allowance_button_visible)
|
||||
|
||||
def test_can_reset_attempts(self):
|
||||
"""
|
||||
Make sure that Exam attempts are visible and can be reset.
|
||||
"""
|
||||
|
||||
# Given that an exam has been configured to be a proctored exam.
|
||||
self._create_a_timed_exam_and_attempt()
|
||||
|
||||
# When I log in as an instructor,
|
||||
self.log_in_as_instructor()
|
||||
|
||||
# And visit the Student Proctored Exam Attempts Section of Instructor Dashboard's Proctoring tab
|
||||
instructor_dashboard_page = self.visit_instructor_dashboard()
|
||||
exam_attempts_section = instructor_dashboard_page.select_proctoring().select_exam_attempts_section()
|
||||
|
||||
# Then I can see the search text field
|
||||
self.assertTrue(exam_attempts_section.is_search_text_field_visible)
|
||||
|
||||
# And I can see one attempt by a student.
|
||||
self.assertTrue(exam_attempts_section.is_student_attempt_visible)
|
||||
|
||||
# And I can remove the attempt by clicking the "x" at the end of the row.
|
||||
exam_attempts_section.remove_student_attempt()
|
||||
self.assertFalse(exam_attempts_section.is_student_attempt_visible)
|
||||
|
||||
|
||||
@attr('shard_5')
|
||||
class EntranceExamGradeTest(BaseInstructorDashboardTest):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user