Commit bbcec0cf by Sanford Student

displaying title of sequence item in tooltip instead of children

parent 393e885e
......@@ -218,13 +218,9 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
rendered_child = child.render(STUDENT_VIEW, context)
fragment.add_frag_resources(rendered_child)
# `titles` is a list of titles to inject into the sequential tooltip display.
# We omit any blank titles to avoid blank lines in the tooltip display.
titles = [title.strip() for title in child.get_content_titles() if title.strip()]
childinfo = {
'content': rendered_child.content,
'title': "\n".join(titles),
'page_title': titles[0] if titles else '',
'page_title': getattr(child, 'tooltip_title', ''),
'progress_status': Progress.to_js_status_str(progress),
'progress_detail': Progress.to_js_detail_str(progress),
'type': child.get_icon_class(),
......@@ -232,8 +228,7 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
'bookmarked': is_bookmarked,
'path': " > ".join(display_names + [child.display_name_with_default]),
}
if childinfo['title'] == '':
childinfo['title'] = child.display_name_with_default_escaped
contents.append(childinfo)
params = {
......
......@@ -356,6 +356,10 @@ class SplitTestModule(SplitTestFields, XModule, StudioEditableModule):
return (group.name, group.id)
return (None, None)
@property
def tooltip_title(self):
return getattr(self.child, 'tooltip_title', '')
def validate(self):
"""
Message for either error or warning validation message/s.
......@@ -695,3 +699,5 @@ class SplitTestDescriptor(SplitTestFields, SequenceDescriptor, StudioEditableDes
)
self.children.append(dest_usage_key) # pylint: disable=no-member
self.group_id_to_child[unicode(group.id)] = dest_usage_key
tooltip_title = module_attr('tooltip_title')
......@@ -161,3 +161,8 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
lambda course_key, block_location, child: block_location,
)
self.assertEquals(actual_next_sequence_location, expected_prev_sequence_location)
def test_tooltip(self):
html = self._get_rendered_student_view(self.sequence_3_1, requested_child=None)
for child in self.sequence_3_1.children:
self.assertIn("'page_title': '{}'".format(child.name), html)
......@@ -366,6 +366,13 @@ class XModuleMixin(XModuleFields, XBlock):
return course_metadata_utils.display_name_with_default_escaped(self)
@property
def tooltip_title(self):
"""
Return the title for the sequence item containing this xmodule as its top level item.
"""
return self.display_name_with_default
@property
def xblock_kvs(self):
"""
Retrieves the internal KeyValueStore for this XModule.
......
......@@ -103,20 +103,20 @@ class CourseNavPage(PageObject):
self.q(css=subsection_css).first.click()
self._on_section_promise(section_title, subsection_title).fulfill()
def go_to_sequential(self, sequential_title):
def go_to_vertical(self, vertical_title):
"""
Within a section/subsection, navigate to the sequential with `sequential_title`.
Within a section/subsection, navigate to the vertical with `vertical_title`.
"""
# Get the index of the item in the sequence
all_items = self.sequence_items
try:
seq_index = all_items.index(sequential_title)
seq_index = all_items.index(vertical_title)
except ValueError:
msg = "Could not find sequential '{0}'. Available sequentials: [{1}]".format(
sequential_title, ", ".join(all_items)
vertical_title, ", ".join(all_items)
)
self.warning(msg)
......
......@@ -212,7 +212,7 @@ class CertificateProgressPageTest(UniqueCourseTest):
self.course_nav.go_to_section('Test Section', 'Test Subsection')
# Navigate to Test Problem 1
self.course_nav.go_to_sequential('Test Problem 1')
self.course_nav.go_to_vertical('Test Problem 1')
# Select correct value for from select menu
self.course_nav.q(css='select option[value="{}"]'.format('blue')).first.click()
......@@ -232,7 +232,7 @@ class CertificateProgressPageTest(UniqueCourseTest):
self.course_nav.go_to_section('Test Section 2', 'Test Subsection 2')
# Navigate to Test Problem 2
self.course_nav.go_to_sequential('Test Problem 2')
self.course_nav.go_to_vertical('Test Problem 2')
# Fill in the answer of the problem
self.course_nav.q(css='input[id^=input_][id$=_2_1]').fill('A*x^2 + sqrt(y)')
......
......@@ -824,13 +824,13 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
self.assertEqual(3, len(self.course_nav.sections['Test Section']))
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit")
self.assertEqual(["Html Child in locked unit", "Html Child in unlocked unit"], self.course_nav.sequence_items)
self.assertEqual([u'Locked Unit', u'Unlocked Unit'], self.course_nav.sequence_items)
self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
self.assertEqual(["Html Child in visible unit"], self.course_nav.sequence_items)
self.assertEqual([u'Test Unit'], self.course_nav.sequence_items)
self.course_nav.go_to_section("Test Section", "Locked Subsection")
self.assertEqual(["Html Child in locked subsection"], self.course_nav.sequence_items)
self.assertEqual([u'Test Unit'], self.course_nav.sequence_items)
def test_visible_to_student(self):
"""
......@@ -846,10 +846,10 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
self.assertEqual(2, len(self.course_nav.sections['Test Section']))
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit")
self.assertEqual(["Html Child in unlocked unit"], self.course_nav.sequence_items)
self.assertEqual([u'Unlocked Unit'], self.course_nav.sequence_items)
self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
self.assertEqual(["Html Child in visible unit"], self.course_nav.sequence_items)
self.assertEqual([u'Test Unit'], self.course_nav.sequence_items)
@attr('shard_1')
......
......@@ -61,7 +61,7 @@ class VideoBaseTest(UniqueCourseTest):
self.metadata = None
self.assets = []
self.verticals = None
self.contents_of_verticals = None
self.youtube_configuration = {}
self.user_info = {}
......@@ -102,28 +102,28 @@ class VideoBaseTest(UniqueCourseTest):
:return: a list of XBlockFixtureDesc
"""
xblock_verticals = []
_verticals = self.verticals
_contents_of_verticals = self.contents_of_verticals
# Video tests require at least one vertical with a single video.
if not _verticals:
_verticals = [[{'display_name': 'Video', 'metadata': self.metadata}]]
if not _contents_of_verticals:
_contents_of_verticals = [[{'display_name': 'Video', 'metadata': self.metadata}]]
for vertical_index, vertical in enumerate(_verticals):
for vertical_index, vertical in enumerate(_contents_of_verticals):
xblock_verticals.append(self._create_single_vertical(vertical, vertical_index))
return xblock_verticals
def _create_single_vertical(self, vertical, vertical_index):
def _create_single_vertical(self, vertical_contents, vertical_index):
"""
Create a single course vertical of type XBlockFixtureDesc with category `vertical`.
A single course vertical can contain single or multiple video modules.
:param vertical: vertical data list
:param vertical_contents: a list of items for the vertical to contain
:param vertical_index: index for the vertical display name
:return: XBlockFixtureDesc
"""
xblock_course_vertical = XBlockFixtureDesc('vertical', 'Test Vertical-{0}'.format(vertical_index))
for video in vertical:
for video in vertical_contents:
xblock_course_vertical.add_children(
XBlockFixtureDesc('video', video['display_name'], metadata=video.get('metadata')))
......@@ -512,13 +512,13 @@ class YouTubeVideoTest(VideoBaseTest):
data_c = {'track': 'http://example.org/', 'download_track': True}
html5_c_metadata = self.metadata_for_mode('html5', additional_data=data_c)
self.verticals = [
self.contents_of_verticals = [
[{'display_name': 'A', 'metadata': youtube_a_metadata}],
[{'display_name': 'B', 'metadata': youtube_b_metadata}],
[{'display_name': 'C', 'metadata': html5_c_metadata}]
]
# open the section with videos (open video "A")
# open the section with videos (open vertical containing video "A")
self.navigate_to_video()
# check if we can download transcript in "srt" format that has text "00:00:00,260"
......@@ -530,14 +530,14 @@ class YouTubeVideoTest(VideoBaseTest):
# check if we can download transcript in "txt" format that has text "Welcome to edX."
self.assertTrue(self.video.downloaded_transcript_contains_text('txt', 'Welcome to edX.'))
# open video "B"
self.course_nav.go_to_sequential('B')
# open vertical containing video "B"
self.course_nav.go_to_vertical('Test Vertical-1')
# check if we can download transcript in "txt" format that has text "Equal transcripts"
self.assertTrue(self.video.downloaded_transcript_contains_text('txt', 'Equal transcripts'))
# open video "C"
self.course_nav.go_to_sequential('C')
# open vertical containing video "C"
self.course_nav.go_to_vertical('Test Vertical-2')
# menu "download_transcript" doesn't exist
self.assertFalse(self.video.is_menu_present('download_transcript'))
......@@ -635,7 +635,7 @@ class YouTubeVideoTest(VideoBaseTest):
Given it has videos "A,B" in "Youtube" mode in position "1" of sequential
And videos "C,D" in "Youtube" mode in position "2" of sequential
"""
self.verticals = [
self.contents_of_verticals = [
[{'display_name': 'A'}, {'display_name': 'B'}],
[{'display_name': 'C'}, {'display_name': 'D'}]
]
......@@ -675,7 +675,9 @@ class YouTubeVideoTest(VideoBaseTest):
And a video "B" in "Youtube" mode in position "2" of sequential
And a video "C" in "HTML5" mode in position "3" of sequential
"""
self.verticals = [
# vertical titles are created in VideoBaseTest._create_single_vertical
# and are of the form Test Vertical-{_} where _ is the index in self.contents_of_verticals
self.contents_of_verticals = [
[{'display_name': 'A'}], [{'display_name': 'B'}],
[{'display_name': 'C', 'metadata': self.metadata_for_mode('html5')}]
]
......@@ -683,17 +685,17 @@ class YouTubeVideoTest(VideoBaseTest):
self.navigate_to_video()
# select the "2.0" speed on video "A"
self.course_nav.go_to_sequential('A')
self.course_nav.go_to_vertical('Test Vertical-0')
self.video.wait_for_video_player_render()
self.video.speed = '2.0'
# select the "0.50" speed on video "B"
self.course_nav.go_to_sequential('B')
self.course_nav.go_to_vertical('Test Vertical-1')
self.video.wait_for_video_player_render()
self.video.speed = '0.50'
# open video "C"
self.course_nav.go_to_sequential('C')
self.course_nav.go_to_vertical('Test Vertical-2')
self.video.wait_for_video_player_render()
# Since the playback speed was set to .5 in "B", this video will also be impacted
......@@ -701,8 +703,8 @@ class YouTubeVideoTest(VideoBaseTest):
# does not have a .5 playback option, so the closest possible (.75) should be selected.
self.video.verify_speed_changed('0.75x')
# open video "A"
self.course_nav.go_to_sequential('A')
# go to the vertical containing video "A"
self.course_nav.go_to_vertical('Test Vertical-0')
# Video "A" should still play at speed 2.0 because it was explicitly set to that.
self.assertEqual(self.video.speed, '2.0x')
......@@ -710,8 +712,8 @@ class YouTubeVideoTest(VideoBaseTest):
# reload the page
self.video.reload_page()
# open video "A"
self.course_nav.go_to_sequential('A')
# go to the vertical containing video "A"
self.course_nav.go_to_vertical('Test Vertical-0')
# check if video "A" should start playing at speed "2.0"
self.assertEqual(self.video.speed, '2.0x')
......@@ -719,14 +721,14 @@ class YouTubeVideoTest(VideoBaseTest):
# select the "1.0" speed on video "A"
self.video.speed = '1.0'
# open video "B"
self.course_nav.go_to_sequential('B')
# go to the vertical containing "B"
self.course_nav.go_to_vertical('Test Vertical-1')
# Video "B" should still play at speed .5 because it was explicitly set to that.
self.assertEqual(self.video.speed, '0.50x')
# open video "C"
self.course_nav.go_to_sequential('C')
# go to the vertical containing video "C"
self.course_nav.go_to_vertical('Test Vertical-2')
# The change of speed for Video "A" should impact Video "C" because it still has
# not been explicitly set to a speed.
......@@ -882,7 +884,7 @@ class YouTubeVideoTest(VideoBaseTest):
}
}
self.verticals = [
self.contents_of_verticals = [
[{'display_name': 'A'}, {'display_name': 'B', 'metadata': self.metadata_for_mode('html5')}],
[{'display_name': 'C'}]
]
......
......@@ -255,8 +255,7 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase, Milest
resp = self.client.get(url)
self.assertRedirects(resp, expected_url, status_code=302, target_status_code=200)
resp = self.client.get(expected_url)
self.assertNotIn('Exam Problem - Problem 1', resp.content)
self.assertNotIn('Exam Problem - Problem 2', resp.content)
self.assertNotIn('Exam Vertical - Unit 1', resp.content)
def test_entrance_exam_content_presence(self):
"""
......@@ -273,8 +272,7 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase, Milest
resp = self.client.get(url)
self.assertRedirects(resp, expected_url, status_code=302, target_status_code=200)
resp = self.client.get(expected_url)
self.assertIn('Exam Problem - Problem 1', resp.content)
self.assertIn('Exam Problem - Problem 2', resp.content)
self.assertIn('Exam Vertical - Unit 1', resp.content)
def test_get_entrance_exam_content(self):
"""
......
......@@ -24,7 +24,6 @@ class SplitTestBase(SharedModuleStoreTestCase):
COURSE_NUMBER = 'split-test-base'
ICON_CLASSES = None
TOOLTIPS = None
HIDDEN_CONTENT = None
VISIBLE_CONTENT = None
@classmethod
......@@ -63,6 +62,9 @@ class SplitTestBase(SharedModuleStoreTestCase):
CourseEnrollmentFactory.create(user=self.student, course_id=self.course.id)
self.client.login(username=self.student.username, password='test')
self.included_usage_keys = None
self.excluded_usage_keys = None
def _video(self, parent, group):
"""
Returns a video component with parent ``parent``
......@@ -128,35 +130,34 @@ class SplitTestBase(SharedModuleStoreTestCase):
for tooltip in self.TOOLTIPS[user_tag]:
self.assertIn(tooltip, content)
for hidden in self.HIDDEN_CONTENT[user_tag]:
self.assertNotIn(hidden, content)
unicode_content = content.decode("utf-8")
for key in self.included_usage_keys[user_tag]:
self.assertIn(unicode(key), unicode_content)
for key in self.excluded_usage_keys[user_tag]:
self.assertNotIn(unicode(key), unicode_content)
# Assert that we can see the data from the appropriate test condition
for visible in self.VISIBLE_CONTENT[user_tag]:
self.assertIn(visible, content)
class TestVertSplitTestVert(SplitTestBase):
class TestSplitTestVert(SplitTestBase):
"""
Tests related to xmodule/split_test_module
Tests a sequential whose top-level vertical is determined by a split test.
"""
__test__ = True
COURSE_NUMBER = 'vert-split-vert'
COURSE_NUMBER = 'test-split-test-vert-vert'
ICON_CLASSES = [
'seq_problem',
'seq_video',
]
TOOLTIPS = [
['Group 0 Sees This Video', "Group 0 Sees This Problem"],
['Group 1 Sees This Video', 'Group 1 Sees This HTML'],
]
HIDDEN_CONTENT = [
['Condition 0 vertical'],
['Condition 1 vertical'],
]
# Data is html encoded, because it's inactive inside the
# sequence until javascript is executed
VISIBLE_CONTENT = [
......@@ -167,21 +168,13 @@ class TestVertSplitTestVert(SplitTestBase):
def setUp(self):
# We define problem compenents that we need but don't explicitly call elsewhere.
# pylint: disable=unused-variable
super(TestVertSplitTestVert, self).setUp()
super(TestSplitTestVert, self).setUp()
# vert <- split_test
# split_test cond 0 = vert <- {video, problem}
# split_test cond 1 = vert <- {video, html}
vert1 = ItemFactory.create(
parent_location=self.sequential.location,
category="vertical",
display_name="Split test vertical",
)
c0_url = self.course.id.make_usage_key("vertical", "split_test_cond0")
c1_url = self.course.id.make_usage_key("vertical", "split_test_cond1")
split_test = ItemFactory.create(
parent_location=vert1.location,
parent_location=self.sequential.location,
category="split_test",
display_name="Split test",
user_partition_id='0',
......@@ -206,26 +199,32 @@ class TestVertSplitTestVert(SplitTestBase):
video1 = self._video(cond1vert, 1)
html1 = self._html(cond1vert, 1)
self.included_usage_keys = [
[video0.location, problem0.location],
[video1.location, html1.location],
]
class TestSplitTestVert(SplitTestBase):
self.excluded_usage_keys = [
[video1.location, html1.location],
[video0.location, problem0.location],
]
class TestVertSplitTestVert(SplitTestBase):
"""
Tests related to xmodule/split_test_module
Tests a sequential whose top-level vertical contains a split test determining content within that vertical.
"""
__test__ = True
COURSE_NUMBER = 'split-vert'
COURSE_NUMBER = 'test-vert-split-test-vert'
ICON_CLASSES = [
'seq_problem',
'seq_video',
]
TOOLTIPS = [
['Group 0 Sees This Video', "Group 0 Sees This Problem"],
['Group 1 Sees This Video', 'Group 1 Sees This HTML'],
]
HIDDEN_CONTENT = [
['Condition 0 vertical'],
['Condition 1 vertical'],
['Split test vertical'],
['Split test vertical'],
]
# Data is html encoded, because it's inactive inside the
......@@ -238,15 +237,18 @@ class TestSplitTestVert(SplitTestBase):
def setUp(self):
# We define problem compenents that we need but don't explicitly call elsewhere.
# pylint: disable=unused-variable
super(TestSplitTestVert, self).setUp()
super(TestVertSplitTestVert, self).setUp()
# split_test cond 0 = vert <- {video, problem}
# split_test cond 1 = vert <- {video, html}
vert1 = ItemFactory.create(
parent_location=self.sequential.location,
category="vertical",
display_name="Split test vertical",
)
c0_url = self.course.id.make_usage_key("vertical", "split_test_cond0")
c1_url = self.course.id.make_usage_key("vertical", "split_test_cond1")
split_test = ItemFactory.create(
parent_location=self.sequential.location,
parent_location=vert1.location,
category="split_test",
display_name="Split test",
user_partition_id='0',
......@@ -256,8 +258,8 @@ class TestSplitTestVert(SplitTestBase):
cond0vert = ItemFactory.create(
parent_location=split_test.location,
category="vertical",
display_name="Condition 0 vertical",
location=c0_url,
display_name="Condition 0 Vertical",
location=c0_url
)
video0 = self._video(cond0vert, 0)
problem0 = self._problem(cond0vert, 0)
......@@ -265,12 +267,22 @@ class TestSplitTestVert(SplitTestBase):
cond1vert = ItemFactory.create(
parent_location=split_test.location,
category="vertical",
display_name="Condition 1 vertical",
location=c1_url,
display_name="Condition 1 Vertical",
location=c1_url
)
video1 = self._video(cond1vert, 1)
html1 = self._html(cond1vert, 1)
self.included_usage_keys = [
[video0.location, problem0.location],
[video1.location, html1.location],
]
self.excluded_usage_keys = [
[video1.location, html1.location],
[video0.location, problem0.location],
]
@attr('shard_1')
class SplitTestPosition(SharedModuleStoreTestCase):
......
......@@ -198,16 +198,27 @@ class ViewsTestCase(ModuleStoreTestCase):
parent_location=self.chapter.location,
due=datetime(2013, 9, 18, 11, 30, 00),
)
self.vertical = ItemFactory.create(category='vertical', parent_location=self.section.location)
self.component = ItemFactory.create(
self.vertical = ItemFactory.create(
category='vertical',
parent_location=self.section.location,
display_name='Vertical 1'
)
self.problem = ItemFactory.create(
category='problem',
parent_location=self.vertical.location,
display_name='Problem 1',
)
self.section2 = ItemFactory.create(category='sequential', parent_location=self.chapter.location)
self.vertical2 = ItemFactory.create(category='vertical', parent_location=self.section2.location)
ItemFactory.create(
self.section2 = ItemFactory.create(
category='sequential',
parent_location=self.chapter.location
)
self.vertical2 = ItemFactory.create(
category='vertical',
parent_location=self.section2.location,
display_name='Vertical 2'
)
self.problem2 = ItemFactory.create(
category='problem',
parent_location=self.vertical2.location,
display_name='Problem 2',
......@@ -229,15 +240,15 @@ class ViewsTestCase(ModuleStoreTestCase):
def test_index_success(self):
response = self._verify_index_response()
self.assertIn('Problem 2', response.content)
self.assertIn(unicode(self.problem2.location), response.content.decode("utf-8"))
# re-access to the main course page redirects to last accessed view.
url = reverse('courseware', kwargs={'course_id': unicode(self.course_key)})
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
response = self.client.get(response.url) # pylint: disable=no-member
self.assertNotIn('Problem 1', response.content)
self.assertIn('Problem 2', response.content)
self.assertNotIn(unicode(self.problem.location), response.content.decode("utf-8"))
self.assertIn(unicode(self.problem2.location), response.content.decode("utf-8"))
def test_index_nonexistent_chapter(self):
self._verify_index_response(expected_response_code=404, chapter_name='non-existent')
......@@ -540,7 +551,7 @@ class ViewsTestCase(ModuleStoreTestCase):
url = reverse('submission_history', kwargs={
'course_id': unicode(self.course_key),
'student_username': 'dummy',
'location': unicode(self.component.location),
'location': unicode(self.problem.location),
})
response = self.client.get(url)
# Tests that we do not get an "Invalid x" response when passing correct arguments to view
......
......@@ -23,7 +23,7 @@
id="tab_${idx}">
<i class="icon fa seq_${item['type']}" aria-hidden="true"></i>
<i class="fa fa-fw fa-bookmark bookmark-icon ${"is-hidden" if not item['bookmarked'] else "bookmarked"}" aria-hidden="true"></i>
<div class="sequence-tooltip sr"><span class="sr">${item['type']}&nbsp;</span>${item['title']}<span class="sr bookmark-icon-sr">&nbsp;${_("Bookmarked") if item['bookmarked'] else ""}</span></div>
<div class="sequence-tooltip sr"><span class="sr">${item['type']}&nbsp;</span>${item['page_title']}<span class="sr bookmark-icon-sr">&nbsp;${_("Bookmarked") if item['bookmarked'] else ""}</span></div>
</button>
</li>
% endfor
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment