diff --git a/common/test/acceptance/pages/lms/teams.py b/common/test/acceptance/pages/lms/teams.py index 9ad67de7c8..89805bf23e 100644 --- a/common/test/acceptance/pages/lms/teams.py +++ b/common/test/acceptance/pages/lms/teams.py @@ -102,6 +102,11 @@ class TeamsPage(CoursePage): """ Click on the breadcrumb for a specific topic """ self.q(css='a.nav-item').filter(text=topic)[0].click() + @property + def warning_message(self): + """Return the text of the team warning message.""" + return self.q(css='.warning').results[0].text + class MyTeamsPage(CoursePage, PaginatedUIMixin, TeamCardsMixin): """ diff --git a/common/test/acceptance/tests/lms/test_teams.py b/common/test/acceptance/tests/lms/test_teams.py index c071613fbd..b8ba0105aa 100644 --- a/common/test/acceptance/tests/lms/test_teams.py +++ b/common/test/acceptance/tests/lms/test_teams.py @@ -197,6 +197,42 @@ class TeamsTabTest(TeamsTabBase): ) self.verify_teams_present(True) + @ddt.data( + 'topics/{topic_id}', + 'topics/{topic_id}/search', + 'topics/{topic_id}/{team_id}/edit-team', + 'teams/{topic_id}/{team_id}' + ) + def test_unauthorized_error_message(self, route): + """Ensure that an error message is shown to the user if they attempt + to take an action which makes an AJAX request while not signed + in. + """ + topics = self.create_topics(1) + topic = topics[0] + self.set_team_configuration({ + u'max_team_size': 10, + u'topics': topics + }) + team = self.create_teams(topic, 1)[0] + self.teams_page.visit() + self.browser.delete_cookie('sessionid') + url = self.browser.current_url.split('#')[0] + self.browser.get( + '{url}#{route}'.format( + url=url, + route=route.format( + topic_id=topic['id'], + team_id=team['id'] + ) + ) + ) + self.teams_page.wait_for_ajax() + self.assertEqual( + self.teams_page.warning_message, + u"Your request could not be completed. Reload the page and try again." + ) + @ddt.data( ('browse', '.topics-list'), # TODO: find a reliable way to match the "My Teams" tab diff --git a/lms/djangoapps/teams/static/teams/js/spec/teams_tab_factory_spec.js b/lms/djangoapps/teams/static/teams/js/spec/teams_tab_factory_spec.js index c32a0827b5..ee4639e6a4 100644 --- a/lms/djangoapps/teams/static/teams/js/spec/teams_tab_factory_spec.js +++ b/lms/djangoapps/teams/static/teams/js/spec/teams_tab_factory_spec.js @@ -4,8 +4,6 @@ define(['jquery', 'backbone', 'teams/js/teams_tab_factory', 'use strict'; describe("Teams Tab Factory", function() { - var teamsTab; - var initializeTeamsTabFactory = function() { TeamsTabFactory(TeamSpecHelpers.createMockContext()); }; @@ -19,6 +17,11 @@ define(['jquery', 'backbone', 'teams/js/teams_tab_factory', }); it('can render the "Teams" tab', function() { + // Hack to make sure the URL fragments from earlier + // tests don't interfere with Backbone routing by the + // teams tab view + document.location.hash = ''; + initializeTeamsTabFactory(); expect($('.teams-content').text()).toContain('See all teams in your course, organized by topic'); }); diff --git a/lms/djangoapps/teams/static/teams/js/spec/views/teams_tab_spec.js b/lms/djangoapps/teams/static/teams/js/spec/views/teams_tab_spec.js index 786cb08b63..922db6b5f5 100644 --- a/lms/djangoapps/teams/static/teams/js/spec/views/teams_tab_spec.js +++ b/lms/djangoapps/teams/static/teams/js/spec/views/teams_tab_spec.js @@ -39,9 +39,7 @@ define([ spyOn($.fn, 'focus'); }); - afterEach(function () { - Backbone.history.stop(); - }); + afterEach(Backbone.history.stop); describe('Navigation', function () { it('displays and focuses an error message when trying to navigate to a nonexistent page', function () { @@ -76,6 +74,24 @@ define([ expectError(teamsTabView, 'The team "no_such_team" could not be found.'); expectFocus(teamsTabView.$('.warning')); }); + + it('displays and focuses an error message when it receives a 401 AJAX response', function () { + var requests = AjaxHelpers.requests(this), + teamsTabView = createTeamsTabView().render(); + teamsTabView.router.navigate('topics/' + TeamSpecHelpers.testTopicID, {trigger: true}); + AjaxHelpers.respondWithError(requests, 401); + expectError(teamsTabView, "Your request could not be completed. Reload the page and try again."); + expectFocus(teamsTabView.$('.warning')); + }); + + it('displays and focuses an error message when it receives a 500 AJAX response', function () { + var requests = AjaxHelpers.requests(this), + teamsTabView = createTeamsTabView().render(); + teamsTabView.router.navigate('topics/' + TeamSpecHelpers.testTopicID, {trigger: true}); + AjaxHelpers.respondWithError(requests, 500); + expectError(teamsTabView, "Your request could not be completed due to a server problem. Reload the page and try again. If the issue persists, click the Help tab to report the problem."); + expectFocus(teamsTabView.$('.warning')); + }); }); describe('Discussion privileges', function () { diff --git a/lms/djangoapps/teams/static/teams/js/teams_tab_factory.js b/lms/djangoapps/teams/static/teams/js/teams_tab_factory.js index d736d4da99..bd4c4f8f67 100644 --- a/lms/djangoapps/teams/static/teams/js/teams_tab_factory.js +++ b/lms/djangoapps/teams/static/teams/js/teams_tab_factory.js @@ -1,8 +1,7 @@ ;(function (define) { 'use strict'; - - define(['jquery', 'underscore', 'backbone', 'teams/js/views/teams_tab'], - function ($, _, Backbone, TeamsTabView) { + define(['jquery', 'teams/js/views/teams_tab'], + function ($, TeamsTabView) { return function (options) { var teamsTab = new TeamsTabView({ el: $('.teams-content'), diff --git a/lms/djangoapps/teams/static/teams/js/views/teams_tab.js b/lms/djangoapps/teams/static/teams/js/views/teams_tab.js index ae36b4fd09..21becabd75 100644 --- a/lms/djangoapps/teams/static/teams/js/views/teams_tab.js +++ b/lms/djangoapps/teams/static/teams/js/views/teams_tab.js @@ -19,11 +19,12 @@ 'teams/js/views/topic_teams', 'teams/js/views/edit_team', 'teams/js/views/team_profile_header_actions', + 'teams/js/views/team_utils', 'text!teams/templates/teams_tab.underscore'], function (Backbone, _, gettext, SearchFieldView, HeaderView, HeaderModel, TabbedView, TopicModel, TopicCollection, TeamModel, TeamCollection, TeamMembershipCollection, TopicsView, TeamProfileView, MyTeamsView, TopicTeamsView, TeamEditView, - TeamProfileHeaderActionsView, teamsTemplate) { + TeamProfileHeaderActionsView, TeamUtils, teamsTemplate) { var TeamsHeaderModel = HeaderModel.extend({ initialize: function () { _.extend(this.defaults, {nav_aria_label: gettext('teams')}); @@ -144,6 +145,15 @@ start: function() { Backbone.history.start(); + $(document).ajaxError(function (event, xhr) { + if (xhr.status === 401) { + TeamUtils.showMessage(gettext("Your request could not be completed. Reload the page and try again.")); + } + else if (xhr.status === 500) { + TeamUtils.showMessage(gettext("Your request could not be completed due to a server problem. Reload the page and try again. If the issue persists, click the Help tab to report the problem.")); + } + }); + // Navigate to the default page if there is no history: // 1. If the user belongs to at least one team, jump to the "My Teams" page // 2. If not, then jump to the "Browse" page