Commit 652ad7ae by Robert Raposa Committed by Diana Huang

Add course outline bokchoy tests.

- Rename CourseOutlinePage to StudioCourseOutlinePage in lms tests.
- Introduce CourseHomePage with outline child.
- Add a11y, breadcrumbs, and waffle.
parent 574d0703
......@@ -227,9 +227,9 @@ class CourseFixture(XBlockContainerFixture):
self._configure_course()
@property
def course_outline(self):
def studio_course_outline_as_json(self):
"""
Retrieves course outline in JSON format.
Retrieves Studio course outline in JSON format.
"""
url = STUDIO_BASE_URL + '/course/' + self._course_key + "?format=json"
response = self.session.get(url, headers=self.headers)
......
"""
LMS Course Home page object
"""
from bok_choy.page_object import PageObject
from common.test.acceptance.pages.lms.course_page import CoursePage
from common.test.acceptance.pages.lms.courseware import CoursewarePage
class CourseHomePage(CoursePage):
"""
Course home page, including course outline.
"""
url_path = "course/"
def is_browser_on_page(self):
return self.q(css='.course-outline').present
def __init__(self, browser, course_id):
super(CourseHomePage, self).__init__(browser, course_id)
self.course_id = course_id
self.outline = CourseOutlinePage(browser, self)
# TODO: TNL-6546: Remove the following
self.unified_course_view = False
class CourseOutlinePage(PageObject):
"""
Course outline fragment of page.
"""
url = None
def __init__(self, browser, parent_page):
super(CourseOutlinePage, self).__init__(browser)
self.parent_page = parent_page
self.courseware_page = CoursewarePage(self.browser, self.parent_page.course_id)
def is_browser_on_page(self):
return self.parent_page.is_browser_on_page
@property
def sections(self):
"""
Return a dictionary representation of sections and subsections.
Example:
{
'Introduction': ['Course Overview'],
'Week 1': ['Lesson 1', 'Lesson 2', 'Homework']
'Final Exam': ['Final Exam']
}
You can use these titles in `go_to_section` to navigate to the section.
"""
# Dict to store the result
outline_dict = dict()
section_titles = self._section_titles()
# Get the section titles for each chapter
for sec_index, sec_title in enumerate(section_titles):
if len(section_titles) < 1:
self.warning("Could not find subsections for '{0}'".format(sec_title))
else:
# Add one to convert list index (starts at 0) to CSS index (starts at 1)
outline_dict[sec_title] = self._subsection_titles(sec_index + 1)
return outline_dict
def go_to_section(self, section_title, subsection_title):
"""
Go to the section in the courseware.
Every section must have at least one subsection, so specify
both the section and subsection title.
Example:
go_to_section("Week 1", "Lesson 1")
"""
# Get the section by index
try:
section_index = self._section_titles().index(section_title)
except ValueError:
self.warning("Could not find section '{0}'".format(section_title))
return
# Get the subsection by index
try:
subsection_index = self._subsection_titles(section_index + 1).index(subsection_title)
except ValueError:
msg = "Could not find subsection '{0}' in section '{1}'".format(subsection_title, section_title)
self.warning(msg)
return
# Convert list indices (start at zero) to CSS indices (start at 1)
subsection_css = (
".outline-item.section:nth-of-type({0}) .subsection:nth-of-type({1}) .outline-item"
).format(section_index + 1, subsection_index + 1)
# Click the subsection and ensure that the page finishes reloading
self.q(css=subsection_css).first.click()
self.courseware_page.wait_for_page()
# TODO: TNL-6546: Remove this if/visit_unified_course_view
if self.parent_page.unified_course_view:
self.courseware_page.nav.visit_unified_course_view()
self._wait_for_course_section(section_title, subsection_title)
def _section_titles(self):
"""
Return a list of all section titles on the page.
"""
section_css = '.section-name span'
return self.q(css=section_css).map(lambda el: el.text.strip()).results
def _subsection_titles(self, section_index):
"""
Return a list of all subsection titles on the page
for the section at index `section_index` (starts at 1).
"""
# Retrieve the subsection title for the section
# Add one to the list index to get the CSS index, which starts at one
subsection_css = (
# TODO: TNL-6387: Will need to switch to this selector for subsections
# ".outline-item.section:nth-of-type({0}) .subsection span:nth-of-type(1)"
".outline-item.section:nth-of-type({0}) .subsection a"
).format(section_index)
return self.q(
css=subsection_css
).map(
lambda el: el.get_attribute('innerHTML').strip()
).results
def _wait_for_course_section(self, section_title, subsection_title):
"""
Ensures the user navigates to the course content page with the correct section and subsection.
"""
self.wait_for(
promise_check_func=lambda: self.courseware_page.nav.is_on_section(section_title, subsection_title),
description="Waiting for course page with section '{0}' and subsection '{1}'".format(section_title, subsection_title)
)
"""
Course navigation page object
"""
import re
from bok_choy.page_object import PageObject, unguarded
from bok_choy.promise import EmptyPromise
class CourseNavPage(PageObject):
"""
Navigate sections and sequences in the courseware.
"""
url = None
def is_browser_on_page(self):
return self.q(css='div.course-index').present
@property
def sections(self):
"""
Return a dictionary representation of sections and subsections.
Example:
{
'Introduction': ['Course Overview'],
'Week 1': ['Lesson 1', 'Lesson 2', 'Homework']
'Final Exam': ['Final Exam']
}
You can use these titles in `go_to_section` to navigate to the section.
"""
# Dict to store the result
nav_dict = dict()
section_titles = self._section_titles()
# Get the section titles for each chapter
for sec_index, sec_title in enumerate(section_titles):
if len(section_titles) < 1:
self.warning("Could not find subsections for '{0}'".format(sec_title))
else:
# Add one to convert list index (starts at 0) to CSS index (starts at 1)
nav_dict[sec_title] = self._subsection_titles(sec_index + 1)
return nav_dict
@property
def sequence_items(self):
"""
Return a list of sequence items on the page.
Sequence items are one level below subsections in the course nav.
Example return value:
['Chemical Bonds Video', 'Practice Problems', 'Homework']
"""
seq_css = 'ol#sequence-list>li>.nav-item>.sequence-tooltip'
return self.q(css=seq_css).map(self._clean_seq_titles).results
def go_to_section(self, section_title, subsection_title):
"""
Go to the section in the courseware.
Every section must have at least one subsection, so specify
both the section and subsection title.
Example:
go_to_section("Week 1", "Lesson 1")
"""
# For test stability, disable JQuery animations (opening / closing menus)
self.browser.execute_script("jQuery.fx.off = true;")
# Get the section by index
try:
sec_index = self._section_titles().index(section_title)
except ValueError:
self.warning("Could not find section '{0}'".format(section_title))
return
# Click the section to ensure it's open (no harm in clicking twice if it's already open)
# Add one to convert from list index to CSS index
section_css = '.course-navigation .chapter:nth-of-type({0})'.format(sec_index + 1)
self.q(css=section_css).first.click()
# Get the subsection by index
try:
subsec_index = self._subsection_titles(sec_index + 1).index(subsection_title)
except ValueError:
msg = "Could not find subsection '{0}' in section '{1}'".format(subsection_title, section_title)
self.warning(msg)
return
# Convert list indices (start at zero) to CSS indices (start at 1)
subsection_css = (
".course-navigation .chapter-content-container:nth-of-type({0}) "
".menu-item:nth-of-type({1})"
).format(sec_index + 1, subsec_index + 1)
# Click the subsection and ensure that the page finishes reloading
self.q(css=subsection_css).first.click()
self._on_section_promise(section_title, subsection_title).fulfill()
def go_to_vertical(self, vertical_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(vertical_title)
except ValueError:
msg = "Could not find sequential '{0}'. Available sequentials: [{1}]".format(
vertical_title, ", ".join(all_items)
)
self.warning(msg)
else:
# Click on the sequence item at the correct index
# Convert the list index (starts at 0) to a CSS index (starts at 1)
seq_css = "ol#sequence-list>li:nth-of-type({0})>.nav-item".format(seq_index + 1)
self.q(css=seq_css).first.click()
# Click triggers an ajax event
self.wait_for_ajax()
def _section_titles(self):
"""
Return a list of all section titles on the page.
"""
chapter_css = '.course-navigation .chapter .group-heading'
return self.q(css=chapter_css).map(lambda el: el.text.strip()).results
def _subsection_titles(self, section_index):
"""
Return a list of all subsection titles on the page
for the section at index `section_index` (starts at 1).
"""
# Retrieve the subsection title for the section
# Add one to the list index to get the CSS index, which starts at one
subsection_css = (
".course-navigation .chapter-content-container:nth-of-type({0}) "
".menu-item a p:nth-of-type(1)"
).format(section_index)
# If the element is visible, we can get its text directly
# Otherwise, we need to get the HTML
# It *would* make sense to always get the HTML, but unfortunately
# the open tab has some child <span> tags that we don't want.
return self.q(
css=subsection_css
).map(
lambda el: el.text.strip().split('\n')[0] if el.is_displayed() else el.get_attribute('innerHTML').strip()
).results
def _on_section_promise(self, section_title, subsection_title):
"""
Return a `Promise` that is fulfilled when the user is on
the correct section and subsection.
"""
desc = "currently at section '{0}' and subsection '{1}'".format(section_title, subsection_title)
return EmptyPromise(
lambda: self.is_on_section(section_title, subsection_title), desc
)
@unguarded
def is_on_section(self, section_title, subsection_title):
"""
Return a boolean indicating whether the user is on the section and subsection
with the specified titles.
This assumes that the currently expanded section is the one we're on
That's true right after we click the section/subsection, but not true in general
(the user could go to a section, then expand another tab).
"""
current_section_list = self.q(css='.course-navigation .chapter.is-open .group-heading').text
current_subsection_list = self.q(css='.course-navigation .chapter-content-container .menu-item.active a p').text
if len(current_section_list) == 0:
self.warning("Could not find the current section")
return False
elif len(current_subsection_list) == 0:
self.warning("Could not find current subsection")
return False
else:
return (
current_section_list[0].strip() == section_title and
current_subsection_list[0].strip().split('\n')[0] == subsection_title
)
# Regular expression to remove HTML span tags from a string
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.search(element.get_attribute('innerHTML')).groups()[0].strip()
@property
def active_subsection_url(self):
"""
return the url of the active subsection in the left nav
"""
return self.q(css='.chapter-content-container .menu-item.active a').attrs('href')[0]
......@@ -8,9 +8,9 @@ import requests
from common.test.acceptance.pages.studio.auto_auth import AutoAuthPage as StudioAutoAuthPage
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage as LmsAutoAuthPage
from common.test.acceptance.pages.lms.bookmarks import BookmarksPage
from common.test.acceptance.pages.lms.course_home import CourseHomePage
from common.test.acceptance.pages.lms.courseware import CoursewarePage
from common.test.acceptance.pages.lms.course_nav import CourseNavPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
from common.test.acceptance.pages.common.logout import LogoutPage
from common.test.acceptance.pages.common import BASE_URL
......@@ -72,7 +72,7 @@ class BookmarksTest(BookmarksTestMixin):
"""
super(BookmarksTest, self).setUp()
self.course_outline_page = CourseOutlinePage(
self.studio_course_outline_page = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
......@@ -80,8 +80,8 @@ class BookmarksTest(BookmarksTestMixin):
)
self.courseware_page = CoursewarePage(self.browser, self.course_id)
self.course_home_page = CourseHomePage(self.browser, self.course_id)
self.bookmarks_page = BookmarksPage(self.browser, self.course_id)
self.course_nav = CourseNavPage(self.browser)
# Get session to be used for bookmarking units
self.session = requests.Session()
......@@ -166,10 +166,10 @@ class BookmarksTest(BookmarksTestMixin):
).visit()
# Visit course outline page in studio.
self.course_outline_page.visit()
self.course_outline_page.wait_for_page()
self.studio_course_outline_page.visit()
self.studio_course_outline_page.wait_for_page()
self.course_outline_page.section_at(index).delete()
self.studio_course_outline_page.section_at(index).delete()
# Logout and login as a student.
LogoutPage(self.browser).visit()
......@@ -232,11 +232,11 @@ class BookmarksTest(BookmarksTestMixin):
"""
Update and publish the block/unit display name.
"""
self.course_outline_page.visit()
self.course_outline_page.wait_for_page()
self.studio_course_outline_page.visit()
self.studio_course_outline_page.wait_for_page()
self.course_outline_page.expand_all_subsections()
section = self.course_outline_page.section_at(0)
self.studio_course_outline_page.expand_all_subsections()
section = self.studio_course_outline_page.section_at(0)
container_page = section.subsection_at(0).unit_at(0).go_to()
self.course_fixture._update_xblock(container_page.locator, { # pylint: disable=protected-access
......@@ -267,7 +267,8 @@ class BookmarksTest(BookmarksTestMixin):
"""
self._test_setup()
for index in range(2):
self.course_nav.go_to_section('TestSection{}'.format(index), 'TestSubsection{}'.format(index))
self.course_home_page.visit()
self.course_home_page.outline.go_to_section('TestSection{}'.format(index), 'TestSubsection{}'.format(index))
self._toggle_bookmark_and_verify(True, 'bookmarked', 1)
self.bookmarks_page.click_bookmarks_button(False)
......
......@@ -7,10 +7,11 @@ from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureD
from common.test.acceptance.fixtures.certificates import CertificateConfigFixture
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from common.test.acceptance.pages.lms.certificate_page import CertificatePage
from common.test.acceptance.pages.lms.course_home import CourseHomePage
from common.test.acceptance.pages.lms.course_info import CourseInfoPage
from common.test.acceptance.pages.lms.tab_nav import TabNavPage
from common.test.acceptance.pages.lms.course_nav import CourseNavPage
from common.test.acceptance.pages.lms.courseware import CoursewarePage
from common.test.acceptance.pages.lms.progress import ProgressPage
from common.test.acceptance.pages.lms.tab_nav import TabNavPage
@attr(shard=5)
......@@ -154,7 +155,8 @@ class CertificateProgressPageTest(UniqueCourseTest):
self.course_info_page = CourseInfoPage(self.browser, self.course_id)
self.progress_page = ProgressPage(self.browser, self.course_id)
self.course_nav = CourseNavPage(self.browser)
self.courseware_page = CoursewarePage(self.browser, self.course_id)
self.course_home_page = CourseHomePage(self.browser, self.course_id)
self.tab_nav = TabNavPage(self.browser)
def log_in_as_unique_user(self):
......@@ -205,38 +207,42 @@ class CertificateProgressPageTest(UniqueCourseTest):
Problems were added in the setUp
"""
self.course_info_page.visit()
self.tab_nav.go_to_tab('Course')
# self.course_info_page.visit()
# self.tab_nav.go_to_tab('Course')
#
# # TODO: TNL-6546: Remove extra visit call.
self.course_home_page.visit()
# Navigate to Test Subsection in Test Section Section
self.course_nav.go_to_section('Test Section', 'Test Subsection')
self.course_home_page.outline.go_to_section('Test Section', 'Test Subsection')
# Navigate to Test Problem 1
self.course_nav.go_to_vertical('Test Problem 1')
self.courseware_page.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()
self.courseware_page.q(css='select option[value="{}"]'.format('blue')).first.click()
# Select correct radio button for the answer
self.course_nav.q(css='fieldset div.field:nth-child(4) input').nth(0).click()
self.courseware_page.q(css='fieldset div.field:nth-child(4) input').nth(0).click()
# Select correct radio buttons for the answer
self.course_nav.q(css='fieldset div.field:nth-child(2) input').nth(1).click()
self.course_nav.q(css='fieldset div.field:nth-child(4) input').nth(1).click()
self.courseware_page.q(css='fieldset div.field:nth-child(2) input').nth(1).click()
self.courseware_page.q(css='fieldset div.field:nth-child(4) input').nth(1).click()
# Submit the answer
self.course_nav.q(css='button.submit').click()
self.course_nav.wait_for_ajax()
self.courseware_page.q(css='button.submit').click()
self.courseware_page.wait_for_ajax()
# Navigate to the 'Test Subsection 2' of 'Test Section 2'
self.course_nav.go_to_section('Test Section 2', 'Test Subsection 2')
self.course_home_page.visit()
self.course_home_page.outline.go_to_section('Test Section 2', 'Test Subsection 2')
# Navigate to Test Problem 2
self.course_nav.go_to_vertical('Test Problem 2')
self.courseware_page.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)')
self.courseware_page.q(css='input[id^=input_][id$=_2_1]').fill('A*x^2 + sqrt(y)')
# Submit the answer
self.course_nav.q(css='button.submit').click()
self.course_nav.wait_for_ajax()
self.courseware_page.q(css='button.submit').click()
self.courseware_page.wait_for_ajax()
......@@ -8,7 +8,7 @@ import textwrap
from nose.plugins.attrib import attr
from common.test.acceptance.tests.helpers import UniqueCourseTest, TestWithSearchIndexMixin
from common.test.acceptance.pages.studio.auto_auth import AutoAuthPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
from common.test.acceptance.pages.studio.library import StudioLibraryContentEditor, StudioLibraryContainerXBlockWrapper
from common.test.acceptance.pages.lms.courseware import CoursewarePage
from common.test.acceptance.pages.lms.library import LibraryContentXBlockWrapper
......@@ -44,7 +44,7 @@ class LibraryContentTestBase(UniqueCourseTest):
self.courseware_page = CoursewarePage(self.browser, self.course_id)
self.course_outline = CourseOutlinePage(
self.studio_course_outline = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
......@@ -116,9 +116,9 @@ class LibraryContentTestBase(UniqueCourseTest):
if change_login:
LogoutPage(self.browser).visit()
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
self.course_outline.visit()
self.studio_course_outline.visit()
subsection = self.course_outline.section(SECTION_NAME).subsection(SUBSECTION_NAME)
subsection = self.studio_course_outline.section(SECTION_NAME).subsection(SUBSECTION_NAME)
return subsection.expand_subsection().unit(UNIT_NAME).go_to()
def _goto_library_block_page(self, block_id=None):
......
......@@ -7,7 +7,7 @@ import uuid
from common.test.acceptance.tests.helpers import remove_file
from common.test.acceptance.pages.common.logout import LogoutPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
from common.test.acceptance.pages.lms.courseware_search import CoursewareSearchPage
from common.test.acceptance.pages.lms.staff_view import StaffPage
from common.test.acceptance.fixtures.course import XBlockFixtureDesc
......@@ -45,7 +45,7 @@ class CoursewareSearchCohortTest(ContainerBase):
super(CoursewareSearchCohortTest, self).setUp(is_staff=is_staff)
self.staff_user = self.user
self.course_outline = CourseOutlinePage(
self.studio_course_outline = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
......@@ -101,9 +101,9 @@ class CoursewareSearchCohortTest(ContainerBase):
Reindex course content on studio course page
"""
self._auto_auth(self.staff_user["username"], self.staff_user["email"], True)
self.course_outline.visit()
self.course_outline.start_reindex()
self.course_outline.wait_for_ajax()
self.studio_course_outline.visit()
self.studio_course_outline.start_reindex()
self.studio_course_outline.wait_for_ajax()
def _goto_staff_page(self):
"""
......
......@@ -11,7 +11,7 @@ from common.test.acceptance.pages.common.logout import LogoutPage
from common.test.acceptance.pages.common.utils import click_css
from common.test.acceptance.pages.studio.utils import add_html_component, type_in_codemirror
from common.test.acceptance.pages.studio.auto_auth import AutoAuthPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
from common.test.acceptance.pages.studio.container import ContainerPage
from common.test.acceptance.pages.lms.courseware_search import CoursewareSearchPage
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
......@@ -54,7 +54,7 @@ class CoursewareSearchTest(UniqueCourseTest):
super(CoursewareSearchTest, self).setUp()
self.courseware_search_page = CoursewareSearchPage(self.browser, self.course_id)
self.course_outline = CourseOutlinePage(
self.studio_course_outline = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
......@@ -91,8 +91,8 @@ class CoursewareSearchTest(UniqueCourseTest):
Publish content on studio course page under specified section
"""
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
self.course_outline.visit()
subsection = self.course_outline.section_at(section_index).subsection_at(0)
self.studio_course_outline.visit()
subsection = self.studio_course_outline.section_at(section_index).subsection_at(0)
subsection.expand_subsection()
unit = subsection.unit_at(0)
unit.publish()
......@@ -102,8 +102,8 @@ class CoursewareSearchTest(UniqueCourseTest):
Edit chapter name on studio course page under specified section
"""
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
self.course_outline.visit()
section = self.course_outline.section_at(section_index)
self.studio_course_outline.visit()
section = self.studio_course_outline.section_at(section_index)
section.change_name(self.EDITED_CHAPTER_NAME)
def _studio_add_content(self, section_index):
......@@ -113,8 +113,8 @@ class CoursewareSearchTest(UniqueCourseTest):
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
# create a unit in course outline
self.course_outline.visit()
subsection = self.course_outline.section_at(section_index).subsection_at(0)
self.studio_course_outline.visit()
subsection = self.studio_course_outline.section_at(section_index).subsection_at(0)
subsection.expand_subsection()
subsection.add_unit()
......@@ -134,9 +134,9 @@ class CoursewareSearchTest(UniqueCourseTest):
"""
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
self.course_outline.visit()
self.course_outline.start_reindex()
self.course_outline.wait_for_ajax()
self.studio_course_outline.visit()
self.studio_course_outline.start_reindex()
self.studio_course_outline.wait_for_ajax()
def _search_for_content(self, search_term):
"""
......
......@@ -9,7 +9,7 @@ from common.test.acceptance.pages.common.logout import LogoutPage
from common.test.acceptance.pages.common.utils import click_css
from common.test.acceptance.pages.studio.utils import add_html_component, type_in_codemirror
from common.test.acceptance.pages.studio.auto_auth import AutoAuthPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
from common.test.acceptance.pages.studio.container import ContainerPage
from common.test.acceptance.pages.lms.dashboard_search import DashboardSearchPage
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
......@@ -60,10 +60,10 @@ class DashboardSearchTest(AcceptanceTest):
}
# generate course fixtures and outline pages
self.course_outlines = {}
self.studio_course_outlines = {}
self.course_fixtures = {}
for key, course_info in self.courses.iteritems():
course_outline = CourseOutlinePage(
studio_course_outline = StudioCourseOutlinePage(
self.browser,
course_info['org'],
course_info['number'],
......@@ -89,7 +89,7 @@ class DashboardSearchTest(AcceptanceTest):
)
).install()
self.course_outlines[key] = course_outline
self.studio_course_outlines[key] = studio_course_outline
self.course_fixtures[key] = course_fix
def tearDown(self):
......@@ -106,13 +106,13 @@ class DashboardSearchTest(AcceptanceTest):
LogoutPage(self.browser).visit()
AutoAuthPage(self.browser, username=username, email=email, staff=staff).visit()
def _studio_add_content(self, course_outline, html_content):
def _studio_add_content(self, studio_course_outline, html_content):
"""
Add content to first section on studio course page.
"""
# create a unit in course outline
course_outline.visit()
subsection = course_outline.section_at(0).subsection_at(0)
studio_course_outline.visit()
subsection = studio_course_outline.section_at(0).subsection_at(0)
subsection.expand_subsection()
subsection.add_unit()
......@@ -126,12 +126,12 @@ class DashboardSearchTest(AcceptanceTest):
type_in_codemirror(unit_page, 0, html_content)
click_css(unit_page, '.action-save', 0)
def _studio_publish_content(self, course_outline):
def _studio_publish_content(self, studio_course_outline):
"""
Publish content in first section on studio course page.
"""
course_outline.visit()
subsection = course_outline.section_at(0).subsection_at(0)
studio_course_outline.visit()
subsection = studio_course_outline.section_at(0).subsection_at(0)
subsection.expand_subsection()
unit = subsection.unit_at(0)
unit.publish()
......@@ -167,9 +167,9 @@ class DashboardSearchTest(AcceptanceTest):
# Create content in studio without publishing.
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
self._studio_add_content(self.course_outlines['A'], html_content)
self._studio_add_content(self.course_outlines['B'], html_content)
self._studio_add_content(self.course_outlines['C'], html_content)
self._studio_add_content(self.studio_course_outlines['A'], html_content)
self._studio_add_content(self.studio_course_outlines['B'], html_content)
self._studio_add_content(self.studio_course_outlines['C'], html_content)
# Do a search, there should be no results shown.
self._auto_auth(self.USERNAME, self.EMAIL, False)
......@@ -179,9 +179,9 @@ class DashboardSearchTest(AcceptanceTest):
# Publish in studio to trigger indexing.
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
self._studio_publish_content(self.course_outlines['A'])
self._studio_publish_content(self.course_outlines['B'])
self._studio_publish_content(self.course_outlines['C'])
self._studio_publish_content(self.studio_course_outlines['A'])
self._studio_publish_content(self.studio_course_outlines['B'])
self._studio_publish_content(self.studio_course_outlines['C'])
# Do the search again, this time we expect results from courses A & B, but not C
self._auto_auth(self.USERNAME, self.EMAIL, False)
......
......@@ -9,7 +9,7 @@ from nose.plugins.attrib import attr
from common.test.acceptance.tests.helpers import UniqueCourseTest, EventsTestMixin
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from common.test.acceptance.pages.lms.course_nav import CourseNavPage
from common.test.acceptance.pages.lms.course_home import CourseHomePage
from common.test.acceptance.pages.lms.courseware import CoursewarePage
from common.test.acceptance.pages.lms.edxnotes import EdxNotesUnitPage, EdxNotesPage, EdxNotesPageNoContent
from common.test.acceptance.fixtures.edxnotes import EdxNotesFixture, Note, Range
......@@ -26,7 +26,7 @@ class EdxNotesTestMixin(UniqueCourseTest):
"""
super(EdxNotesTestMixin, self).setUp()
self.courseware_page = CoursewarePage(self.browser, self.course_id)
self.course_nav = CourseNavPage(self.browser)
self.course_home_page = CourseHomePage(self.browser, self.course_id)
self.note_unit_page = EdxNotesUnitPage(self.browser, self.course_id)
self.notes_page = EdxNotesPage(self.browser, self.course_id)
......@@ -1504,7 +1504,8 @@ class EdxNotesToggleNotesTest(EdxNotesTestMixin):
self.assertEqual(len(self.note_unit_page.notes), 0)
self.courseware_page.go_to_sequential_position(2)
self.assertEqual(len(self.note_unit_page.notes), 0)
self.course_nav.go_to_section(u"Test Section 1", u"Test Subsection 2")
self.course_home_page.visit()
self.course_home_page.outline.go_to_section(u"Test Section 1", u"Test Subsection 2")
self.assertEqual(len(self.note_unit_page.notes), 0)
def test_can_reenable_all_notes(self):
......@@ -1530,5 +1531,6 @@ class EdxNotesToggleNotesTest(EdxNotesTestMixin):
self.assertGreater(len(self.note_unit_page.notes), 0)
self.courseware_page.go_to_sequential_position(2)
self.assertGreater(len(self.note_unit_page.notes), 0)
self.course_nav.go_to_section(u"Test Section 1", u"Test Subsection 2")
self.course_home_page.visit()
self.course_home_page.outline.go_to_section(u"Test Section 1", u"Test Subsection 2")
self.assertGreater(len(self.note_unit_page.notes), 0)
......@@ -43,7 +43,7 @@ class EntranceExamTest(UniqueCourseTest):
).install()
entrance_exam_subsection = None
outline = course_fixture.course_outline
outline = course_fixture.studio_course_outline_as_json
for child in outline['child_info']['children']:
if child.get('display_name') == "Entrance Exam":
entrance_exam_subsection = child['child_info']['children'][0]
......
......@@ -6,7 +6,7 @@ from textwrap import dedent
from common.test.acceptance.tests.helpers import UniqueCourseTest
from common.test.acceptance.pages.studio.auto_auth import AutoAuthPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
from common.test.acceptance.pages.lms.courseware import CoursewarePage
from common.test.acceptance.pages.lms.problem import ProblemPage
from common.test.acceptance.pages.lms.staff_view import StaffPage
......@@ -29,7 +29,7 @@ class GatingTest(UniqueCourseTest):
self.logout_page = LogoutPage(self.browser)
self.courseware_page = CoursewarePage(self.browser, self.course_id)
self.course_outline = CourseOutlinePage(
self.studio_course_outline = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
......@@ -89,10 +89,10 @@ class GatingTest(UniqueCourseTest):
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
# Make the first subsection a prerequisite
self.course_outline.visit()
self.course_outline.open_subsection_settings_dialog(0)
self.course_outline.select_advanced_tab(desired_item='gated_content')
self.course_outline.make_gating_prerequisite()
self.studio_course_outline.visit()
self.studio_course_outline.open_subsection_settings_dialog(0)
self.studio_course_outline.select_advanced_tab(desired_item='gated_content')
self.studio_course_outline.make_gating_prerequisite()
def _setup_gated_subsection(self):
"""
......@@ -102,10 +102,10 @@ class GatingTest(UniqueCourseTest):
self._auto_auth(self.STAFF_USERNAME, self.STAFF_EMAIL, True)
# Gate the second subsection based on the score achieved in the first subsection
self.course_outline.visit()
self.course_outline.open_subsection_settings_dialog(1)
self.course_outline.select_advanced_tab(desired_item='gated_content')
self.course_outline.add_prerequisite_to_subsection("80")
self.studio_course_outline.visit()
self.studio_course_outline.open_subsection_settings_dialog(1)
self.studio_course_outline.select_advanced_tab(desired_item='gated_content')
self.studio_course_outline.add_prerequisite_to_subsection("80")
def _fulfill_prerequisite(self):
"""
......@@ -127,23 +127,23 @@ class GatingTest(UniqueCourseTest):
self._setup_prereq()
# Assert settings are displayed correctly for a prerequisite subsection
self.course_outline.visit()
self.course_outline.open_subsection_settings_dialog(0)
self.course_outline.select_advanced_tab(desired_item='gated_content')
self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_visible())
self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_checked())
self.assertFalse(self.course_outline.gating_prerequisites_dropdown_is_visible())
self.assertFalse(self.course_outline.gating_prerequisite_min_score_is_visible())
self.studio_course_outline.visit()
self.studio_course_outline.open_subsection_settings_dialog(0)
self.studio_course_outline.select_advanced_tab(desired_item='gated_content')
self.assertTrue(self.studio_course_outline.gating_prerequisite_checkbox_is_visible())
self.assertTrue(self.studio_course_outline.gating_prerequisite_checkbox_is_checked())
self.assertFalse(self.studio_course_outline.gating_prerequisites_dropdown_is_visible())
self.assertFalse(self.studio_course_outline.gating_prerequisite_min_score_is_visible())
self._setup_gated_subsection()
# Assert settings are displayed correctly for a gated subsection
self.course_outline.visit()
self.course_outline.open_subsection_settings_dialog(1)
self.course_outline.select_advanced_tab(desired_item='gated_content')
self.assertTrue(self.course_outline.gating_prerequisite_checkbox_is_visible())
self.assertTrue(self.course_outline.gating_prerequisites_dropdown_is_visible())
self.assertTrue(self.course_outline.gating_prerequisite_min_score_is_visible())
self.studio_course_outline.visit()
self.studio_course_outline.open_subsection_settings_dialog(1)
self.studio_course_outline.select_advanced_tab(desired_item='gated_content')
self.assertTrue(self.studio_course_outline.gating_prerequisite_checkbox_is_visible())
self.assertTrue(self.studio_course_outline.gating_prerequisites_dropdown_is_visible())
self.assertTrue(self.studio_course_outline.gating_prerequisite_min_score_is_visible())
def test_gated_subsection_in_lms_for_student(self):
"""
......
......@@ -12,7 +12,7 @@ from flaky import flaky
from common.test.acceptance.tests.helpers import UniqueCourseTest, get_modal_alert, EventsTestMixin
from common.test.acceptance.pages.common.logout import LogoutPage
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
from common.test.acceptance.pages.lms.create_mode import ModeCreationPage
from common.test.acceptance.pages.lms.courseware import CoursewarePage
from common.test.acceptance.pages.lms.instructor_dashboard import InstructorDashboardPage, EntranceExamAdmin
......@@ -227,7 +227,7 @@ class ProctoredExamsTest(BaseInstructorDashboardTest):
self.courseware_page = CoursewarePage(self.browser, self.course_id)
self.course_outline = CourseOutlinePage(
self.studio_course_outline = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
......@@ -301,15 +301,15 @@ class ProctoredExamsTest(BaseInstructorDashboardTest):
# Visit the course outline page in studio
LogoutPage(self.browser).visit()
self._auto_auth("STAFF_TESTER", "staff101@example.com", True)
self.course_outline.visit()
self.studio_course_outline.visit()
# open the exam settings to make it a proctored exam.
self.course_outline.open_subsection_settings_dialog()
self.studio_course_outline.open_subsection_settings_dialog()
# select advanced settings tab
self.course_outline.select_advanced_tab()
self.studio_course_outline.select_advanced_tab()
self.course_outline.make_exam_proctored()
self.studio_course_outline.make_exam_proctored()
# login as a verified student and visit the courseware.
LogoutPage(self.browser).visit()
......@@ -327,15 +327,15 @@ class ProctoredExamsTest(BaseInstructorDashboardTest):
# Visit the course outline page in studio
LogoutPage(self.browser).visit()
self._auto_auth("STAFF_TESTER", "staff101@example.com", True)
self.course_outline.visit()
self.studio_course_outline.visit()
# open the exam settings to make it a proctored exam.
self.course_outline.open_subsection_settings_dialog()
self.studio_course_outline.open_subsection_settings_dialog()
# select advanced settings tab
self.course_outline.select_advanced_tab()
self.studio_course_outline.select_advanced_tab()
self.course_outline.make_exam_timed()
self.studio_course_outline.make_exam_timed()
# login as a verified student and visit the courseware.
LogoutPage(self.browser).visit()
......
......@@ -6,9 +6,8 @@ import json
from common.test.acceptance.tests.helpers import remove_file
from common.test.acceptance.pages.common.logout import LogoutPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
from common.test.acceptance.pages.lms.courseware_search import CoursewareSearchPage
from common.test.acceptance.pages.lms.course_nav import CourseNavPage
from common.test.acceptance.fixtures.course import XBlockFixtureDesc
from common.test.acceptance.tests.helpers import create_user_partition_json
......@@ -44,8 +43,7 @@ class SplitTestCoursewareSearchTest(ContainerBase):
self.staff_user = self.user
self.courseware_search_page = CoursewareSearchPage(self.browser, self.course_id)
self.course_navigation_page = CourseNavPage(self.browser)
self.course_outline = CourseOutlinePage(
self.studio_course_outline = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
......@@ -68,9 +66,9 @@ class SplitTestCoursewareSearchTest(ContainerBase):
Reindex course content on studio course page
"""
self._auto_auth(self.staff_user["username"], self.staff_user["email"], True)
self.course_outline.visit()
self.course_outline.start_reindex()
self.course_outline.wait_for_ajax()
self.studio_course_outline.visit()
self.studio_course_outline.start_reindex()
self.studio_course_outline.wait_for_ajax()
def _create_group_configuration(self):
"""
......
......@@ -20,7 +20,7 @@ from ...pages.lms.problem import ProblemPage
from ...pages.lms.progress import ProgressPage
from ...pages.studio.component_editor import ComponentEditorView
from ...pages.studio.utils import type_in_codemirror
from ...pages.studio.overview import CourseOutlinePage
from ...pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
class ProgressPageBaseTest(UniqueCourseTest):
......@@ -43,7 +43,7 @@ class ProgressPageBaseTest(UniqueCourseTest):
self.progress_page = ProgressPage(self.browser, self.course_id)
self.logout_page = LogoutPage(self.browser)
self.course_outline = CourseOutlinePage(
self.studio_course_outline = StudioCourseOutlinePage(
self.browser,
self.course_info['org'],
self.course_info['number'],
......@@ -140,11 +140,11 @@ class PersistentGradesTest(ProgressPageBaseTest):
Adds a unit to the subsection, which
should not affect a persisted subsection grade.
"""
self.course_outline.visit()
subsection = self.course_outline.section(self.SECTION_NAME).subsection(self.SUBSECTION_NAME)
self.studio_course_outline.visit()
subsection = self.studio_course_outline.section(self.SECTION_NAME).subsection(self.SUBSECTION_NAME)
subsection.expand_subsection()
subsection.add_unit()
self.course_outline.wait_for_ajax()
self.studio_course_outline.wait_for_ajax()
subsection.publish()
def _set_staff_lock_on_subsection(self, locked):
......@@ -152,8 +152,8 @@ class PersistentGradesTest(ProgressPageBaseTest):
Sets staff lock for a subsection, which should hide the
subsection score from students on the progress page.
"""
self.course_outline.visit()
subsection = self.course_outline.section_at(0).subsection_at(0)
self.studio_course_outline.visit()
subsection = self.studio_course_outline.section_at(0).subsection_at(0)
subsection.set_staff_lock(locked)
self.assertEqual(subsection.has_staff_lock_warning, locked)
......@@ -163,9 +163,9 @@ class PersistentGradesTest(ProgressPageBaseTest):
along with its container unit, so any changes can
be published.
"""
self.course_outline.visit()
self.course_outline.section_at(0).subsection_at(0).expand_subsection()
unit = self.course_outline.section_at(0).subsection_at(0).unit(self.UNIT_NAME).go_to()
self.studio_course_outline.visit()
self.studio_course_outline.section_at(0).subsection_at(0).expand_subsection()
unit = self.studio_course_outline.section_at(0).subsection_at(0).unit(self.UNIT_NAME).go_to()
component = unit.xblocks[1]
return unit, component
......@@ -289,8 +289,8 @@ class SubsectionGradingPolicyTest(ProgressPageBaseTest):
If a section index is not provided, 0 is assumed.
"""
with self._logged_in_session(staff=True):
self.course_outline.visit()
modal = self.course_outline.section_at(section).subsection_at(0).edit()
self.studio_course_outline.visit()
modal = self.studio_course_outline.section_at(section).subsection_at(0).edit()
modal.policy = policy
modal.save()
......
......@@ -12,8 +12,8 @@ from nose.plugins.attrib import attr
from common.test.acceptance.pages.studio.settings_advanced import AdvancedSettingsPage
from common.test.acceptance.pages.studio.overview import CourseOutlinePage, ContainerPage, ExpandCollapseLinkState
from common.test.acceptance.pages.studio.utils import add_discussion, drag, verify_ordering
from common.test.acceptance.pages.lms.course_home import CourseHomePage
from common.test.acceptance.pages.lms.courseware import CoursewarePage
from common.test.acceptance.pages.lms.course_nav import CourseNavPage
from common.test.acceptance.pages.lms.staff_view import StaffPage
from common.test.acceptance.fixtures.config import ConfigModelFixture
from common.test.acceptance.fixtures.course import XBlockFixtureDesc
......@@ -1490,7 +1490,7 @@ class PublishSectionTest(CourseOutlineTest):
The first subsection has 2 units, and the second subsection has one unit.
"""
self.courseware = CoursewarePage(self.browser, self.course_id)
self.course_nav = CourseNavPage(self.browser)
self.course_home_page = CourseHomePage(self.browser, self.course_id)
course_fixture.add_children(
XBlockFixtureDesc('chapter', SECTION_NAME).add_children(
XBlockFixtureDesc('sequential', SUBSECTION_NAME).add_children(
......@@ -1578,7 +1578,8 @@ class PublishSectionTest(CourseOutlineTest):
self.assertEqual(1, self.courseware.num_xblock_components)
self.courseware.go_to_sequential_position(2)
self.assertEqual(1, self.courseware.num_xblock_components)
self.course_nav.go_to_section(SECTION_NAME, 'Test Subsection 2')
self.course_home_page.visit()
self.course_home_page.outline.go_to_section(SECTION_NAME, 'Test Subsection 2')
self.assertEqual(1, self.courseware.num_xblock_components)
def _add_unpublished_content(self):
......
......@@ -15,7 +15,6 @@ from common.test.acceptance.tests.helpers import UniqueCourseTest, is_youtube_av
from common.test.acceptance.pages.lms.video.video import VideoPage
from common.test.acceptance.pages.lms.tab_nav import TabNavPage
from common.test.acceptance.pages.lms.courseware import CoursewarePage
from common.test.acceptance.pages.lms.course_nav import CourseNavPage
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
from common.test.acceptance.pages.lms.course_info import CourseInfoPage
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc
......@@ -53,7 +52,6 @@ class VideoBaseTest(UniqueCourseTest):
self.video = VideoPage(self.browser)
self.tab_nav = TabNavPage(self.browser)
self.course_nav = CourseNavPage(self.browser)
self.courseware = CoursewarePage(self.browser, self.course_id)
self.course_info_page = CourseInfoPage(self.browser, self.course_id)
self.auth_page = AutoAuthPage(self.browser, course_id=self.course_id)
......@@ -531,7 +529,7 @@ class YouTubeVideoTest(VideoBaseTest):
self.assertTrue(self.video.downloaded_transcript_contains_text(file_type, search_text))
# open vertical containing video "C"
self.course_nav.go_to_vertical('Test Vertical-2')
self.courseware.nav.go_to_vertical('Test Vertical-2')
# menu "download_transcript" doesn't exist
self.assertFalse(self.video.is_menu_present('download_transcript'))
......@@ -678,17 +676,17 @@ class YouTubeVideoTest(VideoBaseTest):
self.navigate_to_video()
# select the "2.0" speed on video "A"
self.course_nav.go_to_vertical('Test Vertical-0')
self.courseware.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_vertical('Test Vertical-1')
self.courseware.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_vertical('Test Vertical-2')
self.courseware.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
......@@ -697,7 +695,7 @@ class YouTubeVideoTest(VideoBaseTest):
self.video.verify_speed_changed('0.75x')
# go to the vertical containing video "A"
self.course_nav.go_to_vertical('Test Vertical-0')
self.courseware.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')
......@@ -706,7 +704,7 @@ class YouTubeVideoTest(VideoBaseTest):
self.video.reload_page()
# go to the vertical containing video "A"
self.course_nav.go_to_vertical('Test Vertical-0')
self.courseware.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')
......@@ -715,13 +713,13 @@ class YouTubeVideoTest(VideoBaseTest):
self.video.speed = '1.0'
# go to the vertical containing "B"
self.course_nav.go_to_vertical('Test Vertical-1')
self.courseware.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')
# go to the vertical containing video "C"
self.course_nav.go_to_vertical('Test Vertical-2')
self.courseware.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.
......
......@@ -25,7 +25,6 @@ from django.http import (
QueryDict,
)
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.utils.decorators import method_decorator
from django.utils.timezone import UTC
from django.utils.translation import ugettext as _
......
......@@ -223,6 +223,18 @@ ECOMMERCE_API_URL = 'http://localhost:8043/api/v2/'
LMS_ROOT_URL = "http://localhost:8000"
DOC_LINK_BASE_URL = 'http://edx.readthedocs.io/projects/edx-guide-for-students'
# TODO: TNL-6546: Remove this waffle and flag code.
from django.db.utils import ProgrammingError
from waffle.models import Flag
try:
flag, created = Flag.objects.get_or_create(name='unified_course_view')
flag.everyone = True
flag.save
WAFFLE_OVERRIDE = True
except ProgrammingError:
# during initial reset_db, the table for the flag doesn't yet exist.
pass
#####################################################################
# Lastly, see if the developer has any local overrides.
try:
......
......@@ -36,14 +36,8 @@ ${HTML(outline_fragment.foot_html())}
</%block>
<%block name="content">
<section class="course-view container" id="course-container">
<div class="course-view container" id="course-container">
<header class="page-header has-secondary">
## Breadcrumb navigation
<div class="page-header-main">
<nav aria-label="${_('Discussions')}" class="sr-is-focusable" tabindex="-1">
<div class="has-breadcrumbs"></div>
</nav>
</div>
<div class="page-header-secondary">
<div class="form-actions">
<a class="btn" href="${reverse('courseware', kwargs={'course_id': unicode(course.id.to_deprecated_string())})}">
......@@ -52,15 +46,15 @@ ${HTML(outline_fragment.foot_html())}
</div>
<div class="page-header-search">
<form class="search-form" role="search">
<label class="field-label sr-only" for="search" id="search-hint">Search the course</label>
<label class="field-label sr-only" for="search" id="search-hint">${_('Search the course')}</label>
<input
class="field-input input-text search-input"
type="search"
name="search"
id="search"
placeholder="Search the course"
placeholder="${_('Search the course')}'"
/>
<button class="btn btn-small search-btn" type="button">Search</button>
<button class="btn btn-small search-btn" type="button">${_('Search')}</button>
</form>
</div>
</div>
......@@ -68,5 +62,5 @@ ${HTML(outline_fragment.foot_html())}
<div class="page-content">
${HTML(outline_fragment.body_html())}
</div>
</section>
</div>
</%block>
......@@ -10,7 +10,7 @@ from django.utils.translation import ugettext as _
CourseOutlineFactory('.block-tree');
</%static:require_module_async>
<section class="course-outline" id="main">
<div class="course-outline" id="main" tabindex="-1">
<ol class="block-tree" role="tree">
% for section in blocks.get('children') or []:
<li
......@@ -39,4 +39,4 @@ from django.utils.translation import ugettext as _
</li>
% endfor
</ol>
</section>
</div>
......@@ -46,7 +46,7 @@ class CourseOutlineFragmentView(FragmentView):
user=request.user,
nav_depth=3,
requested_fields=['children', 'display_name', 'type'],
block_types_filter=['course', 'chapter', 'vertical', 'sequential']
block_types_filter=['course', 'chapter', 'sequential']
)
course_block_tree = all_blocks['blocks'][all_blocks['root']] # Get the root of the block tree
......
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