275 lines
10 KiB
Python
275 lines
10 KiB
Python
"""
|
|
End-to-end tests for the LMS that utilize the
|
|
progress page.
|
|
"""
|
|
|
|
|
|
from contextlib import contextmanager
|
|
|
|
|
|
from ...fixtures.course import CourseFixture, XBlockFixtureDesc
|
|
from ...pages.common.logout import LogoutPage
|
|
from ...pages.lms.courseware import CoursewarePage
|
|
from ...pages.lms.problem import ProblemPage
|
|
from ...pages.lms.progress import ProgressPage
|
|
from ...pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
|
|
from ..helpers import (
|
|
UniqueCourseTest,
|
|
auto_auth,
|
|
create_multiple_choice_problem,
|
|
)
|
|
|
|
|
|
class ProgressPageBaseTest(UniqueCourseTest):
|
|
"""
|
|
Provides utility methods for tests retrieving
|
|
scores from the progress page.
|
|
"""
|
|
USERNAME = "STUDENT_TESTER"
|
|
EMAIL = "student101@example.com"
|
|
SECTION_NAME = 'Test Section 1'
|
|
SUBSECTION_NAME = 'Test Subsection 1'
|
|
UNIT_NAME = 'Test Unit 1'
|
|
PROBLEM_NAME = 'Test Problem 1'
|
|
PROBLEM_NAME_2 = 'Test Problem 2'
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
|
self.problem_page = ProblemPage(self.browser)
|
|
self.progress_page = ProgressPage(self.browser, self.course_id)
|
|
self.logout_page = LogoutPage(self.browser)
|
|
|
|
self.studio_course_outline = StudioCourseOutlinePage(
|
|
self.browser,
|
|
self.course_info['org'],
|
|
self.course_info['number'],
|
|
self.course_info['run']
|
|
)
|
|
|
|
# Install a course with problems
|
|
self.course_fix = CourseFixture(
|
|
self.course_info['org'],
|
|
self.course_info['number'],
|
|
self.course_info['run'],
|
|
self.course_info['display_name']
|
|
)
|
|
|
|
self.problem1 = create_multiple_choice_problem(self.PROBLEM_NAME)
|
|
self.problem2 = create_multiple_choice_problem(self.PROBLEM_NAME_2)
|
|
|
|
self.course_fix.add_children(
|
|
XBlockFixtureDesc('chapter', self.SECTION_NAME).add_children(
|
|
XBlockFixtureDesc('sequential', self.SUBSECTION_NAME).add_children(
|
|
XBlockFixtureDesc('vertical', self.UNIT_NAME).add_children(self.problem1, self.problem2)
|
|
)
|
|
),
|
|
XBlockFixtureDesc('chapter', "Lab Section").add_children(
|
|
XBlockFixtureDesc('sequential', "Lab Subsection").add_children(
|
|
XBlockFixtureDesc('vertical', "Lab Unit").add_children(
|
|
create_multiple_choice_problem("Lab Exercise")
|
|
)
|
|
)
|
|
)
|
|
).install()
|
|
|
|
# Auto-auth register for the course.
|
|
auto_auth(self.browser, self.USERNAME, self.EMAIL, False, self.course_id)
|
|
|
|
def _answer_problem_correctly(self):
|
|
"""
|
|
Submit a correct answer to the problem.
|
|
"""
|
|
self._answer_problem(choice=2)
|
|
|
|
def _answer_problem(self, choice):
|
|
"""
|
|
Submit the given choice for the problem.
|
|
"""
|
|
self.courseware_page.go_to_sequential_position(1)
|
|
self.problem_page.click_choice(f'choice_choice_{choice}')
|
|
self.problem_page.click_submit()
|
|
|
|
def _get_section_score(self):
|
|
"""
|
|
Return a list of scores from the progress page.
|
|
"""
|
|
self.progress_page.visit()
|
|
return self.progress_page.section_score(self.SECTION_NAME, self.SUBSECTION_NAME) # lint-amnesty, pylint: disable=no-member
|
|
|
|
def _get_problem_scores(self):
|
|
"""
|
|
Return a list of scores from the progress page.
|
|
"""
|
|
self.progress_page.visit()
|
|
return self.progress_page.scores(self.SECTION_NAME, self.SUBSECTION_NAME) # lint-amnesty, pylint: disable=no-member
|
|
|
|
@contextmanager
|
|
def _logged_in_session(self, staff=False):
|
|
"""
|
|
Ensure that the user is logged in and out appropriately at the beginning
|
|
and end of the current test. But if there's an error, don't log out
|
|
before capturing a screenshot.
|
|
"""
|
|
self.logout_page.visit()
|
|
if staff:
|
|
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
|
else:
|
|
auto_auth(self.browser, self.USERNAME, self.EMAIL, False, self.course_id)
|
|
yield
|
|
self.logout_page.visit()
|
|
|
|
|
|
class SubsectionGradingPolicyBase(ProgressPageBaseTest):
|
|
"""
|
|
Base class for testing a subsection and its impact to
|
|
the progress page
|
|
"""
|
|
def setUp(self):
|
|
super().setUp()
|
|
self._set_policy_for_subsection("Homework", 0)
|
|
self._set_policy_for_subsection("Lab", 1)
|
|
|
|
def _set_policy_for_subsection(self, policy, section=0):
|
|
"""
|
|
Set the grading policy for the first subsection in the specified section.
|
|
If a section index is not provided, 0 is assumed.
|
|
"""
|
|
with self._logged_in_session(staff=True):
|
|
self.studio_course_outline.visit()
|
|
modal = self.studio_course_outline.section_at(section).subsection_at(0).edit()
|
|
modal.policy = policy
|
|
modal.save()
|
|
|
|
def _check_scores_and_page_text(self, problem_scores, section_score, text):
|
|
"""
|
|
Asserts that the given problem and section scores, and text,
|
|
appear on the progress page.
|
|
"""
|
|
assert self._get_problem_scores() == problem_scores
|
|
assert self._get_section_score() == section_score
|
|
assert self.progress_page.text_on_page(text) # lint-amnesty, pylint: disable=no-member
|
|
|
|
def _check_tick_text(self, index, sr_text, label, label_hidden=True):
|
|
"""
|
|
Check the label and sr text for a horizontal (X-axis) tick.
|
|
"""
|
|
assert sr_text == self.progress_page.x_tick_sr_text(index)
|
|
assert [label, ('true' if label_hidden else None)] == self.progress_page.x_tick_label(index)
|
|
|
|
|
|
class SubsectionGradingPolicyA11yTest(SubsectionGradingPolicyBase):
|
|
"""
|
|
Class to test the accessibility of subsection grading
|
|
"""
|
|
a11y = True
|
|
|
|
def test_axis_a11y(self):
|
|
"""
|
|
Tests that the progress chart axes have appropriate a11y (screenreader) markup.
|
|
"""
|
|
with self._logged_in_session():
|
|
self.courseware_page.visit()
|
|
# Answer the first HW problem (the unit contains 2 problems, only one will be answered correctly)
|
|
self._answer_problem_correctly()
|
|
self.courseware_page.click_next_button_on_top()
|
|
# Answer the first Lab problem (unit only contains a single problem)
|
|
self._answer_problem_correctly()
|
|
|
|
self.progress_page.a11y_audit.config.set_rules({
|
|
"ignore": [
|
|
'aria-valid-attr', # TODO: LEARNER-6611 & LEARNER-6865
|
|
'region', # TODO: AC-932
|
|
]
|
|
})
|
|
self.progress_page.visit()
|
|
|
|
# Verify the basic a11y of the progress page
|
|
self.progress_page.a11y_audit.check_for_accessibility_errors()
|
|
|
|
# Verify that y-Axis labels are aria-hidden
|
|
assert ['100%', 'true'] == self.progress_page.y_tick_label(0)
|
|
assert ['0%', 'true'] == self.progress_page.y_tick_label(1)
|
|
assert ['Pass 50%', 'true'] == self.progress_page.y_tick_label(2)
|
|
# Verify x-Axis labels and sr-text
|
|
self._check_tick_text(0, ['Homework 1 - Test Subsection 1 - 50% (1/2)'], 'HW 01')
|
|
|
|
# Homeworks 2-10 are checked in the for loop below.
|
|
|
|
self._check_tick_text(
|
|
10,
|
|
['Homework 11 Unreleased - 0% (?/?)', 'The lowest 2 Homework scores are dropped.'],
|
|
'HW 11'
|
|
)
|
|
|
|
self._check_tick_text(
|
|
11,
|
|
['Homework 12 Unreleased - 0% (?/?)', 'The lowest 2 Homework scores are dropped.'],
|
|
'HW 12'
|
|
)
|
|
|
|
self._check_tick_text(12, ['Homework Average = 5%'], 'HW Avg')
|
|
self._check_tick_text(13, ['Lab 1 - Lab Subsection - 100% (1/1)'], 'Lab 01')
|
|
|
|
# Labs 2-10 are checked in the for loop below.
|
|
|
|
self._check_tick_text(
|
|
23,
|
|
['Lab 11 Unreleased - 0% (?/?)', 'The lowest 2 Lab scores are dropped.'],
|
|
'Lab 11'
|
|
)
|
|
self._check_tick_text(
|
|
24,
|
|
['Lab 12 Unreleased - 0% (?/?)', 'The lowest 2 Lab scores are dropped.'],
|
|
'Lab 12'
|
|
)
|
|
|
|
self._check_tick_text(25, ['Lab Average = 10%'], 'Lab Avg')
|
|
self._check_tick_text(26, ['Midterm Exam = 0%'], 'Midterm')
|
|
self._check_tick_text(27, ['Final Exam = 0%'], 'Final')
|
|
|
|
self._check_tick_text(
|
|
28,
|
|
['Homework = 0.75% of a possible 15.00%', 'Lab = 1.50% of a possible 15.00%'],
|
|
'Total',
|
|
False # The label "Total" should NOT be aria-hidden
|
|
)
|
|
|
|
# The grading policy has 12 Homeworks and 12 Labs. Most of them are unpublished,
|
|
# with no additional information.
|
|
for i in range(1, 10):
|
|
self._check_tick_text(
|
|
i,
|
|
['Homework {index} Unreleased - 0% (?/?)'.format(index=i + 1)],
|
|
'HW 0{index}'.format(index=i + 1) if i < 9 else 'HW {index}'.format(index=i + 1)
|
|
)
|
|
self._check_tick_text(
|
|
i + 13,
|
|
['Lab {index} Unreleased - 0% (?/?)'.format(index=i + 1)],
|
|
'Lab 0{index}'.format(index=i + 1) if i < 9 else 'Lab {index}'.format(index=i + 1)
|
|
)
|
|
|
|
# Verify the overall score. The first element in the array is the sr-only text, and the
|
|
# second is the total text (including the sr-only text).
|
|
assert ['Overall Score', 'Overall Score\n2%'] == self.progress_page.graph_overall_score()
|
|
|
|
|
|
class ProgressPageA11yTest(ProgressPageBaseTest):
|
|
"""
|
|
Class to test the accessibility of the progress page.
|
|
"""
|
|
a11y = True
|
|
|
|
def test_progress_page_a11y(self):
|
|
"""
|
|
Test the accessibility of the progress page.
|
|
"""
|
|
self.progress_page.a11y_audit.config.set_rules({
|
|
"ignore": [
|
|
'aria-valid-attr', # TODO: LEARNER-6611 & LEARNER-6865
|
|
'region', # TODO: AC-932
|
|
]
|
|
})
|
|
self.progress_page.visit()
|
|
self.progress_page.a11y_audit.check_for_accessibility_errors()
|