Merge pull request #5404 from edx/waheed/tnl353-wrote-annotation-bok-choy-test
Converted annotation lettuce test into bok-choy test.
This commit is contained in:
91
common/test/acceptance/pages/lms/annotation_component.py
Normal file
91
common/test/acceptance/pages/lms/annotation_component.py
Normal file
@@ -0,0 +1,91 @@
|
||||
"""
|
||||
Annotation Component Page.
|
||||
"""
|
||||
from bok_choy.page_object import PageObject
|
||||
from selenium.webdriver import ActionChains
|
||||
|
||||
|
||||
class AnnotationComponentPage(PageObject):
|
||||
"""
|
||||
View of annotation component page.
|
||||
"""
|
||||
|
||||
url = None
|
||||
active_problem = 0
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='.annotatable-title').present
|
||||
|
||||
@property
|
||||
def component_name(self):
|
||||
"""
|
||||
Return the current problem name.
|
||||
"""
|
||||
return self.q(css='.annotatable-title').text[0]
|
||||
|
||||
def click_reply_annotation(self, problem):
|
||||
"""
|
||||
Mouse over on annotation selector and click on "Reply to Annotation".
|
||||
"""
|
||||
annotation_span_selector = '.annotatable-span[data-problem-id="{}"]'.format(problem)
|
||||
self.mouse_hover(self.browser.find_element_by_css_selector(annotation_span_selector))
|
||||
self.wait_for_element_visibility(annotation_span_selector, "Reply to Annotation link is visible")
|
||||
|
||||
annotation_reply_selector = '.annotatable-reply[data-problem-id="{}"]'.format(problem)
|
||||
self.q(css=annotation_reply_selector).click()
|
||||
|
||||
self.active_problem = problem
|
||||
|
||||
def active_problem_selector(self, sub_selector):
|
||||
"""
|
||||
Return css selector for current active problem with sub_selector.
|
||||
"""
|
||||
return 'div[data-problem-id="{}"] {}'.format(
|
||||
self.q(css='.vert-{}'.format(self.active_problem+1)).map(
|
||||
lambda el: el.get_attribute('data-id')).results[0],
|
||||
sub_selector,
|
||||
)
|
||||
|
||||
def mouse_hover(self, element):
|
||||
"""
|
||||
Mouse over on given element.
|
||||
"""
|
||||
mouse_hover_action = ActionChains(self.browser).move_to_element(element)
|
||||
mouse_hover_action.perform()
|
||||
|
||||
def check_scroll_to_problem(self):
|
||||
"""
|
||||
Return visibility of active problem's input selector.
|
||||
"""
|
||||
annotation_input_selector = self.active_problem_selector('.annotation-input')
|
||||
return self.q(css=annotation_input_selector).visible
|
||||
|
||||
def answer_problem(self):
|
||||
"""
|
||||
Submit correct answer for active problem.
|
||||
"""
|
||||
self.q(css=self.active_problem_selector('.comment')).fill('Test Response')
|
||||
self.q(css=self.active_problem_selector('.tag[data-id="{}"]'.format(self.active_problem))).click()
|
||||
self.q(css=self.active_problem_selector('.check')).click()
|
||||
|
||||
def check_feedback(self):
|
||||
"""
|
||||
Return visibility of active problem's feedback.
|
||||
"""
|
||||
self.wait_for_element_visibility(
|
||||
self.active_problem_selector('.tag-status.correct'), "Correct is visible"
|
||||
)
|
||||
return self.q(css=self.active_problem_selector('.tag-status.correct')).visible
|
||||
|
||||
def click_return_to_annotation(self):
|
||||
"""
|
||||
Click on active problem's "Return to Annotation" link.
|
||||
"""
|
||||
self.q(css=self.active_problem_selector('.annotation-return')).click()
|
||||
|
||||
def check_scroll_to_annotation(self):
|
||||
"""
|
||||
Return visibility of active annotation component header.
|
||||
"""
|
||||
annotation_header_selector = '.annotation-header'
|
||||
return self.q(css=annotation_header_selector).visible
|
||||
139
common/test/acceptance/tests/test_annotatable.py
Normal file
139
common/test/acceptance/tests/test_annotatable.py
Normal file
@@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
E2E tests for the LMS.
|
||||
"""
|
||||
import time
|
||||
|
||||
from .helpers import UniqueCourseTest
|
||||
from ..pages.studio.auto_auth import AutoAuthPage
|
||||
from ..pages.lms.courseware import CoursewarePage
|
||||
from ..pages.lms.annotation_component import AnnotationComponentPage
|
||||
from ..fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
from ..fixtures.xqueue import XQueueResponseFixture
|
||||
from textwrap import dedent
|
||||
|
||||
|
||||
def _correctness(choice, target):
|
||||
if choice == target:
|
||||
return "correct"
|
||||
elif abs(choice - target) == 1:
|
||||
return "partially-correct"
|
||||
else:
|
||||
return "incorrect"
|
||||
|
||||
|
||||
class AnnotatableProblemTest(UniqueCourseTest):
|
||||
"""
|
||||
Tests for annotation components.
|
||||
"""
|
||||
USERNAME = "STAFF_TESTER"
|
||||
EMAIL = "johndoe@example.com"
|
||||
|
||||
|
||||
DATA_TEMPLATE = dedent("""\
|
||||
<annotatable>
|
||||
<instructions>Instruction text</instructions>
|
||||
<p>{}</p>
|
||||
</annotatable>
|
||||
""")
|
||||
|
||||
ANNOTATION_TEMPLATE = dedent("""\
|
||||
Before {0}.
|
||||
<annotation title="region {0}" body="Comment {0}" highlight="yellow" problem="{0}">
|
||||
Region Contents {0}
|
||||
</annotation>
|
||||
After {0}.
|
||||
""")
|
||||
|
||||
PROBLEM_TEMPLATE = dedent("""\
|
||||
<problem max_attempts="1" weight="">
|
||||
<annotationresponse>
|
||||
<annotationinput>
|
||||
<title>Question {number}</title>
|
||||
<text>Region Contents {number}</text>
|
||||
<comment>What number is this region?</comment>
|
||||
<comment_prompt>Type your response below:</comment_prompt>
|
||||
<tag_prompt>What number is this region?</tag_prompt>
|
||||
<options>
|
||||
{options}
|
||||
</options>
|
||||
</annotationinput>
|
||||
</annotationresponse>
|
||||
<solution>
|
||||
This problem is checking region {number}
|
||||
</solution>
|
||||
</problem>
|
||||
""")
|
||||
|
||||
OPTION_TEMPLATE = """<option choice="{correctness}">{number}</option>"""
|
||||
|
||||
def setUp(self):
|
||||
super(AnnotatableProblemTest, self).setUp()
|
||||
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
|
||||
# Install a course with two annotations and two annotations problems.
|
||||
course_fix = CourseFixture(
|
||||
self.course_info['org'], self.course_info['number'],
|
||||
self.course_info['run'], self.course_info['display_name']
|
||||
)
|
||||
|
||||
self.annotation_count = 2
|
||||
course_fix.add_children(
|
||||
XBlockFixtureDesc('chapter', 'Test Section').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection').add_children(
|
||||
XBlockFixtureDesc('vertical', 'Test Annotation Vertical').add_children(
|
||||
XBlockFixtureDesc('annotatable', 'Test Annotation Module',
|
||||
data=self.DATA_TEMPLATE.format("\n".join(
|
||||
self.ANNOTATION_TEMPLATE.format(i) for i in xrange(self.annotation_count)
|
||||
))),
|
||||
XBlockFixtureDesc('problem', 'Test Annotation Problem 0',
|
||||
data=self.PROBLEM_TEMPLATE.format(number=0, options="\n".join(
|
||||
self.OPTION_TEMPLATE.format(
|
||||
number=k,
|
||||
correctness=_correctness(k, 0))
|
||||
for k in xrange(self.annotation_count)
|
||||
))),
|
||||
XBlockFixtureDesc('problem', 'Test Annotation Problem 1',
|
||||
data=self.PROBLEM_TEMPLATE.format(number=1, options="\n".join(
|
||||
self.OPTION_TEMPLATE.format(
|
||||
number=k,
|
||||
correctness=_correctness(k, 1))
|
||||
for k in xrange(self.annotation_count)
|
||||
)))
|
||||
)
|
||||
)
|
||||
)
|
||||
).install()
|
||||
|
||||
# Auto-auth register for the course.
|
||||
AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL,
|
||||
course_id=self.course_id, staff=False).visit()
|
||||
|
||||
def _goto_annotation_component_page(self):
|
||||
"""
|
||||
Open annotation component page with assertion.
|
||||
"""
|
||||
self.courseware_page.visit()
|
||||
annotation_component_page = AnnotationComponentPage(self.browser)
|
||||
self.assertEqual(
|
||||
annotation_component_page.component_name, 'TEST ANNOTATION MODULE'.format()
|
||||
)
|
||||
return annotation_component_page
|
||||
|
||||
def test_annotation_component(self):
|
||||
"""
|
||||
Test annotation components links to annotation problems.
|
||||
"""
|
||||
|
||||
annotation_component_page = self._goto_annotation_component_page()
|
||||
|
||||
for i in xrange(self.annotation_count):
|
||||
annotation_component_page.click_reply_annotation(i)
|
||||
self.assertTrue(annotation_component_page.check_scroll_to_problem())
|
||||
|
||||
annotation_component_page.answer_problem()
|
||||
self.assertTrue(annotation_component_page.check_feedback())
|
||||
|
||||
annotation_component_page.click_return_to_annotation()
|
||||
self.assertTrue(annotation_component_page.check_scroll_to_annotation())
|
||||
@@ -7,22 +7,3 @@ Feature: LMS.Annotatable Component
|
||||
When I view the annotatable component
|
||||
Then the annotatable component has rendered
|
||||
And the annotatable component has 2 highlighted passages
|
||||
|
||||
# Disabling due to frequent intermittent failures. TNL-353
|
||||
# This features's acceptance tests should be rewritten in bok-choy, rather than fixed here.
|
||||
#
|
||||
# Scenario: An Annotatable component links to annonation problems in the LMS
|
||||
# Given that a course has an annotatable component with 2 annotations
|
||||
# And the course has 2 annotatation problems
|
||||
# When I view the annotatable component
|
||||
# And I click "Reply to annotation" on passage <problem>
|
||||
# Then I am scrolled to that annotation problem
|
||||
# When I answer that annotation problem
|
||||
# Then I receive feedback on that annotation problem
|
||||
# When I click "Return to annotation" on that problem
|
||||
# Then I am scrolled to the annotatable component
|
||||
#
|
||||
# Examples:
|
||||
# | problem |
|
||||
# | 0 |
|
||||
# | 1 |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import textwrap
|
||||
|
||||
from lettuce import world, steps
|
||||
from nose.tools import assert_in, assert_equals, assert_true
|
||||
from nose.tools import assert_in, assert_equals
|
||||
|
||||
from common import i_am_registered_for_the_course, visit_scenario_item
|
||||
|
||||
@@ -100,74 +100,6 @@ class AnnotatableSteps(object):
|
||||
assert_equals(len(world.css_find('.annotatable-span.highlight')), count)
|
||||
assert_equals(len(world.css_find('.annotatable-span.highlight-yellow')), count)
|
||||
|
||||
def add_problems(self, step, count):
|
||||
r"""the course has (?P<count>\d+) annotatation problems$"""
|
||||
count = int(count)
|
||||
|
||||
for i in xrange(count):
|
||||
world.scenario_dict.setdefault('PROBLEMS', []).append(
|
||||
world.ItemFactory(
|
||||
parent_location=world.scenario_dict['ANNOTATION_VERTICAL'].location,
|
||||
category='problem',
|
||||
display_name="Test Annotation Problem {}".format(i),
|
||||
data=PROBLEM_TEMPLATE.format(
|
||||
number=i,
|
||||
options="\n".join(
|
||||
OPTION_TEMPLATE.format(
|
||||
number=k,
|
||||
correctness=_correctness(k, i)
|
||||
)
|
||||
for k in xrange(count)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def click_reply(self, step, problem):
|
||||
r"""I click "Reply to annotation" on passage (?P<problem>\d+)$"""
|
||||
problem = int(problem)
|
||||
|
||||
annotation_span_selector = '.annotatable-span[data-problem-id="{}"]'.format(problem)
|
||||
|
||||
world.css_find(annotation_span_selector).first.mouse_over()
|
||||
|
||||
annotation_reply_selector = '.annotatable-reply[data-problem-id="{}"]'.format(problem)
|
||||
assert_equals(len(world.css_find(annotation_reply_selector)), 1)
|
||||
world.css_click(annotation_reply_selector)
|
||||
|
||||
self.active_problem = problem
|
||||
|
||||
def active_problem_selector(self, subselector):
|
||||
return 'div[data-problem-id="{}"] {}'.format(
|
||||
world.scenario_dict['PROBLEMS'][self.active_problem].location.to_deprecated_string(),
|
||||
subselector,
|
||||
)
|
||||
|
||||
def check_scroll_to_problem(self, step):
|
||||
r"""I am scrolled to that annotation problem$"""
|
||||
annotation_input_selector = self.active_problem_selector('.annotation-input')
|
||||
assert_true(world.css_visible(annotation_input_selector))
|
||||
|
||||
def answer_problem(self, step):
|
||||
r"""I answer that annotation problem$"""
|
||||
world.css_fill(self.active_problem_selector('.comment'), 'Test Response')
|
||||
world.css_click(self.active_problem_selector('.tag[data-id="{}"]'.format(self.active_problem)))
|
||||
world.css_click(self.active_problem_selector('.check'))
|
||||
|
||||
def check_feedback(self, step):
|
||||
r"""I receive feedback on that annotation problem$"""
|
||||
world.wait_for_visible(self.active_problem_selector('.tag-status.correct'))
|
||||
assert_equals(len(world.css_find(self.active_problem_selector('.tag-status.correct'))), 1)
|
||||
assert_equals(len(world.css_find(self.active_problem_selector('.show'))), 1)
|
||||
|
||||
def click_return_to(self, step):
|
||||
r"""I click "Return to annotation" on that problem$"""
|
||||
world.css_click(self.active_problem_selector('.annotation-return'))
|
||||
|
||||
def check_scroll_to_annotatable(self, step):
|
||||
r"""I am scrolled to the annotatable component$"""
|
||||
assert_true(world.css_visible('.annotation-header'))
|
||||
|
||||
# This line is required by @steps in order to actually bind the step
|
||||
# regexes
|
||||
AnnotatableSteps()
|
||||
|
||||
Reference in New Issue
Block a user