Add course outline bokchoy tests.
- Rename CourseOutlinePage to StudioCourseOutlinePage in lms tests. - Introduce CourseHomePage with outline child. - Add a11y, breadcrumbs, and waffle.
This commit is contained in:
committed by
Diana Huang
parent
574d070392
commit
652ad7ae73
@@ -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)
|
||||
|
||||
148
common/test/acceptance/pages/lms/course_home.py
Normal file
148
common/test/acceptance/pages/lms/course_home.py
Normal file
@@ -0,0 +1,148 @@
|
||||
"""
|
||||
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)
|
||||
)
|
||||
@@ -1,212 +0,0 @@
|
||||
"""
|
||||
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]
|
||||
@@ -2,10 +2,13 @@
|
||||
Courseware page.
|
||||
"""
|
||||
|
||||
from common.test.acceptance.pages.lms.course_page import CoursePage
|
||||
from bok_choy.page_object import PageObject, unguarded
|
||||
from bok_choy.promise import EmptyPromise
|
||||
import re
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
|
||||
from common.test.acceptance.pages.lms.course_page import CoursePage
|
||||
|
||||
|
||||
class CoursewarePage(CoursePage):
|
||||
"""
|
||||
@@ -17,8 +20,12 @@ class CoursewarePage(CoursePage):
|
||||
section_selector = '.chapter'
|
||||
subsection_selector = '.chapter-content-container a'
|
||||
|
||||
def __init__(self, browser, course_id):
|
||||
super(CoursewarePage, self).__init__(browser, course_id)
|
||||
self.nav = CourseNavPage(browser, self)
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='body.courseware').present
|
||||
return self.q(css='.course-content').present
|
||||
|
||||
@property
|
||||
def chapter_count_in_navigation(self):
|
||||
@@ -27,6 +34,7 @@ class CoursewarePage(CoursePage):
|
||||
"""
|
||||
return len(self.q(css='nav.course-navigation a.chapter'))
|
||||
|
||||
# TODO: TNL-6546: Remove and find callers.
|
||||
@property
|
||||
def num_sections(self):
|
||||
"""
|
||||
@@ -34,6 +42,7 @@ class CoursewarePage(CoursePage):
|
||||
"""
|
||||
return len(self.q(css=self.section_selector))
|
||||
|
||||
# TODO: TNL-6546: Remove and find callers.
|
||||
@property
|
||||
def num_subsections(self):
|
||||
"""
|
||||
@@ -319,3 +328,255 @@ class CoursewareSequentialTabPage(CoursePage):
|
||||
return the body of the sequential currently selected
|
||||
"""
|
||||
return self.q(css='#seq_content .xblock').text[0]
|
||||
|
||||
|
||||
class CourseNavPage(PageObject):
|
||||
"""
|
||||
Handles navigation on the courseware pages, including sequence navigation and
|
||||
breadcrumbs.
|
||||
"""
|
||||
|
||||
url = None
|
||||
|
||||
def __init__(self, browser, parent_page):
|
||||
super(CourseNavPage, self).__init__(browser)
|
||||
self.parent_page = parent_page
|
||||
# TODO: TNL-6546: Remove the following
|
||||
self.unified_course_view = False
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.parent_page.is_browser_on_page
|
||||
|
||||
# TODO: TNL-6546: Remove method, outline no longer on courseware 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
|
||||
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
|
||||
|
||||
# TODO: TNL-6546: Remove method, outline no longer on courseware page
|
||||
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()
|
||||
|
||||
# TODO: TNL-6546: Remove method, outline no longer on courseware page
|
||||
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
|
||||
|
||||
# TODO: TNL-6546: Remove method, outline no longer on courseware page
|
||||
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
|
||||
|
||||
# TODO: TNL-6546: Remove method, outline no longer on courseware page
|
||||
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
|
||||
)
|
||||
|
||||
def go_to_outline(self):
|
||||
"""
|
||||
Navigates using breadcrumb to the course outline on the course home page.
|
||||
|
||||
Returns CourseHomePage page object.
|
||||
"""
|
||||
# To avoid circular dependency, importing inside the function
|
||||
from common.test.acceptance.pages.lms.course_home import CourseHomePage
|
||||
|
||||
course_home_page = CourseHomePage(self.browser, self.parent_page.course_id)
|
||||
self.q(css='.path a').click()
|
||||
course_home_page.wait_for_page()
|
||||
return course_home_page
|
||||
|
||||
@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.
|
||||
|
||||
"""
|
||||
# TODO: TNL-6546: Remove if/else; always use unified_course_view version (if)
|
||||
if self.unified_course_view:
|
||||
# breadcrumb location of form: "SECTION_TITLE > SUBSECTION_TITLE > SEQUENTIAL_TITLE"
|
||||
bread_crumb_current = self.q(css='.position').text
|
||||
if len(bread_crumb_current) != 1:
|
||||
self.warning("Could not find the current bread crumb with section and subsection.")
|
||||
return False
|
||||
|
||||
return bread_crumb_current[0].strip().startswith(section_title + ' > ' + subsection_title + ' > ')
|
||||
|
||||
else:
|
||||
# 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()
|
||||
|
||||
# TODO: TNL-6546: Remove from here and move to course_home.py:CourseOutlinePage
|
||||
@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]
|
||||
|
||||
# TODO: TNL-6546: Remove all references to self.unified_course_view
|
||||
# TODO: TNL-6546: Remove the following function
|
||||
def visit_unified_course_view(self):
|
||||
# use unified_course_view version of the nav
|
||||
self.unified_course_view = True
|
||||
# reload the same page with the unified course view
|
||||
self.browser.get(self.browser.current_url + "&unified_course_view=1")
|
||||
self.wait_for_page()
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -21,26 +21,26 @@ from common.test.acceptance.tests.helpers import (
|
||||
select_option_by_text,
|
||||
get_selected_option_text
|
||||
)
|
||||
from common.test.acceptance.pages.common.logout import LogoutPage
|
||||
from common.test.acceptance.pages.lms import BASE_URL
|
||||
from common.test.acceptance.pages.lms.account_settings import AccountSettingsPage
|
||||
from common.test.acceptance.pages.lms.auto_auth import AutoAuthPage
|
||||
from common.test.acceptance.pages.lms.create_mode import ModeCreationPage
|
||||
from common.test.acceptance.pages.common.logout import LogoutPage
|
||||
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.progress import ProgressPage
|
||||
from common.test.acceptance.pages.lms.dashboard import DashboardPage
|
||||
from common.test.acceptance.pages.lms.problem import ProblemPage
|
||||
from common.test.acceptance.pages.lms.video.video import VideoPage
|
||||
from common.test.acceptance.pages.lms.courseware import CoursewarePage
|
||||
from common.test.acceptance.pages.studio.settings import SettingsPage
|
||||
from common.test.acceptance.pages.lms.login_and_register import CombinedLoginAndRegisterPage, ResetPasswordPage
|
||||
from common.test.acceptance.pages.lms.track_selection import TrackSelectionPage
|
||||
from common.test.acceptance.pages.lms.pay_and_verify import PaymentAndVerificationFlow, FakePaymentPage
|
||||
from common.test.acceptance.pages.lms.course_wiki import (
|
||||
CourseWikiPage, CourseWikiEditPage, CourseWikiHistoryPage, CourseWikiChildrenPage
|
||||
)
|
||||
from common.test.acceptance.pages.lms.courseware import CoursewarePage
|
||||
from common.test.acceptance.pages.lms.dashboard import DashboardPage
|
||||
from common.test.acceptance.pages.lms.login_and_register import CombinedLoginAndRegisterPage, ResetPasswordPage
|
||||
from common.test.acceptance.pages.lms.pay_and_verify import PaymentAndVerificationFlow, FakePaymentPage
|
||||
from common.test.acceptance.pages.lms.progress import ProgressPage
|
||||
from common.test.acceptance.pages.lms.problem import ProblemPage
|
||||
from common.test.acceptance.pages.lms.tab_nav import TabNavPage
|
||||
from common.test.acceptance.pages.lms.track_selection import TrackSelectionPage
|
||||
from common.test.acceptance.pages.lms.video.video import VideoPage
|
||||
from common.test.acceptance.pages.studio.settings import SettingsPage
|
||||
from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc, CourseUpdateDesc
|
||||
|
||||
|
||||
@@ -634,7 +634,6 @@ class CourseWikiTest(UniqueCourseTest):
|
||||
children_page.a11y_audit.check_for_accessibility_errors()
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
class HighLevelTabTest(UniqueCourseTest):
|
||||
"""
|
||||
Tests that verify each of the high-level tabs available within a course.
|
||||
@@ -651,7 +650,8 @@ class HighLevelTabTest(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.course_home_page = CourseHomePage(self.browser, self.course_id)
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
self.tab_nav = TabNavPage(self.browser)
|
||||
self.video = VideoPage(self.browser)
|
||||
|
||||
@@ -678,13 +678,16 @@ class HighLevelTabTest(UniqueCourseTest):
|
||||
),
|
||||
XBlockFixtureDesc('chapter', 'Test Section 2').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection 2'),
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection 3'),
|
||||
XBlockFixtureDesc('sequential', 'Test Subsection 3').add_children(
|
||||
XBlockFixtureDesc('problem', 'Test Problem A', data=load_data_str('multiple_choice.xml'))
|
||||
),
|
||||
)
|
||||
).install()
|
||||
|
||||
# Auto-auth register for the course
|
||||
AutoAuthPage(self.browser, course_id=self.course_id).visit()
|
||||
|
||||
@attr(shard=1)
|
||||
def test_course_info(self):
|
||||
"""
|
||||
Navigate to the course info page.
|
||||
@@ -702,6 +705,7 @@ class HighLevelTabTest(UniqueCourseTest):
|
||||
self.assertEqual(len(handout_links), 1)
|
||||
self.assertIn('demoPDF.pdf', handout_links[0])
|
||||
|
||||
@attr(shard=1)
|
||||
def test_progress(self):
|
||||
"""
|
||||
Navigate to the progress page.
|
||||
@@ -719,6 +723,7 @@ class HighLevelTabTest(UniqueCourseTest):
|
||||
actual_scores = self.progress_page.scores(CHAPTER, SECTION)
|
||||
self.assertEqual(actual_scores, EXPECTED_SCORES)
|
||||
|
||||
@attr(shard=1)
|
||||
def test_static_tab(self):
|
||||
"""
|
||||
Navigate to a static tab (course content)
|
||||
@@ -728,6 +733,7 @@ class HighLevelTabTest(UniqueCourseTest):
|
||||
self.tab_nav.go_to_tab('Test Static Tab')
|
||||
self.assertTrue(self.tab_nav.is_on_tab('Test Static Tab'))
|
||||
|
||||
@attr(shard=1)
|
||||
def test_static_tab_with_mathjax(self):
|
||||
"""
|
||||
Navigate to a static tab (course content)
|
||||
@@ -740,6 +746,7 @@ class HighLevelTabTest(UniqueCourseTest):
|
||||
# Verify that Mathjax has rendered
|
||||
self.tab_nav.mathjax_has_rendered()
|
||||
|
||||
@attr(shard=1)
|
||||
def test_wiki_tab_first_time(self):
|
||||
"""
|
||||
Navigate to the course wiki tab. When the wiki is accessed for
|
||||
@@ -760,6 +767,8 @@ class HighLevelTabTest(UniqueCourseTest):
|
||||
)
|
||||
self.assertEqual(expected_article_name, course_wiki.article_name)
|
||||
|
||||
# TODO: TNL-6546: This whole function will be able to go away, replaced by test_course_home below.
|
||||
@attr(shard=1)
|
||||
def test_courseware_nav(self):
|
||||
"""
|
||||
Navigate to a particular unit in the course.
|
||||
@@ -774,26 +783,80 @@ class HighLevelTabTest(UniqueCourseTest):
|
||||
'Test Section 2': ['Test Subsection 2', 'Test Subsection 3']
|
||||
}
|
||||
|
||||
actual_sections = self.course_nav.sections
|
||||
actual_sections = self.courseware_page.nav.sections
|
||||
|
||||
for section, subsections in EXPECTED_SECTIONS.iteritems():
|
||||
self.assertIn(section, actual_sections)
|
||||
self.assertEqual(actual_sections[section], EXPECTED_SECTIONS[section])
|
||||
|
||||
# Navigate to a particular section
|
||||
self.course_nav.go_to_section('Test Section', 'Test Subsection')
|
||||
self.courseware_page.nav.go_to_section('Test Section', 'Test Subsection')
|
||||
|
||||
# Check the sequence items
|
||||
EXPECTED_ITEMS = ['Test Problem 1', 'Test Problem 2', 'Test HTML']
|
||||
|
||||
actual_items = self.course_nav.sequence_items
|
||||
actual_items = self.courseware_page.nav.sequence_items
|
||||
self.assertEqual(len(actual_items), len(EXPECTED_ITEMS))
|
||||
for expected in EXPECTED_ITEMS:
|
||||
self.assertIn(expected, actual_items)
|
||||
|
||||
# Navigate to a particular section other than the default landing section.
|
||||
self.course_nav.go_to_section('Test Section 2', 'Test Subsection 3')
|
||||
self.assertTrue(self.course_nav.is_on_section('Test Section 2', 'Test Subsection 3'))
|
||||
self.courseware_page.nav.go_to_section('Test Section 2', 'Test Subsection 3')
|
||||
self.assertTrue(self.courseware_page.nav.is_on_section('Test Section 2', 'Test Subsection 3'))
|
||||
|
||||
@attr(shard=1)
|
||||
def test_course_home(self):
|
||||
"""
|
||||
Navigate to the course home page using the tab.
|
||||
|
||||
Includes smoke test of course outline, courseware page, and breadcrumbs.
|
||||
|
||||
"""
|
||||
# TODO: TNL-6546: Use tab navigation and remove course_home_page.visit().
|
||||
#self.course_info_page.visit()
|
||||
#self.tab_nav.go_to_tab('Course')
|
||||
self.course_home_page.visit()
|
||||
|
||||
# TODO: TNL-6546: Remove unified_course_view.
|
||||
self.course_home_page.unified_course_view = True
|
||||
self.courseware_page.nav.unified_course_view = True
|
||||
|
||||
# Check that the tab lands on the course home page.
|
||||
self.assertTrue(self.course_home_page.is_browser_on_page())
|
||||
|
||||
# Check that the course navigation appears correctly
|
||||
EXPECTED_SECTIONS = {
|
||||
'Test Section': ['Test Subsection'],
|
||||
'Test Section 2': ['Test Subsection 2', 'Test Subsection 3']
|
||||
}
|
||||
|
||||
actual_sections = self.course_home_page.outline.sections
|
||||
for section, subsections in EXPECTED_SECTIONS.iteritems():
|
||||
self.assertIn(section, actual_sections)
|
||||
self.assertEqual(actual_sections[section], EXPECTED_SECTIONS[section])
|
||||
|
||||
# Navigate to a particular section
|
||||
self.course_home_page.outline.go_to_section('Test Section', 'Test Subsection')
|
||||
|
||||
# Check the sequence items on the courseware page
|
||||
EXPECTED_ITEMS = ['Test Problem 1', 'Test Problem 2', 'Test HTML']
|
||||
|
||||
actual_items = self.courseware_page.nav.sequence_items
|
||||
self.assertEqual(len(actual_items), len(EXPECTED_ITEMS))
|
||||
for expected in EXPECTED_ITEMS:
|
||||
self.assertIn(expected, actual_items)
|
||||
|
||||
# Use outline breadcrumb to get back to course home page.
|
||||
self.courseware_page.nav.go_to_outline()
|
||||
|
||||
# Navigate to a particular section other than the default landing section.
|
||||
self.course_home_page.outline.go_to_section('Test Section 2', 'Test Subsection 3')
|
||||
self.assertTrue(self.courseware_page.nav.is_on_section('Test Section 2', 'Test Subsection 3'))
|
||||
|
||||
@attr('a11y')
|
||||
def test_course_home_a11y(self):
|
||||
self.course_home_page.visit()
|
||||
self.course_home_page.a11y_audit.check_for_accessibility_errors()
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
@@ -878,7 +941,6 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
|
||||
).install()
|
||||
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
self.course_nav = CourseNavPage(self.browser)
|
||||
|
||||
def test_visible_to_staff(self):
|
||||
"""
|
||||
@@ -891,16 +953,16 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
|
||||
course_id=self.course_id, staff=True).visit()
|
||||
|
||||
self.courseware_page.visit()
|
||||
self.assertEqual(3, len(self.course_nav.sections['Test Section']))
|
||||
self.assertEqual(3, len(self.courseware_page.nav.sections['Test Section']))
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit")
|
||||
self.assertEqual([u'Locked Unit', u'Unlocked Unit'], self.course_nav.sequence_items)
|
||||
self.courseware_page.nav.go_to_section("Test Section", "Subsection With Locked Unit")
|
||||
self.assertEqual([u'Locked Unit', u'Unlocked Unit'], self.courseware_page.nav.sequence_items)
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
|
||||
self.assertEqual([u'Test Unit'], self.course_nav.sequence_items)
|
||||
self.courseware_page.nav.go_to_section("Test Section", "Unlocked Subsection")
|
||||
self.assertEqual([u'Test Unit'], self.courseware_page.nav.sequence_items)
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Locked Subsection")
|
||||
self.assertEqual([u'Test Unit'], self.course_nav.sequence_items)
|
||||
self.courseware_page.nav.go_to_section("Test Section", "Locked Subsection")
|
||||
self.assertEqual([u'Test Unit'], self.courseware_page.nav.sequence_items)
|
||||
|
||||
def test_visible_to_student(self):
|
||||
"""
|
||||
@@ -913,13 +975,13 @@ class VisibleToStaffOnlyTest(UniqueCourseTest):
|
||||
course_id=self.course_id, staff=False).visit()
|
||||
|
||||
self.courseware_page.visit()
|
||||
self.assertEqual(2, len(self.course_nav.sections['Test Section']))
|
||||
self.assertEqual(2, len(self.courseware_page.nav.sections['Test Section']))
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit")
|
||||
self.assertEqual([u'Unlocked Unit'], self.course_nav.sequence_items)
|
||||
self.courseware_page.nav.go_to_section("Test Section", "Subsection With Locked Unit")
|
||||
self.assertEqual([u'Unlocked Unit'], self.courseware_page.nav.sequence_items)
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
|
||||
self.assertEqual([u'Test Unit'], self.course_nav.sequence_items)
|
||||
self.courseware_page.nav.go_to_section("Test Section", "Unlocked Subsection")
|
||||
self.assertEqual([u'Test Unit'], self.courseware_page.nav.sequence_items)
|
||||
|
||||
|
||||
@attr(shard=1)
|
||||
@@ -1065,7 +1127,7 @@ class ProblemExecutionTest(UniqueCourseTest):
|
||||
super(ProblemExecutionTest, self).setUp()
|
||||
|
||||
self.course_info_page = CourseInfoPage(self.browser, self.course_id)
|
||||
self.course_nav = CourseNavPage(self.browser)
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
self.tab_nav = TabNavPage(self.browser)
|
||||
|
||||
# Install a course with sections and problems.
|
||||
@@ -1112,7 +1174,7 @@ class ProblemExecutionTest(UniqueCourseTest):
|
||||
# Navigate to the problem page
|
||||
self.course_info_page.visit()
|
||||
self.tab_nav.go_to_tab('Course')
|
||||
self.course_nav.go_to_section('Test Section', 'Test Subsection')
|
||||
self.courseware_page.nav.go_to_section('Test Section', 'Test Subsection')
|
||||
|
||||
problem_page = ProblemPage(self.browser)
|
||||
self.assertEqual(problem_page.problem_name.upper(), 'PYTHON PROBLEM')
|
||||
@@ -1391,6 +1453,6 @@ class CourseInfoA11yTest(UniqueCourseTest):
|
||||
self.course_info_page = CourseInfoPage(self.browser, self.course_id)
|
||||
AutoAuthPage(self.browser, course_id=self.course_id).visit()
|
||||
|
||||
def test_course_home_a11y(self):
|
||||
def test_course_info_a11y(self):
|
||||
self.course_info_page.visit()
|
||||
self.course_info_page.a11y_audit.check_for_accessibility_errors()
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -13,7 +13,7 @@ from nose.plugins.attrib import attr
|
||||
from ..helpers import UniqueCourseTest, EventsTestMixin, auto_auth, create_multiple_choice_problem
|
||||
from ...fixtures.course import CourseFixture, XBlockFixtureDesc
|
||||
from ...pages.common.logout import LogoutPage
|
||||
from ...pages.lms.course_nav import CourseNavPage
|
||||
from ...pages.lms.course_home import CourseHomePage
|
||||
from ...pages.lms.courseware import CoursewarePage, CoursewareSequentialTabPage
|
||||
from ...pages.lms.create_mode import ModeCreationPage
|
||||
from ...pages.lms.dashboard import DashboardPage
|
||||
@@ -23,7 +23,7 @@ from ...pages.lms.progress import ProgressPage
|
||||
from ...pages.lms.staff_view import StaffPage
|
||||
from ...pages.lms.track_selection import TrackSelectionPage
|
||||
from ...pages.studio.auto_auth import AutoAuthPage
|
||||
from ...pages.studio.overview import CourseOutlinePage
|
||||
from ...pages.studio.overview import CourseOutlinePage as StudioCourseOutlinePage
|
||||
|
||||
|
||||
@attr(shard=9)
|
||||
@@ -38,9 +38,9 @@ class CoursewareTest(UniqueCourseTest):
|
||||
super(CoursewareTest, 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.course_outline = CourseOutlinePage(
|
||||
self.studio_course_outline = StudioCourseOutlinePage(
|
||||
self.browser,
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
@@ -94,10 +94,10 @@ class CoursewareTest(UniqueCourseTest):
|
||||
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
||||
|
||||
# Visit course outline page in studio.
|
||||
self.course_outline.visit()
|
||||
self.studio_course_outline.visit()
|
||||
|
||||
# Set release date for subsection in future.
|
||||
self.course_outline.change_problem_release_date()
|
||||
self.studio_course_outline.change_problem_release_date()
|
||||
|
||||
# Logout and login as a student.
|
||||
LogoutPage(self.browser).visit()
|
||||
@@ -116,11 +116,10 @@ class CoursewareTest(UniqueCourseTest):
|
||||
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))
|
||||
self.course_home_page.visit()
|
||||
self.course_home_page.outline.go_to_section('Test Section {}'.format(index), 'Test Subsection {}'.format(index))
|
||||
courseware_page_breadcrumb = self.courseware_page.breadcrumb
|
||||
expected_breadcrumb = self._create_breadcrumb(index) # pylint: disable=no-member
|
||||
self.assertEqual(courseware_page_breadcrumb, expected_breadcrumb)
|
||||
@@ -140,7 +139,7 @@ class ProctoredExamTest(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'],
|
||||
@@ -234,10 +233,10 @@ class ProctoredExamTest(UniqueCourseTest):
|
||||
"""
|
||||
LogoutPage(self.browser).visit()
|
||||
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
||||
self.course_outline.visit()
|
||||
self.studio_course_outline.visit()
|
||||
|
||||
self.course_outline.open_subsection_settings_dialog()
|
||||
self.assertTrue(self.course_outline.proctoring_items_are_displayed())
|
||||
self.studio_course_outline.open_subsection_settings_dialog()
|
||||
self.assertTrue(self.studio_course_outline.proctoring_items_are_displayed())
|
||||
|
||||
def test_proctored_exam_flow(self):
|
||||
"""
|
||||
@@ -251,11 +250,11 @@ class ProctoredExamTest(UniqueCourseTest):
|
||||
"""
|
||||
LogoutPage(self.browser).visit()
|
||||
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
||||
self.course_outline.visit()
|
||||
self.course_outline.open_subsection_settings_dialog()
|
||||
self.studio_course_outline.visit()
|
||||
self.studio_course_outline.open_subsection_settings_dialog()
|
||||
|
||||
self.course_outline.select_advanced_tab()
|
||||
self.course_outline.make_exam_proctored()
|
||||
self.studio_course_outline.select_advanced_tab()
|
||||
self.studio_course_outline.make_exam_proctored()
|
||||
|
||||
LogoutPage(self.browser).visit()
|
||||
self._login_as_a_verified_user()
|
||||
@@ -272,11 +271,11 @@ class ProctoredExamTest(UniqueCourseTest):
|
||||
"""
|
||||
LogoutPage(self.browser).visit()
|
||||
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
||||
self.course_outline.visit()
|
||||
self.course_outline.open_subsection_settings_dialog()
|
||||
self.studio_course_outline.visit()
|
||||
self.studio_course_outline.open_subsection_settings_dialog()
|
||||
|
||||
self.course_outline.select_advanced_tab()
|
||||
self.course_outline.make_exam_timed(hide_after_due=hide_after_due)
|
||||
self.studio_course_outline.select_advanced_tab()
|
||||
self.studio_course_outline.make_exam_timed(hide_after_due=hide_after_due)
|
||||
|
||||
LogoutPage(self.browser).visit()
|
||||
self._login_as_a_verified_user()
|
||||
@@ -312,9 +311,9 @@ class ProctoredExamTest(UniqueCourseTest):
|
||||
|
||||
LogoutPage(self.browser).visit()
|
||||
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
||||
self.course_outline.visit()
|
||||
self.studio_course_outline.visit()
|
||||
last_week = (datetime.today() - timedelta(days=7)).strftime("%m/%d/%Y")
|
||||
self.course_outline.change_problem_due_date(last_week)
|
||||
self.studio_course_outline.change_problem_due_date(last_week)
|
||||
|
||||
LogoutPage(self.browser).visit()
|
||||
auto_auth(self.browser, self.USERNAME, self.EMAIL, False, self.course_id)
|
||||
@@ -355,26 +354,26 @@ class ProctoredExamTest(UniqueCourseTest):
|
||||
"""
|
||||
LogoutPage(self.browser).visit()
|
||||
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
||||
self.course_outline.visit()
|
||||
self.studio_course_outline.visit()
|
||||
|
||||
self.course_outline.open_subsection_settings_dialog()
|
||||
self.course_outline.select_advanced_tab()
|
||||
self.studio_course_outline.open_subsection_settings_dialog()
|
||||
self.studio_course_outline.select_advanced_tab()
|
||||
|
||||
self.course_outline.select_none_exam()
|
||||
self.assertFalse(self.course_outline.time_allotted_field_visible())
|
||||
self.assertFalse(self.course_outline.exam_review_rules_field_visible())
|
||||
self.studio_course_outline.select_none_exam()
|
||||
self.assertFalse(self.studio_course_outline.time_allotted_field_visible())
|
||||
self.assertFalse(self.studio_course_outline.exam_review_rules_field_visible())
|
||||
|
||||
self.course_outline.select_timed_exam()
|
||||
self.assertTrue(self.course_outline.time_allotted_field_visible())
|
||||
self.assertFalse(self.course_outline.exam_review_rules_field_visible())
|
||||
self.studio_course_outline.select_timed_exam()
|
||||
self.assertTrue(self.studio_course_outline.time_allotted_field_visible())
|
||||
self.assertFalse(self.studio_course_outline.exam_review_rules_field_visible())
|
||||
|
||||
self.course_outline.select_proctored_exam()
|
||||
self.assertTrue(self.course_outline.time_allotted_field_visible())
|
||||
self.assertTrue(self.course_outline.exam_review_rules_field_visible())
|
||||
self.studio_course_outline.select_proctored_exam()
|
||||
self.assertTrue(self.studio_course_outline.time_allotted_field_visible())
|
||||
self.assertTrue(self.studio_course_outline.exam_review_rules_field_visible())
|
||||
|
||||
self.course_outline.select_practice_exam()
|
||||
self.assertTrue(self.course_outline.time_allotted_field_visible())
|
||||
self.assertFalse(self.course_outline.exam_review_rules_field_visible())
|
||||
self.studio_course_outline.select_practice_exam()
|
||||
self.assertTrue(self.studio_course_outline.time_allotted_field_visible())
|
||||
self.assertFalse(self.studio_course_outline.exam_review_rules_field_visible())
|
||||
|
||||
|
||||
@attr(shard=9)
|
||||
@@ -389,8 +388,9 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin):
|
||||
super(CoursewareMultipleVerticalsTest, self).setUp()
|
||||
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
self.course_home_page = CourseHomePage(self.browser, self.course_id)
|
||||
|
||||
self.course_outline = CourseOutlinePage(
|
||||
self.studio_course_outline = StudioCourseOutlinePage(
|
||||
self.browser,
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
@@ -433,10 +433,10 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin):
|
||||
# Auto-auth register for the course.
|
||||
AutoAuthPage(self.browser, username=self.USERNAME, email=self.EMAIL,
|
||||
course_id=self.course_id, staff=False).visit()
|
||||
self.courseware_page.visit()
|
||||
self.course_nav = CourseNavPage(self.browser)
|
||||
|
||||
def test_navigation_buttons(self):
|
||||
self.courseware_page.visit()
|
||||
|
||||
# start in first section
|
||||
self.assert_navigation_state('Test Section 1', 'Test Subsection 1,1', 0, next_enabled=True, prev_enabled=False)
|
||||
|
||||
@@ -549,10 +549,13 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin):
|
||||
sequence_ui_events
|
||||
)
|
||||
|
||||
# TODO: TNL-6546: Delete this whole test if these events are going away(?)
|
||||
def test_outline_selected_events(self):
|
||||
self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,2')
|
||||
self.courseware_page.visit()
|
||||
|
||||
self.course_nav.go_to_section('Test Section 2', 'Test Subsection 2,1')
|
||||
self.courseware_page.nav.go_to_section('Test Section 1', 'Test Subsection 1,2')
|
||||
|
||||
self.courseware_page.nav.go_to_section('Test Section 2', 'Test Subsection 2,1')
|
||||
|
||||
# test UI events emitted by navigating via the course outline
|
||||
filter_selected_events = lambda event: event.get('name', '') == 'edx.ui.lms.outline.selected'
|
||||
@@ -588,8 +591,10 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin):
|
||||
When I navigate via the left-hand nav
|
||||
Then a link clicked event is logged
|
||||
"""
|
||||
self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,2')
|
||||
self.course_nav.go_to_section('Test Section 2', 'Test Subsection 2,1')
|
||||
self.courseware_page.visit()
|
||||
|
||||
self.courseware_page.nav.go_to_section('Test Section 1', 'Test Subsection 1,2')
|
||||
self.courseware_page.nav.go_to_section('Test Section 2', 'Test Subsection 2,1')
|
||||
|
||||
filter_link_clicked = lambda event: event.get('name', '') == 'edx.ui.lms.link_clicked'
|
||||
link_clicked_events = self.wait_for_events(event_filter=filter_link_clicked, timeout=2)
|
||||
@@ -601,15 +606,17 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin):
|
||||
"""
|
||||
Verifies that the navigation state is as expected.
|
||||
"""
|
||||
self.assertTrue(self.course_nav.is_on_section(section_title, subsection_title))
|
||||
self.assertTrue(self.courseware_page.nav.is_on_section(section_title, subsection_title))
|
||||
self.assertEquals(self.courseware_page.sequential_position, subsection_position)
|
||||
self.assertEquals(self.courseware_page.is_next_button_enabled, next_enabled)
|
||||
self.assertEquals(self.courseware_page.is_previous_button_enabled, prev_enabled)
|
||||
|
||||
def test_tab_position(self):
|
||||
# test that using the position in the url direct to correct tab in courseware
|
||||
self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,1')
|
||||
subsection_url = self.course_nav.active_subsection_url
|
||||
self.course_home_page.visit()
|
||||
|
||||
self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 1,1')
|
||||
subsection_url = self.courseware_page.nav.active_subsection_url
|
||||
url_part_list = subsection_url.split('/')
|
||||
self.assertEqual(len(url_part_list), 9)
|
||||
|
||||
@@ -657,7 +664,8 @@ class CoursewareMultipleVerticalsTest(UniqueCourseTest, EventsTestMixin):
|
||||
"""
|
||||
Run accessibility audit for the problem type.
|
||||
"""
|
||||
self.course_nav.go_to_section('Test Section 1', 'Test Subsection 1,1')
|
||||
self.course_home_page.visit()
|
||||
self.course_home_page.outline.go_to_section('Test Section 1', 'Test Subsection 1,1')
|
||||
# Set the scope to the sequence navigation
|
||||
self.courseware_page.a11y_audit.config.set_scope(
|
||||
include=['div.sequence-nav'])
|
||||
@@ -840,7 +848,7 @@ class SubsectionHiddenAfterDueDateTest(UniqueCourseTest):
|
||||
self.courseware_page = CoursewarePage(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'],
|
||||
@@ -876,11 +884,11 @@ class SubsectionHiddenAfterDueDateTest(UniqueCourseTest):
|
||||
"""
|
||||
self.logout_page.visit()
|
||||
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
||||
self.course_outline.visit()
|
||||
self.course_outline.open_subsection_settings_dialog()
|
||||
self.studio_course_outline.visit()
|
||||
self.studio_course_outline.open_subsection_settings_dialog()
|
||||
|
||||
self.course_outline.select_advanced_tab('hide_after_due_date')
|
||||
self.course_outline.make_subsection_hidden_after_due_date()
|
||||
self.studio_course_outline.select_advanced_tab('hide_after_due_date')
|
||||
self.studio_course_outline.make_subsection_hidden_after_due_date()
|
||||
|
||||
self.logout_page.visit()
|
||||
auto_auth(self.browser, self.USERNAME, self.EMAIL, False, self.course_id)
|
||||
@@ -916,9 +924,9 @@ class SubsectionHiddenAfterDueDateTest(UniqueCourseTest):
|
||||
|
||||
self.logout_page.visit()
|
||||
auto_auth(self.browser, "STAFF_TESTER", "staff101@example.com", True, self.course_id)
|
||||
self.course_outline.visit()
|
||||
self.studio_course_outline.visit()
|
||||
last_week = (datetime.today() - timedelta(days=7)).strftime("%m/%d/%Y")
|
||||
self.course_outline.change_problem_due_date(last_week)
|
||||
self.studio_course_outline.change_problem_due_date(last_week)
|
||||
|
||||
self.logout_page.visit()
|
||||
auto_auth(self.browser, self.USERNAME, self.EMAIL, False, self.course_id)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user