Commit f71e78b6 by jsa

add acceptance tests for user profile pagination.

JIRA: FOR-492
parent 909903eb
...@@ -8,8 +8,14 @@ from .http import StubHttpRequestHandler, StubHttpService ...@@ -8,8 +8,14 @@ from .http import StubHttpRequestHandler, StubHttpService
class StubCommentsServiceHandler(StubHttpRequestHandler): class StubCommentsServiceHandler(StubHttpRequestHandler):
@property
def _params(self):
return urlparse.parse_qs(urlparse.urlparse(self.path).query)
def do_GET(self): def do_GET(self):
pattern_handlers = { pattern_handlers = {
"/api/v1/users/(?P<user_id>\\d+)/active_threads$": self.do_user_profile,
"/api/v1/users/(?P<user_id>\\d+)$": self.do_user, "/api/v1/users/(?P<user_id>\\d+)$": self.do_user,
"/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,
...@@ -34,12 +40,34 @@ class StubCommentsServiceHandler(StubHttpRequestHandler): ...@@ -34,12 +40,34 @@ class StubCommentsServiceHandler(StubHttpRequestHandler):
self.send_json_response({}) self.send_json_response({})
def do_user(self, user_id): def do_user(self, user_id):
self.send_json_response({ response = {
"id": user_id, "id": user_id,
"upvoted_ids": [], "upvoted_ids": [],
"downvoted_ids": [], "downvoted_ids": [],
"subscribed_thread_ids": [], "subscribed_thread_ids": [],
}) }
if 'course_id' in self._params:
response.update({
"threads_count": 1,
"comments_count": 2
})
self.send_json_response(response)
def do_user_profile(self, user_id):
if 'active_threads' in self.server.config:
user_threads = self.server.config['active_threads'][:]
params = self._params
page = int(params.get("page", ["1"])[0])
per_page = int(params.get("per_page", ["20"])[0])
num_pages = max(len(user_threads) - 1, 1) / per_page + 1
user_threads = user_threads[(page - 1) * per_page:page * per_page]
self.send_json_response({
"collection": user_threads,
"page": page,
"num_pages": num_pages
})
else:
self.send_response(404, content="404 Not Found")
def do_thread(self, thread_id): def do_thread(self, thread_id):
if thread_id in self.server.config.get('threads', {}): if thread_id in self.server.config.get('threads', {}):
......
...@@ -87,3 +87,19 @@ class SingleThreadViewFixture(object): ...@@ -87,3 +87,19 @@ class SingleThreadViewFixture(object):
"comments": json.dumps(self._get_comment_map()) "comments": json.dumps(self._get_comment_map())
} }
) )
class UserProfileViewFixture(object):
def __init__(self, threads):
self.threads = threads
def push(self):
"""
Push the data to the stub comments service.
"""
requests.put(
'{}/set_config'.format(COMMENTS_STUB_URL),
data={
"active_threads": json.dumps(self.threads),
}
)
...@@ -231,3 +231,76 @@ class InlineDiscussionThreadPage(DiscussionThreadPage): ...@@ -231,3 +231,76 @@ class InlineDiscussionThreadPage(DiscussionThreadPage):
"Thread expanded" "Thread expanded"
).fulfill() ).fulfill()
class DiscussionUserProfilePage(CoursePage):
TEXT_NEXT = u'Next >'
TEXT_PREV = u'< Previous'
PAGING_SELECTOR = "a.discussion-pagination[data-page-number]"
def __init__(self, browser, course_id, user_id, username, page=1):
super(DiscussionUserProfilePage, self).__init__(browser, course_id)
self.url_path = "discussion/forum/dummy/users/{}?page={}".format(user_id, page)
self.username = username
def is_browser_on_page(self):
return (
self.q(css='section.discussion-user-threads[data-course-id="{}"]'.format(self.course_id)).present
and
self.q(css='section.user-profile div.sidebar-username').present
and
self.q(css='section.user-profile div.sidebar-username').text[0] == self.username
)
def get_shown_thread_ids(self):
elems = self.q(css="article.discussion-thread")
return [elem.get_attribute("id")[7:] for elem in elems]
def get_current_page(self):
return int(self.q(css="nav.discussion-paginator li.current-page").text[0])
def _check_pager(self, text, page_number=None):
"""
returns True if 'text' matches the text in any of the pagination elements. If
page_number is provided, only return True if the element points to that result
page.
"""
elems = self.q(css=self.PAGING_SELECTOR).filter(lambda elem: elem.text == text)
if page_number:
elems = elems.filter(lambda elem: int(elem.get_attribute('data-page-number')) == page_number)
return elems.present
def get_clickable_pages(self):
return sorted([
int(elem.get_attribute('data-page-number'))
for elem in self.q(css=self.PAGING_SELECTOR)
if str(elem.text).isdigit()
])
def is_prev_button_shown(self, page_number=None):
return self._check_pager(self.TEXT_PREV, page_number)
def is_next_button_shown(self, page_number=None):
return self._check_pager(self.TEXT_NEXT, page_number)
def _click_pager_with_text(self, text, page_number):
"""
click the first pagination element with whose text is `text` and ensure
the resulting page number matches `page_number`.
"""
targets = [elem for elem in self.q(css=self.PAGING_SELECTOR) if elem.text == text]
targets[0].click()
EmptyPromise(
lambda: self.get_current_page() == page_number,
"navigated to desired page"
).fulfill()
def click_prev_page(self):
self._click_pager_with_text(self.TEXT_PREV, self.get_current_page() - 1)
def click_next_page(self):
self._click_pager_with_text(self.TEXT_NEXT, self.get_current_page() + 1)
def click_on_page(self, page_number):
self._click_pager_with_text(unicode(page_number), page_number)
...@@ -10,10 +10,11 @@ from ..pages.lms.courseware import CoursewarePage ...@@ -10,10 +10,11 @@ from ..pages.lms.courseware import CoursewarePage
from ..pages.lms.discussion import ( from ..pages.lms.discussion import (
DiscussionTabSingleThreadPage, DiscussionTabSingleThreadPage,
InlineDiscussionPage, InlineDiscussionPage,
InlineDiscussionThreadPage InlineDiscussionThreadPage,
DiscussionUserProfilePage
) )
from ..fixtures.course import CourseFixture, XBlockFixtureDesc from ..fixtures.course import CourseFixture, XBlockFixtureDesc
from ..fixtures.discussion import SingleThreadViewFixture, Thread, Response, Comment from ..fixtures.discussion import SingleThreadViewFixture, UserProfileViewFixture, Thread, Response, Comment
class DiscussionResponsePaginationTestMixin(object): class DiscussionResponsePaginationTestMixin(object):
...@@ -301,3 +302,106 @@ class InlineDiscussionTest(UniqueCourseTest, DiscussionResponsePaginationTestMix ...@@ -301,3 +302,106 @@ class InlineDiscussionTest(UniqueCourseTest, DiscussionResponsePaginationTestMix
def test_expand_discussion_empty(self): def test_expand_discussion_empty(self):
self.discussion_page.expand_discussion() self.discussion_page.expand_discussion()
self.assertEqual(self.discussion_page.get_num_displayed_threads(), 0) self.assertEqual(self.discussion_page.get_num_displayed_threads(), 0)
class DiscussionUserProfileTest(UniqueCourseTest):
"""
Tests for user profile page in discussion tab.
"""
PAGE_SIZE = 20 # django_comment_client.forum.views.THREADS_PER_PAGE
PROFILED_USERNAME = "profiled-user"
def setUp(self):
super(DiscussionUserProfileTest, self).setUp()
CourseFixture(**self.course_info).install()
# The following line creates a user enrolled in our course, whose
# threads will be viewed, but not the one who will view the page.
# It isn't necessary to log them in, but using the AutoAuthPage
# saves a lot of code.
self.profiled_user_id = AutoAuthPage(
self.browser,
username=self.PROFILED_USERNAME,
course_id=self.course_id
).visit().get_user_id()
# now create a second user who will view the profile.
self.user_id = AutoAuthPage(
self.browser,
course_id=self.course_id
).visit().get_user_id()
def check_pages(self, num_threads):
# set up the stub server to return the desired amount of thread results
threads = [Thread(id=uuid4().hex) for _ in range(num_threads)]
UserProfileViewFixture(threads).push()
# navigate to default view (page 1)
page = DiscussionUserProfilePage(
self.browser,
self.course_id,
self.profiled_user_id,
self.PROFILED_USERNAME
)
page.visit()
current_page = 1
total_pages = max(num_threads - 1, 1) / self.PAGE_SIZE + 1
all_pages = range(1, total_pages + 1)
def _check_page():
# ensure the page being displayed as "current" is the expected one
self.assertEqual(page.get_current_page(), current_page)
# ensure the expected threads are being shown in the right order
threads_expected = threads[(current_page - 1) * self.PAGE_SIZE:current_page * self.PAGE_SIZE]
self.assertEqual(page.get_shown_thread_ids(), [t["id"] for t in threads_expected])
# ensure the clickable page numbers are the expected ones
self.assertEqual(page.get_clickable_pages(), [
p for p in all_pages
if p != current_page
and p - 2 <= current_page <= p + 2
or (current_page > 2 and p == 1)
or (current_page < total_pages and p == total_pages)
])
# ensure the previous button is shown, but only if it should be.
# when it is shown, make sure it works.
if current_page > 1:
self.assertTrue(page.is_prev_button_shown(current_page - 1))
page.click_prev_page()
self.assertEqual(page.get_current_page(), current_page - 1)
page.click_next_page()
self.assertEqual(page.get_current_page(), current_page)
else:
self.assertFalse(page.is_prev_button_shown())
# ensure the next button is shown, but only if it should be.
if current_page < total_pages:
self.assertTrue(page.is_next_button_shown(current_page + 1))
else:
self.assertFalse(page.is_next_button_shown())
# click all the way up through each page
for i in range(current_page, total_pages):
_check_page()
if current_page < total_pages:
page.click_on_page(current_page + 1)
current_page += 1
# click all the way back down
for i in range(current_page, 0, -1):
_check_page()
if current_page > 1:
page.click_on_page(current_page - 1)
current_page -= 1
def test_0_threads(self):
self.check_pages(0)
def test_1_thread(self):
self.check_pages(1)
def test_20_threads(self):
self.check_pages(20)
def test_21_threads(self):
self.check_pages(21)
def test_151_threads(self):
self.check_pages(151)
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