Courseware problems bokchoy tests
This commit is contained in:
@@ -14,6 +14,13 @@ class ProblemPage(PageObject):
|
||||
|
||||
url = None
|
||||
CSS_PROBLEM_HEADER = '.problem-header'
|
||||
status_indicators = {
|
||||
'correct': ['span.correct'],
|
||||
'incorrect': ['span.incorrect'],
|
||||
'unanswered': ['span.unanswered'],
|
||||
'submitted': ['span.submitted'],
|
||||
'unsubmitted': ['.unsubmitted']
|
||||
}
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='.xblock-student_view').present
|
||||
@@ -498,3 +505,31 @@ class ProblemPage(PageObject):
|
||||
Returns the text in the special "sr" region used for display status.
|
||||
"""
|
||||
return self.q(css='#reader-feedback').text[0]
|
||||
|
||||
@property
|
||||
def submission_feedback(self):
|
||||
"""
|
||||
Returns the submission feedback of the problem
|
||||
"""
|
||||
return self.q(css='div[class="submission-feedback"]').text[0].split('\n')[0]
|
||||
|
||||
@property
|
||||
def answer(self):
|
||||
"""
|
||||
Returns the answer of the problem
|
||||
"""
|
||||
return self.q(css='p[class="answer"]').text[0]
|
||||
|
||||
@property
|
||||
def score_notification(self):
|
||||
"""
|
||||
Returns the score after the submission of answer
|
||||
"""
|
||||
self.wait_for_element_visibility('.notification-submit .notification-message', 'Problem score is visible')
|
||||
return self.q(css='.notification-submit .notification-message').text[0]
|
||||
|
||||
def is_present(self, selector):
|
||||
"""
|
||||
Checks for the presence of the locator
|
||||
"""
|
||||
return self.q(css=selector).present
|
||||
|
||||
@@ -10,6 +10,7 @@ from abc import ABCMeta, abstractmethod
|
||||
import ddt
|
||||
import pytest
|
||||
from selenium.webdriver import ActionChains
|
||||
from bok_choy.promise import BrokenPromise
|
||||
|
||||
from capa.tests.response_xml_factory import (
|
||||
AnnotationResponseXMLFactory,
|
||||
@@ -91,6 +92,7 @@ class ProblemTypeTestBase(ProblemsTest, EventsTestMixin):
|
||||
'incorrect': ['span.incorrect'],
|
||||
'unanswered': ['span.unanswered'],
|
||||
'submitted': ['span.submitted'],
|
||||
'unsubmitted': ['.unsubmitted']
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
@@ -128,6 +130,23 @@ class ProblemTypeTestBase(ProblemsTest, EventsTestMixin):
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
self.problem_page.wait_for_element_visibility(selector, msg)
|
||||
|
||||
def problem_status(self, status):
|
||||
"""
|
||||
Returns the status of problem
|
||||
Args:
|
||||
status(string): status of the problem which is to be checked
|
||||
|
||||
Returns:
|
||||
True: If provided status is present on the page
|
||||
False: If provided status is not present on the page
|
||||
"""
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
try:
|
||||
self.problem_page.wait_for_element_visibility(selector, 'Status not present', timeout=10)
|
||||
return True
|
||||
except BrokenPromise:
|
||||
return False
|
||||
|
||||
@abstractmethod
|
||||
def answer_problem(self, correctness):
|
||||
"""
|
||||
@@ -160,6 +179,7 @@ class ProblemTypeA11yTestMixin(object):
|
||||
self.problem_page.a11y_audit.check_for_accessibility_errors()
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ProblemTypeTestMixin(ProblemTypeA11yTestMixin):
|
||||
"""
|
||||
Test cases shared amongst problem types.
|
||||
@@ -385,6 +405,105 @@ class ProblemTypeTestMixin(ProblemTypeA11yTestMixin):
|
||||
self.problem_page.click_submit()
|
||||
self.problem_page.wait_partial_notification()
|
||||
|
||||
@ddt.data('correct', 'incorrect')
|
||||
def test_reset_problem(self, correctness):
|
||||
"""
|
||||
Scenario: I can reset a problem
|
||||
|
||||
Given I am viewing a problem with randomization: always and with reset button: on
|
||||
And I answer a problem as <correctness>
|
||||
When I reset the problem
|
||||
Then my answer is marked "unanswered"
|
||||
And The problem displays a "blank" answer
|
||||
"""
|
||||
self.answer_problem(correctness)
|
||||
self.problem_page.click_submit()
|
||||
self.problem_page.click_reset()
|
||||
self.assertTrue(self.problem_status('unanswered'))
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ChangingAnswerOfProblemTestMixin(object):
|
||||
"""
|
||||
Test the effect of changing the answers of problem
|
||||
"""
|
||||
|
||||
@ddt.data(['correct', '1/1 point (ungraded)'], ['incorrect', '0/1 point (ungraded)'])
|
||||
@ddt.unpack
|
||||
def test_checkbox_score_after_answer_and_reset(self, correctness, score):
|
||||
"""
|
||||
Scenario: I can see my score on problem when I answer it and after I reset it
|
||||
|
||||
Given I am viewing problem
|
||||
When I answer problem with <correctness>
|
||||
Then I should see a <score>
|
||||
When I reset the problem
|
||||
Then I should see a score of points possible: 0/1 point (ungraded)
|
||||
"""
|
||||
self.answer_problem(correctness)
|
||||
self.problem_page.click_submit()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, score)
|
||||
self.problem_page.click_reset()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, '0/1 point (ungraded)')
|
||||
|
||||
@ddt.data(['correct', 'incorrect'], ['incorrect', 'correct'])
|
||||
@ddt.unpack
|
||||
def test_reset_correctness_after_changing_answer(self, initial_correctness, other_correctness):
|
||||
"""
|
||||
Scenario: I can reset the correctness of a problem after changing my answer
|
||||
|
||||
Given I am viewing problem
|
||||
Then my problem's answer is marked "unanswered"
|
||||
When I answer and submit the problem with <initial correctness>
|
||||
Then my problem's answer is marked with <initial correctness>
|
||||
And I input an answer as <other correctness>
|
||||
Then my problem's answer is marked "unanswered"
|
||||
"""
|
||||
self.assertTrue(self.problem_status('unanswered'))
|
||||
self.answer_problem(initial_correctness)
|
||||
self.problem_page.click_submit()
|
||||
|
||||
self.assertTrue(self.problem_status(initial_correctness))
|
||||
|
||||
self.answer_problem(other_correctness)
|
||||
self.assertTrue(self.problem_status('unanswered'))
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class NonRandomizedProblemTypeTestMixin(ProblemTypeA11yTestMixin):
|
||||
"""
|
||||
Test the effect of 'randomization: never'
|
||||
"""
|
||||
can_submit_blank = False
|
||||
can_update_save_notification = True
|
||||
|
||||
def test_non_randomized_problem_correctly(self):
|
||||
"""
|
||||
Scenario: The reset button doesn't show up
|
||||
|
||||
Given I am viewing a problem with "randomization": never and with "reset button": on
|
||||
And I answer problem problem problem correctly
|
||||
Then The "Reset" button does not appear
|
||||
"""
|
||||
self.answer_problem("correct")
|
||||
self.problem_page.click_submit()
|
||||
self.assertFalse(self.problem_page.is_reset_button_present())
|
||||
|
||||
def test_non_randomized_problem_incorrectly(self):
|
||||
"""
|
||||
Scenario: I can reset a non-randomized problem that I answered incorrectly
|
||||
|
||||
Given I am viewing problem with "randomization": never and with "reset button": on
|
||||
And I answer problem incorrectly
|
||||
When I reset the problem
|
||||
Then my problem answer is marked "unanswered"
|
||||
And the problem problem displays a "blank" answer
|
||||
"""
|
||||
self.answer_problem("incorrect")
|
||||
self.problem_page.click_submit()
|
||||
self.problem_page.click_reset()
|
||||
self.assertTrue(self.problem_status('unanswered'))
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ProblemNeverShowCorrectnessMixin(object):
|
||||
@@ -567,7 +686,8 @@ class CheckboxProblemTypeBase(ProblemTypeTestBase):
|
||||
self.problem_page.click_choice("choice_3")
|
||||
|
||||
|
||||
class CheckboxProblemTypeTest(CheckboxProblemTypeBase, ProblemTypeTestMixin):
|
||||
@ddt.ddt
|
||||
class CheckboxProblemTypeTest(CheckboxProblemTypeBase, ProblemTypeTestMixin, ChangingAnswerOfProblemTestMixin):
|
||||
"""
|
||||
Standard tests for the Checkbox Problem Type
|
||||
"""
|
||||
@@ -587,6 +707,24 @@ class CheckboxProblemTypeTest(CheckboxProblemTypeBase, ProblemTypeTestMixin):
|
||||
self.problem_page.wait_for_show_answer_notification()
|
||||
|
||||
|
||||
class CheckboxProblemTypeTestNonRandomized(CheckboxProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for the non-randomized checkbox problem
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class CheckboxProblemTypeNeverShowCorrectnessTest(CheckboxProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
Ensure that correctness can be withheld for Checkbox Problem Type problems.
|
||||
@@ -594,6 +732,7 @@ class CheckboxProblemTypeNeverShowCorrectnessTest(CheckboxProblemTypeBase, Probl
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MultipleChoiceProblemTypeBase(ProblemTypeTestBase):
|
||||
"""
|
||||
ProblemTypeTestBase specialization Multiple Choice Problem Type
|
||||
@@ -617,6 +756,23 @@ class MultipleChoiceProblemTypeBase(ProblemTypeTestBase):
|
||||
'submitted': ['label.choicegroup_submitted', 'span.submitted'],
|
||||
}
|
||||
|
||||
def problem_status(self, status):
|
||||
"""
|
||||
Returns the status of problem
|
||||
Args:
|
||||
status(string): status of the problem which is to be checked
|
||||
|
||||
Returns:
|
||||
True: If provided status is present on the page
|
||||
False: If provided status is not present on the page
|
||||
"""
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
try:
|
||||
self.problem_page.wait_for_element_visibility(selector, 'Status not present', timeout=10)
|
||||
return True
|
||||
except BrokenPromise:
|
||||
return False
|
||||
|
||||
def answer_problem(self, correctness):
|
||||
"""
|
||||
Answer multiple choice problem.
|
||||
@@ -627,6 +783,7 @@ class MultipleChoiceProblemTypeBase(ProblemTypeTestBase):
|
||||
self.problem_page.click_choice("choice_choice_2")
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MultipleChoiceProblemTypeTest(MultipleChoiceProblemTypeBase, ProblemTypeTestMixin):
|
||||
"""
|
||||
Standard tests for the Multiple Choice Problem Type
|
||||
@@ -660,6 +817,189 @@ class MultipleChoiceProblemTypeTest(MultipleChoiceProblemTypeBase, ProblemTypeTe
|
||||
self.problem_page.wait_for_show_answer_notification()
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MultipleChoiceProblemResetCorrectnessAfterChangingAnswerTest(MultipleChoiceProblemTypeBase):
|
||||
"""
|
||||
Tests for Multiple choice problem with changing answers
|
||||
"""
|
||||
|
||||
@ddt.data(['correct', '1/1 point (ungraded)'], ['incorrect', '0/1 point (ungraded)'])
|
||||
@ddt.unpack
|
||||
def test_mcq_score_after_answer_and_reset(self, correctness, score):
|
||||
"""
|
||||
Scenario: I can see my score on a multiple choice problem when I answer it and after I reset it
|
||||
|
||||
Given I am viewing a multiple choice problem
|
||||
When I answer a multiple choice problem <correctness>
|
||||
Then I should see a <score>
|
||||
When I reset the problem
|
||||
Then I should see a score of points possible: 0/1 point (ungraded)
|
||||
"""
|
||||
self.answer_problem(correctness)
|
||||
self.problem_page.click_submit()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, score)
|
||||
self.problem_page.click_reset()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, '0/1 point (ungraded)')
|
||||
|
||||
@ddt.data(['correct', 'incorrect'], ['incorrect', 'correct'])
|
||||
@ddt.unpack
|
||||
def test_reset_correctness_after_changing_answer(self, initial_correctness, other_correctness):
|
||||
"""
|
||||
Scenario: I can reset the correctness of a multiple choice problem after changing my answer
|
||||
|
||||
Given I am viewing a multiple choice problem
|
||||
When I answer a multiple choice problem <initial_correctness>
|
||||
Then my multiple choice answer is marked <initial_correctness>
|
||||
And I reset the problem
|
||||
Then my multiple choice answer is NOT marked <initial_correctness>
|
||||
And my multiple choice answer is NOT marked <other_correctness>
|
||||
"""
|
||||
self.assertTrue(self.problem_status("unanswered"))
|
||||
self.answer_problem(initial_correctness)
|
||||
self.problem_page.click_submit()
|
||||
|
||||
self.assertTrue(self.problem_status(initial_correctness))
|
||||
self.problem_page.click_reset()
|
||||
|
||||
self.assertFalse(self.problem_status(initial_correctness))
|
||||
self.assertFalse(self.problem_status(other_correctness))
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class MultipleChoiceProblemTypeTestNonRandomized(MultipleChoiceProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized multiple choice problem
|
||||
"""
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True, 'max_attempts': 3}
|
||||
)
|
||||
|
||||
def test_non_randomized_multiple_choice_with_multiple_attempts(self):
|
||||
"""
|
||||
Scenario: I can answer a problem with multiple attempts correctly but cannot reset because randomization is off
|
||||
|
||||
Given I am viewing a randomization "never" "multiple choice" problem with "3" attempts with reset
|
||||
Then I should see "You have used 0 of 3 attempts" somewhere in the page
|
||||
When I answer a "multiple choice" problem "correctly"
|
||||
Then The "Reset" button does not appear
|
||||
"""
|
||||
self.assertEqual(
|
||||
self.problem_page.submission_feedback,
|
||||
"You have used 0 of 3 attempts",
|
||||
"All 3 attempts are not available"
|
||||
)
|
||||
|
||||
self.answer_problem("correct")
|
||||
self.problem_page.click_submit()
|
||||
self.assertFalse(self.problem_page.is_reset_button_present())
|
||||
|
||||
|
||||
class MultipleChoiceProblemTypeTestOneAttempt(MultipleChoiceProblemTypeBase):
|
||||
"""
|
||||
Test Multiple choice problem with single attempt
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True, 'max_attempts': 1}
|
||||
)
|
||||
|
||||
def test_answer_with_one_attempt_correctly(self):
|
||||
"""
|
||||
Scenario: I can answer a problem with one attempt correctly and can not reset
|
||||
|
||||
Given I am viewing a "multiple choice" problem with "1" attempt
|
||||
When I answer a "multiple choice" problem "correctly"
|
||||
Then The "Reset" button does not appear
|
||||
"""
|
||||
self.answer_problem("correct")
|
||||
self.problem_page.click_submit()
|
||||
self.assertFalse(self.problem_page.is_reset_button_present())
|
||||
|
||||
|
||||
class MultipleChoiceProblemTypeTestMultipleAttempt(MultipleChoiceProblemTypeBase):
|
||||
"""
|
||||
Test Multiple choice problem with multiple attempts
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'always', 'show_reset_button': True, 'max_attempts': 3}
|
||||
)
|
||||
|
||||
def test_answer_with_multiple_attempt_correctly(self):
|
||||
"""
|
||||
Scenario: I can answer a problem with multiple attempts correctly and still reset the problem
|
||||
|
||||
Given I am viewing a "multiple choice" problem with "3" attempts
|
||||
Then I should see "You have used 0 of 3 attempts" somewhere in the page
|
||||
When I answer a "multiple choice" problem "correctly"
|
||||
Then The "Reset" button does appear
|
||||
"""
|
||||
self.assertEqual(
|
||||
self.problem_page.submission_feedback,
|
||||
"You have used 0 of 3 attempts",
|
||||
"All 3 attempts are not available"
|
||||
)
|
||||
self.answer_problem("correct")
|
||||
self.problem_page.click_submit()
|
||||
self.assertTrue(self.problem_page.is_reset_button_present())
|
||||
|
||||
def test_learner_can_see_attempts_left(self):
|
||||
"""
|
||||
Scenario: I can view how many attempts I have left on a problem
|
||||
|
||||
Given I am viewing a "multiple choice" problem with "3" attempts
|
||||
Then I should see "You have used 0 of 3 attempts" somewhere in the page
|
||||
When I answer a "multiple choice" problem "incorrectly"
|
||||
And I reset the problem
|
||||
Then I should see "You have used 1 of 3 attempts" somewhere in the page
|
||||
When I answer a "multiple choice" problem "incorrectly"
|
||||
And I reset the problem
|
||||
Then I should see "You have used 2 of 3 attempts" somewhere in the page
|
||||
And The "Submit" button does appear
|
||||
When I answer a "multiple choice" problem "correctly"
|
||||
Then The "Reset" button does not appear
|
||||
"""
|
||||
for attempts_used in range(3):
|
||||
self.assertEqual(
|
||||
self.problem_page.submission_feedback,
|
||||
"You have used {} of 3 attempts".format(str(attempts_used)),
|
||||
"All 3 attempts are not available"
|
||||
)
|
||||
if attempts_used == 2:
|
||||
self.assertTrue(self.problem_page.is_submit_disabled())
|
||||
self.answer_problem("correct")
|
||||
self.problem_page.click_submit()
|
||||
self.assertFalse(self.problem_page.is_reset_button_present())
|
||||
else:
|
||||
self.answer_problem("incorrect")
|
||||
self.problem_page.click_submit()
|
||||
self.problem_page.click_reset()
|
||||
|
||||
|
||||
class MultipleChoiceProblemTypeNeverShowCorrectnessTest(MultipleChoiceProblemTypeBase,
|
||||
ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
@@ -692,6 +1032,23 @@ class RadioProblemTypeBase(ProblemTypeTestBase):
|
||||
'submitted': ['label.choicegroup_submitted', 'span.submitted'],
|
||||
}
|
||||
|
||||
def problem_status(self, status):
|
||||
"""
|
||||
Returns the status of problem
|
||||
Args:
|
||||
status(string): status of the problem which is to be checked
|
||||
|
||||
Returns:
|
||||
True: If provided status is present on the page
|
||||
False: If provided status is not present on the page
|
||||
"""
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
try:
|
||||
self.problem_page.wait_for_element_visibility(selector, 'Status not present', timeout=10)
|
||||
return True
|
||||
except BrokenPromise:
|
||||
return False
|
||||
|
||||
def answer_problem(self, correctness):
|
||||
"""
|
||||
Answer radio problem.
|
||||
@@ -702,6 +1059,7 @@ class RadioProblemTypeBase(ProblemTypeTestBase):
|
||||
self.problem_page.click_choice("choice_1")
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class RadioProblemTypeTest(RadioProblemTypeBase, ProblemTypeTestMixin):
|
||||
"""
|
||||
Standard tests for the Multiple Radio Problem Type
|
||||
@@ -709,6 +1067,72 @@ class RadioProblemTypeTest(RadioProblemTypeBase, ProblemTypeTestMixin):
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class RadioProblemResetCorrectnessAfterChangingAnswerTest(RadioProblemTypeBase):
|
||||
"""
|
||||
Tests for Radio problem with changing answers
|
||||
"""
|
||||
|
||||
@ddt.data(['correct', '1/1 point (ungraded)'], ['incorrect', '0/1 point (ungraded)'])
|
||||
@ddt.unpack
|
||||
def test_radio_score_after_answer_and_reset(self, correctness, score):
|
||||
"""
|
||||
Scenario: I can see my score on a radio problem when I answer it and after I reset it
|
||||
|
||||
Given I am viewing a radio problem
|
||||
When I answer a radio problem <correctness>
|
||||
Then I should see a <score>
|
||||
When I reset the problem
|
||||
Then I should see a score of points possible: 0/1 point (ungraded)
|
||||
"""
|
||||
self.answer_problem(correctness)
|
||||
self.problem_page.click_submit()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, score)
|
||||
self.problem_page.click_reset()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, '0/1 point (ungraded)')
|
||||
|
||||
@ddt.data(['correct', 'incorrect'], ['incorrect', 'correct'])
|
||||
@ddt.unpack
|
||||
def test_reset_correctness_after_changing_answer(self, initial_correctness, other_correctness):
|
||||
"""
|
||||
Scenario: I can reset the correctness of a radio problem after changing my answer
|
||||
|
||||
Given I am viewing a radio problem
|
||||
When I answer a radio problem with <initial_correctness>
|
||||
Then my radio answer is marked <initial_correctness>
|
||||
And I reset the problem
|
||||
Then my radio problem's answer is NOT marked <initial_correctness>
|
||||
And my radio problem's answer is NOT marked <other_correctness>
|
||||
"""
|
||||
self.assertTrue(self.problem_status("unanswered"))
|
||||
self.answer_problem(initial_correctness)
|
||||
self.problem_page.click_submit()
|
||||
|
||||
self.assertTrue(self.problem_status(initial_correctness))
|
||||
self.problem_page.click_reset()
|
||||
|
||||
self.assertFalse(self.problem_status(initial_correctness))
|
||||
self.assertFalse(self.problem_status(other_correctness))
|
||||
|
||||
|
||||
class RadioProblemTypeTestNonRandomized(RadioProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized radio problem
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class RadioProblemTypeNeverShowCorrectnessTest(RadioProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
Ensure that correctness can be withheld for Radio Problem Type problems.
|
||||
@@ -743,13 +1167,33 @@ class DropDownProblemTypeBase(ProblemTypeTestBase):
|
||||
select_option_by_text(selector_element, answer)
|
||||
|
||||
|
||||
class DropDownProblemTypeTest(DropDownProblemTypeBase, ProblemTypeTestMixin):
|
||||
@ddt.ddt
|
||||
class DropdownProblemTypeTest(DropDownProblemTypeBase, ProblemTypeTestMixin, ChangingAnswerOfProblemTestMixin):
|
||||
"""
|
||||
Standard tests for the Multiple Radio Problem Type
|
||||
Standard tests for the Dropdown Problem Type
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class DropDownProblemTypeTestNonRandomized(DropDownProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized Dropdown problem
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class DropDownProblemTypeNeverShowCorrectnessTest(DropDownProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
Ensure that correctness can be withheld for Drop Down Problem Type problems.
|
||||
@@ -781,6 +1225,23 @@ class StringProblemTypeBase(ProblemTypeTestBase):
|
||||
'submitted': ['span.submitted'],
|
||||
}
|
||||
|
||||
def problem_status(self, status):
|
||||
"""
|
||||
Returns the status of problem
|
||||
Args:
|
||||
status(string): status of the problem which is to be checked
|
||||
|
||||
Returns:
|
||||
True: If provided status is present on the page
|
||||
False: If provided status is not present on the page
|
||||
"""
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
try:
|
||||
self.problem_page.wait_for_element_visibility(selector, 'Status not present', timeout=10)
|
||||
return True
|
||||
except BrokenPromise:
|
||||
return False
|
||||
|
||||
def answer_problem(self, correctness):
|
||||
"""
|
||||
Answer string problem.
|
||||
@@ -825,8 +1286,26 @@ class NumericalProblemTypeBase(ProblemTypeTestBase):
|
||||
'incorrect': ['div.incorrect'],
|
||||
'unanswered': ['div.unanswered', 'div.unsubmitted'],
|
||||
'submitted': ['div.submitted'],
|
||||
'unsubmitted': ['div.unsubmitted']
|
||||
}
|
||||
|
||||
def problem_status(self, status):
|
||||
"""
|
||||
Returns the status of problem
|
||||
Args:
|
||||
status(string): status of the problem which is to be checked
|
||||
|
||||
Returns:
|
||||
True: If provided status is present on the page
|
||||
False: If provided status is not present on the page
|
||||
"""
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
try:
|
||||
self.problem_page.wait_for_element_visibility(selector, 'Status not present', timeout=10)
|
||||
return True
|
||||
except BrokenPromise:
|
||||
return False
|
||||
|
||||
def answer_problem(self, correctness):
|
||||
"""
|
||||
Answer numerical problem.
|
||||
@@ -841,7 +1320,8 @@ class NumericalProblemTypeBase(ProblemTypeTestBase):
|
||||
self.problem_page.fill_answer(textvalue)
|
||||
|
||||
|
||||
class NumericalProblemTypeTest(NumericalProblemTypeBase, ProblemTypeTestMixin):
|
||||
@ddt.ddt
|
||||
class NumericalProblemTypeTest(NumericalProblemTypeBase, ProblemTypeTestMixin, ChangingAnswerOfProblemTestMixin):
|
||||
"""
|
||||
Standard tests for the Numerical Problem Type
|
||||
"""
|
||||
@@ -871,6 +1351,54 @@ class NumericalProblemTypeTest(NumericalProblemTypeBase, ProblemTypeTestMixin):
|
||||
self.problem_page.wait_for_focus_on_problem_meta()
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class NumericalProblemTypeTestNonRandomized(NumericalProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized Numerical problem
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class NumericalProblemTypeTestViewAnswer(NumericalProblemTypeBase):
|
||||
"""
|
||||
Test learner can view Numerical problem's answer
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'showanswer': 'always'}
|
||||
)
|
||||
|
||||
def test_learner_can_view_answer(self):
|
||||
"""
|
||||
Scenario: I can view the answer if the problem has it:
|
||||
|
||||
Given I am viewing a "numerical" that shows the answer "always"
|
||||
When I press the button with the label "Show Answer"
|
||||
And I should see "4.14159" somewhere in the page
|
||||
"""
|
||||
self.problem_page.click_show()
|
||||
self.assertEqual(self.problem_page.answer, '4.14159')
|
||||
|
||||
|
||||
class NumericalProblemTypeNeverShowCorrectnessTest(NumericalProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
Ensure that correctness can be withheld for Numerical Problem Type problems.
|
||||
@@ -878,6 +1406,7 @@ class NumericalProblemTypeNeverShowCorrectnessTest(NumericalProblemTypeBase, Pro
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class FormulaProblemTypeBase(ProblemTypeTestBase):
|
||||
"""
|
||||
ProblemTypeTestBase specialization for Formula Problem Type
|
||||
@@ -904,6 +1433,23 @@ class FormulaProblemTypeBase(ProblemTypeTestBase):
|
||||
'submitted': ['div.submitted'],
|
||||
}
|
||||
|
||||
def problem_status(self, status):
|
||||
"""
|
||||
Returns the status of problem
|
||||
Args:
|
||||
status(string): status of the problem which is to be checked
|
||||
|
||||
Returns:
|
||||
True: If provided status is present on the page
|
||||
False: If provided status is not present on the page
|
||||
"""
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
try:
|
||||
self.problem_page.wait_for_element_visibility(selector, 'Status not present', timeout=10)
|
||||
return True
|
||||
except BrokenPromise:
|
||||
return False
|
||||
|
||||
def answer_problem(self, correctness):
|
||||
"""
|
||||
Answer formula problem.
|
||||
@@ -912,13 +1458,32 @@ class FormulaProblemTypeBase(ProblemTypeTestBase):
|
||||
self.problem_page.fill_answer(textvalue)
|
||||
|
||||
|
||||
class FormulaProblemTypeTest(FormulaProblemTypeBase, ProblemTypeTestMixin):
|
||||
@ddt.ddt
|
||||
class FormulaProblemTypeTest(FormulaProblemTypeBase, ProblemTypeTestMixin, ChangingAnswerOfProblemTestMixin):
|
||||
"""
|
||||
Standard tests for the Formula Problem Type
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class FormulaProblemTypeTestNonRandomized(FormulaProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized Formula problem
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class FormulaProblemTypeNeverShowCorrectnessTest(FormulaProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
Ensure that correctness can be withheld for Formula Problem Type problems.
|
||||
@@ -926,6 +1491,7 @@ class FormulaProblemTypeNeverShowCorrectnessTest(FormulaProblemTypeBase, Problem
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ScriptProblemTypeBase(ProblemTypeTestBase):
|
||||
"""
|
||||
ProblemTypeTestBase specialization for Script Problem Type
|
||||
@@ -961,6 +1527,22 @@ class ScriptProblemTypeBase(ProblemTypeTestBase):
|
||||
'submitted': ['div.submitted'],
|
||||
}
|
||||
|
||||
def problem_status(self, status):
|
||||
"""
|
||||
Returns the status of problem
|
||||
Args:
|
||||
status(string): status of the problem which is to be checked
|
||||
|
||||
Returns:
|
||||
True: If provided status is present on the page
|
||||
"""
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
try:
|
||||
self.problem_page.wait_for_element_visibility(selector, 'Status is present', timeout=10)
|
||||
return True
|
||||
except BrokenPromise:
|
||||
return False
|
||||
|
||||
def answer_problem(self, correctness):
|
||||
"""
|
||||
Answer script problem.
|
||||
@@ -978,6 +1560,7 @@ class ScriptProblemTypeBase(ProblemTypeTestBase):
|
||||
self.problem_page.fill_answer(second_addend, input_num=1)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ScriptProblemTypeTest(ScriptProblemTypeBase, ProblemTypeTestMixin):
|
||||
"""
|
||||
Standard tests for the Script Problem Type
|
||||
@@ -985,6 +1568,70 @@ class ScriptProblemTypeTest(ScriptProblemTypeBase, ProblemTypeTestMixin):
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ScriptProblemResetAfterAnswerTest(ScriptProblemTypeBase):
|
||||
"""
|
||||
Test Script problem by resetting answers
|
||||
"""
|
||||
|
||||
@ddt.data(['correct', 'incorrect'], ['incorrect', 'correct'])
|
||||
@ddt.unpack
|
||||
def test_reset_correctness_after_changing_answer(self, initial_correctness, other_correctness):
|
||||
"""
|
||||
Scenario: I can reset the correctness of a problem after changing my answer
|
||||
|
||||
Given I am viewing a script problem
|
||||
Then my script problem's answer is marked "unanswered"
|
||||
When I answer a script problem initial correctness
|
||||
And I input an answer on a script problem other correctness
|
||||
Then my script problem answer is marked "unanswered"
|
||||
"""
|
||||
self.assertTrue(self.problem_status('unanswered'))
|
||||
self.answer_problem(initial_correctness)
|
||||
self.problem_page.click_submit()
|
||||
|
||||
self.assertTrue(self.problem_status(initial_correctness))
|
||||
|
||||
self.answer_problem(other_correctness)
|
||||
self.assertTrue(self.problem_status('unanswered'))
|
||||
|
||||
@ddt.data(['correct', '2/2 points (ungraded)'], ['incorrect', '0/2 points (ungraded)'])
|
||||
@ddt.unpack
|
||||
def test_script_score_after_answer_and_reset(self, correctness, score):
|
||||
"""
|
||||
Scenario: I can see my score on a script problem when I answer it and after I reset it
|
||||
|
||||
Given I am viewing a script problem
|
||||
When I answer a script problem correct/incorrect
|
||||
Then I should see a score
|
||||
When I reset the problem
|
||||
Then I should see a score of points possible: 0/2 points (ungraded)
|
||||
"""
|
||||
self.answer_problem(correctness)
|
||||
self.problem_page.click_submit()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, score)
|
||||
self.problem_page.click_reset()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, '0/2 points (ungraded)')
|
||||
|
||||
|
||||
class ScriptProblemTypeTestNonRandomized(ScriptProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized Script problem
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class ScriptProblemTypeNeverShowCorrectnessTest(ScriptProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
Ensure that correctness can be withheld for Script Problem Type problems.
|
||||
@@ -1162,6 +1809,23 @@ class RadioTextProblemTypeBase(ChoiceTextProblemTypeTestBase):
|
||||
'submitted': ['section.choicetextgroup_submitted', 'span.submitted'],
|
||||
}
|
||||
|
||||
def problem_status(self, status):
|
||||
"""
|
||||
Returns the status of problem
|
||||
Args:
|
||||
status(string): status of the problem which is to be checked
|
||||
|
||||
Returns:
|
||||
True: If provided status is present on the page
|
||||
False: If provided status is not present on the page
|
||||
"""
|
||||
selector = ', '.join(self.status_indicators[status])
|
||||
try:
|
||||
self.problem_page.wait_for_element_visibility(selector, 'Status not present', timeout=10)
|
||||
return True
|
||||
except BrokenPromise:
|
||||
return False
|
||||
|
||||
def setUp(self, *args, **kwargs):
|
||||
"""
|
||||
Additional setup for RadioTextProblemTypeBase
|
||||
@@ -1177,6 +1841,7 @@ class RadioTextProblemTypeBase(ChoiceTextProblemTypeTestBase):
|
||||
})
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class RadioTextProblemTypeTest(RadioTextProblemTypeBase, ProblemTypeTestMixin):
|
||||
"""
|
||||
Standard tests for the Radio Text Problem Type
|
||||
@@ -1184,6 +1849,72 @@ class RadioTextProblemTypeTest(RadioTextProblemTypeBase, ProblemTypeTestMixin):
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class RadioTextProblemResetCorrectnessAfterChangingAnswerTest(RadioTextProblemTypeBase):
|
||||
"""
|
||||
Tests for Radio Text problem with changing answers
|
||||
"""
|
||||
|
||||
@ddt.data(['correct', '1/1 point (ungraded)'], ['incorrect', '0/1 point (ungraded)'])
|
||||
@ddt.unpack
|
||||
def test_mcq_score_after_answer_and_reset(self, correctness, score):
|
||||
"""
|
||||
Scenario: I can see my score on a radio text problem when I answer it and after I reset it
|
||||
|
||||
Given I am viewing a radio text problem
|
||||
When I answer a radio text problem correct/incorrect
|
||||
Then I should see a score
|
||||
When I reset the problem
|
||||
Then I should see a score of points possible: (1/1 point (ungraded) -- 0/1 point (ungraded)
|
||||
"""
|
||||
self.answer_problem(correctness)
|
||||
self.problem_page.click_submit()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, score)
|
||||
self.problem_page.click_reset()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, '0/1 point (ungraded)')
|
||||
|
||||
@ddt.data(['correct', 'incorrect'], ['incorrect', 'correct'])
|
||||
@ddt.unpack
|
||||
def test_reset_correctness_after_changing_answer(self, initial_correctness, other_correctness):
|
||||
"""
|
||||
Scenario: I can reset the correctness of a multiple choice problem after changing my answer
|
||||
|
||||
Given I am viewing a radio text problem
|
||||
When I answer a radio text problem InitialCorrectness
|
||||
Then my radio text answer is marked InitialCorrectness
|
||||
And I reset the problem
|
||||
Then my answer is NOT marked InitialCorrectness
|
||||
And my answer is NOT marked OtherCorrectness
|
||||
"""
|
||||
self.assertTrue(self.problem_status("unanswered"))
|
||||
self.answer_problem(initial_correctness)
|
||||
self.problem_page.click_submit()
|
||||
|
||||
self.assertTrue(self.problem_status(initial_correctness))
|
||||
self.problem_page.click_reset()
|
||||
|
||||
self.assertFalse(self.problem_status(initial_correctness))
|
||||
self.assertFalse(self.problem_status(other_correctness))
|
||||
|
||||
|
||||
class RadioTextProblemTypeTestNonRandomized(RadioTextProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized Radio text problem
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class RadioTextProblemTypeNeverShowCorrectnessTest(RadioTextProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
Ensure that correctness can be withheld for Radio + Text Problem Type problems.
|
||||
@@ -1233,6 +1964,24 @@ class CheckboxTextProblemTypeTest(CheckboxTextProblemTypeBase, ProblemTypeTestMi
|
||||
pass
|
||||
|
||||
|
||||
class CheckboxTextProblemTypeTestNonRandomized(CheckboxTextProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized Checkbox problem
|
||||
"""
|
||||
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class CheckboxTextProblemTypeNeverShowCorrectnessTest(CheckboxTextProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
"""
|
||||
Ensure that correctness can be withheld for Checkbox + Text Problem Type problems.
|
||||
@@ -1273,11 +2022,37 @@ class ImageProblemTypeBase(ProblemTypeTestBase):
|
||||
chain.perform()
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ImageProblemTypeTest(ImageProblemTypeBase, ProblemTypeTestMixin):
|
||||
"""
|
||||
Standard tests for the Image Problem Type
|
||||
"""
|
||||
pass
|
||||
def test_image_problem_score_with_blank_answer(self):
|
||||
"""
|
||||
Scenario: I can see my score on a problem to which I submit a blank answer
|
||||
Given I am viewing aN image problem
|
||||
When I submit a problem
|
||||
Then I should see a score of Points Possible: 0/1 point (ungraded)
|
||||
"""
|
||||
self.problem_page.click_submit()
|
||||
self.assertEqual(self.problem_page.problem_progress_graded_value, '0/1 point (ungraded)')
|
||||
|
||||
|
||||
class ImageProblemTypeTestNonRandomized(ImageProblemTypeBase, NonRandomizedProblemTypeTestMixin):
|
||||
"""
|
||||
Tests for non-randomized Image problem
|
||||
"""
|
||||
def get_problem(self):
|
||||
"""
|
||||
Creates a {problem_type} problem
|
||||
"""
|
||||
# Generate the problem XML using capa.tests.response_xml_factory
|
||||
return XBlockFixtureDesc(
|
||||
'problem',
|
||||
self.problem_name,
|
||||
data=self.factory.build_xml(**self.factory_kwargs),
|
||||
metadata={'rerandomize': 'never', 'show_reset_button': True}
|
||||
)
|
||||
|
||||
|
||||
class ImageProblemTypeNeverShowCorrectnessTest(ImageProblemTypeBase, ProblemNeverShowCorrectnessMixin):
|
||||
|
||||
Reference in New Issue
Block a user