Commit 95af1843 by Andy Armstrong Committed by Brian Jacobel

Fix inline discussion Bok Choy tests

parent 82d05c6e
......@@ -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):
"""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment