Commit 8e3a77fa by Greg Price

Add acceptance tests for forum response pagination

parent 02a233b7
"""
Stub implementation of cs_comments_service for acceptance tests
"""
from datetime import datetime
import re
import urlparse
from .http import StubHttpRequestHandler, StubHttpService
class StubCommentsServiceHandler(StubHttpRequestHandler):
def do_GET(self):
pattern_handlers = {
"/api/v1/users/(?P<user_id>\\d+)$": self.do_user,
"/api/v1/threads$": self.do_threads,
"/api/v1/threads/(?P<thread_id>\\w+)$": self.do_thread,
}
path = urlparse.urlparse(self.path).path
for pattern in pattern_handlers:
match = re.match(pattern, path)
if match:
pattern_handlers[pattern](**match.groupdict())
return
self.send_response(404, content="404 Not Found")
def do_PUT(self):
self.send_response(204, "")
def do_user(self, user_id):
self.send_json_response({
"id": user_id,
"upvoted_ids": [],
"downvoted_ids": [],
"subscribed_thread_ids": [],
})
def do_thread(self, thread_id):
match = re.search("(?P<num>\\d+)_responses", thread_id)
resp_total = int(match.group("num")) if match else 0
thread = {
"id": thread_id,
"commentable_id": "dummy",
"type": "thread",
"title": "Thread title",
"body": "Thread body",
"created_at": datetime.utcnow().isoformat(),
"unread_comments_count": 0,
"comments_count": resp_total,
"votes": {"up_count": 0},
"abuse_flaggers": [],
"closed": "closed" in thread_id,
}
params = urlparse.parse_qs(urlparse.urlparse(self.path).query)
if "recursive" in params and params["recursive"][0] == "True":
thread["resp_total"] = resp_total
thread["children"] = []
resp_skip = int(params.get("resp_skip", ["0"])[0])
resp_limit = int(params.get("resp_limit", ["10000"])[0])
num_responses = min(resp_limit, resp_total - resp_skip)
self.log_message("Generating {} children; resp_limit={} resp_total={} resp_skip={}".format(num_responses, resp_limit, resp_total, resp_skip))
for i in range(num_responses):
response_id = str(resp_skip + i)
thread["children"].append({
"id": str(response_id),
"type": "comment",
"body": response_id,
"created_at": datetime.utcnow().isoformat(),
"votes": {"up_count": 0},
"abuse_flaggers": [],
})
self.send_json_response(thread)
def do_threads(self):
self.send_json_response({"collection": [], "page": 1, "num_pages": 1})
class StubCommentsService(StubHttpService):
HANDLER_CLASS = StubCommentsServiceHandler
...@@ -202,6 +202,13 @@ class StubHttpRequestHandler(BaseHTTPRequestHandler, object): ...@@ -202,6 +202,13 @@ class StubHttpRequestHandler(BaseHTTPRequestHandler, object):
if content is not None: if content is not None:
self.wfile.write(content) self.wfile.write(content)
def send_json_response(self, content):
"""
Send a response with status code 200, the given content serialized as
JSON, and the Content-Type header set appropriately
"""
self.send_response(200, json.dumps(content), {"Content-Type": "application/json"})
def _format_msg(self, format_str, *args): def _format_msg(self, format_str, *args):
""" """
Format message for logging. Format message for logging.
......
...@@ -4,6 +4,7 @@ Command-line utility to start a stub service. ...@@ -4,6 +4,7 @@ Command-line utility to start a stub service.
import sys import sys
import time import time
import logging import logging
from .comments import StubCommentsService
from .xqueue import StubXQueueService from .xqueue import StubXQueueService
from .youtube import StubYouTubeService from .youtube import StubYouTubeService
from .ora import StubOraService from .ora import StubOraService
...@@ -14,7 +15,8 @@ USAGE = "USAGE: python -m stubs.start SERVICE_NAME PORT_NUM [CONFIG_KEY=CONFIG_V ...@@ -14,7 +15,8 @@ USAGE = "USAGE: python -m stubs.start SERVICE_NAME PORT_NUM [CONFIG_KEY=CONFIG_V
SERVICES = { SERVICES = {
'xqueue': StubXQueueService, 'xqueue': StubXQueueService,
'youtube': StubYouTubeService, 'youtube': StubYouTubeService,
'ora': StubOraService 'ora': StubOraService,
'comments': StubCommentsService,
} }
# Log to stdout, including debug messages # Log to stdout, including debug messages
......
from bok_choy.page_object import unguarded
from bok_choy.promise import EmptyPromise, fulfill
from .course_page import CoursePage
class DiscussionSingleThreadPage(CoursePage):
def __init__(self, browser, course_id, thread_id):
super(DiscussionSingleThreadPage, self).__init__(browser, course_id)
self.thread_id = thread_id
def is_browser_on_page(self):
return self.is_css_present(
"body.discussion .discussion-article[data-id='{thread_id}']".format(thread_id=self.thread_id)
)
@property
@unguarded
def url_path(self):
return "discussion/forum/dummy/threads/" + self.thread_id
def _get_element_text(self, selector):
"""
Returns the text of the first element matching the given selector, or
None if no such element exists
"""
text_list = self.css_text(selector)
return text_list[0] if text_list else None
def get_response_total_text(self):
"""Returns the response count text, or None if not present"""
return self._get_element_text(".response-count")
def get_num_displayed_responses(self):
"""Returns the number of responses actually rendered"""
return self.css_count(".discussion-response")
def get_shown_responses_text(self):
"""Returns the shown response count text, or None if not present"""
return self._get_element_text(".response-display-count")
def get_load_responses_button_text(self):
"""Returns the load more responses button text, or None if not present"""
return self._get_element_text(".load-response-button")
def load_more_responses(self):
"""Clicks the laod more responses button and waits for responses to load"""
self.css_click(".load-response-button")
fulfill(EmptyPromise(
lambda: not self.is_css_present(".loading"),
"Loading more responses completed"
))
def has_add_response_button(self):
"""Returns true if the add response button is visible, false otherwise"""
return (
self.is_css_present(".add-response-btn") and
self.css_map(".add-response-btn", lambda el: el.visible)[0]
)
def click_add_response_button(self):
"""
Clicks the add response button and ensures that the response text
field receives focus
"""
self.css_click(".add-response-btn")
fulfill(EmptyPromise(
lambda: self.is_css_present("#wmd-input-reply-body-{thread_id}:focus".format(thread_id=self.thread_id)),
"Response field received focus"
))
"""
Tests for discussion pages
"""
from .helpers import UniqueCourseTest
from ..pages.studio.auto_auth import AutoAuthPage
from ..pages.lms.discussion_single_thread import DiscussionSingleThreadPage
from ..fixtures.course import CourseFixture
class DiscussionSingleThreadTest(UniqueCourseTest):
"""
Tests for the discussion page displaying a single thread
"""
def setUp(self):
super(DiscussionSingleThreadTest, self).setUp()
# Create a course to register for
CourseFixture(**self.course_info).install()
AutoAuthPage(self.browser, course_id=self.course_id).visit()
def test_no_responses(self):
page = DiscussionSingleThreadPage(self.browser, self.course_id, "0_responses")
page.visit()
self.assertEqual(page.get_response_total_text(), "0 responses")
self.assertFalse(page.has_add_response_button())
self.assertEqual(page.get_num_displayed_responses(), 0)
self.assertEqual(page.get_shown_responses_text(), None)
self.assertIsNone(page.get_load_responses_button_text())
def test_few_responses(self):
page = DiscussionSingleThreadPage(self.browser, self.course_id, "5_responses")
page.visit()
self.assertEqual(page.get_response_total_text(), "5 responses")
self.assertEqual(page.get_num_displayed_responses(), 5)
self.assertEqual(page.get_shown_responses_text(), "Showing all responses")
self.assertIsNone(page.get_load_responses_button_text())
def test_two_response_pages(self):
page = DiscussionSingleThreadPage(self.browser, self.course_id, "50_responses")
page.visit()
self.assertEqual(page.get_response_total_text(), "50 responses")
self.assertEqual(page.get_num_displayed_responses(), 25)
self.assertEqual(page.get_shown_responses_text(), "Showing first 25 responses")
self.assertEqual(page.get_load_responses_button_text(), "Load all responses")
page.load_more_responses()
self.assertEqual(page.get_num_displayed_responses(), 50)
self.assertEqual(page.get_shown_responses_text(), "Showing all responses")
self.assertEqual(page.get_load_responses_button_text(), None)
def test_three_response_pages(self):
page = DiscussionSingleThreadPage(self.browser, self.course_id, "150_responses")
page.visit()
self.assertEqual(page.get_response_total_text(), "150 responses")
self.assertEqual(page.get_num_displayed_responses(), 25)
self.assertEqual(page.get_shown_responses_text(), "Showing first 25 responses")
self.assertEqual(page.get_load_responses_button_text(), "Load next 100 responses")
page.load_more_responses()
self.assertEqual(page.get_num_displayed_responses(), 125)
self.assertEqual(page.get_shown_responses_text(), "Showing first 125 responses")
self.assertEqual(page.get_load_responses_button_text(), "Load all responses")
page.load_more_responses()
self.assertEqual(page.get_num_displayed_responses(), 150)
self.assertEqual(page.get_shown_responses_text(), "Showing all responses")
self.assertEqual(page.get_load_responses_button_text(), None)
def test_add_response_button(self):
page = DiscussionSingleThreadPage(self.browser, self.course_id, "5_responses")
page.visit()
self.assertTrue(page.has_add_response_button())
page.click_add_response_button()
def test_add_response_button_closed_thread(self):
page = DiscussionSingleThreadPage(self.browser, self.course_id, "5_responses_closed")
page.visit()
self.assertFalse(page.has_add_response_button())
...@@ -41,6 +41,11 @@ BOK_CHOY_STUBS = { ...@@ -41,6 +41,11 @@ BOK_CHOY_STUBS = {
:port => 8041, :port => 8041,
:log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_ora.log"), :log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_ora.log"),
:config => '' :config => ''
},
:comments => {
:port => 4567,
:log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_comments.log")
} }
} }
......
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