Add/Remove Bookmark button to each unit in LMS courseware.
TNL-1957
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
$sequence--border-color: #C8C8C8;
|
||||
|
||||
$link-color: rgb(26, 161, 222);
|
||||
// repeated extends - needed since LMS styling was referenced
|
||||
.block-link {
|
||||
border-left: 1px solid lighten($sequence--border-color, 10%);
|
||||
@@ -36,7 +36,7 @@ $sequence--border-color: #C8C8C8;
|
||||
// TODO (cpennington): This doesn't work anymore. XModules aren't able to
|
||||
// import from external sources.
|
||||
@extend .topbar;
|
||||
margin: -4px 0 ($baseline*1.5);
|
||||
margin: -4px 0 $baseline;
|
||||
position: relative;
|
||||
border-bottom: none;
|
||||
z-index: 0;
|
||||
@@ -119,6 +119,10 @@ $sequence--border-color: #C8C8C8;
|
||||
-webkit-font-smoothing: antialiased; // Clear up the lines on the icons
|
||||
}
|
||||
|
||||
i.fa-bookmark {
|
||||
color: $link-color;
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
|
||||
.icon {
|
||||
@@ -142,6 +146,10 @@ $sequence--border-color: #C8C8C8;
|
||||
.icon {
|
||||
color: rgb(10, 10, 10);
|
||||
}
|
||||
|
||||
i.fa-bookmark {
|
||||
color: $link-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,3 +303,4 @@ nav.sequence-bottom {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ class @Sequence
|
||||
|
||||
bind: ->
|
||||
@$('#sequence-list a').click @goto
|
||||
@el.on 'bookmark:add', @addBookmarkIconToActiveNavItem
|
||||
@el.on 'bookmark:remove', @removeBookmarkIconFromActiveNavItem
|
||||
|
||||
initProgress: ->
|
||||
@progressTable = {} # "#problem_#{id}" -> progress
|
||||
@@ -102,8 +104,9 @@ class @Sequence
|
||||
@mark_active new_position
|
||||
|
||||
current_tab = @contents.eq(new_position - 1)
|
||||
@content_container.html(current_tab.text()).attr("aria-labelledby", current_tab.attr("aria-labelledby"))
|
||||
|
||||
bookmarked = if @el.find('.active .bookmark-icon').hasClass('bookmarked') then true else false
|
||||
@content_container.html(current_tab.text()).attr("aria-labelledby", current_tab.attr("aria-labelledby")).data('bookmarked', bookmarked)
|
||||
XBlock.initializeBlocks(@content_container, @requestToken)
|
||||
|
||||
window.update_schematics() # For embedded circuit simulator exercises in 6.002x
|
||||
@@ -116,6 +119,8 @@ class @Sequence
|
||||
sequence_links = @content_container.find('a.seqnav')
|
||||
sequence_links.click @goto
|
||||
|
||||
@el.find('.path').html(@el.find('.nav-item.active').data('path'))
|
||||
|
||||
@sr_container.focus();
|
||||
# @$("a.active").blur()
|
||||
|
||||
@@ -180,3 +185,13 @@ class @Sequence
|
||||
element.removeClass("inactive")
|
||||
.removeClass("visited")
|
||||
.addClass("active")
|
||||
|
||||
addBookmarkIconToActiveNavItem: (event) =>
|
||||
event.preventDefault()
|
||||
@el.find('.nav-item.active .bookmark-icon').removeClass('is-hidden').addClass('bookmarked')
|
||||
@el.find('.nav-item.active .bookmark-icon-sr').text(gettext('Bookmarked'))
|
||||
|
||||
removeBookmarkIconFromActiveNavItem: (event) =>
|
||||
event.preventDefault()
|
||||
@el.find('.nav-item.active .bookmark-icon').removeClass('bookmarked').addClass('is-hidden')
|
||||
@el.find('.nav-item.active .bookmark-icon-sr').text('')
|
||||
|
||||
@@ -316,6 +316,7 @@ class LibraryContentModule(LibraryContentFields, XModule, StudioEditableModule):
|
||||
fragment.add_content(self.system.render_template('vert_module.html', {
|
||||
'items': contents,
|
||||
'xblock_context': context,
|
||||
'show_bookmark_button': False,
|
||||
}))
|
||||
return fragment
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/* JavaScript for Vertical Student View. */
|
||||
window.VerticalStudentView = function (runtime, element) {
|
||||
|
||||
RequireJS.require(['js/bookmarks/views/bookmark_button'], function (BookmarkButton) {
|
||||
var $element = $(element);
|
||||
var $bookmarkButtonElement = $element.find('.bookmark-button');
|
||||
|
||||
return new BookmarkButton({
|
||||
el: $bookmarkButtonElement,
|
||||
bookmarkId: $bookmarkButtonElement.data('bookmarkId'),
|
||||
usageId: $element.data('usageId'),
|
||||
bookmarked: $element.parent('#seq_content').data('bookmarked'),
|
||||
apiUrl: $(".courseware-bookmarks-button").data('bookmarksApiUrl')
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -55,7 +55,6 @@ class SequenceFields(object):
|
||||
scope=Scope.settings,
|
||||
)
|
||||
|
||||
|
||||
class ProctoringFields(object):
|
||||
"""
|
||||
Fields that are specific to Proctored or Timed Exams
|
||||
@@ -119,9 +118,12 @@ class ProctoringFields(object):
|
||||
|
||||
@XBlock.wants('proctoring')
|
||||
@XBlock.wants('credit')
|
||||
class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
''' Layout module which lays out content in a temporal sequence
|
||||
'''
|
||||
@XBlock.needs("user")
|
||||
@XBlock.needs("bookmarks")
|
||||
class SequenceModule(SequenceFields, XModule):
|
||||
"""
|
||||
Layout module which lays out content in a temporal sequence
|
||||
"""
|
||||
js = {
|
||||
'coffee': [resource_string(__name__, 'js/src/sequence/display.coffee')],
|
||||
'js': [resource_string(__name__, 'js/src/sequence/display/jquery.sequence.js')],
|
||||
@@ -182,7 +184,12 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
contents = []
|
||||
|
||||
fragment = Fragment()
|
||||
context = context or {}
|
||||
|
||||
bookmarks_service = self.runtime.service(self, "bookmarks")
|
||||
context["username"] = self.runtime.service(self, "user").get_current_user().opt_attrs['edx-platform.username']
|
||||
|
||||
display_names = [self.get_parent().display_name or '', self.display_name or '']
|
||||
# Is this sequential part of a timed or proctored exam?
|
||||
if self.is_time_limited:
|
||||
view_html = self._time_limited_student_view(context)
|
||||
@@ -194,6 +201,9 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
return fragment
|
||||
|
||||
for child in self.get_display_items():
|
||||
is_bookmarked = bookmarks_service.is_bookmarked(usage_key=child.scope_ids.usage_id)
|
||||
context["bookmarked"] = is_bookmarked
|
||||
|
||||
progress = child.get_progress()
|
||||
rendered_child = child.render(STUDENT_VIEW, context)
|
||||
fragment.add_frag_resources(rendered_child)
|
||||
@@ -209,6 +219,8 @@ class SequenceModule(SequenceFields, ProctoringFields, XModule):
|
||||
'progress_detail': Progress.to_js_detail_str(progress),
|
||||
'type': child.get_icon_class(),
|
||||
'id': child.scope_ids.usage_id.to_deprecated_string(),
|
||||
'bookmarked': is_bookmarked,
|
||||
'path': " > ".join(display_names + [child.display_name or '']),
|
||||
}
|
||||
if childinfo['title'] == '':
|
||||
childinfo['title'] = child.display_name_with_default
|
||||
|
||||
@@ -37,18 +37,31 @@ class BaseVerticalBlockTest(XModuleXmlImportTest):
|
||||
self.vertical = course_seq.get_children()[0]
|
||||
self.vertical.xmodule_runtime = self.module_system
|
||||
|
||||
self.username = "bilbo"
|
||||
self.default_context = {"bookmarked": False, "username": self.username}
|
||||
|
||||
|
||||
class VerticalBlockTestCase(BaseVerticalBlockTest):
|
||||
"""
|
||||
Tests for the VerticalBlock.
|
||||
"""
|
||||
def assert_bookmark_info_in(self, content):
|
||||
"""
|
||||
Assert content has all the bookmark info.
|
||||
"""
|
||||
self.assertIn('bookmark_id', content)
|
||||
self.assertIn('{},{}'.format(self.username, unicode(self.vertical.location)), content)
|
||||
self.assertIn('bookmarked', content)
|
||||
self.assertIn('show_bookmark_button', content)
|
||||
|
||||
def test_render_student_view(self):
|
||||
"""
|
||||
Test the rendering of the student view.
|
||||
"""
|
||||
html = self.module_system.render(self.vertical, STUDENT_VIEW, {}).content
|
||||
html = self.module_system.render(self.vertical, STUDENT_VIEW, self.default_context).content
|
||||
self.assertIn(self.test_html_1, html)
|
||||
self.assertIn(self.test_html_2, html)
|
||||
self.assert_bookmark_info_in(html)
|
||||
|
||||
def test_render_studio_view(self):
|
||||
"""
|
||||
|
||||
@@ -54,7 +54,14 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
|
||||
fragment.add_content(self.system.render_template('vert_module.html', {
|
||||
'items': contents,
|
||||
'xblock_context': context,
|
||||
'show_bookmark_button': True,
|
||||
'bookmarked': child_context['bookmarked'],
|
||||
'bookmark_id': "{},{}".format(child_context['username'], unicode(self.location))
|
||||
}))
|
||||
|
||||
fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/vertical_student_view.js'))
|
||||
fragment.initialize_js('VerticalStudentView')
|
||||
|
||||
return fragment
|
||||
|
||||
def author_view(self, context):
|
||||
|
||||
@@ -7,7 +7,7 @@ from .course_page import CoursePage
|
||||
|
||||
class BookmarksPage(CoursePage):
|
||||
"""
|
||||
Coursware Bookmarks Page.
|
||||
Courseware Bookmarks Page.
|
||||
"""
|
||||
url = None
|
||||
url_path = "courseware/"
|
||||
@@ -23,10 +23,11 @@ class BookmarksPage(CoursePage):
|
||||
""" Check if bookmarks button is visible """
|
||||
return self.q(css=self.BOOKMARKS_BUTTON_SELECTOR).visible
|
||||
|
||||
def click_bookmarks_button(self):
|
||||
def click_bookmarks_button(self, wait_for_results=True):
|
||||
""" Click on Bookmarks button """
|
||||
self.q(css=self.BOOKMARKS_BUTTON_SELECTOR).first.click()
|
||||
EmptyPromise(self.results_present, "Bookmarks results present").fulfill()
|
||||
if wait_for_results:
|
||||
EmptyPromise(self.results_present, "Bookmarks results present").fulfill()
|
||||
|
||||
def results_present(self):
|
||||
""" Check if bookmarks results are present """
|
||||
@@ -53,9 +54,9 @@ class BookmarksPage(CoursePage):
|
||||
breadcrumbs = self.q(css=self.BOOKMARKED_BREADCRUMBS).text
|
||||
return [breadcrumb.replace('\n', '').split('-') for breadcrumb in breadcrumbs]
|
||||
|
||||
def click_bookmark(self, index):
|
||||
def click_bookmarked_block(self, index):
|
||||
"""
|
||||
Click on bookmark at index `index`
|
||||
Click on bookmarked block at index `index`
|
||||
|
||||
Arguments:
|
||||
index (int): bookmark index in the list
|
||||
|
||||
@@ -193,13 +193,13 @@ class CourseNavPage(PageObject):
|
||||
)
|
||||
|
||||
# Regular expression to remove HTML span tags from a string
|
||||
REMOVE_SPAN_TAG_RE = re.compile(r'<span.+/span>')
|
||||
REMOVE_SPAN_TAG_RE = re.compile(r'</span>(.+)<span')
|
||||
|
||||
def _clean_seq_titles(self, element):
|
||||
"""
|
||||
Clean HTML of sequence titles, stripping out span tags and returning the first line.
|
||||
"""
|
||||
return self.REMOVE_SPAN_TAG_RE.sub('', element.get_attribute('innerHTML')).strip().split('\n')[0]
|
||||
return self.REMOVE_SPAN_TAG_RE.search(element.get_attribute('innerHTML')).groups()[0].strip()
|
||||
|
||||
def go_to_sequential_position(self, sequential_position):
|
||||
"""
|
||||
|
||||
@@ -3,6 +3,7 @@ Courseware page.
|
||||
"""
|
||||
|
||||
from .course_page import CoursePage
|
||||
from bok_choy.promise import EmptyPromise
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
|
||||
|
||||
@@ -177,6 +178,32 @@ class CoursewarePage(CoursePage):
|
||||
attribute_value = lambda el: el.get_attribute('data-id')
|
||||
return self.q(css='#sequence-list a').filter(get_active).map(attribute_value).results[0]
|
||||
|
||||
@property
|
||||
def breadcrumb(self):
|
||||
""" Return the course tree breadcrumb shown above the sequential bar """
|
||||
return [part.strip() for part in self.q(css='.path').text[0].split('>')]
|
||||
|
||||
def bookmark_button_visible(self):
|
||||
""" Check if bookmark button is visible """
|
||||
EmptyPromise(lambda: self.q(css='.bookmark-button').visible, "Bookmark button visible").fulfill()
|
||||
return True
|
||||
|
||||
@property
|
||||
def bookmark_button_state(self):
|
||||
""" Return `bookmarked` if button is in bookmarked state else '' """
|
||||
return 'bookmarked' if self.q(css='.bookmark-button.bookmarked').present else ''
|
||||
|
||||
@property
|
||||
def bookmark_icon_visible(self):
|
||||
""" Check if bookmark icon is visible on active sequence nav item """
|
||||
return self.q(css='.active .bookmark-icon').visible
|
||||
|
||||
def click_bookmark_unit_button(self):
|
||||
""" Bookmark a unit by clicking on Bookmark button """
|
||||
previous_state = self.bookmark_button_state
|
||||
self.q(css='.bookmark-button').first.click()
|
||||
EmptyPromise(lambda: self.bookmark_button_state != previous_state, "Bookmark button toggled").fulfill()
|
||||
|
||||
|
||||
class CoursewareSequentialTabPage(CoursePage):
|
||||
"""
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
"""
|
||||
End-to-end tests for the courseware unit bookmarks.
|
||||
"""
|
||||
import json
|
||||
import requests
|
||||
|
||||
from ...pages.studio.auto_auth import AutoAuthPage
|
||||
from ...pages.lms.bookmarks import BookmarksPage
|
||||
from ...pages.lms.courseware import CoursewarePage
|
||||
from ...pages.lms.course_nav import CourseNavPage
|
||||
from ...pages.studio.overview import CourseOutlinePage
|
||||
from ...pages.common.logout import LogoutPage
|
||||
|
||||
from ...fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
from ...fixtures import LMS_BASE_URL
|
||||
from ..helpers import EventsTestMixin, UniqueCourseTest, is_404_page
|
||||
|
||||
|
||||
@@ -22,30 +20,29 @@ class BookmarksTestMixin(EventsTestMixin, UniqueCourseTest):
|
||||
"""
|
||||
USERNAME = "STUDENT"
|
||||
EMAIL = "student@example.com"
|
||||
COURSE_TREE_INFO = [
|
||||
['TestSection1', 'TestSubsection1', 'TestProblem1'],
|
||||
['TestSection2', 'TestSubsection2', 'TestProblem2']
|
||||
]
|
||||
|
||||
def create_course_fixture(self):
|
||||
""" Create course fixture """
|
||||
def create_course_fixture(self, num_chapters):
|
||||
"""
|
||||
Create course fixture
|
||||
|
||||
Arguments:
|
||||
num_chapters: number of chapters to create
|
||||
"""
|
||||
self.course_fixture = CourseFixture( # pylint: disable=attribute-defined-outside-init
|
||||
self.course_info['org'], self.course_info['number'],
|
||||
self.course_info['run'], self.course_info['display_name']
|
||||
)
|
||||
|
||||
self.course_fixture.add_children(
|
||||
XBlockFixtureDesc('chapter', self.COURSE_TREE_INFO[0][0]).add_children(
|
||||
XBlockFixtureDesc('sequential', self.COURSE_TREE_INFO[0][1]).add_children(
|
||||
XBlockFixtureDesc('problem', self.COURSE_TREE_INFO[0][2])
|
||||
xblocks = []
|
||||
for index in range(num_chapters):
|
||||
xblocks += [
|
||||
XBlockFixtureDesc('chapter', 'TestSection{}'.format(index)).add_children(
|
||||
XBlockFixtureDesc('sequential', 'TestSubsection{}'.format(index)).add_children(
|
||||
XBlockFixtureDesc('vertical', 'TestVertical{}'.format(index))
|
||||
)
|
||||
)
|
||||
),
|
||||
XBlockFixtureDesc('chapter', self.COURSE_TREE_INFO[1][0]).add_children(
|
||||
XBlockFixtureDesc('sequential', self.COURSE_TREE_INFO[1][1]).add_children(
|
||||
XBlockFixtureDesc('problem', self.COURSE_TREE_INFO[1][2])
|
||||
)
|
||||
)
|
||||
).install()
|
||||
]
|
||||
self.course_fixture.add_children(*xblocks).install()
|
||||
|
||||
|
||||
class BookmarksTest(BookmarksTestMixin):
|
||||
@@ -66,35 +63,64 @@ class BookmarksTest(BookmarksTestMixin):
|
||||
self.course_info['run']
|
||||
)
|
||||
|
||||
self.create_course_fixture()
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
self.bookmarks_page = BookmarksPage(self.browser, self.course_id)
|
||||
self.course_nav = CourseNavPage(self.browser)
|
||||
|
||||
def _test_setup(self, num_chapters=2):
|
||||
"""
|
||||
Setup test settings.
|
||||
|
||||
Arguments:
|
||||
num_chapters: number of chapters to create in course
|
||||
"""
|
||||
self.create_course_fixture(num_chapters)
|
||||
|
||||
# Auto-auth register for the course.
|
||||
AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL, course_id=self.course_id).visit()
|
||||
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
self.courseware_page.visit()
|
||||
self.bookmarks = BookmarksPage(self.browser, self.course_id)
|
||||
|
||||
# Use auto-auth to retrieve the session for a logged in user
|
||||
self.session = requests.Session()
|
||||
response = self.session.get(LMS_BASE_URL + "/auto_auth?username=STUDENT&email=student@example.com")
|
||||
self.assertTrue(response.ok, "Failed to get session info")
|
||||
def _bookmark_unit(self, index):
|
||||
"""
|
||||
Bookmark a unit
|
||||
|
||||
def _bookmark_unit(self, course_id, usage_id):
|
||||
""" Bookmark a single unit """
|
||||
csrftoken = self.session.cookies['csrftoken']
|
||||
headers = {'Content-type': 'application/json', "X-CSRFToken": csrftoken}
|
||||
url = LMS_BASE_URL + "/api/bookmarks/v0/bookmarks/?course_id=" + course_id + '&fields=path'
|
||||
data = json.dumps({'usage_id': usage_id})
|
||||
Arguments:
|
||||
index: unit index to bookmark
|
||||
"""
|
||||
self.course_nav.go_to_section('TestSection{}'.format(index), 'TestSubsection{}'.format(index))
|
||||
self.courseware_page.click_bookmark_unit_button()
|
||||
|
||||
response = self.session.post(url, data=data, headers=headers, cookies=self.session.cookies)
|
||||
response = json.loads(response.text)
|
||||
self.assertTrue(response['usage_id'] == usage_id, "Failed to bookmark unit")
|
||||
def _bookmark_units(self, num_units):
|
||||
"""
|
||||
Bookmark first `num_units` units by visiting them
|
||||
|
||||
def _bookmarks_blocks(self, xblocks):
|
||||
""" Bookmark all units in a course """
|
||||
for xblock in xblocks:
|
||||
self._bookmark_unit(self.course_id, usage_id=xblock.locator)
|
||||
Arguments:
|
||||
num_units(int): Number of units to bookmarks
|
||||
"""
|
||||
for index in range(num_units):
|
||||
self._bookmark_unit(index)
|
||||
|
||||
def _breadcrumb(self, num_units):
|
||||
"""
|
||||
Creates breadcrumbs for the first `num_units`
|
||||
|
||||
Arguments:
|
||||
num_units(int): Number of units for which we want to create breadcrumbs
|
||||
|
||||
Returns:
|
||||
list of breadcrumbs
|
||||
"""
|
||||
breadcrumbs = []
|
||||
for index in range(num_units):
|
||||
breadcrumbs.append(
|
||||
[
|
||||
'TestSection{}'.format(index),
|
||||
'TestSubsection{}'.format(index),
|
||||
'TestVertical{}'.format(index)
|
||||
]
|
||||
)
|
||||
return breadcrumbs
|
||||
|
||||
def _delete_section(self, index):
|
||||
""" Delete a section at index `index` """
|
||||
@@ -119,6 +145,39 @@ class BookmarksTest(BookmarksTestMixin):
|
||||
self.courseware_page.visit()
|
||||
self.courseware_page.wait_for_page()
|
||||
|
||||
def _toggle_bookmark_and_verify(self, bookmark_icon_state, bookmark_button_state, bookmarked_count):
|
||||
"""
|
||||
Bookmark/Un-Bookmark a unit and then verify
|
||||
"""
|
||||
self.assertTrue(self.courseware_page.bookmark_button_visible)
|
||||
self.courseware_page.click_bookmark_unit_button()
|
||||
self.assertEqual(self.courseware_page.bookmark_icon_visible, bookmark_icon_state)
|
||||
self.assertEqual(self.courseware_page.bookmark_button_state, bookmark_button_state)
|
||||
self.bookmarks_page.click_bookmarks_button()
|
||||
self.assertEqual(self.bookmarks_page.count(), bookmarked_count)
|
||||
|
||||
def test_bookmark_button(self):
|
||||
"""
|
||||
Scenario: Bookmark unit button toggles correctly
|
||||
|
||||
Given that I am a registered user
|
||||
And I visit my courseware page
|
||||
For first 2 units
|
||||
I visit the unit
|
||||
And I can see the Bookmark button
|
||||
When I click on Bookmark button
|
||||
Then unit should be bookmarked
|
||||
Then I click again on the bookmark button
|
||||
And I should see a unit un-bookmarked
|
||||
"""
|
||||
self._test_setup()
|
||||
for index in range(2):
|
||||
self.course_nav.go_to_section('TestSection{}'.format(index), 'TestSubsection{}'.format(index))
|
||||
|
||||
self._toggle_bookmark_and_verify(True, 'bookmarked', 1)
|
||||
self.bookmarks_page.click_bookmarks_button(False)
|
||||
self._toggle_bookmark_and_verify(False, '', 0)
|
||||
|
||||
def test_empty_bookmarks_list(self):
|
||||
"""
|
||||
Scenario: An empty bookmarks list is shown if there are no bookmarked units.
|
||||
@@ -130,15 +189,16 @@ class BookmarksTest(BookmarksTestMixin):
|
||||
Then I should see an empty bookmarks list
|
||||
And empty bookmarks list content is correct
|
||||
"""
|
||||
self.assertTrue(self.bookmarks.bookmarks_button_visible())
|
||||
self.bookmarks.click_bookmarks_button()
|
||||
self.assertEqual(self.bookmarks.results_header_text(), 'MY BOOKMARKS')
|
||||
self.assertEqual(self.bookmarks.empty_header_text(), 'You have not bookmarked any courseware pages yet.')
|
||||
self._test_setup()
|
||||
self.assertTrue(self.bookmarks_page.bookmarks_button_visible())
|
||||
self.bookmarks_page.click_bookmarks_button()
|
||||
self.assertEqual(self.bookmarks_page.results_header_text(), 'MY BOOKMARKS')
|
||||
self.assertEqual(self.bookmarks_page.empty_header_text(), 'You have not bookmarked any courseware pages yet.')
|
||||
|
||||
empty_list_text = ("Use bookmarks to help you easily return to courseware pages. To bookmark a page, "
|
||||
"select Bookmark in the upper right corner of that page. To see a list of all your "
|
||||
"bookmarks, select Bookmarks in the upper left corner of any courseware page.")
|
||||
self.assertEqual(self.bookmarks.empty_list_text(), empty_list_text)
|
||||
self.assertEqual(self.bookmarks_page.empty_list_text(), empty_list_text)
|
||||
|
||||
def test_bookmarks_list(self):
|
||||
"""
|
||||
@@ -160,27 +220,30 @@ class BookmarksTest(BookmarksTestMixin):
|
||||
# discarded by the current version of MySQL we are using due to the
|
||||
# lack of support. Due to which order of bookmarked units will be
|
||||
# incorrect.
|
||||
xblocks = self.course_fixture.get_nested_xblocks(category="problem")
|
||||
self._bookmarks_blocks(xblocks)
|
||||
self._test_setup()
|
||||
self._bookmark_units(2)
|
||||
|
||||
self.bookmarks.click_bookmarks_button()
|
||||
self.assertTrue(self.bookmarks.results_present())
|
||||
self.assertEqual(self.bookmarks.results_header_text(), 'MY BOOKMARKS')
|
||||
self.assertEqual(self.bookmarks.count(), 2)
|
||||
self.bookmarks_page.click_bookmarks_button()
|
||||
self.assertTrue(self.bookmarks_page.results_present())
|
||||
self.assertEqual(self.bookmarks_page.results_header_text(), 'MY BOOKMARKS')
|
||||
self.assertEqual(self.bookmarks_page.count(), 2)
|
||||
|
||||
bookmarked_breadcrumbs = self.bookmarks.breadcrumbs()
|
||||
bookmarked_breadcrumbs = self.bookmarks_page.breadcrumbs()
|
||||
|
||||
# Verify bookmarked breadcrumbs
|
||||
self.assertItemsEqual(bookmarked_breadcrumbs, self.COURSE_TREE_INFO)
|
||||
breadcrumbs = self._breadcrumb(2)
|
||||
self.assertItemsEqual(bookmarked_breadcrumbs, breadcrumbs)
|
||||
|
||||
# get usage ids for units
|
||||
xblocks = self.course_fixture.get_nested_xblocks(category="vertical")
|
||||
xblock_usage_ids = [xblock.locator for xblock in xblocks]
|
||||
# Verify link navigation
|
||||
for index in range(2):
|
||||
self.bookmarks.click_bookmark(index)
|
||||
self.bookmarks_page.click_bookmarked_block(index)
|
||||
self.courseware_page.wait_for_page()
|
||||
self.assertTrue(self.courseware_page.active_usage_id() in xblock_usage_ids)
|
||||
self.courseware_page.visit().wait_for_page()
|
||||
self.bookmarks.click_bookmarks_button()
|
||||
self.bookmarks_page.click_bookmarks_button()
|
||||
|
||||
def test_unreachable_bookmark(self):
|
||||
"""
|
||||
@@ -195,13 +258,34 @@ class BookmarksTest(BookmarksTestMixin):
|
||||
When I click on deleted bookmark
|
||||
Then I should navigated to 404 page
|
||||
"""
|
||||
self._bookmarks_blocks(self.course_fixture.get_nested_xblocks(category="problem"))
|
||||
|
||||
self._test_setup()
|
||||
self._bookmark_units(2)
|
||||
self._delete_section(0)
|
||||
|
||||
self.bookmarks.click_bookmarks_button()
|
||||
self.assertTrue(self.bookmarks.results_present())
|
||||
self.assertEqual(self.bookmarks.count(), 2)
|
||||
self.bookmarks_page.click_bookmarks_button()
|
||||
self.assertTrue(self.bookmarks_page.results_present())
|
||||
self.assertEqual(self.bookmarks_page.count(), 2)
|
||||
|
||||
self.bookmarks.click_bookmark(0)
|
||||
self.bookmarks_page.click_bookmarked_block(1)
|
||||
self.assertTrue(is_404_page(self.browser))
|
||||
|
||||
def test_page_size_limit(self):
|
||||
"""
|
||||
Scenario: We can get more bookmarks if page size is greater than default page size.
|
||||
Note:
|
||||
* Current Bookmarks API page_size value is 10.
|
||||
* page_size value in bookmarks client side is set to 500.
|
||||
|
||||
Given that I am a registered user
|
||||
And I visit my courseware page
|
||||
And I have bookmarked all the units available
|
||||
Then I click on Bookmarks button
|
||||
And I should see a bookmarked list
|
||||
And bookmark list contains 11 bookmarked items
|
||||
"""
|
||||
self._test_setup(11)
|
||||
self._bookmark_units(11)
|
||||
|
||||
self.bookmarks_page.click_bookmarks_button()
|
||||
self.assertTrue(self.bookmarks_page.results_present())
|
||||
self.assertEqual(self.bookmarks_page.count(), 11)
|
||||
|
||||
@@ -29,6 +29,7 @@ class CoursewareTest(UniqueCourseTest):
|
||||
super(CoursewareTest, self).setUp()
|
||||
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
self.course_nav = CourseNavPage(self.browser)
|
||||
|
||||
self.course_outline = CourseOutlinePage(
|
||||
self.browser,
|
||||
@@ -38,12 +39,12 @@ class CoursewareTest(UniqueCourseTest):
|
||||
)
|
||||
|
||||
# Install a course with sections/problems, tabs, updates, and handouts
|
||||
course_fix = CourseFixture(
|
||||
self.course_fix = CourseFixture(
|
||||
self.course_info['org'], self.course_info['number'],
|
||||
self.course_info['run'], self.course_info['display_name']
|
||||
)
|
||||
|
||||
course_fix.add_children(
|
||||
self.course_fix.add_children(
|
||||
XBlockFixtureDesc('chapter', 'Test Section 1').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection 1').add_children(
|
||||
XBlockFixtureDesc('problem', 'Test Problem 1')
|
||||
@@ -67,6 +68,18 @@ 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 _create_breadcrumb(self, index):
|
||||
""" Create breadcrumb """
|
||||
return ['Test Section {}'.format(index), 'Test Subsection {}'.format(index), 'Test Problem {}'.format(index)]
|
||||
|
||||
def _auto_auth(self, username, email, staff):
|
||||
"""
|
||||
Logout and login with given credentials.
|
||||
@@ -92,6 +105,9 @@ class CoursewareTest(UniqueCourseTest):
|
||||
# Set release date for subsection in future.
|
||||
self.course_outline.change_problem_release_date_in_studio()
|
||||
|
||||
# Wait for 2 seconds to save new date.
|
||||
time.sleep(2)
|
||||
|
||||
# Logout and login as a student.
|
||||
LogoutPage(self.browser).visit()
|
||||
self._auto_auth(self.USERNAME, self.EMAIL, False)
|
||||
@@ -246,6 +262,23 @@ class ProctoredExamTest(UniqueCourseTest):
|
||||
self.courseware_page.start_timed_exam()
|
||||
self.assertTrue(self.courseware_page.is_timer_bar_present)
|
||||
|
||||
def test_course_tree_breadcrumb(self):
|
||||
"""
|
||||
Scenario: Correct course tree breadcrumb is shown.
|
||||
|
||||
Given that I am a registered user
|
||||
And I visit my courseware page
|
||||
Then I should see correct course tree breadcrumb
|
||||
"""
|
||||
self.courseware_page.visit()
|
||||
|
||||
xblocks = self.course_fix.get_nested_xblocks(category="problem")
|
||||
for index in range(1, len(xblocks) + 1):
|
||||
self.course_nav.go_to_section('Test Section {}'.format(index), 'Test Subsection {}'.format(index))
|
||||
courseware_page_breadcrumb = self.courseware_page.breadcrumb
|
||||
expected_breadcrumb = self._create_breadcrumb(index)
|
||||
self.assertEqual(courseware_page_breadcrumb, expected_breadcrumb)
|
||||
|
||||
def test_time_allotted_field_is_not_visible_with_none_exam(self):
|
||||
"""
|
||||
Given that I am a staff member
|
||||
|
||||
Reference in New Issue
Block a user