Fix inline discussion Bok Choy tests
This commit is contained in:
committed by
Brian Jacobel
parent
82d05c6eb7
commit
95af18430c
@@ -51,6 +51,7 @@ class Thread(ContentFactory):
|
||||
group_id = None
|
||||
pinned = False
|
||||
read = False
|
||||
context = "course"
|
||||
|
||||
|
||||
class Comment(ContentFactory):
|
||||
|
||||
@@ -15,6 +15,54 @@ class DiscussionPageMixin(object):
|
||||
def is_ajax_finished(self):
|
||||
return self.browser.execute_script("return jQuery.active") == 0
|
||||
|
||||
def find_visible_element(self, selector):
|
||||
"""
|
||||
Finds a single visible element with the specified selector.
|
||||
"""
|
||||
full_selector = selector
|
||||
if self.root_selector:
|
||||
full_selector = self.root_selector + " " + full_selector
|
||||
elements = self.q(css=full_selector)
|
||||
return next((element for element in elements if element.is_displayed()), None)
|
||||
|
||||
@property
|
||||
def new_post_button(self):
|
||||
"""
|
||||
Returns the new post button if visible, else it returns None.
|
||||
"""
|
||||
return self.find_visible_element(".new-post-btn")
|
||||
|
||||
@property
|
||||
def new_post_form(self):
|
||||
"""
|
||||
Returns the new post form if visible, else it returns None.
|
||||
"""
|
||||
return self.find_visible_element(".forum-new-post-form")
|
||||
|
||||
def click_new_post_button(self):
|
||||
"""
|
||||
Clicks the 'New Post' button.
|
||||
"""
|
||||
self.wait_for(
|
||||
lambda: self.new_post_button,
|
||||
description="Waiting for new post button"
|
||||
)
|
||||
self.new_post_button.click()
|
||||
self.wait_for(
|
||||
lambda: self.new_post_form,
|
||||
description="Waiting for new post form"
|
||||
)
|
||||
|
||||
def click_cancel_new_post(self):
|
||||
"""
|
||||
Clicks the 'Cancel' button from the new post form.
|
||||
"""
|
||||
self.click_element(".cancel")
|
||||
self.wait_for(
|
||||
lambda: not self.new_post_form,
|
||||
"Waiting for new post form to close"
|
||||
)
|
||||
|
||||
|
||||
class DiscussionThreadPage(PageObject, DiscussionPageMixin):
|
||||
url = None
|
||||
@@ -470,12 +518,15 @@ class DiscussionTabSingleThreadPage(CoursePage):
|
||||
return len(self.q(css=".forum-nav-thread").results) == thread_count
|
||||
|
||||
|
||||
class InlineDiscussionPage(PageObject):
|
||||
class InlineDiscussionPage(PageObject, DiscussionPageMixin):
|
||||
"""
|
||||
Acceptance tests for inline discussions.
|
||||
"""
|
||||
url = None
|
||||
|
||||
def __init__(self, browser, discussion_id):
|
||||
super(InlineDiscussionPage, self).__init__(browser)
|
||||
self._discussion_selector = (
|
||||
self.root_selector = (
|
||||
".discussion-module[data-discussion-id='{discussion_id}'] ".format(
|
||||
discussion_id=discussion_id
|
||||
)
|
||||
@@ -486,11 +537,11 @@ class InlineDiscussionPage(PageObject):
|
||||
Returns a query corresponding to the given CSS selector within the scope
|
||||
of this discussion page
|
||||
"""
|
||||
return self.q(css=self._discussion_selector + " " + selector)
|
||||
return self.q(css=self.root_selector + " " + selector)
|
||||
|
||||
def is_browser_on_page(self):
|
||||
self.wait_for_ajax()
|
||||
return self.q(css=self._discussion_selector).present
|
||||
return self.q(css=self.root_selector).present
|
||||
|
||||
def is_discussion_expanded(self):
|
||||
return self._find_within(".discussion").present
|
||||
@@ -506,58 +557,41 @@ class InlineDiscussionPage(PageObject):
|
||||
def get_num_displayed_threads(self):
|
||||
return len(self._find_within(".forum-nav-thread"))
|
||||
|
||||
def has_thread(self, thread_id):
|
||||
"""Returns true if this page is showing the thread with the specified id."""
|
||||
return self._find_within('.discussion-thread#thread_{}'.format(thread_id)).present
|
||||
|
||||
def element_exists(self, selector):
|
||||
return self.q(css=self._discussion_selector + " " + selector).present
|
||||
|
||||
def is_new_post_opened(self):
|
||||
return self._find_within(".new-post-article").visible
|
||||
return self.q(css=self.root_selector + " " + selector).present
|
||||
|
||||
def click_element(self, selector):
|
||||
self.wait_for_element_presence(
|
||||
"{discussion} {selector}".format(discussion=self._discussion_selector, selector=selector),
|
||||
"{discussion} {selector}".format(discussion=self.root_selector, selector=selector),
|
||||
"{selector} is visible".format(selector=selector)
|
||||
)
|
||||
self._find_within(selector).click()
|
||||
|
||||
def click_cancel_new_post(self):
|
||||
self.click_element(".cancel")
|
||||
EmptyPromise(
|
||||
lambda: not self.is_new_post_opened(),
|
||||
"New post closed"
|
||||
).fulfill()
|
||||
|
||||
def click_new_post_button(self):
|
||||
self.click_element(".new-post-btn")
|
||||
EmptyPromise(
|
||||
self.is_new_post_opened,
|
||||
"New post opened"
|
||||
).fulfill()
|
||||
|
||||
@wait_for_js
|
||||
def _is_element_visible(self, selector):
|
||||
query = self._find_within(selector)
|
||||
return query.present and query.visible
|
||||
|
||||
def show_thread(self, thread_id):
|
||||
"""
|
||||
Clicks the link for the specified thread to show the detailed view.
|
||||
"""
|
||||
thread_selector = ".forum-nav-thread[data-id='{thread_id}'] .forum-nav-thread-link".format(thread_id=thread_id)
|
||||
self._find_within(thread_selector).first.click()
|
||||
self.thread_page = InlineDiscussionThreadPage(self.browser, thread_id) # pylint: disable=attribute-defined-outside-init
|
||||
self.thread_page.wait_for_page()
|
||||
|
||||
|
||||
class InlineDiscussionThreadPage(DiscussionThreadPage):
|
||||
"""
|
||||
Page object to manipulate an individual thread view in an inline discussion.
|
||||
"""
|
||||
def __init__(self, browser, thread_id):
|
||||
super(InlineDiscussionThreadPage, self).__init__(
|
||||
browser,
|
||||
"body.courseware .discussion-module #thread_{thread_id}".format(thread_id=thread_id)
|
||||
".discussion-module .discussion-article[data-id='{thread_id}']".format(thread_id=thread_id)
|
||||
)
|
||||
|
||||
def expand(self):
|
||||
"""Clicks the link to expand the thread"""
|
||||
self._find_within(".forum-thread-expand").first.click()
|
||||
EmptyPromise(
|
||||
lambda: bool(self.get_response_total_text()),
|
||||
"Thread expanded"
|
||||
).fulfill()
|
||||
|
||||
def is_thread_anonymous(self):
|
||||
return not self.q(css=".posted-details > .username").present
|
||||
|
||||
@@ -682,6 +716,7 @@ class DiscussionTabHomePage(CoursePage, DiscussionPageMixin):
|
||||
def __init__(self, browser, course_id):
|
||||
super(DiscussionTabHomePage, self).__init__(browser, course_id)
|
||||
self.url_path = "discussion/forum/"
|
||||
self.root_selector = None
|
||||
|
||||
def is_browser_on_page(self):
|
||||
return self.q(css=".discussion-body section.home-header").present
|
||||
@@ -733,18 +768,6 @@ class DiscussionTabHomePage(CoursePage, DiscussionPageMixin):
|
||||
"waiting for dismissed alerts to disappear"
|
||||
).fulfill()
|
||||
|
||||
def click_new_post_button(self):
|
||||
"""
|
||||
Clicks the 'New Post' button.
|
||||
"""
|
||||
self.new_post_button.click()
|
||||
EmptyPromise(
|
||||
lambda: (
|
||||
self.new_post_form
|
||||
),
|
||||
"New post action succeeded"
|
||||
).fulfill()
|
||||
|
||||
def click_element(self, selector):
|
||||
"""
|
||||
Clicks the element specified by selector
|
||||
@@ -752,22 +775,6 @@ class DiscussionTabHomePage(CoursePage, DiscussionPageMixin):
|
||||
element = self.q(css=selector)
|
||||
return element.click()
|
||||
|
||||
@property
|
||||
def new_post_button(self):
|
||||
"""
|
||||
Returns the new post button.
|
||||
"""
|
||||
elements = self.q(css=".new-post-btn")
|
||||
return elements.first if elements.visible and len(elements) == 1 else None
|
||||
|
||||
@property
|
||||
def new_post_form(self):
|
||||
"""
|
||||
Returns the new post form.
|
||||
"""
|
||||
elements = self.q(css=".forum-new-post-form")
|
||||
return elements[0] if elements.visible and len(elements) == 1 else None
|
||||
|
||||
def set_new_post_editor_value(self, new_body):
|
||||
"""
|
||||
Set the Discussions new post editor (wmd) with the content in new_body
|
||||
|
||||
@@ -37,6 +37,22 @@ class BaseDiscussionMixin(object):
|
||||
self.setup_thread_page(thread_id)
|
||||
return thread_id
|
||||
|
||||
def setup_multiple_threads(self, thread_count, **thread_kwargs):
|
||||
"""
|
||||
Set up multiple threads on the page by passing 'thread_count'.
|
||||
"""
|
||||
self.thread_ids = [] # pylint: disable=attribute-defined-outside-init
|
||||
threads = [] # pylint: disable=attribute-defined-outside-init
|
||||
for i in range(thread_count):
|
||||
thread_id = "test_thread_{}_{}".format(i, uuid4().hex)
|
||||
thread_body = "Dummy long text body." * 50
|
||||
threads.append(
|
||||
Thread(id=thread_id, commentable_id=self.discussion_id, body=thread_body, **thread_kwargs),
|
||||
)
|
||||
self.thread_ids.append(thread_id)
|
||||
thread_fixture = MultipleThreadFixture(threads)
|
||||
thread_fixture.push()
|
||||
|
||||
|
||||
class CohortTestMixin(object):
|
||||
"""
|
||||
|
||||
@@ -129,8 +129,8 @@ class InlineDiscussionTest(UniqueCourseTest):
|
||||
discussion_page = InlineDiscussionPage(self.browser, self.discussion_id)
|
||||
discussion_page.expand_discussion()
|
||||
self.assertEqual(discussion_page.get_num_displayed_threads(), 1)
|
||||
self.thread_page = InlineDiscussionThreadPage(self.browser, thread_id) # pylint: disable=attribute-defined-outside-init
|
||||
self.thread_page.expand()
|
||||
discussion_page.show_thread(thread_id)
|
||||
self.thread_page = discussion_page.thread_page # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
def refresh_thread_page(self, thread_id):
|
||||
self.browser.refresh()
|
||||
|
||||
@@ -1031,41 +1031,8 @@ class InlineDiscussionTest(UniqueCourseTest, DiscussionResponsePaginationTestMix
|
||||
|
||||
def setup_thread_page(self, thread_id):
|
||||
self.discussion_page.expand_discussion()
|
||||
self.assertEqual(self.discussion_page.get_num_displayed_threads(), 1)
|
||||
self.thread_page = InlineDiscussionThreadPage(self.browser, thread_id) # pylint: disable=attribute-defined-outside-init
|
||||
self.thread_page.expand()
|
||||
|
||||
def setup_multiple_inline_threads(self, thread_count):
|
||||
"""
|
||||
Set up multiple treads on the page by passing 'thread_count'
|
||||
"""
|
||||
threads = []
|
||||
for i in range(thread_count):
|
||||
thread_id = "test_thread_{}_{}".format(i, uuid4().hex)
|
||||
threads.append(
|
||||
Thread(id=thread_id, commentable_id=self.discussion_id),
|
||||
)
|
||||
self.thread_ids.append(thread_id)
|
||||
thread_fixture = MultipleThreadFixture(threads)
|
||||
thread_fixture.add_response(
|
||||
Response(id="response1"),
|
||||
[Comment(id="comment1", user_id="other"), Comment(id="comment2", user_id=self.user_id)],
|
||||
threads[0]
|
||||
)
|
||||
thread_fixture.push()
|
||||
|
||||
def test_page_while_expanding_inline_discussion(self):
|
||||
"""
|
||||
Tests for the Inline Discussion page with multiple treads. Page should not focus 'thread-wrapper'
|
||||
after loading responses.
|
||||
"""
|
||||
self.setup_multiple_inline_threads(thread_count=3)
|
||||
self.discussion_page.expand_discussion()
|
||||
thread_page = InlineDiscussionThreadPage(self.browser, self.thread_ids[0])
|
||||
thread_page.expand()
|
||||
|
||||
# Check if 'thread-wrapper' is focused after expanding thread
|
||||
self.assertFalse(thread_page.check_if_selector_is_focused(selector='.thread-wrapper'))
|
||||
self.discussion_page.show_thread(thread_id)
|
||||
self.thread_page = self.discussion_page.thread_page # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
@attr('a11y')
|
||||
def test_inline_a11y(self):
|
||||
@@ -1097,7 +1064,7 @@ class InlineDiscussionTest(UniqueCourseTest, DiscussionResponsePaginationTestMix
|
||||
thread = Thread(id=uuid4().hex, anonymous_to_peers=True, commentable_id=self.discussion_id)
|
||||
thread_fixture = SingleThreadViewFixture(thread)
|
||||
thread_fixture.push()
|
||||
self.setup_thread_page(thread.get("id"))
|
||||
self.setup_thread_page(thread.get("id")) # pylint: disable=no-member
|
||||
self.assertEqual(self.thread_page.is_thread_anonymous(), not is_staff)
|
||||
|
||||
def test_anonymous_to_peers_threads_as_staff(self):
|
||||
@@ -1130,41 +1097,66 @@ class InlineDiscussionTest(UniqueCourseTest, DiscussionResponsePaginationTestMix
|
||||
Response(id="response1"),
|
||||
[Comment(id="comment1", user_id="other"), Comment(id="comment2", user_id=self.user_id)])
|
||||
thread_fixture.push()
|
||||
self.setup_thread_page(thread.get("id"))
|
||||
self.setup_thread_page(thread.get("id")) # pylint: disable=no-member
|
||||
self.assertFalse(self.thread_page.has_add_response_button())
|
||||
self.assertFalse(self.thread_page.is_element_visible("action-more"))
|
||||
|
||||
def test_dual_discussion_xblock(self):
|
||||
"""
|
||||
Scenario: Two discussion xblocks in one unit shouldn't override their actions
|
||||
Given that I'm on courseware page where there are two inline discussion
|
||||
When I click on one discussion xblock new post button
|
||||
Then it should add new post form of that xblock in DOM
|
||||
And I should be shown new post form of that xblock
|
||||
And I shouldn't be shown second discussion xblock new post form
|
||||
And I click on second discussion xblock new post button
|
||||
Then it should add new post form of second xblock in DOM
|
||||
And I should be shown second discussion new post form
|
||||
And I shouldn't be shown first discussion xblock new post form
|
||||
And I have two new post form in the DOM
|
||||
When I click back on first xblock new post button
|
||||
And I should be shown new post form of that xblock
|
||||
And I shouldn't be shown second discussion xblock new post form
|
||||
Given that I'm on a courseware page where there are two inline discussion
|
||||
When I click on the first discussion block's new post button
|
||||
Then I should be shown only the new post form for the first block
|
||||
When I click on the second discussion block's new post button
|
||||
Then I should be shown both new post forms
|
||||
When I cancel the first form
|
||||
Then I should be shown only the new post form for the second block
|
||||
When I cancel the second form
|
||||
And I click on the first discussion block's new post button
|
||||
Then I should be shown only the new post form for the first block
|
||||
When I cancel the first form
|
||||
Then I should be shown none of the forms
|
||||
"""
|
||||
self.discussion_page.wait_for_page()
|
||||
self.additional_discussion_page.wait_for_page()
|
||||
|
||||
# Expand the first discussion, click to add a post
|
||||
self.discussion_page.expand_discussion()
|
||||
self.discussion_page.click_new_post_button()
|
||||
with self.discussion_page.handle_alert():
|
||||
self.discussion_page.click_cancel_new_post()
|
||||
|
||||
# Verify that only the first discussion's form is shown
|
||||
self.assertIsNotNone(self.discussion_page.new_post_form)
|
||||
self.assertIsNone(self.additional_discussion_page.new_post_form)
|
||||
|
||||
# Expand the second discussion, click to add a post
|
||||
self.additional_discussion_page.expand_discussion()
|
||||
self.additional_discussion_page.click_new_post_button()
|
||||
self.assertFalse(self.discussion_page._is_element_visible(".new-post-article"))
|
||||
with self.additional_discussion_page.handle_alert():
|
||||
self.additional_discussion_page.click_cancel_new_post()
|
||||
self.discussion_page.expand_discussion()
|
||||
|
||||
# Verify that both discussion's forms are shown
|
||||
self.assertIsNotNone(self.discussion_page.new_post_form)
|
||||
self.assertIsNotNone(self.additional_discussion_page.new_post_form)
|
||||
|
||||
# Cancel the first form
|
||||
self.discussion_page.click_cancel_new_post()
|
||||
|
||||
# Verify that only the second discussion's form is shown
|
||||
self.assertIsNone(self.discussion_page.new_post_form)
|
||||
self.assertIsNotNone(self.additional_discussion_page.new_post_form)
|
||||
|
||||
# Cancel the second form and click to show the first one
|
||||
self.additional_discussion_page.click_cancel_new_post()
|
||||
self.discussion_page.click_new_post_button()
|
||||
self.assertFalse(self.additional_discussion_page._is_element_visible(".new-post-article"))
|
||||
|
||||
# Verify that only the first discussion's form is shown
|
||||
self.assertIsNotNone(self.discussion_page.new_post_form)
|
||||
self.assertIsNone(self.additional_discussion_page.new_post_form)
|
||||
|
||||
# Cancel the first form
|
||||
self.discussion_page.click_cancel_new_post()
|
||||
|
||||
# Verify that neither discussion's forms are shwon
|
||||
self.assertIsNone(self.discussion_page.new_post_form)
|
||||
self.assertIsNone(self.additional_discussion_page.new_post_form)
|
||||
|
||||
|
||||
@attr(shard=2)
|
||||
|
||||
@@ -1689,7 +1689,8 @@ class TeamPageTest(TeamsTabBase):
|
||||
thread = Thread(
|
||||
id="test_thread_{}".format(uuid4().hex),
|
||||
commentable_id=self.teams[0]['discussion_topic_id'],
|
||||
body="Dummy text body."
|
||||
body="Dummy text body.",
|
||||
context="standalone",
|
||||
)
|
||||
thread_fixture = MultipleThreadFixture([thread])
|
||||
thread_fixture.push()
|
||||
@@ -1718,14 +1719,15 @@ class TeamPageTest(TeamsTabBase):
|
||||
thread = self.setup_thread()
|
||||
self.team_page.visit()
|
||||
self.assertEqual(self.team_page.discussion_id, self.teams[0]['discussion_topic_id'])
|
||||
discussion = self.team_page.discussion_page
|
||||
discussion.wait_for_page()
|
||||
self.assertTrue(discussion.is_discussion_expanded())
|
||||
self.assertEqual(discussion.get_num_displayed_threads(), 1)
|
||||
self.assertTrue(discussion.has_thread(thread['id']))
|
||||
discussion_page = self.team_page.discussion_page
|
||||
discussion_page.wait_for_page()
|
||||
self.assertTrue(discussion_page.is_discussion_expanded())
|
||||
self.assertEqual(discussion_page.get_num_displayed_threads(), 1)
|
||||
discussion_page.show_thread(thread['id'])
|
||||
thread_page = discussion_page.thread_page
|
||||
assertion = self.assertTrue if should_have_permission else self.assertFalse
|
||||
assertion(discussion.q(css='.post-header-actions').present)
|
||||
assertion(discussion.q(css='.add-response').present)
|
||||
assertion(thread_page.q(css='.post-header-actions').present)
|
||||
assertion(thread_page.q(css='.add-response').present)
|
||||
|
||||
def test_discussion_on_my_team_page(self):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user