diff --git a/common/test/acceptance/tests/lms/test_lms_edxnotes.py b/common/test/acceptance/tests/lms/test_lms_edxnotes.py deleted file mode 100644 index c2d7001fa52d154919d02d05c92191e2b77aadb0..0000000000000000000000000000000000000000 --- a/common/test/acceptance/tests/lms/test_lms_edxnotes.py +++ /dev/null @@ -1,1504 +0,0 @@ -""" -Test LMS Notes -""" - - -import random -from datetime import datetime -from unittest import skip -from uuid import uuid4 - -from six.moves import range - -from common.test.acceptance.fixtures.course import CourseFixture, XBlockFixtureDesc -from common.test.acceptance.fixtures.edxnotes import EdxNotesFixture, Note, Range -from common.test.acceptance.pages.common.auto_auth import AutoAuthPage -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 EdxNotesPage, EdxNotesPageNoContent, EdxNotesUnitPage -from common.test.acceptance.tests.helpers import EventsTestMixin, UniqueCourseTest -from openedx.core.lib.tests import attr - - -class EdxNotesTestMixin(UniqueCourseTest): - """ - Creates a course with initial data and contains useful helper methods. - """ - def setUp(self): - """ - Initialize pages and install a course fixture. - """ - super(EdxNotesTestMixin, self).setUp() - self.courseware_page = CoursewarePage(self.browser, self.course_id) - 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) - - self.username = str(uuid4().hex)[:5] - self.email = "{}@email.com".format(self.username) - - self.selector = "annotate-id" - self.edxnotes_fixture = EdxNotesFixture() - self.course_fixture = CourseFixture( - self.course_info["org"], self.course_info["number"], - self.course_info["run"], self.course_info["display_name"] - ) - - self.course_fixture.add_advanced_settings({ - u"edxnotes": {u"value": True} - }) - - self.course_fixture.add_children( - XBlockFixtureDesc("chapter", "Test Section 1").add_children( - XBlockFixtureDesc("sequential", "Test Subsection 1").add_children( - XBlockFixtureDesc("vertical", "Test Unit 1").add_children( - XBlockFixtureDesc( - "html", - "Test HTML 1", - data=u""" - <p><span class="{}">Annotate this!</span></p> - <p>Annotate this</p> - """.format(self.selector) - ), - XBlockFixtureDesc( - "html", - "Test HTML 2", - data=u"""<p><span class="{}">Annotate this!</span></p>""".format(self.selector) - ), - ), - XBlockFixtureDesc("vertical", "Test Unit 2").add_children( - XBlockFixtureDesc( - "html", - "Test HTML 3", - data=u"""<p><span class="{}">Annotate this!</span></p>""".format(self.selector) - ), - ), - ), - XBlockFixtureDesc("sequential", "Test Subsection 2").add_children( - XBlockFixtureDesc("vertical", "Test Unit 3").add_children( - XBlockFixtureDesc( - "html", - "Test HTML 4", - data=u""" - <p><span class="{}">Annotate this!</span></p> - """.format(self.selector) - ), - ), - ), - ), - XBlockFixtureDesc("chapter", "Test Section 2").add_children( - XBlockFixtureDesc("sequential", "Test Subsection 3").add_children( - XBlockFixtureDesc("vertical", "Test Unit 4").add_children( - XBlockFixtureDesc( - "html", - "Test HTML 5", - data=u""" - <p><span class="{}">Annotate this!</span></p> - """.format(self.selector) - ), - XBlockFixtureDesc( - "html", - "Test HTML 6", - data=u"""<p><span class="{}">Annotate this!</span></p>""".format(self.selector) - ), - ), - ), - )).install() - - self.addCleanup(self.edxnotes_fixture.cleanup) - - AutoAuthPage(self.browser, username=self.username, email=self.email, course_id=self.course_id).visit() - - def _add_notes(self): - xblocks = self.course_fixture.get_nested_xblocks(category="html") - notes_list = [] - for index, xblock in enumerate(xblocks): - notes_list.append( - Note( - user=self.username, - usage_id=xblock.locator, - course_id=self.course_fixture._course_key, - ranges=[Range(startOffset=index, endOffset=index + 5)] - ) - ) - - self.edxnotes_fixture.create_notes(notes_list) - self.edxnotes_fixture.install() - - -@attr(shard=18) -class EdxNotesDefaultInteractionsTest(EdxNotesTestMixin): - """ - Tests for creation, editing, deleting annotations inside annotatable components in LMS. - """ - def create_notes(self, components, offset=0): - self.assertGreater(len(components), 0) - index = offset - for component in components: - for note in component.create_note(".{}".format(self.selector)): - note.text = u"TEST TEXT {}".format(index) - index += 1 - - def edit_notes(self, components, offset=0): - self.assertGreater(len(components), 0) - index = offset - for component in components: - self.assertGreater(len(component.notes), 0) - for note in component.edit_note(): - note.text = u"TEST TEXT {}".format(index) - index += 1 - - def edit_tags_in_notes(self, components, tags): - self.assertGreater(len(components), 0) - index = 0 - for component in components: - self.assertGreater(len(component.notes), 0) - for note in component.edit_note(): - note.tags = tags[index] - index += 1 - self.assertEqual(index, len(tags), "Number of supplied tags did not match components") - - def remove_notes(self, components): - self.assertGreater(len(components), 0) - for component in components: - self.assertGreater(len(component.notes), 0) - component.remove_note() - - def assert_notes_are_removed(self, components): - for component in components: - self.assertEqual(0, len(component.notes)) - - def assert_text_in_notes(self, notes): - actual = [note.text for note in notes] - expected = [u"TEST TEXT {}".format(i) for i in range(len(notes))] - self.assertEqual(expected, actual) - - def assert_tags_in_notes(self, notes, expected_tags): - actual = [note.tags for note in notes] - expected = [expected_tags[i] for i in range(len(notes))] - self.assertEqual(expected, actual) - - def test_can_create_notes(self): - """ - Scenario: User can create notes. - Given I have a course with 3 annotatable components - And I open the unit with 2 annotatable components - When I add 2 notes for the first component and 1 note for the second - Then I see that notes were correctly created - When I change sequential position to "2" - And I add note for the annotatable component on the page - Then I see that note was correctly created - When I refresh the page - Then I see that note was correctly stored - When I change sequential position to "1" - Then I see that notes were correctly stored on the page - """ - self.note_unit_page.visit() - - components = self.note_unit_page.components - self.create_notes(components) - self.assert_text_in_notes(self.note_unit_page.notes) - - self.courseware_page.go_to_sequential_position(2) - components = self.note_unit_page.components - self.create_notes(components) - - components = self.note_unit_page.refresh() - self.assert_text_in_notes(self.note_unit_page.notes) - - self.courseware_page.go_to_sequential_position(1) - components = self.note_unit_page.components - self.assert_text_in_notes(self.note_unit_page.notes) - - def test_can_edit_notes(self): - """ - Scenario: User can edit notes. - Given I have a course with 3 components with notes - And I open the unit with 2 annotatable components - When I change text in the notes - Then I see that notes were correctly changed - When I change sequential position to "2" - And I change the note on the page - Then I see that note was correctly changed - When I refresh the page - Then I see that edited note was correctly stored - When I change sequential position to "1" - Then I see that edited notes were correctly stored on the page - """ - self._add_notes() - self.note_unit_page.visit() - - components = self.note_unit_page.components - self.edit_notes(components) - self.assert_text_in_notes(self.note_unit_page.notes) - - self.courseware_page.go_to_sequential_position(2) - components = self.note_unit_page.components - self.edit_notes(components) - self.assert_text_in_notes(self.note_unit_page.notes) - - components = self.note_unit_page.refresh() - self.assert_text_in_notes(self.note_unit_page.notes) - - self.courseware_page.go_to_sequential_position(1) - components = self.note_unit_page.components - self.assert_text_in_notes(self.note_unit_page.notes) - - def test_can_create_note_with_tags(self): - """ - Scenario: a user of notes can define one with tags - Given I have a course with 3 annotatable components - And I open the unit with 2 annotatable components - When I add a note with tags for the first component - And I refresh the page - Then I see that note was correctly stored with its tags - """ - self.note_unit_page.visit() - - components = self.note_unit_page.components - for note in components[0].create_note(".{}".format(self.selector)): - note.tags = ["fruit", "tasty"] - - self.note_unit_page.refresh() - self.assertEqual(["fruit", "tasty"], self.note_unit_page.notes[0].tags) - - def test_can_change_tags(self): - """ - Scenario: a user of notes can edit tags on notes - Given I have a course with 3 components with notes - When I open the unit with 2 annotatable components - And I edit tags on the notes for the 2 annotatable components - Then I see that the tags were correctly changed - And I again edit tags on the notes for the 2 annotatable components - And I refresh the page - Then I see that the tags were correctly changed - """ - self._add_notes() - self.note_unit_page.visit() - - components = self.note_unit_page.components - self.edit_tags_in_notes(components, [["hard"], ["apple", "pear"]]) - self.assert_tags_in_notes(self.note_unit_page.notes, [["hard"], ["apple", "pear"]]) - - self.edit_tags_in_notes(components, [[], ["avocado"]]) - self.assert_tags_in_notes(self.note_unit_page.notes, [[], ["avocado"]]) - - self.note_unit_page.refresh() - self.assert_tags_in_notes(self.note_unit_page.notes, [[], ["avocado"]]) - - def test_sr_labels(self): - """ - Scenario: screen reader labels exist for text and tags fields - Given I have a course with 3 components with notes - When I open the unit with 2 annotatable components - And I open the editor for each note - Then the text and tags fields both have screen reader labels - """ - self._add_notes() - self.note_unit_page.visit() - - # First note is in the first annotatable component, will have field indexes 0 and 1. - for note in self.note_unit_page.components[0].edit_note(): - self.assertTrue(note.has_sr_label(0, 0, "Note")) - self.assertTrue(note.has_sr_label(1, 1, "Tags (space-separated)")) - - # Second note is in the second annotatable component, will have field indexes 2 and 3. - for note in self.note_unit_page.components[1].edit_note(): - self.assertTrue(note.has_sr_label(0, 2, "Note")) - self.assertTrue(note.has_sr_label(1, 3, "Tags (space-separated)")) - - -@attr(shard=4) -class EdxNotesPageTest(EventsTestMixin, EdxNotesTestMixin): - """ - Tests for Notes page. - """ - def _add_notes(self, notes_list): - self.edxnotes_fixture.create_notes(notes_list) - self.edxnotes_fixture.install() - - def _add_default_notes(self, tags=None, extra_notes=0): - """ - Creates 5 test notes by default & number of extra_notes will be created if specified. - If tags are not specified, will populate the notes with some test tag data. - If tags are specified, they will be used for each of the 3 notes that have tags. - """ - xblocks = self.course_fixture.get_nested_xblocks(category="html") - # pylint: disable=attribute-defined-outside-init - self.raw_note_list = [ - Note( - usage_id=xblocks[4].locator, - user=self.username, - course_id=self.course_fixture._course_key, - text="First note", - quote="Annotate this", - updated=datetime(2011, 1, 1, 1, 1, 1, 1).isoformat(), - ), - Note( - usage_id=xblocks[2].locator, - user=self.username, - course_id=self.course_fixture._course_key, - text="", - quote=u"Annotate this", - updated=datetime(2012, 1, 1, 1, 1, 1, 1).isoformat(), - tags=["Review", "cool"] if tags is None else tags - ), - Note( - usage_id=xblocks[0].locator, - user=self.username, - course_id=self.course_fixture._course_key, - text="Third note", - quote="Annotate this", - updated=datetime(2013, 1, 1, 1, 1, 1, 1).isoformat(), - ranges=[Range(startOffset=0, endOffset=18)], - tags=["Cool", "TODO"] if tags is None else tags - ), - Note( - usage_id=xblocks[3].locator, - user=self.username, - course_id=self.course_fixture._course_key, - text="Fourth note", - quote="", - updated=datetime(2014, 1, 1, 1, 1, 1, 1).isoformat(), - tags=["review"] if tags is None else tags - ), - Note( - usage_id=xblocks[1].locator, - user=self.username, - course_id=self.course_fixture._course_key, - text="Fifth note", - quote="Annotate this", - updated=datetime(2015, 1, 1, 1, 1, 1, 1).isoformat() - ), - ] - if extra_notes > 0: - for __ in range(extra_notes): - self.raw_note_list.append( - Note( - usage_id=xblocks[random.choice([0, 1, 2, 3, 4, 5])].locator, - user=self.username, - course_id=self.course_fixture._course_key, # pylint: disable=protected-access - text="Fourth note", - quote="", - updated=datetime(2014, 1, 1, 1, 1, 1, 1).isoformat(), - tags=["review"] if tags is None else tags - ) - ) - self._add_notes(self.raw_note_list) - - def assertNoteContent(self, item, text=None, quote=None, unit_name=None, time_updated=None, tags=None): - """ Verifies the expected properties of the note. """ - self.assertEqual(text, item.text) - if item.quote is not None: - self.assertIn(quote, item.quote) - else: - self.assertIsNone(quote) - self.assertEqual(unit_name, item.unit_name) - self.assertEqual(time_updated, item.time_updated) - self.assertEqual(tags, item.tags) - - def assertChapterContent(self, item, title=None, subtitles=None): - """ - Verifies the expected title and subsection titles (subtitles) for the given chapter. - """ - self.assertEqual(item.title, title) - self.assertEqual(item.subtitles, subtitles) - - def assertGroupContent(self, item, title=None, notes=None): - """ - Verifies the expected title and child notes for the given group. - """ - self.assertEqual(item.title, title) - self.assertEqual(item.notes, notes) - - def assert_viewed_event(self, view=None): - """ - Verifies that the correct view event was captured for the Notes page. - """ - # There will always be an initial event for "Recent Activity" because that is the default view. - # If view is something besides "Recent Activity", expect 2 events, with the second one being - # the view name passed in. - if view == 'Recent Activity': - view = None - actual_events = self.wait_for_events( - event_filter={'event_type': 'edx.course.student_notes.notes_page_viewed'}, - number_of_matches=1 if view is None else 2 - ) - expected_events = [{'event': {'view': 'Recent Activity'}}] - if view: - expected_events.append({'event': {'view': view}}) - self.assert_events_match(expected_events, actual_events) - - def assert_unit_link_event(self, usage_id, view): - """ - Verifies that the correct used_unit_link event was captured for the Notes page. - """ - actual_events = self.wait_for_events( - event_filter={'event_type': 'edx.course.student_notes.used_unit_link'}, - number_of_matches=1 - ) - expected_events = [ - {'event': {'component_usage_id': usage_id, 'view': view}} - ] - self.assert_events_match(expected_events, actual_events) - - def assert_search_event(self, search_string, number_of_results): - """ - Verifies that the correct searched event was captured for the Notes page. - """ - actual_events = self.wait_for_events( - event_filter={'event_type': 'edx.course.student_notes.searched'}, - number_of_matches=1 - ) - expected_events = [ - {'event': {'search_string': search_string, 'number_of_results': number_of_results}} - ] - self.assert_events_match(expected_events, actual_events) - - def _verify_pagination_info( - self, - notes_count_on_current_page, - header_text, - previous_button_enabled, - next_button_enabled, - current_page_number, - total_pages - ): - """ - Verify pagination info - """ - self.assertEqual(self.notes_page.count(), notes_count_on_current_page) - self.assertEqual(self.notes_page.get_pagination_header_text(), header_text) - - if total_pages > 1: - self.assertEqual(self.notes_page.footer_visible, True) - self.assertEqual(self.notes_page.is_previous_page_button_enabled(), previous_button_enabled) - self.assertEqual(self.notes_page.is_next_page_button_enabled(), next_button_enabled) - self.assertEqual(self.notes_page.get_current_page_number(), current_page_number) - self.assertEqual(self.notes_page.get_total_pages, total_pages) - else: - self.assertEqual(self.notes_page.footer_visible, False) - - def search_and_verify(self): - """ - Add, search and verify notes. - """ - self._add_default_notes(extra_notes=22) - self.notes_page.visit() - # Run the search - self.notes_page.search("note") - # No error message appears - self.assertFalse(self.notes_page.is_error_visible) - self.assertIn(u"Search Results", self.notes_page.tabs) - - self.assertEqual(self.notes_page.get_total_pages, 2) - - def test_no_content(self): - """ - Scenario: User can see `No content` message. - Given I have a course without notes - When I open Notes page - Then I see only "You do not have any notes within the course." message - """ - notes_page_empty = EdxNotesPageNoContent(self.browser, self.course_id) - notes_page_empty.visit() - self.assertIn( - "You have not made any notes in this course yet. Other students in this course are using notes to:", - notes_page_empty.no_content_text) - - def test_notes_works_correctly_with_xss(self): - """ - Scenario: Note text & tags should be HTML and JS escaped - Given I am enrolled in a course with notes enabled - When I visit the Notes page, with a Notes text and tag containing HTML characters like < and > - Then the text and tags appear as expected due to having been properly escaped - """ - xblocks = self.course_fixture.get_nested_xblocks(category="html") - self._add_notes([ - Note( - usage_id=xblocks[0].locator, - user=self.username, - course_id=self.course_fixture._course_key, # pylint: disable=protected-access - text='<script>alert("XSS")</script>', - quote="quote", - updated=datetime(2014, 1, 1, 1, 1, 1, 1).isoformat(), - tags=['<script>alert("XSS")</script>'] - ), - Note( - usage_id=xblocks[1].locator, - user=self.username, - course_id=self.course_fixture._course_key, # pylint: disable=protected-access - text='<b>bold</b>', - quote="quote", - updated=datetime(2014, 2, 1, 1, 1, 1, 1).isoformat(), - tags=['<i>bold</i>'] - ) - ]) - self.notes_page.visit() - - notes = self.notes_page.notes - self.assertEqual(len(notes), 2) - - self.assertNoteContent( - notes[0], - quote=u"quote", - text='<b>bold</b>', - unit_name="Test Unit 1", - time_updated="Feb 01, 2014 at 01:01 UTC", - tags=['<i>bold</i>'] - ) - - self.assertNoteContent( - notes[1], - quote=u"quote", - text='<script>alert("XSS")</script>', - unit_name="Test Unit 1", - time_updated="Jan 01, 2014 at 01:01 UTC", - tags=['<script>alert("XSS")</script>'] - ) - - def test_recent_activity_view(self): - """ - Scenario: User can view all notes by recent activity. - Given I have a course with 5 notes - When I open Notes page - Then I see 5 notes sorted by the updated date - And I see correct content in the notes - And an event has fired indicating that the Recent Activity view was selected - """ - self._add_default_notes() - self.notes_page.visit() - notes = self.notes_page.notes - self.assertEqual(len(notes), 5) - - self.assertNoteContent( - notes[0], - quote=u"Annotate this", - text=u"Fifth note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2015 at 01:01 UTC" - ) - - self.assertNoteContent( - notes[1], - text=u"Fourth note", - unit_name="Test Unit 3", - time_updated="Jan 01, 2014 at 01:01 UTC", - tags=["review"] - ) - - self.assertNoteContent( - notes[2], - quote="Annotate this", - text=u"Third note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2013 at 01:01 UTC", - tags=["Cool", "TODO"] - ) - - self.assertNoteContent( - notes[3], - quote=u"Annotate this", - unit_name="Test Unit 2", - time_updated="Jan 01, 2012 at 01:01 UTC", - tags=["Review", "cool"] - ) - - self.assertNoteContent( - notes[4], - quote=u"Annotate this", - text=u"First note", - unit_name="Test Unit 4", - time_updated="Jan 01, 2011 at 01:01 UTC" - ) - - self.assert_viewed_event() - - def test_course_structure_view(self): - """ - Scenario: User can view all notes by location in Course. - Given I have a course with 5 notes - When I open Notes page - And I switch to "Location in Course" view - Then I see 2 groups, 3 sections and 5 notes - And I see correct content in the notes and groups - And an event has fired indicating that the Location in Course view was selected - """ - self._add_default_notes() - self.notes_page.visit().switch_to_tab("structure") - - notes = self.notes_page.notes - groups = self.notes_page.chapter_groups - sections = self.notes_page.subsection_groups - self.assertEqual(len(notes), 5) - self.assertEqual(len(groups), 2) - self.assertEqual(len(sections), 3) - - self.assertChapterContent( - groups[0], - title=u"Test Section 1", - subtitles=[u"Test Subsection 1", u"Test Subsection 2"] - ) - - self.assertGroupContent( - sections[0], - title=u"Test Subsection 1", - notes=[u"Fifth note", u"Third note", None] - ) - - self.assertNoteContent( - notes[0], - quote=u"Annotate this", - text=u"Fifth note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2015 at 01:01 UTC" - ) - - self.assertNoteContent( - notes[1], - quote=u"Annotate this", - text=u"Third note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2013 at 01:01 UTC", - tags=["Cool", "TODO"] - ) - - self.assertNoteContent( - notes[2], - quote=u"Annotate this", - unit_name="Test Unit 2", - time_updated="Jan 01, 2012 at 01:01 UTC", - tags=["Review", "cool"] - ) - - self.assertGroupContent( - sections[1], - title=u"Test Subsection 2", - notes=[u"Fourth note"] - ) - - self.assertNoteContent( - notes[3], - text=u"Fourth note", - unit_name="Test Unit 3", - time_updated="Jan 01, 2014 at 01:01 UTC", - tags=["review"] - ) - - self.assertChapterContent( - groups[1], - title=u"Test Section 2", - subtitles=[u"Test Subsection 3"], - ) - - self.assertGroupContent( - sections[2], - title=u"Test Subsection 3", - notes=[u"First note"] - ) - - self.assertNoteContent( - notes[4], - quote=u"Annotate this", - text=u"First note", - unit_name="Test Unit 4", - time_updated="Jan 01, 2011 at 01:01 UTC" - ) - - self.assert_viewed_event('Location in Course') - - def test_tags_view(self): - """ - Scenario: User can view all notes by associated tags. - Given I have a course with 5 notes and I am viewing the Notes page - When I switch to the "Tags" view - Then I see 4 tag groups - And I see correct content in the notes and groups - And an event has fired indicating that the Tags view was selected - """ - self._add_default_notes() - self.notes_page.visit().switch_to_tab("tags") - - notes = self.notes_page.notes - groups = self.notes_page.tag_groups - self.assertEqual(len(notes), 7) - self.assertEqual(len(groups), 4) - - # Tag group "cool" - self.assertGroupContent( - groups[0], - title=u"cool (2)", - notes=[u"Third note", None] - ) - - self.assertNoteContent( - notes[0], - quote=u"Annotate this", - text=u"Third note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2013 at 01:01 UTC", - tags=["Cool", "TODO"] - ) - - self.assertNoteContent( - notes[1], - quote=u"Annotate this", - unit_name="Test Unit 2", - time_updated="Jan 01, 2012 at 01:01 UTC", - tags=["Review", "cool"] - ) - - # Tag group "review" - self.assertGroupContent( - groups[1], - title=u"review (2)", - notes=[u"Fourth note", None] - ) - - self.assertNoteContent( - notes[2], - text=u"Fourth note", - unit_name="Test Unit 3", - time_updated="Jan 01, 2014 at 01:01 UTC", - tags=["review"] - ) - - self.assertNoteContent( - notes[3], - quote=u"Annotate this", - unit_name="Test Unit 2", - time_updated="Jan 01, 2012 at 01:01 UTC", - tags=["Review", "cool"] - ) - - # Tag group "todo" - self.assertGroupContent( - groups[2], - title=u"todo (1)", - notes=["Third note"] - ) - - self.assertNoteContent( - notes[4], - quote=u"Annotate this", - text=u"Third note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2013 at 01:01 UTC", - tags=["Cool", "TODO"] - ) - - # Notes with no tags - self.assertGroupContent( - groups[3], - title=u"[no tags] (2)", - notes=["Fifth note", "First note"] - ) - - self.assertNoteContent( - notes[5], - quote=u"Annotate this", - text=u"Fifth note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2015 at 01:01 UTC" - ) - - self.assertNoteContent( - notes[6], - quote=u"Annotate this", - text=u"First note", - unit_name="Test Unit 4", - time_updated="Jan 01, 2011 at 01:01 UTC" - ) - - self.assert_viewed_event('Tags') - - def test_easy_access_from_notes_page(self): - """ - Scenario: Ensure that the link to the Unit works correctly. - Given I have a course with 5 notes - When I open Notes page - And I click on the first unit link - Then I see correct text on the unit page and a unit link event was fired - When go back to the Notes page - And I switch to "Location in Course" view - And I click on the second unit link - Then I see correct text on the unit page and a unit link event was fired - When go back to the Notes page - And I switch to "Tags" view - And I click on the first unit link - Then I see correct text on the unit page and a unit link event was fired - When go back to the Notes page - And I run the search with "Fifth" query - And I click on the first unit link - Then I see correct text on the unit page and a unit link event was fired - """ - def assert_page(note, usage_id, view): - """ Verify that clicking on the unit link works properly. """ - quote = note.quote - note.go_to_unit() - self.courseware_page.wait_for_page() - self.assertIn(quote, self.courseware_page.xblock_component_html_content()) - self.assert_unit_link_event(usage_id, view) - self.reset_event_tracking() - - self._add_default_notes() - self.notes_page.visit() - note = self.notes_page.notes[0] - assert_page(note, self.raw_note_list[4]['usage_id'], "Recent Activity") - - self.notes_page.visit() - self.notes_page.switch_to_tab("structure") - note = self.notes_page.notes[1] - assert_page(note, self.raw_note_list[2]['usage_id'], "Location in Course") - - self.notes_page.visit() - self.notes_page.switch_to_tab("tags") - note = self.notes_page.notes[0] - assert_page(note, self.raw_note_list[2]['usage_id'], "Tags") - - self.notes_page.visit() - self.notes_page.search("Fifth") - self.notes_page.wait_for_ajax() - note = self.notes_page.notes[0] - assert_page(note, self.raw_note_list[4]['usage_id'], "Search Results") - - def test_search_behaves_correctly(self): - """ - Scenario: Searching behaves correctly. - Given I have a course with 5 notes - When I open Notes page - When I run the search with " " query - Then I see the following error message "Please enter a term in the search field." - And I do not see "Search Results" tab - When I run the search with "note" query - Then I see that error message disappears - And I see that "Search Results" tab appears with 4 notes found - And an event has fired indicating that the Search Results view was selected - And an event has fired recording the search that was performed - """ - self._add_default_notes() - self.notes_page.visit() - # Run the search with whitespaces only - self.notes_page.search(" ") - # Displays error message - self.assertTrue(self.notes_page.is_error_visible) - self.assertEqual(self.notes_page.error_text, u"Please enter a term in the search field.") - # Search results tab does not appear - self.assertNotIn(u"Search Results", self.notes_page.tabs) - # Run the search with correct query - self.notes_page.search("note") - # Error message disappears - self.assertFalse(self.notes_page.is_error_visible) - self.assertIn(u"Search Results", self.notes_page.tabs) - notes = self.notes_page.notes - self.assertEqual(len(notes), 4) - - self.assertNoteContent( - notes[0], - quote=u"Annotate this", - text=u"Fifth note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2015 at 01:01 UTC" - ) - - self.assertNoteContent( - notes[1], - text=u"Fourth note", - unit_name="Test Unit 3", - time_updated="Jan 01, 2014 at 01:01 UTC", - tags=["review"] - ) - - self.assertNoteContent( - notes[2], - quote="Annotate this", - text=u"Third note", - unit_name="Test Unit 1", - time_updated="Jan 01, 2013 at 01:01 UTC", - tags=["Cool", "TODO"] - ) - - self.assertNoteContent( - notes[3], - quote=u"Annotate this", - text=u"First note", - unit_name="Test Unit 4", - time_updated="Jan 01, 2011 at 01:01 UTC" - ) - - self.assert_viewed_event('Search Results') - self.assert_search_event('note', 4) - - @skip("scroll to tag functionality is disabled") - def test_scroll_to_tag_recent_activity(self): - """ - Scenario: Can scroll to a tag group from the Recent Activity view (default view) - Given I have a course with 5 notes and I open the Notes page - When I click on a tag associated with a note - Then the Tags view tab gets focus and I scroll to the section of notes associated with that tag - """ - self._add_default_notes(["apple", "banana", "kiwi", "pear", "pumpkin", "squash", "zucchini"]) - self.notes_page.visit() - self._scroll_to_tag_and_verify("pear", 3) - - @skip("scroll to tag functionality is disabled") - def test_scroll_to_tag_course_structure(self): - """ - Scenario: Can scroll to a tag group from the Course Structure view - Given I have a course with 5 notes and I open the Notes page and select the Course Structure view - When I click on a tag associated with a note - Then the Tags view tab gets focus and I scroll to the section of notes associated with that tag - """ - self._add_default_notes(["apple", "banana", "kiwi", "pear", "pumpkin", "squash", "zucchini"]) - self.notes_page.visit().switch_to_tab("structure") - self._scroll_to_tag_and_verify("squash", 5) - - @skip("scroll to tag functionality is disabled") - def test_scroll_to_tag_search(self): - """ - Scenario: Can scroll to a tag group from the Search Results view - Given I have a course with 5 notes and I open the Notes page and perform a search - Then the Search view tab opens and gets focus - And when I click on a tag associated with a note - Then the Tags view tab gets focus and I scroll to the section of notes associated with that tag - """ - self._add_default_notes(["apple", "banana", "kiwi", "pear", "pumpkin", "squash", "zucchini"]) - self.notes_page.visit().search("note") - self._scroll_to_tag_and_verify("pumpkin", 4) - - @skip("scroll to tag functionality is disabled") - def test_scroll_to_tag_from_tag_view(self): - """ - Scenario: Can scroll to a tag group from the Tags view - Given I have a course with 5 notes and I open the Notes page and select the Tag view - When I click on a tag associated with a note - Then I scroll to the section of notes associated with that tag - """ - self._add_default_notes(["apple", "banana", "kiwi", "pear", "pumpkin", "squash", "zucchini"]) - self.notes_page.visit().switch_to_tab("tags") - self._scroll_to_tag_and_verify("kiwi", 2) - - def _scroll_to_tag_and_verify(self, tag_name, group_index): - """ Helper method for all scroll to tag tests """ - self.notes_page.notes[1].go_to_tag(tag_name) - - # Because all the notes (with tags) have the same tags, they will end up ordered alphabetically. - pear_group = self.notes_page.tag_groups[group_index] - self.assertEqual(tag_name + " (3)", pear_group.title) - self.assertTrue(pear_group.scrolled_to_top(group_index)) - - def test_tabs_behaves_correctly(self): - """ - Scenario: Tabs behaves correctly. - Given I have a course with 5 notes - When I open Notes page - Then I see only "Recent Activity", "Location in Course", and "Tags" tabs - When I run the search with "note" query - And I see that "Search Results" tab appears with 4 notes found - Then I switch to "Recent Activity" tab - And I see all 5 notes - Then I switch to "Location in Course" tab - And I see all 2 groups and 5 notes - When I switch back to "Search Results" tab - Then I can still see 4 notes found - When I close "Search Results" tab - Then I see that "Recent Activity" tab becomes active - And "Search Results" tab disappears - And I see all 5 notes - """ - self._add_default_notes() - self.notes_page.visit() - - # We're on Recent Activity tab. - self.assertEqual(len(self.notes_page.tabs), 3) - self.assertEqual([u"Recent Activity", u"Location in Course", u"Tags"], self.notes_page.tabs) - self.notes_page.search("note") - # We're on Search Results tab - self.assertEqual(len(self.notes_page.tabs), 4) - self.assertIn(u"Search Results", self.notes_page.tabs) - self.assertEqual(len(self.notes_page.notes), 4) - # We can switch on Recent Activity tab and back. - self.notes_page.switch_to_tab("recent") - self.assertEqual(len(self.notes_page.notes), 5) - self.notes_page.switch_to_tab("structure") - self.assertEqual(len(self.notes_page.chapter_groups), 2) - self.assertEqual(len(self.notes_page.notes), 5) - self.notes_page.switch_to_tab("search") - self.assertEqual(len(self.notes_page.notes), 4) - # Can close search results page - self.notes_page.close_tab() - self.assertEqual(len(self.notes_page.tabs), 3) - self.assertNotIn(u"Search Results", self.notes_page.tabs) - self.assertEqual(len(self.notes_page.notes), 5) - - def test_open_note_when_accessed_from_notes_page(self): - """ - Scenario: Ensure that the link to the Unit opens a note only once. - Given I have a course with 2 sequentials that contain respectively one note and two notes - When I open Notes page - And I click on the first unit link - Then I see the note opened on the unit page - When I switch to the second sequential - I do not see any note opened - When I switch back to first sequential - I do not see any note opened - """ - xblocks = self.course_fixture.get_nested_xblocks(category="html") - self._add_notes([ - Note( - usage_id=xblocks[1].locator, - user=self.username, - course_id=self.course_fixture._course_key, - text="Third note", - quote="Annotate this", - updated=datetime(2012, 1, 1, 1, 1, 1, 1).isoformat(), - ranges=[Range(startOffset=0, endOffset=14)], - ), - Note( - usage_id=xblocks[2].locator, - user=self.username, - course_id=self.course_fixture._course_key, - text="Second note", - quote="Annotate this", - updated=datetime(2013, 1, 1, 1, 1, 1, 1).isoformat(), - ranges=[Range(startOffset=0, endOffset=14)], - ), - Note( - usage_id=xblocks[0].locator, - user=self.username, - course_id=self.course_fixture._course_key, - text="First note", - quote="Annotate this", - updated=datetime(2014, 1, 1, 1, 1, 1, 1).isoformat(), - ranges=[Range(startOffset=0, endOffset=14)], - ), - ]) - self.notes_page.visit() - item = self.notes_page.notes[0] - item.go_to_unit() - self.courseware_page.wait_for_page() - note = self.note_unit_page.notes[0] - self.assertTrue(note.is_visible) - note = self.note_unit_page.notes[1] - self.assertFalse(note.is_visible) - self.courseware_page.go_to_sequential_position(2) - note = self.note_unit_page.notes[0] - self.assertFalse(note.is_visible) - self.courseware_page.go_to_sequential_position(1) - self.courseware_page.wait_for_ajax() - note = self.note_unit_page.notes[0] - self.assertFalse(note.is_visible) - - def test_page_size_limit(self): - """ - Scenario: Verify that we can't get notes more than default page size. - - Given that I am a registered user - And I have a course with 11 notes - When I open Notes page - Then I can see notes list contains 10 items - And I should see paging header and footer with correct data - And I should see disabled previous button - And I should also see enabled next button - """ - self._add_default_notes(extra_notes=21) - self.notes_page.visit() - - self._verify_pagination_info( - notes_count_on_current_page=25, - header_text='Showing 1-25 out of 26 total', - previous_button_enabled=False, - next_button_enabled=True, - current_page_number=1, - total_pages=2 - ) - - def test_pagination_with_single_page(self): - """ - Scenario: Notes list pagination works as expected for single page - Given that I am a registered user - And I have a course with 5 notes - When I open Notes page - Then I can see notes list contains 5 items - And I should see paging header and footer with correct data - And I should see disabled previous and next buttons - """ - self._add_default_notes() - self.notes_page.visit() - self._verify_pagination_info( - notes_count_on_current_page=5, - header_text='Showing 1-5 out of 5 total', - previous_button_enabled=False, - next_button_enabled=False, - current_page_number=1, - total_pages=1 - ) - - def test_next_and_previous_page_button(self): - """ - Scenario: Next & Previous buttons are working as expected for notes list pagination - - Given that I am a registered user - And I have a course with 26 notes - When I open Notes page - Then I can see notes list contains 25 items - And I should see paging header and footer with correct data - And I should see disabled previous button - And I should see enabled next button - - When I click on next page button in footer - Then I should be navigated to second page - And I should see a list with 1 item - And I should see paging header and footer with correct info - And I should see enabled previous button - And I should also see disabled next button - - When I click on previous page button in footer - Then I should be navigated to first page - And I should see a list with 25 items - And I should see paging header and footer with correct info - And I should see disabled previous button - And I should also see enabled next button - """ - self._add_default_notes(extra_notes=21) - self.notes_page.visit() - - self._verify_pagination_info( - notes_count_on_current_page=25, - header_text='Showing 1-25 out of 26 total', - previous_button_enabled=False, - next_button_enabled=True, - current_page_number=1, - total_pages=2 - ) - - self.notes_page.press_next_page_button() - self._verify_pagination_info( - notes_count_on_current_page=1, - header_text='Showing 26-26 out of 26 total', - previous_button_enabled=True, - next_button_enabled=False, - current_page_number=2, - total_pages=2 - ) - self.notes_page.press_previous_page_button() - self._verify_pagination_info( - notes_count_on_current_page=25, - header_text='Showing 1-25 out of 26 total', - previous_button_enabled=False, - next_button_enabled=True, - current_page_number=1, - total_pages=2 - ) - - def test_pagination_with_valid_and_invalid_page_number(self): - """ - Scenario: Notes list pagination works as expected for valid & invalid page number - - Given that I am a registered user - And I have a course with 26 notes - When I open Notes page - Then I can see notes list contains 25 items - And I should see paging header and footer with correct data - And I should see total page value is 2 - When I enter 2 in the page number input - Then I should be navigated to page 2 - - When I enter 3 in the page number input - Then I should not be navigated away from page 2 - """ - self._add_default_notes(extra_notes=21) - self.notes_page.visit() - - self.assertEqual(self.notes_page.get_total_pages, 2) - - # test pagination with valid page number - self.notes_page.go_to_page(2) - self._verify_pagination_info( - notes_count_on_current_page=1, - header_text='Showing 26-26 out of 26 total', - previous_button_enabled=True, - next_button_enabled=False, - current_page_number=2, - total_pages=2 - ) - - # test pagination with invalid page number - self.notes_page.go_to_page(3) - self._verify_pagination_info( - notes_count_on_current_page=1, - header_text='Showing 26-26 out of 26 total', - previous_button_enabled=True, - next_button_enabled=False, - current_page_number=2, - total_pages=2 - ) - - def test_search_behaves_correctly_with_pagination(self): - """ - Scenario: Searching behaves correctly with pagination. - - Given that I am a registered user - And I have a course with 27 notes - When I open Notes page - Then I can see notes list with 25 items - And I should see paging header and footer with correct data - And previous button is disabled - And next button is enabled - When I run the search with "note" query - Then I see no error message - And I see that "Search Results" tab appears with 26 notes found - And an event has fired indicating that the Search Results view was selected - And an event has fired recording the search that was performed - """ - self.search_and_verify() - self._verify_pagination_info( - notes_count_on_current_page=25, - header_text='Showing 1-25 out of 26 total', - previous_button_enabled=False, - next_button_enabled=True, - current_page_number=1, - total_pages=2 - ) - - self.assert_viewed_event('Search Results') - self.assert_search_event('note', 26) - - def test_search_with_next_and_prev_page_button(self): - """ - Scenario: Next & Previous buttons are working as expected for search - - Given that I am a registered user - And I have a course with 27 notes - When I open Notes page - Then I can see notes list with 25 items - And I should see paging header and footer with correct data - And previous button is disabled - And next button is enabled - - When I run the search with "note" query - Then I see that "Search Results" tab appears with 26 notes found - And an event has fired indicating that the Search Results view was selected - And an event has fired recording the search that was performed - - When I click on next page button in footer - Then I should be navigated to second page - And I should see a list with 1 item - And I should see paging header and footer with correct info - And I should see enabled previous button - And I should also see disabled next button - - When I click on previous page button in footer - Then I should be navigated to first page - And I should see a list with 25 items - And I should see paging header and footer with correct info - And I should see disabled previous button - And I should also see enabled next button - """ - self.search_and_verify() - - self._verify_pagination_info( - notes_count_on_current_page=25, - header_text='Showing 1-25 out of 26 total', - previous_button_enabled=False, - next_button_enabled=True, - current_page_number=1, - total_pages=2 - ) - - self.assert_viewed_event('Search Results') - self.assert_search_event('note', 26) - - self.notes_page.press_next_page_button() - self._verify_pagination_info( - notes_count_on_current_page=1, - header_text='Showing 26-26 out of 26 total', - previous_button_enabled=True, - next_button_enabled=False, - current_page_number=2, - total_pages=2 - ) - self.notes_page.press_previous_page_button() - self._verify_pagination_info( - notes_count_on_current_page=25, - header_text='Showing 1-25 out of 26 total', - previous_button_enabled=False, - next_button_enabled=True, - current_page_number=1, - total_pages=2 - ) - - def test_search_with_valid_and_invalid_page_number(self): - """ - Scenario: Notes list pagination works as expected for valid & invalid page number - - Given that I am a registered user - And I have a course with 27 notes - When I open Notes page - Then I can see notes list contains 25 items - And I should see paging header and footer with correct data - And I should see total page value is 2 - - When I run the search with "note" query - Then I see that "Search Results" tab appears with 26 notes found - And an event has fired indicating that the Search Results view was selected - And an event has fired recording the search that was performed - - When I enter 2 in the page number input - Then I should be navigated to page 2 - - When I enter 3 in the page number input - Then I should not be navigated away from page 2 - """ - self.search_and_verify() - - # test pagination with valid page number - self.notes_page.go_to_page(2) - self._verify_pagination_info( - notes_count_on_current_page=1, - header_text='Showing 26-26 out of 26 total', - previous_button_enabled=True, - next_button_enabled=False, - current_page_number=2, - total_pages=2 - ) - - # test pagination with invalid page number - self.notes_page.go_to_page(3) - self._verify_pagination_info( - notes_count_on_current_page=1, - header_text='Showing 26-26 out of 26 total', - previous_button_enabled=True, - next_button_enabled=False, - current_page_number=2, - total_pages=2 - ) - - -@attr(shard=4) -class EdxNotesToggleSingleNoteTest(EdxNotesTestMixin): - """ - Tests for toggling single annotation. - """ - - def setUp(self): - super(EdxNotesToggleSingleNoteTest, self).setUp() - self._add_notes() - self.note_unit_page.visit() - - def test_can_toggle_by_clicking_on_highlighted_text(self): - """ - Scenario: User can toggle a single note by clicking on highlighted text. - Given I have a course with components with notes - When I click on highlighted text - And I move mouse out of the note - Then I see that the note is still shown - When I click outside the note - Then I see the the note is closed - """ - note = self.note_unit_page.notes[0] - - note.click_on_highlight() - self.note_unit_page.move_mouse_to("body") - self.assertTrue(note.is_visible) - self.note_unit_page.click("body") - self.assertFalse(note.is_visible) - - def test_can_toggle_by_clicking_on_the_note(self): - """ - Scenario: User can toggle a single note by clicking on the note. - Given I have a course with components with notes - When I click on the note - And I move mouse out of the note - Then I see that the note is still shown - When I click outside the note - Then I see the the note is closed - """ - note = self.note_unit_page.notes[0] - - note.show().click_on_viewer() - self.note_unit_page.move_mouse_to("body") - self.assertTrue(note.is_visible) - self.note_unit_page.click("body") - self.assertFalse(note.is_visible) - - def test_interaction_between_notes(self): - """ - Scenario: Interactions between notes works well. - Given I have a course with components with notes - When I click on highlighted text in the first component - And I move mouse out of the note - Then I see that the note is still shown - When I click on highlighted text in the second component - Then I see that the new note is shown - """ - note_1 = self.note_unit_page.notes[0] - note_2 = self.note_unit_page.notes[1] - - note_1.click_on_highlight() - self.note_unit_page.move_mouse_to("body") - self.assertTrue(note_1.is_visible) - - note_2.click_on_highlight() - self.assertFalse(note_1.is_visible) - self.assertTrue(note_2.is_visible) - - -@attr(shard=4) -class EdxNotesToggleNotesTest(EdxNotesTestMixin): - """ - Tests for toggling visibility of all notes. - """ - - def setUp(self): - super(EdxNotesToggleNotesTest, self).setUp() - self._add_notes() - self.note_unit_page.visit() - - def test_can_disable_all_notes(self): - """ - Scenario: User can disable all notes. - Given I have a course with components with notes - And I open the unit with annotatable components - When I click on "Show notes" checkbox - Then I do not see any notes on the sequential position - When I change sequential position to "2" - Then I still do not see any notes on the sequential position - When I go to "Test Subsection 2" subsection - Then I do not see any notes on the subsection - """ - # Disable all notes - self.note_unit_page.toggle_visibility() - self.note_unit_page.wait_for(lambda: len(self.note_unit_page.notes) == 0, u"Notes are hidden") - self.courseware_page.go_to_sequential_position(2) - self.note_unit_page.wait_for(lambda: len(self.note_unit_page.notes) == 0, u"Notes are hidden") - self.course_home_page.visit() - self.course_home_page.outline.go_to_section(u"Test Section 1", u"Test Subsection 2") - self.note_unit_page.wait_for(lambda: len(self.note_unit_page.notes) == 0, u"Notes are hidden") - - def test_can_reenable_all_notes(self): - """ - Scenario: User can toggle notes visibility. - Given I have a course with components with notes - And I open the unit with annotatable components - When I click on "Show notes" checkbox - Then I do not see any notes on the sequential position - When I click on "Show notes" checkbox again - Then I see that all notes appear - When I change sequential position to "2" - Then I still can see all notes on the sequential position - When I go to "Test Subsection 2" subsection - Then I can see all notes on the subsection - """ - # Disable notes - self.note_unit_page.toggle_visibility() - self.assertEqual(len(self.note_unit_page.notes), 0) - # Enable notes to make sure that I can enable notes without refreshing - # the page. - self.note_unit_page.toggle_visibility() - self.note_unit_page.wait_for(lambda: len(self.note_unit_page.notes) > 0, u"Notes are visible") - self.courseware_page.go_to_sequential_position(2) - self.note_unit_page.wait_for(lambda: len(self.note_unit_page.notes) > 0, u"Notes are visible") - self.course_home_page.visit() - self.course_home_page.outline.go_to_section(u"Test Section 1", u"Test Subsection 2") - self.note_unit_page.wait_for(lambda: len(self.note_unit_page.notes) > 0, u"Notes are visible")