Add teams-for-topic list view to Teams page.
TNL-1898 Also adds a generic paginated list view and Teams list view. The common PaginatedView takes a collection and a view class for its items and creates a header and footer with correct pagination. The topics list view is refactored to use this generic view. Authors: - Peter Fogg <pfogg@edx.org> - Daniel Friedman <dfriedman58@gmail.com>
This commit is contained in:
43
common/static/common/js/components/views/paginated_view.js
Normal file
43
common/static/common/js/components/views/paginated_view.js
Normal file
@@ -0,0 +1,43 @@
|
||||
;(function(define) {
|
||||
'use strict';
|
||||
define([
|
||||
'backbone',
|
||||
'underscore',
|
||||
'common/js/components/views/paging_header',
|
||||
'common/js/components/views/paging_footer',
|
||||
'common/js/components/views/list',
|
||||
'text!common/templates/components/paginated-view.underscore'
|
||||
], function (Backbone, _, PagingHeader, PagingFooter, ListView, paginatedViewTemplate) {
|
||||
var PaginatedView = Backbone.View.extend({
|
||||
initialize: function () {
|
||||
var ItemListView = ListView.extend({
|
||||
tagName: 'div',
|
||||
className: this.type + '-container',
|
||||
itemViewClass: this.itemViewClass
|
||||
});
|
||||
this.listView = new ItemListView({collection: this.options.collection});
|
||||
this.headerView = this.headerView = new PagingHeader({collection: this.options.collection});
|
||||
this.footerView = new PagingFooter({
|
||||
collection: this.options.collection, hideWhenOnePage: true
|
||||
});
|
||||
this.collection.on('page_changed', function () {
|
||||
this.$('.sr-is-focusable.sr-' + this.type + '-view').focus();
|
||||
}, this);
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.$el.html(_.template(paginatedViewTemplate, {type: this.type}));
|
||||
this.assign(this.listView, '.' + this.type + '-list');
|
||||
this.assign(this.headerView, '.' + this.type + '-paging-header');
|
||||
this.assign(this.footerView, '.' + this.type + '-paging-footer');
|
||||
return this;
|
||||
},
|
||||
|
||||
assign: function (view, selector) {
|
||||
view.setElement(this.$(selector)).render();
|
||||
}
|
||||
});
|
||||
|
||||
return PaginatedView;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -23,7 +23,8 @@
|
||||
var onFirstPage = !this.collection.hasPreviousPage(),
|
||||
onLastPage = !this.collection.hasNextPage();
|
||||
if (this.hideWhenOnePage) {
|
||||
if (this.collection.totalPages <= 1) {
|
||||
if (_.isUndefined(this.collection.totalPages)
|
||||
|| this.collection.totalPages <= 1) {
|
||||
this.$el.addClass('hidden');
|
||||
} else if (this.$el.hasClass('hidden')) {
|
||||
this.$el.removeClass('hidden');
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
render: function () {
|
||||
var message,
|
||||
start = this.collection.start,
|
||||
start = _.isUndefined(this.collection.start) ? 0 : this.collection.start,
|
||||
end = start + this.collection.length,
|
||||
num_items = this.collection.totalCount,
|
||||
num_items = _.isUndefined(this.collection.totalCount) ? 0 : this.collection.totalCount,
|
||||
context = {first_index: Math.min(start + 1, end), last_index: end, num_items: num_items};
|
||||
if (end <= 1) {
|
||||
message = interpolate(gettext('Showing %(first_index)s out of %(num_items)s total'), context, true);
|
||||
|
||||
184
common/static/common/js/spec/components/paginated_view_spec.js
Normal file
184
common/static/common/js/spec/components/paginated_view_spec.js
Normal file
@@ -0,0 +1,184 @@
|
||||
define([
|
||||
'backbone',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'common/js/components/views/paginated_view',
|
||||
'common/js/components/collections/paging_collection'
|
||||
], function (Backbone, _, AjaxHelpers, PaginatedView, PagingCollection) {
|
||||
'use strict';
|
||||
describe('PaginatedView', function () {
|
||||
var TestItemView = Backbone.View.extend({
|
||||
className: 'test-item',
|
||||
tagName: 'div',
|
||||
initialize: function () {
|
||||
this.render();
|
||||
},
|
||||
render: function () {
|
||||
this.$el.text(this.model.get('text'));
|
||||
return this;
|
||||
}
|
||||
}),
|
||||
TestPaginatedView = PaginatedView.extend({type: 'test', itemViewClass: TestItemView}),
|
||||
testCollection,
|
||||
testView,
|
||||
initialItems,
|
||||
nextPageButtonCss = '.next-page-link',
|
||||
previousPageButtonCss = '.previous-page-link',
|
||||
generateItems = function (numItems) {
|
||||
return _.map(_.range(numItems), function (i) {
|
||||
return {
|
||||
text: 'item ' + i
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
setFixtures('<div class="test-container"></div>');
|
||||
initialItems = generateItems(5);
|
||||
testCollection = new PagingCollection({
|
||||
count: 6,
|
||||
num_pages: 2,
|
||||
current_page: 1,
|
||||
start: 0,
|
||||
results: initialItems
|
||||
}, {parse: true});
|
||||
testView = new TestPaginatedView({el: '.test-container', collection: testCollection}).render();
|
||||
});
|
||||
|
||||
/**
|
||||
* Verify that the view's header reflects the page we're currently viewing.
|
||||
* @param matchString the header we expect to see
|
||||
*/
|
||||
function expectHeader(matchString) {
|
||||
expect(testView.$('.test-paging-header').text()).toMatch(matchString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the list view renders the expected items
|
||||
* @param expectedItems an array of topic objects we expect to see
|
||||
*/
|
||||
function expectItems(expectedItems) {
|
||||
var $items = testView.$('.test-item');
|
||||
_.each(expectedItems, function (item, index) {
|
||||
var currentItem = $items.eq(index);
|
||||
expect(currentItem.text()).toMatch(item.text);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the footer reflects the current pagination
|
||||
* @param options a parameters hash containing:
|
||||
* - currentPage: the one-indexed page we expect to be viewing
|
||||
* - totalPages: the total number of pages to page through
|
||||
* - isHidden: whether the footer is expected to be visible
|
||||
*/
|
||||
function expectFooter(options) {
|
||||
var footerEl = testView.$('.test-paging-footer');
|
||||
expect(footerEl.text())
|
||||
.toMatch(new RegExp(options.currentPage + '\\s+out of\\s+\/\\s+' + testCollection.totalPages));
|
||||
expect(footerEl.hasClass('hidden')).toBe(options.isHidden);
|
||||
}
|
||||
|
||||
it('can render the first of many pages', function () {
|
||||
expectHeader('Showing 1-5 out of 6 total');
|
||||
expectItems(initialItems);
|
||||
expectFooter({currentPage: 1, totalPages: 2, isHidden: false});
|
||||
});
|
||||
|
||||
it('can render the only page', function () {
|
||||
initialItems = generateItems(1);
|
||||
testCollection.set(
|
||||
{
|
||||
"count": 1,
|
||||
"num_pages": 1,
|
||||
"current_page": 1,
|
||||
"start": 0,
|
||||
"results": initialItems
|
||||
},
|
||||
{parse: true}
|
||||
);
|
||||
expectHeader('Showing 1 out of 1 total');
|
||||
expectItems(initialItems);
|
||||
expectFooter({currentPage: 1, totalPages: 1, isHidden: true});
|
||||
});
|
||||
|
||||
it('can change to the next page', function () {
|
||||
var requests = AjaxHelpers.requests(this),
|
||||
newItems = generateItems(1);
|
||||
expectHeader('Showing 1-5 out of 6 total');
|
||||
expectItems(initialItems);
|
||||
expectFooter({currentPage: 1, totalPages: 2, isHidden: false});
|
||||
expect(requests.length).toBe(0);
|
||||
testView.$(nextPageButtonCss).click();
|
||||
expect(requests.length).toBe(1);
|
||||
AjaxHelpers.respondWithJson(requests, {
|
||||
"count": 6,
|
||||
"num_pages": 2,
|
||||
"current_page": 2,
|
||||
"start": 5,
|
||||
"results": newItems
|
||||
});
|
||||
expectHeader('Showing 6-6 out of 6 total');
|
||||
expectItems(newItems);
|
||||
expectFooter({currentPage: 2, totalPages: 2, isHidden: false});
|
||||
});
|
||||
|
||||
it('can change to the previous page', function () {
|
||||
var requests = AjaxHelpers.requests(this),
|
||||
previousPageItems;
|
||||
initialItems = generateItems(1);
|
||||
testCollection.set(
|
||||
{
|
||||
"count": 6,
|
||||
"num_pages": 2,
|
||||
"current_page": 2,
|
||||
"start": 5,
|
||||
"results": initialItems
|
||||
},
|
||||
{parse: true}
|
||||
);
|
||||
expectHeader('Showing 6-6 out of 6 total');
|
||||
expectItems(initialItems);
|
||||
expectFooter({currentPage: 2, totalPages: 2, isHidden: false});
|
||||
testView.$(previousPageButtonCss).click();
|
||||
previousPageItems = generateItems(5);
|
||||
AjaxHelpers.respondWithJson(requests, {
|
||||
"count": 6,
|
||||
"num_pages": 2,
|
||||
"current_page": 1,
|
||||
"start": 0,
|
||||
"results": previousPageItems
|
||||
});
|
||||
expectHeader('Showing 1-5 out of 6 total');
|
||||
expectItems(previousPageItems);
|
||||
expectFooter({currentPage: 1, totalPages: 2, isHidden: false});
|
||||
});
|
||||
|
||||
it('sets focus for screen readers', function () {
|
||||
var requests = AjaxHelpers.requests(this);
|
||||
spyOn($.fn, 'focus');
|
||||
testView.$(nextPageButtonCss).click();
|
||||
AjaxHelpers.respondWithJson(requests, {
|
||||
"count": 6,
|
||||
"num_pages": 2,
|
||||
"current_page": 2,
|
||||
"start": 5,
|
||||
"results": generateItems(1)
|
||||
});
|
||||
expect(testView.$('.sr-is-focusable').focus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not change on server error', function () {
|
||||
var requests = AjaxHelpers.requests(this),
|
||||
expectInitialState = function () {
|
||||
expectHeader('Showing 1-5 out of 6 total');
|
||||
expectItems(initialItems);
|
||||
expectFooter({currentPage: 1, totalPages: 2, isHidden: false});
|
||||
};
|
||||
expectInitialState();
|
||||
testView.$(nextPageButtonCss).click();
|
||||
requests[0].respond(500);
|
||||
expectInitialState();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
<div class="sr-is-focusable sr-<%= type %>-view" tabindex="-1"></div>
|
||||
<div class="<%= type %>-paging-header"></div>
|
||||
<div class="<%= type %>-list"></div>
|
||||
<div class="<%= type %>-paging-footer"></div>
|
||||
@@ -156,6 +156,7 @@
|
||||
define([
|
||||
// Run the common tests that use RequireJS.
|
||||
'common-requirejs/include/common/js/spec/components/list_spec.js',
|
||||
'common-requirejs/include/common/js/spec/components/paginated_view_spec.js',
|
||||
'common-requirejs/include/common/js/spec/components/paging_collection_spec.js',
|
||||
'common-requirejs/include/common/js/spec/components/paging_header_spec.js',
|
||||
'common-requirejs/include/common/js/spec/components/paging_footer_spec.js'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Teams page.
|
||||
Teams pages.
|
||||
"""
|
||||
|
||||
from .course_page import CoursePage
|
||||
@@ -9,6 +9,8 @@ from ..common.paging import PaginatedUIMixin
|
||||
|
||||
TOPIC_CARD_CSS = 'div.wrapper-card-core'
|
||||
BROWSE_BUTTON_CSS = 'a.nav-item[data-index="1"]'
|
||||
TEAMS_LINK_CSS = '.action-view'
|
||||
TEAMS_HEADER_CSS = '.teams-header'
|
||||
|
||||
|
||||
class TeamsPage(CoursePage):
|
||||
@@ -53,3 +55,50 @@ class BrowseTopicsPage(CoursePage, PaginatedUIMixin):
|
||||
def topic_cards(self):
|
||||
"""Return a list of the topic cards present on the page."""
|
||||
return self.q(css=TOPIC_CARD_CSS).results
|
||||
|
||||
def browse_teams_for_topic(self, topic_name):
|
||||
"""
|
||||
Show the teams list for `topic_name`.
|
||||
"""
|
||||
self.q(css=TEAMS_LINK_CSS).filter(
|
||||
text='View Teams in the {topic_name} Topic'.format(topic_name=topic_name)
|
||||
)[0].click()
|
||||
self.wait_for_ajax()
|
||||
|
||||
|
||||
class BrowseTeamsPage(CoursePage, PaginatedUIMixin):
|
||||
"""
|
||||
The paginated UI for browsing teams within a Topic on the Teams
|
||||
page.
|
||||
"""
|
||||
def __init__(self, browser, course_id, topic):
|
||||
"""
|
||||
Set up `self.url_path` on instantiation, since it dynamically
|
||||
reflects the current topic. Note that `topic` is a dict
|
||||
representation of a topic following the same convention as a
|
||||
course module's topic.
|
||||
"""
|
||||
super(BrowseTeamsPage, self).__init__(browser, course_id)
|
||||
self.topic = topic
|
||||
self.url_path = "teams/#topics/{topic_id}".format(topic_id=self.topic['id'])
|
||||
|
||||
def is_browser_on_page(self):
|
||||
"""Check if we're on the teams list page for a particular topic."""
|
||||
has_correct_url = self.url.endswith(self.url_path)
|
||||
teams_list_view_present = self.q(css='.teams-main').present
|
||||
return has_correct_url and teams_list_view_present
|
||||
|
||||
@property
|
||||
def header_topic_name(self):
|
||||
"""Get the topic name displayed by the page header"""
|
||||
return self.q(css=TEAMS_HEADER_CSS + ' .page-title')[0].text
|
||||
|
||||
@property
|
||||
def header_topic_description(self):
|
||||
"""Get the topic description displayed by the page header"""
|
||||
return self.q(css=TEAMS_HEADER_CSS + ' .page-description')[0].text
|
||||
|
||||
@property
|
||||
def team_cards(self):
|
||||
"""Get all the team cards on the page."""
|
||||
return self.q(css='.team-card')
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
"""
|
||||
Acceptance tests for the teams feature.
|
||||
"""
|
||||
from ..helpers import UniqueCourseTest
|
||||
from ...pages.lms.teams import TeamsPage, BrowseTopicsPage
|
||||
import json
|
||||
|
||||
from nose.plugins.attrib import attr
|
||||
|
||||
from ..helpers import UniqueCourseTest
|
||||
from ...pages.lms.teams import TeamsPage, BrowseTopicsPage, BrowseTeamsPage
|
||||
from ...fixtures import LMS_BASE_URL
|
||||
from ...fixtures.course import CourseFixture
|
||||
from ...pages.lms.tab_nav import TabNavPage
|
||||
from ...pages.lms.auto_auth import AutoAuthPage
|
||||
from ...pages.lms.course_info import CourseInfoPage
|
||||
|
||||
|
||||
@attr('shard_5')
|
||||
class TeamsTabTest(UniqueCourseTest):
|
||||
"""
|
||||
Tests verifying when the Teams tab is present.
|
||||
"""
|
||||
|
||||
class TeamsTabBase(UniqueCourseTest):
|
||||
"""Base class for Teams Tab tests"""
|
||||
def setUp(self):
|
||||
super(TeamsTabTest, self).setUp()
|
||||
super(TeamsTabBase, self).setUp()
|
||||
self.tab_nav = TabNavPage(self.browser)
|
||||
self.course_info_page = CourseInfoPage(self.browser, self.course_id)
|
||||
self.teams_page = TeamsPage(self.browser, self.course_id)
|
||||
@@ -39,7 +39,8 @@ class TeamsTabTest(UniqueCourseTest):
|
||||
self.course_fixture.install()
|
||||
|
||||
enroll_course_id = self.course_id if enroll_in_course else None
|
||||
AutoAuthPage(self.browser, course_id=enroll_course_id, staff=global_staff).visit()
|
||||
#pylint: disable=attribute-defined-outside-init
|
||||
self.user_info = AutoAuthPage(self.browser, course_id=enroll_course_id, staff=global_staff).visit().user_info
|
||||
self.course_info_page.visit()
|
||||
|
||||
def verify_teams_present(self, present):
|
||||
@@ -54,6 +55,12 @@ class TeamsTabTest(UniqueCourseTest):
|
||||
else:
|
||||
self.assertNotIn("Teams", self.tab_nav.tab_names)
|
||||
|
||||
|
||||
@attr('shard_5')
|
||||
class TeamsTabTest(TeamsTabBase):
|
||||
"""
|
||||
Tests verifying when the Teams tab is present.
|
||||
"""
|
||||
def test_teams_not_enabled(self):
|
||||
"""
|
||||
Scenario: teams tab should not be present if no team configuration is set
|
||||
@@ -118,7 +125,7 @@ class TeamsTabTest(UniqueCourseTest):
|
||||
|
||||
|
||||
@attr('shard_5')
|
||||
class BrowseTopicsTest(TeamsTabTest):
|
||||
class BrowseTopicsTest(TeamsTabBase):
|
||||
"""
|
||||
Tests for the Browse tab of the Teams page.
|
||||
"""
|
||||
@@ -228,3 +235,223 @@ class BrowseTopicsTest(TeamsTabTest):
|
||||
self.assertLess(len(truncated_description), len(initial_description))
|
||||
self.assertTrue(truncated_description.endswith('...'))
|
||||
self.assertIn(truncated_description.split('...')[0], initial_description)
|
||||
|
||||
def test_go_to_teams_list(self):
|
||||
"""
|
||||
Scenario: Clicking on a Topic Card should take you to the
|
||||
teams list for that Topic.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Teams page
|
||||
And I browse topics
|
||||
And I click on the arrow link to view teams for the first topic
|
||||
Then I should be on the browse teams page
|
||||
"""
|
||||
topic = {u"name": u"Example Topic", u"id": u"example_topic", u"description": "Description"}
|
||||
self.set_team_configuration(
|
||||
{u"max_team_size": 1, u"topics": [topic]}
|
||||
)
|
||||
self.topics_page.visit()
|
||||
self.topics_page.browse_teams_for_topic('Example Topic')
|
||||
browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, topic)
|
||||
self.assertTrue(browse_teams_page.is_browser_on_page())
|
||||
self.assertEqual(browse_teams_page.header_topic_name, 'Example Topic')
|
||||
self.assertEqual(browse_teams_page.header_topic_description, 'Description')
|
||||
|
||||
|
||||
@attr('shard_5')
|
||||
class BrowseTeamsWithinTopicTest(TeamsTabBase):
|
||||
"""
|
||||
Tests for browsing Teams within a Topic on the Teams page.
|
||||
"""
|
||||
TEAMS_PAGE_SIZE = 10
|
||||
|
||||
def setUp(self):
|
||||
super(BrowseTeamsWithinTopicTest, self).setUp()
|
||||
self.topic = {u"name": u"Example Topic", u"id": "example_topic", u"description": "Description"}
|
||||
self.set_team_configuration({'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]})
|
||||
self.browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, self.topic)
|
||||
|
||||
def create_teams(self, num_teams):
|
||||
"""Create `num_teams` teams belonging to `self.topic`."""
|
||||
teams = []
|
||||
for i in xrange(num_teams):
|
||||
team = {
|
||||
'course_id': self.course_id,
|
||||
'topic_id': self.topic['id'],
|
||||
'name': 'Team {}'.format(i),
|
||||
'description': 'Description {}'.format(i)
|
||||
}
|
||||
response = self.course_fixture.session.post(
|
||||
LMS_BASE_URL + '/api/team/v0/teams/',
|
||||
data=json.dumps(team),
|
||||
headers=self.course_fixture.headers
|
||||
)
|
||||
teams.append(json.loads(response.text))
|
||||
return teams
|
||||
|
||||
def create_membership(self, username, team_id):
|
||||
"""Assign `username` to `team_id`."""
|
||||
response = self.course_fixture.session.post(
|
||||
LMS_BASE_URL + '/api/team/v0/team_membership/',
|
||||
data=json.dumps({'username': username, 'team_id': team_id}),
|
||||
headers=self.course_fixture.headers
|
||||
)
|
||||
return json.loads(response.text)
|
||||
|
||||
def verify_page_header(self):
|
||||
"""Verify that the page header correctly reflects the current topic's name and description."""
|
||||
self.assertEqual(self.browse_teams_page.header_topic_name, self.topic['name'])
|
||||
self.assertEqual(self.browse_teams_page.header_topic_description, self.topic['description'])
|
||||
|
||||
def verify_teams(self, expected_teams):
|
||||
"""Verify that the list of team cards on the current page match the expected teams in order."""
|
||||
|
||||
def assert_team_equal(expected_team, team_card_name, team_card_description):
|
||||
"""
|
||||
Helper to assert that a single team card has the expected name and
|
||||
description.
|
||||
"""
|
||||
self.assertEqual(expected_team['name'], team_card_name)
|
||||
self.assertEqual(expected_team['description'], team_card_description)
|
||||
|
||||
team_cards = self.browse_teams_page.team_cards
|
||||
team_card_names = [
|
||||
team_card.find_element_by_css_selector('.card-title').text
|
||||
for team_card in team_cards.results
|
||||
]
|
||||
team_card_descriptions = [
|
||||
team_card.find_element_by_css_selector('.card-description').text
|
||||
for team_card in team_cards.results
|
||||
]
|
||||
map(assert_team_equal, expected_teams, team_card_names, team_card_descriptions)
|
||||
|
||||
def verify_on_page(self, page_num, total_teams, pagination_header_text, footer_visible):
|
||||
"""
|
||||
Verify that we are on the correct team list page.
|
||||
|
||||
Arguments:
|
||||
page_num (int): The one-indexed page we expect to be on
|
||||
total_teams (list): An unsorted list of all the teams for the
|
||||
current topic
|
||||
pagination_header_text (str): Text we expect to see in the
|
||||
pagination header.
|
||||
footer_visible (bool): Whether we expect to see the pagination
|
||||
footer controls.
|
||||
"""
|
||||
alphabetized_teams = sorted(total_teams, key=lambda team: team['name'])
|
||||
self.assertEqual(self.browse_teams_page.get_pagination_header_text(), pagination_header_text)
|
||||
self.verify_teams(alphabetized_teams[(page_num - 1) * self.TEAMS_PAGE_SIZE:page_num * self.TEAMS_PAGE_SIZE])
|
||||
self.assertEqual(
|
||||
self.browse_teams_page.pagination_controls_visible(),
|
||||
footer_visible,
|
||||
msg='Expected paging footer to be ' + 'visible' if footer_visible else 'invisible'
|
||||
)
|
||||
|
||||
def test_no_teams(self):
|
||||
"""
|
||||
Scenario: Visiting a topic with no teams should not display any teams.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Teams page for that topic
|
||||
Then I should see the correct page header
|
||||
And I should see a pagination header showing no teams
|
||||
And I should see no teams
|
||||
And I should see a button to add a team
|
||||
And I should not see a pagination footer
|
||||
"""
|
||||
self.browse_teams_page.visit()
|
||||
self.verify_page_header()
|
||||
self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 0 out of 0 total')
|
||||
self.assertEqual(len(self.browse_teams_page.team_cards), 0, msg='Expected to see no team cards')
|
||||
self.assertFalse(
|
||||
self.browse_teams_page.pagination_controls_visible(),
|
||||
msg='Expected paging footer to be invisible'
|
||||
)
|
||||
|
||||
def test_teams_one_page(self):
|
||||
"""
|
||||
Scenario: Visiting a topic with fewer teams than the page size should
|
||||
all those teams on one page.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Teams page for that topic
|
||||
Then I should see the correct page header
|
||||
And I should see a pagination header showing the number of teams
|
||||
And I should see all the expected team cards
|
||||
And I should see a button to add a team
|
||||
And I should not see a pagination footer
|
||||
"""
|
||||
teams = self.create_teams(self.TEAMS_PAGE_SIZE)
|
||||
self.browse_teams_page.visit()
|
||||
self.verify_page_header()
|
||||
self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 1-10 out of 10 total')
|
||||
self.verify_teams(teams)
|
||||
self.assertFalse(
|
||||
self.browse_teams_page.pagination_controls_visible(),
|
||||
msg='Expected paging footer to be invisible'
|
||||
)
|
||||
|
||||
def test_teams_navigation_buttons(self):
|
||||
"""
|
||||
Scenario: The user should be able to page through a topic's team list
|
||||
using navigation buttons when it is longer than the page size.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Teams page for that topic
|
||||
Then I should see the correct page header
|
||||
And I should see that I am on the first page of results
|
||||
When I click on the next page button
|
||||
Then I should see that I am on the second page of results
|
||||
And when I click on the previous page button
|
||||
Then I should see that I am on the first page of results
|
||||
"""
|
||||
teams = self.create_teams(self.TEAMS_PAGE_SIZE + 1)
|
||||
self.browse_teams_page.visit()
|
||||
self.verify_page_header()
|
||||
self.verify_on_page(1, teams, 'Showing 1-10 out of 11 total', True)
|
||||
self.browse_teams_page.press_next_page_button()
|
||||
self.verify_on_page(2, teams, 'Showing 11-11 out of 11 total', True)
|
||||
self.browse_teams_page.press_previous_page_button()
|
||||
self.verify_on_page(1, teams, 'Showing 1-10 out of 11 total', True)
|
||||
|
||||
def test_teams_page_input(self):
|
||||
"""
|
||||
Scenario: The user should be able to page through a topic's team list
|
||||
using the page input when it is longer than the page size.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
When I visit the Teams page for that topic
|
||||
Then I should see the correct page header
|
||||
And I should see that I am on the first page of results
|
||||
When I input the second page
|
||||
Then I should see that I am on the second page of results
|
||||
When I input the first page
|
||||
Then I should see that I am on the first page of results
|
||||
"""
|
||||
teams = self.create_teams(self.TEAMS_PAGE_SIZE + 10)
|
||||
self.browse_teams_page.visit()
|
||||
self.verify_page_header()
|
||||
self.verify_on_page(1, teams, 'Showing 1-10 out of 20 total', True)
|
||||
self.browse_teams_page.go_to_page(2)
|
||||
self.verify_on_page(2, teams, 'Showing 11-20 out of 20 total', True)
|
||||
self.browse_teams_page.go_to_page(1)
|
||||
self.verify_on_page(1, teams, 'Showing 1-10 out of 20 total', True)
|
||||
|
||||
def test_teams_membership(self):
|
||||
"""
|
||||
Scenario: Team cards correctly reflect membership of the team.
|
||||
Given I am enrolled in a course with a team configuration and a topic
|
||||
containing one team
|
||||
And I add myself to the team
|
||||
When I visit the Teams page for that topic
|
||||
Then I should see the correct page header
|
||||
And I should see the team for that topic
|
||||
And I should see that the team card shows my membership
|
||||
"""
|
||||
teams = self.create_teams(1)
|
||||
self.browse_teams_page.visit()
|
||||
self.verify_page_header()
|
||||
self.verify_teams(teams)
|
||||
self.create_membership(self.user_info['username'], teams[0]['id'])
|
||||
self.browser.refresh()
|
||||
self.browse_teams_page.wait_for_ajax()
|
||||
self.assertEqual(
|
||||
self.browse_teams_page.team_cards[0].find_element_by_css_selector('.member-count').text,
|
||||
'1 / 10 Members'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user