Commit 16847d85 by Greg Price

Add bok-choy tests for inline response pagination

parent 3af90ef2
...@@ -14,6 +14,7 @@ class StubCommentsServiceHandler(StubHttpRequestHandler): ...@@ -14,6 +14,7 @@ class StubCommentsServiceHandler(StubHttpRequestHandler):
"/api/v1/threads$": self.do_threads, "/api/v1/threads$": self.do_threads,
"/api/v1/threads/(?P<thread_id>\\w+)$": self.do_thread, "/api/v1/threads/(?P<thread_id>\\w+)$": self.do_thread,
"/api/v1/comments/(?P<comment_id>\\w+)$": self.do_comment, "/api/v1/comments/(?P<comment_id>\\w+)$": self.do_comment,
"/api/v1/(?P<commentable_id>\\w+)/threads$": self.do_commentable,
} }
path = urlparse.urlparse(self.path).path path = urlparse.urlparse(self.path).path
for pattern in pattern_handlers: for pattern in pattern_handlers:
...@@ -63,6 +64,17 @@ class StubCommentsServiceHandler(StubHttpRequestHandler): ...@@ -63,6 +64,17 @@ class StubCommentsServiceHandler(StubHttpRequestHandler):
comment = self.server.config['comments'][comment_id] comment = self.server.config['comments'][comment_id]
self.send_json_response(comment) self.send_json_response(comment)
def do_commentable(self, commentable_id):
self.send_json_response({
"collection": [
thread
for thread in self.server.config.get('threads', {}).values()
if thread.get('commentable_id') == commentable_id
],
"page": 1,
"num_pages": 1,
})
class StubCommentsService(StubHttpService): class StubCommentsService(StubHttpService):
HANDLER_CLASS = StubCommentsServiceHandler HANDLER_CLASS = StubCommentsServiceHandler
"""
Courseware page.
"""
from .course_page import CoursePage
class CoursewarePage(CoursePage):
"""
Course info.
"""
url_path = "courseware"
def is_browser_on_page(self):
return self.q(css='body.courseware').present
from bok_choy.page_object import unguarded from bok_choy.page_object import PageObject
from bok_choy.promise import EmptyPromise from bok_choy.promise import EmptyPromise
from .course_page import CoursePage from .course_page import CoursePage
class DiscussionSingleThreadPage(CoursePage): class DiscussionThreadPage(PageObject):
def __init__(self, browser, course_id, thread_id): url = None
super(DiscussionSingleThreadPage, self).__init__(browser, course_id)
self.thread_id = thread_id
def is_browser_on_page(self): def __init__(self, browser, thread_selector):
return self.q( super(DiscussionThreadPage, self).__init__(browser)
css="body.discussion .discussion-article[data-id='{thread_id}']".format(thread_id=self.thread_id) self.thread_selector = thread_selector
).present
def _find_within(self, selector):
"""
Returns a query corresponding to the given CSS selector within the scope
of this thread page
"""
return self.q(css=self.thread_selector + " " + selector)
@property def is_browser_on_page(self):
@unguarded return self.q(css=self.thread_selector).present
def url_path(self):
return "discussion/forum/dummy/threads/" + self.thread_id
def _get_element_text(self, selector): def _get_element_text(self, selector):
""" """
Returns the text of the first element matching the given selector, or Returns the text of the first element matching the given selector, or
None if no such element exists None if no such element exists
""" """
text_list = self.q(css=selector).text text_list = self._find_within(selector).text
return text_list[0] if text_list else None return text_list[0] if text_list else None
def _is_element_visible(self, selector):
query = self._find_within(selector)
return query.present and query.visible
def get_response_total_text(self): def get_response_total_text(self):
"""Returns the response count text, or None if not present""" """Returns the response count text, or None if not present"""
return self._get_element_text(".response-count") return self._get_element_text(".response-count")
def get_num_displayed_responses(self): def get_num_displayed_responses(self):
"""Returns the number of responses actually rendered""" """Returns the number of responses actually rendered"""
return len(self.q(css=".discussion-response").results) return len(self._find_within(".discussion-response"))
def get_shown_responses_text(self): def get_shown_responses_text(self):
"""Returns the shown response count text, or None if not present""" """Returns the shown response count text, or None if not present"""
...@@ -45,7 +51,7 @@ class DiscussionSingleThreadPage(CoursePage): ...@@ -45,7 +51,7 @@ class DiscussionSingleThreadPage(CoursePage):
def load_more_responses(self): def load_more_responses(self):
"""Clicks the load more responses button and waits for responses to load""" """Clicks the load more responses button and waits for responses to load"""
self.q(css=".load-response-button").first.click() self._find_within(".load-response-button").click()
def _is_ajax_finished(): def _is_ajax_finished():
return self.browser.execute_script("return jQuery.active") == 0 return self.browser.execute_script("return jQuery.active") == 0
...@@ -64,25 +70,19 @@ class DiscussionSingleThreadPage(CoursePage): ...@@ -64,25 +70,19 @@ class DiscussionSingleThreadPage(CoursePage):
Clicks the add response button and ensures that the response text Clicks the add response button and ensures that the response text
field receives focus field receives focus
""" """
self.q(css=".add-response-btn").first.click() self._find_within(".add-response-btn").first.click()
EmptyPromise( EmptyPromise(
lambda: self.q(css="#wmd-input-reply-body-{thread_id}:focus".format(thread_id=self.thread_id)), lambda: self._find_within(".discussion-reply-new textarea:focus").present,
"Response field received focus" "Response field received focus"
).fulfill() ).fulfill()
def _is_element_visible(self, selector):
return (
self.q(css=selector).present and
self.q(css=selector).visible
)
def is_response_editor_visible(self, response_id): def is_response_editor_visible(self, response_id):
"""Returns true if the response editor is present, false otherwise""" """Returns true if the response editor is present, false otherwise"""
return self._is_element_visible(".response_{} .edit-post-body".format(response_id)) return self._is_element_visible(".response_{} .edit-post-body".format(response_id))
def start_response_edit(self, response_id): def start_response_edit(self, response_id):
"""Click the edit button for the response, loading the editing view""" """Click the edit button for the response, loading the editing view"""
self.q(css=".response_{} .discussion-response .action-edit".format(response_id)).first.click() self._find_within(".response_{} .discussion-response .action-edit".format(response_id)).first.click()
EmptyPromise( EmptyPromise(
lambda: self.is_response_editor_visible(response_id), lambda: self.is_response_editor_visible(response_id),
"Response edit started" "Response edit started"
...@@ -105,7 +105,7 @@ class DiscussionSingleThreadPage(CoursePage): ...@@ -105,7 +105,7 @@ class DiscussionSingleThreadPage(CoursePage):
def delete_comment(self, comment_id): def delete_comment(self, comment_id):
with self.handle_alert(): with self.handle_alert():
self.q(css="#comment_{} div.action-delete".format(comment_id)).first.click() self._find_within("#comment_{} div.action-delete".format(comment_id)).first.click()
EmptyPromise( EmptyPromise(
lambda: not self.is_comment_visible(comment_id), lambda: not self.is_comment_visible(comment_id),
"Deleted comment was removed" "Deleted comment was removed"
...@@ -120,12 +120,12 @@ class DiscussionSingleThreadPage(CoursePage): ...@@ -120,12 +120,12 @@ class DiscussionSingleThreadPage(CoursePage):
return self._is_element_visible(".edit-comment-body[data-id='{}']".format(comment_id)) return self._is_element_visible(".edit-comment-body[data-id='{}']".format(comment_id))
def _get_comment_editor_value(self, comment_id): def _get_comment_editor_value(self, comment_id):
return self.q(css="#wmd-input-edit-comment-body-{}".format(comment_id)).text[0] return self._find_within("#wmd-input-edit-comment-body-{}".format(comment_id)).text[0]
def start_comment_edit(self, comment_id): def start_comment_edit(self, comment_id):
"""Click the edit button for the comment, loading the editing view""" """Click the edit button for the comment, loading the editing view"""
old_body = self.get_comment_body(comment_id) old_body = self.get_comment_body(comment_id)
self.q(css="#comment_{} .action-edit".format(comment_id)).first.click() self._find_within("#comment_{} .action-edit".format(comment_id)).first.click()
EmptyPromise( EmptyPromise(
lambda: ( lambda: (
self.is_comment_editor_visible(comment_id) and self.is_comment_editor_visible(comment_id) and
...@@ -137,11 +137,11 @@ class DiscussionSingleThreadPage(CoursePage): ...@@ -137,11 +137,11 @@ class DiscussionSingleThreadPage(CoursePage):
def set_comment_editor_value(self, comment_id, new_body): def set_comment_editor_value(self, comment_id, new_body):
"""Replace the contents of the comment editor""" """Replace the contents of the comment editor"""
self.q(css="#comment_{} .wmd-input".format(comment_id)).fill(new_body) self._find_within("#comment_{} .wmd-input".format(comment_id)).fill(new_body)
def submit_comment_edit(self, comment_id, new_comment_body): def submit_comment_edit(self, comment_id, new_comment_body):
"""Click the submit button on the comment editor""" """Click the submit button on the comment editor"""
self.q(css="#comment_{} .post-update".format(comment_id)).first.click() self._find_within("#comment_{} .post-update".format(comment_id)).first.click()
EmptyPromise( EmptyPromise(
lambda: ( lambda: (
not self.is_comment_editor_visible(comment_id) and not self.is_comment_editor_visible(comment_id) and
...@@ -153,7 +153,7 @@ class DiscussionSingleThreadPage(CoursePage): ...@@ -153,7 +153,7 @@ class DiscussionSingleThreadPage(CoursePage):
def cancel_comment_edit(self, comment_id, original_body): def cancel_comment_edit(self, comment_id, original_body):
"""Click the cancel button on the comment editor""" """Click the cancel button on the comment editor"""
self.q(css="#comment_{} .post-cancel".format(comment_id)).first.click() self._find_within("#comment_{} .post-cancel".format(comment_id)).first.click()
EmptyPromise( EmptyPromise(
lambda: ( lambda: (
not self.is_comment_editor_visible(comment_id) and not self.is_comment_editor_visible(comment_id) and
...@@ -162,3 +162,72 @@ class DiscussionSingleThreadPage(CoursePage): ...@@ -162,3 +162,72 @@ class DiscussionSingleThreadPage(CoursePage):
), ),
"Comment edit was canceled" "Comment edit was canceled"
).fulfill() ).fulfill()
class DiscussionTabSingleThreadPage(CoursePage):
def __init__(self, browser, course_id, thread_id):
super(DiscussionTabSingleThreadPage, self).__init__(browser, course_id)
self.thread_page = DiscussionThreadPage(
browser,
"body.discussion .discussion-article[data-id='{thread_id}']".format(thread_id=thread_id)
)
self.url_path = "discussion/forum/dummy/threads/" + thread_id
def is_browser_on_page(self):
return self.thread_page.is_browser_on_page()
def __getattr__(self, name):
return getattr(self.thread_page, name)
class InlineDiscussionPage(PageObject):
url = None
def __init__(self, browser, discussion_id):
super(InlineDiscussionPage, self).__init__(browser)
self._discussion_selector = (
"body.courseware .discussion-module[data-discussion-id='{discussion_id}'] ".format(
discussion_id=discussion_id
)
)
def _find_within(self, selector):
"""
Returns a query corresponding to the given CSS selector within the scope
of this discussion page
"""
return self.q(css=self._discussion_selector + " " + selector)
def is_browser_on_page(self):
return self.q(css=self._discussion_selector).present
def is_discussion_expanded(self):
return self._find_within(".discussion").present
def expand_discussion(self):
"""Click the link to expand the discussion"""
self._find_within(".discussion-show").first.click()
EmptyPromise(
self.is_discussion_expanded,
"Discussion expanded"
).fulfill()
def get_num_displayed_threads(self):
return len(self._find_within(".discussion-thread"))
class InlineDiscussionThreadPage(DiscussionThreadPage):
def __init__(self, browser, thread_id):
super(InlineDiscussionThreadPage, self).__init__(
browser,
"body.courseware .discussion-module #thread_{thread_id}".format(thread_id=thread_id)
)
def expand(self):
"""Clicks the link to expand the thread"""
self._find_within(".expand-post").first.click()
EmptyPromise(
lambda: bool(self.get_response_total_text()),
"Thread expanded"
).fulfill()
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