Support staff locking on the unit page
STUD-1873
This commit is contained in:
@@ -10,16 +10,24 @@ class StaffPage(PageObject):
|
||||
"""
|
||||
|
||||
url = None
|
||||
STAFF_STATUS_CSS = '#staffstatus'
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css='#staffstatus').present
|
||||
return self.q(css=self.STAFF_STATUS_CSS).present
|
||||
|
||||
@property
|
||||
def staff_status(self):
|
||||
"""
|
||||
Return the current status, either Staff view or Student view
|
||||
"""
|
||||
return self.q(css='#staffstatus').text[0]
|
||||
return self.q(css=self.STAFF_STATUS_CSS).text[0]
|
||||
|
||||
def toggle_staff_view(self):
|
||||
"""
|
||||
Toggle between staff view and student view.
|
||||
"""
|
||||
self.q(css=self.STAFF_STATUS_CSS).first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def open_staff_debug_info(self):
|
||||
"""
|
||||
|
||||
@@ -81,6 +81,33 @@ class ContainerPage(PageObject):
|
||||
"""
|
||||
return self.q(css='.pub-status').first.text[0]
|
||||
|
||||
@property
|
||||
def release_title(self):
|
||||
"""
|
||||
Returns the title before the release date in the publishing sidebar component.
|
||||
"""
|
||||
return self.q(css='.wrapper-release .title').first.text[0]
|
||||
|
||||
@property
|
||||
def release_date(self):
|
||||
"""
|
||||
Returns the release date of the unit (with ancestor inherited from), as displayed
|
||||
in the publishing sidebar component.
|
||||
"""
|
||||
return self.q(css='.wrapper-release .copy').first.text[0]
|
||||
|
||||
@property
|
||||
def currently_visible_to_students(self):
|
||||
"""
|
||||
Returns True if the unit is marked as currently visible to students
|
||||
(meaning that a warning is being displayed).
|
||||
"""
|
||||
warnings = self.q(css='.container-message .warning')
|
||||
if not warnings.is_present():
|
||||
return False
|
||||
warning_text = warnings.first.text[0]
|
||||
return warning_text == "This content is live for students. Edit with caution."
|
||||
|
||||
@property
|
||||
def publish_action(self):
|
||||
"""
|
||||
@@ -96,6 +123,22 @@ class ContainerPage(PageObject):
|
||||
self.q(css='a.button.action-primary').first.click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
def toggle_staff_lock(self):
|
||||
"""
|
||||
Toggles "hide from students" which enables or disables a staff-only lock.
|
||||
|
||||
Returns True if the lock is now enabled, else False.
|
||||
"""
|
||||
class_attribute_values = self.q(css='a.action-staff-lock>i').attrs('class')
|
||||
was_locked_initially = 'icon-check' in class_attribute_values
|
||||
if not was_locked_initially:
|
||||
self.q(css='a.action-staff-lock').first.click()
|
||||
else:
|
||||
click_css(self, 'a.action-staff-lock', 0, require_notification=False)
|
||||
self.q(css='a.button.action-primary').first.click()
|
||||
self.wait_for_ajax()
|
||||
return not was_locked_initially
|
||||
|
||||
def view_published_version(self):
|
||||
"""
|
||||
Clicks "View Published Version", which will open the published version of the unit page in the LMS.
|
||||
|
||||
@@ -16,6 +16,7 @@ from ..pages.lms.progress import ProgressPage
|
||||
from ..pages.lms.dashboard import DashboardPage
|
||||
from ..pages.lms.video.video import VideoPage
|
||||
from ..pages.xblock.acid import AcidView
|
||||
from ..pages.lms.courseware import CoursewarePage
|
||||
from ..fixtures.course import CourseFixture, XBlockFixtureDesc, CourseUpdateDesc
|
||||
|
||||
|
||||
@@ -421,3 +422,87 @@ class XBlockAcidChildTest(XBlockAcidBase):
|
||||
@skip('This will fail until we fix support of children in pure XBlocks')
|
||||
def test_acid_block(self):
|
||||
super(XBlockAcidChildTest, self).test_acid_block()
|
||||
|
||||
|
||||
class VisibleToStaffOnlyTest(UniqueCourseTest):
|
||||
"""
|
||||
Tests that content with visible_to_staff_only set to True cannot be viewed by students.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(VisibleToStaffOnlyTest, self).setUp()
|
||||
|
||||
course_fix = CourseFixture(
|
||||
self.course_info['org'],
|
||||
self.course_info['number'],
|
||||
self.course_info['run'],
|
||||
self.course_info['display_name']
|
||||
)
|
||||
|
||||
course_fix.add_children(
|
||||
XBlockFixtureDesc('chapter', 'Test Section').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Subsection With Locked Unit').add_children(
|
||||
XBlockFixtureDesc('vertical', 'Locked Unit', metadata={'visible_to_staff_only': True}).add_children(
|
||||
XBlockFixtureDesc('html', 'Html Child in locked unit', data="<html>Visible only to staff</html>"),
|
||||
),
|
||||
XBlockFixtureDesc('vertical', 'Unlocked Unit').add_children(
|
||||
XBlockFixtureDesc('html', 'Html Child in unlocked unit', data="<html>Visible only to all</html>"),
|
||||
)
|
||||
),
|
||||
XBlockFixtureDesc('sequential', 'Unlocked Subsection').add_children(
|
||||
XBlockFixtureDesc('vertical', 'Test Unit').add_children(
|
||||
XBlockFixtureDesc('html', 'Html Child in visible unit', data="<html>Visible to all</html>"),
|
||||
)
|
||||
),
|
||||
XBlockFixtureDesc('sequential', 'Locked Subsection', metadata={'visible_to_staff_only': True}).add_children(
|
||||
XBlockFixtureDesc('vertical', 'Test Unit').add_children(
|
||||
XBlockFixtureDesc(
|
||||
'html', 'Html Child in locked subsection', data="<html>Visible only to staff</html>"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
).install()
|
||||
|
||||
self.courseware_page = CoursewarePage(self.browser, self.course_id)
|
||||
self.course_nav = CourseNavPage(self.browser)
|
||||
|
||||
def test_visible_to_staff(self):
|
||||
"""
|
||||
Scenario: All content is visible for a user marked is_staff (different from course staff)
|
||||
Given some of the course content has been marked 'visible_to_staff_only'
|
||||
And I am logged on with an account marked 'is_staff'
|
||||
Then I can see all course content
|
||||
"""
|
||||
AutoAuthPage(self.browser, username="STAFF_TESTER", email="johndoe_staff@example.com",
|
||||
course_id=self.course_id, staff=True).visit()
|
||||
|
||||
self.courseware_page.visit()
|
||||
self.assertEqual(3, len(self.course_nav.sections['Test Section']))
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit")
|
||||
self.assertEqual(["Html Child in locked unit", "Html Child in unlocked unit"], self.course_nav.sequence_items)
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
|
||||
self.assertEqual(["Html Child in visible unit"], self.course_nav.sequence_items)
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Locked Subsection")
|
||||
self.assertEqual(["Html Child in locked subsection"], self.course_nav.sequence_items)
|
||||
|
||||
def test_visible_to_student(self):
|
||||
"""
|
||||
Scenario: Content marked 'visible_to_staff_only' is not visible for students in the course
|
||||
Given some of the course content has been marked 'visible_to_staff_only'
|
||||
And I am logged on with an authorized student account
|
||||
Then I can only see content without 'visible_to_staff_only' set to True
|
||||
"""
|
||||
AutoAuthPage(self.browser, username="STUDENT_TESTER", email="johndoe_student@example.com",
|
||||
course_id=self.course_id, staff=False).visit()
|
||||
|
||||
self.courseware_page.visit()
|
||||
self.assertEqual(2, len(self.course_nav.sections['Test Section']))
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Subsection With Locked Unit")
|
||||
self.assertEqual(["Html Child in unlocked unit"], self.course_nav.sequence_items)
|
||||
|
||||
self.course_nav.go_to_section("Test Section", "Unlocked Subsection")
|
||||
self.assertEqual(["Html Child in visible unit"], self.course_nav.sequence_items)
|
||||
|
||||
@@ -11,9 +11,12 @@ from ..fixtures.course import XBlockFixtureDesc
|
||||
from ..pages.studio.component_editor import ComponentEditorView
|
||||
from ..pages.studio.utils import add_discussion
|
||||
from ..pages.lms.courseware import CoursewarePage
|
||||
from ..pages.lms.staff_view import StaffPage
|
||||
|
||||
from unittest import skip
|
||||
from acceptance.tests.base_studio_test import StudioCourseTest
|
||||
import datetime
|
||||
from bok_choy.promise import Promise
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
@@ -46,15 +49,15 @@ class ContainerBase(StudioCourseTest):
|
||||
container = unit.xblocks[1].go_to_container()
|
||||
return container
|
||||
|
||||
def go_to_unit_page(self):
|
||||
def go_to_unit_page(self, section_name='Test Section', subsection_name='Test Subsection', unit_name='Test Unit'):
|
||||
"""
|
||||
Go to the test unit page.
|
||||
|
||||
If make_draft is true, the unit page will be put into draft mode.
|
||||
"""
|
||||
self.outline.visit()
|
||||
subsection = self.outline.section('Test Section').subsection('Test Subsection')
|
||||
return subsection.toggle_expand().unit('Test Unit').go_to()
|
||||
subsection = self.outline.section(section_name).subsection(subsection_name)
|
||||
return subsection.toggle_expand().unit(unit_name).go_to()
|
||||
|
||||
def verify_ordering(self, container, expected_orderings):
|
||||
"""
|
||||
@@ -379,6 +382,8 @@ class UnitPublishingTest(ContainerBase):
|
||||
|
||||
PUBLISHED_STATUS = "Publishing Status\nPublished"
|
||||
DRAFT_STATUS = "Publishing Status\nDraft (Unpublished changes)"
|
||||
LOCKED_STATUS = "Publishing Status\nUnpublished (Staff only)"
|
||||
RELEASE_TITLE_RELEASED = "RELEASED:"
|
||||
|
||||
def setup_fixtures(self):
|
||||
"""
|
||||
@@ -393,6 +398,8 @@ class UnitPublishingTest(ContainerBase):
|
||||
self.course_info['run'],
|
||||
self.course_info['display_name']
|
||||
)
|
||||
past_start_date = datetime.datetime(1974, 6, 22)
|
||||
self.past_start_date_text = "Jun 22, 1974 at 00:00 UTC"
|
||||
|
||||
course_fix.add_children(
|
||||
XBlockFixtureDesc('chapter', 'Test Section').add_children(
|
||||
@@ -401,6 +408,20 @@ class UnitPublishingTest(ContainerBase):
|
||||
XBlockFixtureDesc('html', 'Test html', data=self.html_content)
|
||||
)
|
||||
)
|
||||
),
|
||||
XBlockFixtureDesc('chapter', 'Unlocked Section', metadata={'start': past_start_date.isoformat()}).add_children(
|
||||
XBlockFixtureDesc('sequential', 'Unlocked Subsection').add_children(
|
||||
XBlockFixtureDesc('vertical', 'Unlocked Unit').add_children(
|
||||
XBlockFixtureDesc('problem', '<problem></problem>', data=self.html_content)
|
||||
)
|
||||
)
|
||||
),
|
||||
XBlockFixtureDesc('chapter', 'Section With Locked Unit').add_children(
|
||||
XBlockFixtureDesc('sequential', 'Subsection With Locked Unit', metadata={'start': past_start_date.isoformat()}).add_children(
|
||||
XBlockFixtureDesc('vertical', 'Locked Unit', metadata={'visible_to_staff_only': True}).add_children(
|
||||
XBlockFixtureDesc('discussion', '', data=self.html_content)
|
||||
)
|
||||
)
|
||||
)
|
||||
).install()
|
||||
|
||||
@@ -408,61 +429,220 @@ class UnitPublishingTest(ContainerBase):
|
||||
|
||||
def test_publishing(self):
|
||||
"""
|
||||
Test the state changes when a published unit has draft changes.
|
||||
Scenario: The publish title changes based on whether or not draft content exists
|
||||
Given I have a published unit with no unpublished changes
|
||||
When I go to the unit page in Studio
|
||||
Then the title in the Publish information box is "Published"
|
||||
And the Publish button is disabled
|
||||
And when I add a component to the unit
|
||||
Then the title in the Publish information box is "Draft (Unpublished changes)"
|
||||
And the Publish button is enabled
|
||||
And when I click the Publish button
|
||||
Then the title in the Publish information box is "Published"
|
||||
"""
|
||||
unit = self.go_to_unit_page()
|
||||
self.assertEqual(self.PUBLISHED_STATUS, unit.publish_title)
|
||||
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
|
||||
# Start date set in course fixture to 1970.
|
||||
self._verify_release_date_info(
|
||||
unit, self.RELEASE_TITLE_RELEASED, 'Jan 01, 1970 at 00:00 UTC with Section "Test Section"'
|
||||
)
|
||||
# Should not be able to click on Publish action -- but I don't know how to test that it is not clickable.
|
||||
# TODO: continue discussion with Muhammad and Jay about this.
|
||||
|
||||
# Add a component to the page so it will have unpublished changes.
|
||||
add_discussion(unit)
|
||||
self.assertEqual(self.DRAFT_STATUS, unit.publish_title)
|
||||
self._verify_publish_title(unit, self.DRAFT_STATUS)
|
||||
unit.publish_action.click()
|
||||
unit.wait_for_ajax()
|
||||
self.assertEqual(self.PUBLISHED_STATUS, unit.publish_title)
|
||||
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
|
||||
|
||||
def test_discard_changes(self):
|
||||
"""
|
||||
Test the state after discard changes.
|
||||
Scenario: The publish title changes after "Discard Changes" is clicked
|
||||
Given I have a published unit with no unpublished changes
|
||||
When I go to the unit page in Studio
|
||||
Then the Discard Changes button is disabled
|
||||
And I add a component to the unit
|
||||
Then the title in the Publish information box is "Draft (Unpublished changes)"
|
||||
And the Discard Changes button is enabled
|
||||
And when I click the Discard Changes button
|
||||
Then the title in the Publish information box is "Published"
|
||||
"""
|
||||
unit = self.go_to_unit_page()
|
||||
add_discussion(unit)
|
||||
self.assertEqual(self.DRAFT_STATUS, unit.publish_title)
|
||||
self._verify_publish_title(unit, self.DRAFT_STATUS)
|
||||
unit.discard_changes()
|
||||
self.assertEqual(self.PUBLISHED_STATUS, unit.publish_title)
|
||||
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
|
||||
|
||||
def test_view_live_no_changes(self):
|
||||
"""
|
||||
Tests viewing of live with initial published content.
|
||||
Scenario: "View Live" shows published content in LMS
|
||||
Given I have a published unit with no unpublished changes
|
||||
When I go to the unit page in Studio
|
||||
Then the View Live button is enabled
|
||||
And when I click on the View Live button
|
||||
Then I see the published content in LMS
|
||||
"""
|
||||
unit = self.go_to_unit_page()
|
||||
unit.view_published_version()
|
||||
self.assertEqual(1, self.courseware.num_xblock_components)
|
||||
self.assertEqual('html', self.courseware.xblock_component_type(0))
|
||||
self._verify_components_visible(['html'])
|
||||
|
||||
def test_view_live_changes(self):
|
||||
"""
|
||||
Tests that viewing of live with draft content does not show the draft content.
|
||||
Scenario: "View Live" does not show draft content in LMS
|
||||
Given I have a published unit with no unpublished changes
|
||||
When I go to the unit page in Studio
|
||||
And when I add a component to the unit
|
||||
And when I click on the View Live button
|
||||
Then I see the published content in LMS
|
||||
And I do not see the unpublished component
|
||||
"""
|
||||
unit = self.go_to_unit_page()
|
||||
add_discussion(unit)
|
||||
unit.view_published_version()
|
||||
self.assertEqual(1, self.courseware.num_xblock_components)
|
||||
self.assertEqual('html', self.courseware.xblock_component_type(0))
|
||||
self._verify_components_visible(['html'])
|
||||
self.assertEqual(self.html_content, self.courseware.xblock_component_html_content(0))
|
||||
|
||||
def test_view_live_after_publish(self):
|
||||
"""
|
||||
Tests viewing of live after creating draft and publishing it.
|
||||
Scenario: "View Live" shows newly published content
|
||||
Given I have a published unit with no unpublished changes
|
||||
When I go to the unit page in Studio
|
||||
And when I add a component to the unit
|
||||
And when I click the Publish button
|
||||
And when I click on the View Live button
|
||||
Then I see the newly published component
|
||||
"""
|
||||
unit = self.go_to_unit_page()
|
||||
add_discussion(unit)
|
||||
unit.publish_action.click()
|
||||
unit.view_published_version()
|
||||
self.assertEqual(2, self.courseware.num_xblock_components)
|
||||
self.assertEqual('html', self.courseware.xblock_component_type(0))
|
||||
self.assertEqual('discussion', self.courseware.xblock_component_type(1))
|
||||
self._verify_components_visible(['html', 'discussion'])
|
||||
|
||||
def test_initially_unlocked_visible_to_students(self):
|
||||
"""
|
||||
Scenario: An unlocked unit with release date in the past is visible to students
|
||||
Given I have a published unlocked unit with release date in the past
|
||||
When I go to the unit page in Studio
|
||||
Then the unit has a warning that it is visible to students
|
||||
And it is marked as "RELEASED" with release date in the past visible
|
||||
And when I click on the View Live Button
|
||||
And when I view the course as a student
|
||||
Then I see the content in the unit
|
||||
"""
|
||||
unit = self.go_to_unit_page("Unlocked Section", "Unlocked Subsection", "Unlocked Unit")
|
||||
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
|
||||
self.assertTrue(unit.currently_visible_to_students)
|
||||
self._verify_release_date_info(
|
||||
unit, self.RELEASE_TITLE_RELEASED, self.past_start_date_text + ' with Section "Unlocked Section"'
|
||||
)
|
||||
unit.view_published_version()
|
||||
self._verify_student_view_visible(['problem'])
|
||||
|
||||
def test_locked_visible_to_staff_only(self):
|
||||
"""
|
||||
Scenario: After locking a unit with release date in the past, it is only visible to staff
|
||||
Given I have a published unlocked unit with release date in the past
|
||||
When I go to the unit page in Studio
|
||||
And when I select "Hide from students"
|
||||
Then the unit does not have a warning that it is visible to students
|
||||
And when I click on the View Live Button
|
||||
Then I see the content in the unit when logged in as staff
|
||||
And when I view the course as a student
|
||||
Then I do not see any content in the unit
|
||||
"""
|
||||
unit = self.go_to_unit_page("Unlocked Section", "Unlocked Subsection", "Unlocked Unit")
|
||||
checked = unit.toggle_staff_lock()
|
||||
self.assertTrue(checked)
|
||||
self.assertFalse(unit.currently_visible_to_students)
|
||||
self._verify_publish_title(unit, self.LOCKED_STATUS)
|
||||
unit.view_published_version()
|
||||
# Will initially be in staff view, locked component should be visible.
|
||||
self._verify_components_visible(['problem'])
|
||||
# Switch to student view and verify not visible
|
||||
self._verify_student_view_locked()
|
||||
|
||||
def test_initially_locked_not_visible_to_students(self):
|
||||
"""
|
||||
Scenario: A locked unit with release date in the past is not visible to students
|
||||
Given I have a published locked unit with release date in the past
|
||||
When I go to the unit page in Studio
|
||||
Then the unit does not have a warning that it is visible to students
|
||||
And it is marked as "RELEASED" with release date in the past visible
|
||||
And when I click on the View Live Button
|
||||
And when I view the course as a student
|
||||
Then I do not see any content in the unit
|
||||
"""
|
||||
unit = self.go_to_unit_page("Section With Locked Unit", "Subsection With Locked Unit", "Locked Unit")
|
||||
self._verify_publish_title(unit, self.LOCKED_STATUS)
|
||||
self.assertFalse(unit.currently_visible_to_students)
|
||||
self._verify_release_date_info(
|
||||
unit, self.RELEASE_TITLE_RELEASED,
|
||||
self.past_start_date_text + ' with Subsection "Subsection With Locked Unit"'
|
||||
)
|
||||
unit.view_published_version()
|
||||
self._verify_student_view_locked()
|
||||
|
||||
def test_unlocked_visible_to_all(self):
|
||||
"""
|
||||
Scenario: After unlocking a unit with release date in the past, it is visible to both students and staff
|
||||
Given I have a published unlocked unit with release date in the past
|
||||
When I go to the unit page in Studio
|
||||
And when I deselect "Hide from students"
|
||||
Then the unit does have a warning that it is visible to students
|
||||
And when I click on the View Live Button
|
||||
Then I see the content in the unit when logged in as staff
|
||||
And when I view the course as a student
|
||||
Then I see the content in the unit
|
||||
"""
|
||||
unit = self.go_to_unit_page("Section With Locked Unit", "Subsection With Locked Unit", "Locked Unit")
|
||||
checked = unit.toggle_staff_lock()
|
||||
self.assertFalse(checked)
|
||||
self._verify_publish_title(unit, self.PUBLISHED_STATUS)
|
||||
self.assertTrue(unit.currently_visible_to_students)
|
||||
unit.view_published_version()
|
||||
# Will initially be in staff view, components always visible.
|
||||
self._verify_components_visible(['discussion'])
|
||||
# Switch to student view and verify visible.
|
||||
self._verify_student_view_visible(['discussion'])
|
||||
|
||||
def _verify_student_view_locked(self):
|
||||
"""
|
||||
Verifies no component is visible when viewing as a student.
|
||||
"""
|
||||
StaffPage(self.browser).toggle_staff_view()
|
||||
self.assertEqual(0, self.courseware.num_xblock_components)
|
||||
|
||||
def _verify_student_view_visible(self, expected_components):
|
||||
"""
|
||||
Verifies expected components are visible when viewing as a student.
|
||||
"""
|
||||
StaffPage(self.browser).toggle_staff_view()
|
||||
self._verify_components_visible(expected_components)
|
||||
|
||||
def _verify_components_visible(self, expected_components):
|
||||
"""
|
||||
Verifies the expected components are visible (and there are no extras).
|
||||
"""
|
||||
self.assertEqual(len(expected_components), self.courseware.num_xblock_components)
|
||||
for index, component in enumerate(expected_components):
|
||||
self.assertEqual(component, self.courseware.xblock_component_type(index))
|
||||
|
||||
def _verify_release_date_info(self, unit, expected_title, expected_date):
|
||||
"""
|
||||
Verifies how the release date is displayed in the publishing sidebar.
|
||||
"""
|
||||
self.assertEqual(expected_title, unit.release_title)
|
||||
self.assertEqual(expected_date, unit.release_date)
|
||||
|
||||
def _verify_publish_title(self, unit, expected_title):
|
||||
"""
|
||||
Waits for the publish title to change to the expected value.
|
||||
"""
|
||||
def wait_for_title_change():
|
||||
return (unit.publish_title == expected_title, unit.publish_title)
|
||||
|
||||
Promise(wait_for_title_change, "Publish title incorrect. Found '" + unit.publish_title + "'").fulfill()
|
||||
|
||||
# TODO: need to work with Jay/Christine to get testing of "Preview" working.
|
||||
# def test_preview(self):
|
||||
|
||||
Reference in New Issue
Block a user