diff --git a/common/test/acceptance/pages/lms/teams.py b/common/test/acceptance/pages/lms/teams.py index cf24e937df..1f3779efaf 100644 --- a/common/test/acceptance/pages/lms/teams.py +++ b/common/test/acceptance/pages/lms/teams.py @@ -205,7 +205,7 @@ class BrowseTeamsPage(CoursePage, PaginatedUIMixin): self.wait_for_ajax() -class CreateTeamPage(CoursePage, FieldsMixin): +class CreateOrEditTeamPage(CoursePage, FieldsMixin): """ Create team page. """ @@ -216,7 +216,7 @@ class CreateTeamPage(CoursePage, FieldsMixin): representation of a topic following the same convention as a course module's topic. """ - super(CreateTeamPage, self).__init__(browser, course_id) + super(CreateOrEditTeamPage, self).__init__(browser, course_id) self.topic = topic self.url_path = "teams/#topics/{topic_id}/create-team".format(topic_id=self.topic['id']) @@ -410,3 +410,12 @@ class TeamPage(CoursePage, PaginatedUIMixin): """Navigate to the 'All Topics' page.""" self.q(css='.breadcrumbs a').results[0].click() self.wait_for_ajax() + + @property + def edit_team_button_present(self): + """ Returns True if Edit Team button is present else False """ + return self.q(css='.form-actions .action-edit-team').present + + def click_edit_team_button(self): + """ Click on Edit Team button""" + self.q(css='.form-actions .action-edit-team').first.click() diff --git a/common/test/acceptance/tests/lms/test_teams.py b/common/test/acceptance/tests/lms/test_teams.py index 3e9c36367b..cc851343d4 100644 --- a/common/test/acceptance/tests/lms/test_teams.py +++ b/common/test/acceptance/tests/lms/test_teams.py @@ -20,7 +20,7 @@ from ...pages.lms.auto_auth import AutoAuthPage from ...pages.lms.course_info import CourseInfoPage from ...pages.lms.learner_profile import LearnerProfilePage from ...pages.lms.tab_nav import TabNavPage -from ...pages.lms.teams import TeamsPage, MyTeamsPage, BrowseTopicsPage, BrowseTeamsPage, CreateTeamPage, TeamPage +from ...pages.lms.teams import TeamsPage, MyTeamsPage, BrowseTopicsPage, BrowseTeamsPage, CreateOrEditTeamPage, TeamPage TOPICS_PER_PAGE = 12 @@ -335,7 +335,7 @@ class BrowseTopicsTest(TeamsTabBase): browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, topic) self.assertTrue(browse_teams_page.is_browser_on_page()) browse_teams_page.click_create_team_link() - create_team_page = CreateTeamPage(self.browser, self.course_id, topic) + create_team_page = CreateOrEditTeamPage(self.browser, self.course_id, topic) create_team_page.value_for_text_field(field_id='name', value='Team Name', press_enter=False) create_team_page.value_for_textarea_field( field_id='description', @@ -629,47 +629,109 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase): @attr('shard_5') -class CreateTeamTest(TeamsTabBase): +class TeamFormActions(TeamsTabBase): + """ + Base class for create & edit team. + """ + TEAM_DESCRIPTION = 'The Avengers are a fictional team of superheroes.' + + topic = {'name': 'Example Topic', 'id': 'example_topic', 'description': 'Description'} + TEAMS_NAME = 'Avengers' + + def verify_page_header(self, title, description, breadcrumbs): + """ + Verify that the page header correctly reflects the + create team header, description and breadcrumb. + """ + self.assertEqual(self.create_or_edit_team_page.header_page_name, title) + self.assertEqual(self.create_or_edit_team_page.header_page_description, description) + self.assertEqual(self.create_or_edit_team_page.header_page_breadcrumbs, breadcrumbs) + + def verify_and_navigate_to_create_team_page(self): + """Navigates to the create team page and verifies.""" + self.browse_teams_page.click_create_team_link() + self.verify_page_header( + title='Create a New Team', + description='Create a new team if you can\'t find existing teams to ' + 'join, or if you would like to learn with friends you know.', + breadcrumbs=self.topic['name'] + ) + + def verify_and_navigate_to_edit_team_page(self): + """Navigates to the edit team page and verifies.""" + # pylint: disable=no-member + self.assertEqual(self.team_page.team_name, self.team['name']) + self.assertTrue(self.team_page.edit_team_button_present) + + self.team_page.click_edit_team_button() + + self.create_or_edit_team_page.wait_for_page() + + # Edit page header. + self.verify_page_header( + title='Edit Team', + description='If you make significant changes, make sure you notify ' + 'members of the team before making these changes.', + breadcrumbs='All Topics {topic_name} {team_name}'.format( + topic_name=self.topic['name'], + team_name=self.team['name'] + ) + ) + + def verify_team_info(self, name, description, location, language): + """Verify the team information on team page.""" + # pylint: disable=no-member + self.assertEqual(self.team_page.team_name, name) + self.assertEqual(self.team_page.team_description, description) + self.assertEqual(self.team_page.team_location, location) + self.assertEqual(self.team_page.team_language, language) + + def fill_create_or_edit_form(self): + """Fill the create/edit team form fields with appropriate values.""" + self.create_or_edit_team_page.value_for_text_field(field_id='name', value=self.TEAMS_NAME, press_enter=False) + self.create_or_edit_team_page.value_for_textarea_field( + field_id='description', + value=self.TEAM_DESCRIPTION + ) + self.create_or_edit_team_page.value_for_dropdown_field(field_id='language', value='English') + self.create_or_edit_team_page.value_for_dropdown_field(field_id='country', value='Pakistan') + + def verify_all_fields_exist(self): + """ + Verify the fields for create/edit page. + """ + self.assertEqual( + self.create_or_edit_team_page.message_for_field('name'), + 'A name that identifies your team (maximum 255 characters).' + ) + self.assertEqual( + self.create_or_edit_team_page.message_for_textarea_field('description'), + 'A short description of the team to help other learners understand ' + 'the goals or direction of the team (maximum 300 characters).' + ) + self.assertEqual( + self.create_or_edit_team_page.message_for_field('country'), + 'The country that team members primarily identify with.' + ) + self.assertEqual( + self.create_or_edit_team_page.message_for_field('language'), + 'The language that team members primarily use to communicate with each other.' + ) + + +@ddt.ddt +class CreateTeamTest(TeamFormActions): """ Tests for creating a new Team within a Topic on the Teams page. """ def setUp(self): super(CreateTeamTest, self).setUp() - self.topic = {'name': 'Example Topic', 'id': 'example_topic', 'description': 'Description'} self.set_team_configuration({'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]}) + + self.create_or_edit_team_page = CreateOrEditTeamPage(self.browser, self.course_id, self.topic) self.browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, self.topic) self.browse_teams_page.visit() - self.create_team_page = CreateTeamPage(self.browser, self.course_id, self.topic) - self.team_name = 'Avengers' - - def verify_page_header(self): - """ - Verify that the page header correctly reflects the - create team header, description and breadcrumb. - """ - self.assertEqual(self.create_team_page.header_page_name, 'Create a New Team') - self.assertEqual( - self.create_team_page.header_page_description, - 'Create a new team if you can\'t find existing teams to join, ' - 'or if you would like to learn with friends you know.' - ) - self.assertEqual(self.create_team_page.header_page_breadcrumbs, self.topic['name']) - - def verify_and_navigate_to_create_team_page(self): - """Navigates to the create team page and verifies.""" - self.browse_teams_page.click_create_team_link() - self.verify_page_header() - - def fill_create_form(self): - """Fill the create team form fields with appropriate values.""" - self.create_team_page.value_for_text_field(field_id='name', value=self.team_name, press_enter=False) - self.create_team_page.value_for_textarea_field( - field_id='description', - value='The Avengers are a fictional team of superheroes.' - ) - self.create_team_page.value_for_dropdown_field(field_id='language', value='English') - self.create_team_page.value_for_dropdown_field(field_id='country', value='Pakistan') def test_user_can_see_create_team_page(self): """ @@ -683,23 +745,7 @@ class CreateTeamTest(TeamsTabBase): And I should also see the help messages for fields. """ self.verify_and_navigate_to_create_team_page() - self.assertEqual( - self.create_team_page.message_for_field('name'), - 'A name that identifies your team (maximum 255 characters).' - ) - self.assertEqual( - self.create_team_page.message_for_textarea_field('description'), - 'A short description of the team to help other learners understand ' - 'the goals or direction of the team (maximum 300 characters).' - ) - self.assertEqual( - self.create_team_page.message_for_field('country'), - 'The country that team members primarily identify with.' - ) - self.assertEqual( - self.create_team_page.message_for_field('language'), - 'The language that team members primarily use to communicate with each other.' - ) + self.verify_all_fields_exist() def test_user_can_see_error_message_for_missing_data(self): """ @@ -711,14 +757,14 @@ class CreateTeamTest(TeamsTabBase): Then I should see the error message and highlighted fields. """ self.verify_and_navigate_to_create_team_page() - self.create_team_page.submit_form() + self.create_or_edit_team_page.submit_form() self.assertEqual( - self.create_team_page.validation_message_text, + self.create_or_edit_team_page.validation_message_text, 'Check the highlighted fields below and try again.' ) - self.assertTrue(self.create_team_page.error_for_field(field_id='name')) - self.assertTrue(self.create_team_page.error_for_field(field_id='description')) + self.assertTrue(self.create_or_edit_team_page.error_for_field(field_id='name')) + self.assertTrue(self.create_or_edit_team_page.error_for_field(field_id='description')) def test_user_can_see_error_message_for_incorrect_data(self): """ @@ -733,7 +779,7 @@ class CreateTeamTest(TeamsTabBase): self.verify_and_navigate_to_create_team_page() # Fill the name field with >255 characters to see validation message. - self.create_team_page.value_for_text_field( + self.create_or_edit_team_page.value_for_text_field( field_id='name', value='EdX is a massive open online course (MOOC) provider and online learning platform. ' 'It hosts online university-level courses in a wide range of disciplines to a worldwide ' @@ -745,13 +791,13 @@ class CreateTeamTest(TeamsTabBase): 'edX has more than 4 million users taking more than 500 courses online.', press_enter=False ) - self.create_team_page.submit_form() + self.create_or_edit_team_page.submit_form() self.assertEqual( - self.create_team_page.validation_message_text, + self.create_or_edit_team_page.validation_message_text, 'Check the highlighted fields below and try again.' ) - self.assertTrue(self.create_team_page.error_for_field(field_id='name')) + self.assertTrue(self.create_or_edit_team_page.error_for_field(field_id='name')) def test_user_can_create_new_team_successfully(self): """ @@ -767,16 +813,19 @@ class CreateTeamTest(TeamsTabBase): And the number of teams should be updated on the topic card And if I switch to "My Team", the newly created team is displayed """ + AutoAuthPage(self.browser, course_id=self.course_id).visit() + self.browse_teams_page.visit() + self.verify_and_navigate_to_create_team_page() - self.fill_create_form() - self.create_team_page.submit_form() + self.fill_create_or_edit_form() + self.create_or_edit_team_page.submit_form() # Verify that the page is shown for the new team team_page = TeamPage(self.browser, self.course_id) team_page.wait_for_page() - self.assertEqual(team_page.team_name, self.team_name) - self.assertEqual(team_page.team_description, 'The Avengers are a fictional team of superheroes.') + self.assertEqual(team_page.team_name, self.TEAMS_NAME) + self.assertEqual(team_page.team_description, self.TEAM_DESCRIPTION) self.assertEqual(team_page.team_user_membership_text, 'You are a member of this team.') # Verify the new team was added to the topic list @@ -802,7 +851,7 @@ class CreateTeamTest(TeamsTabBase): self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 0 out of 0 total') self.verify_and_navigate_to_create_team_page() - self.create_team_page.cancel_team() + self.create_or_edit_team_page.cancel_team() self.assertTrue(self.browse_teams_page.is_browser_on_page()) self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 0 out of 0 total') @@ -813,6 +862,157 @@ class CreateTeamTest(TeamsTabBase): self.verify_my_team_count(0) +@ddt.ddt +class EditTeamTest(TeamFormActions): + """ + Tests for editing the team. + """ + + def setUp(self): + super(EditTeamTest, self).setUp() + + self.set_team_configuration( + {'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]}, + global_staff=True + ) + self.create_or_edit_team_page = CreateOrEditTeamPage(self.browser, self.course_id, self.topic) + + self.team = self.create_teams(self.topic, num_teams=1)[0] + self.team_page = TeamPage(self.browser, self.course_id, team=self.team) + self.team_page.visit() + + def test_staff_can_navigate_to_edit_team_page(self): + """ + Scenario: The user should be able to see and navigate to the edit team page. + Given I am staff user for a course with a team + When I visit the Team profile page + Then I should see the Edit Team button + And When I click edit team button + Then I should see the edit team page + And I should see the edit team header + And I should also see the help messages for fields + """ + self.verify_and_navigate_to_edit_team_page() + self.verify_all_fields_exist() + + def test_staff_can_edit_team_successfully(self): + """ + Scenario: The staff should be able to edit team successfully. + Given I am staff user for a course with a team + When I visit the Team profile page + Then I should see the Edit Team button + And When I click edit team button + Then I should see the edit team page + When I edit all the fields with appropriate data + And I click Update button + Then I should see the page for my team with updated data + """ + self.verify_team_info( + name=self.team['name'], + description=self.team['description'], + location='Afghanistan', + language='Afar' + ) + self.verify_and_navigate_to_edit_team_page() + + self.fill_create_or_edit_form() + self.create_or_edit_team_page.submit_form() + + self.team_page.wait_for_page() + + self.verify_team_info( + name=self.TEAMS_NAME, + description=self.TEAM_DESCRIPTION, + location='Pakistan', + language='English' + ) + + def test_staff_can_cancel_the_team_edit(self): + """ + Scenario: The user should be able to cancel the editing of team. + Given I am staff user for a course with a team + When I visit the Team profile page + Then I should see the Edit Team button + And When I click edit team button + Then I should see the edit team page + Then I should see the Edit Team header + When I click Cancel button + Then I should see team page page without changes. + """ + self.verify_team_info( + name=self.team['name'], + description=self.team['description'], + location='Afghanistan', + language='Afar' + ) + + self.verify_and_navigate_to_edit_team_page() + + self.fill_create_or_edit_form() + self.create_or_edit_team_page.cancel_team() + + self.team_page.wait_for_page() + + self.verify_team_info( + name=self.team['name'], + description=self.team['description'], + location='Afghanistan', + language='Afar' + ) + + def test_student_cannot_see_edit_button(self): + """ + Scenario: The student should not see the edit team button. + Given I am student for a course with a team + When I visit the Team profile page + Then I should not see the Edit Team button + """ + AutoAuthPage(self.browser, course_id=self.course_id).visit() + self.team_page.visit() + self.assertFalse(self.team_page.edit_team_button_present) + + @ddt.data('Moderator', 'Community TA', 'Administrator') + def test_discussion_privileged_user_can_edit_team(self, role): + """ + Scenario: The user with specified role should see the edit team button. + Given I am user with privileged role for a course with a team + When I visit the Team profile page + Then I should see the Edit Team button + """ + kwargs = { + 'course_id': self.course_id, + 'staff': False + } + if role is not None: + kwargs['roles'] = role + + AutoAuthPage(self.browser, **kwargs).visit() + + self.team_page.visit() + self.teams_page.wait_for_page() + self.assertTrue(self.team_page.edit_team_button_present) + + self.verify_team_info( + name=self.team['name'], + description=self.team['description'], + location='Afghanistan', + language='Afar' + ) + self.verify_and_navigate_to_edit_team_page() + + self.fill_create_or_edit_form() + self.create_or_edit_team_page.submit_form() + + self.team_page.wait_for_page() + + self.verify_team_info( + name=self.TEAMS_NAME, + description=self.TEAM_DESCRIPTION, + location='Pakistan', + language='English' + ) + + @attr('shard_5') @ddt.ddt class TeamPageTest(TeamsTabBase): diff --git a/lms/djangoapps/teams/static/teams/js/spec/views/edit_team_spec.js b/lms/djangoapps/teams/static/teams/js/spec/views/edit_team_spec.js index 427ea232d5..5a68d3163b 100644 --- a/lms/djangoapps/teams/static/teams/js/spec/views/edit_team_spec.js +++ b/lms/djangoapps/teams/static/teams/js/spec/views/edit_team_spec.js @@ -4,13 +4,14 @@ define([ 'backbone', 'common/js/spec_helpers/ajax_helpers', 'teams/js/views/edit_team', + 'teams/js/models/team', 'teams/js/spec_helpers/team_spec_helpers' -], function ($, _, Backbone, AjaxHelpers, TeamEditView, TeamSpecHelpers) { +], function ($, _, Backbone, AjaxHelpers, TeamEditView, TeamModel, TeamSpecHelpers) { 'use strict'; - describe('EditTeam', function () { + describe('CreateEditTeam', function() { var teamsUrl = '/api/team/v0/teams/', - teamsData = { + createTeamData = { id: null, name: "TeamName", is_active: null, @@ -18,11 +19,17 @@ define([ topic_id: "awesomeness", date_created: "", description: "TeamDescription", - country: "c", - language: "a", + country: "US", + language: "en", membership: [], last_activity_at: '' }, + editTeamData = { + name: "UpdatedAvengers", + description: "We do not discuss about avengers.", + country: "US", + language: "en" + }, verifyValidation = function (requests, teamEditView, fieldsData) { _.each(fieldsData, function (fieldData) { teamEditView.$(fieldData[0]).val(fieldData[1]); @@ -32,33 +39,57 @@ define([ var message = teamEditView.$('.wrapper-msg'); expect(message.hasClass('is-hidden')).toBeFalsy(); - expect(message.find('.title').text().trim()).toBe("Your team could not be created!"); + var actionMessage = (teamAction === 'create' ? 'Your team could not be created.' : 'Your team could not be updated.'); + expect(message.find('.title').text().trim()).toBe(actionMessage); expect(message.find('.copy').text().trim()).toBe( "Check the highlighted fields below and try again." ); _.each(fieldsData, function (fieldData) { - if(fieldData[2] === 'error') { + if (fieldData[2] === 'error') { expect(teamEditView.$(fieldData[0].split(" ")[0] + '.error').length).toBe(1); - } else if(fieldData[2] === 'success') { + } else if (fieldData[2] === 'success') { expect(teamEditView.$(fieldData[0].split(" ")[0] + '.error').length).toBe(0); } }); expect(requests.length).toBe(0); - }; + }, + editTeamID = 'av', + teamAction; + + var createEditTeamView = function () { + var teamModel = {}; + if (teamAction === 'edit') { + teamModel = new TeamModel( + { + id: editTeamID, + name: 'Avengers', + description: 'Team of dumbs', + language: 'en', + country: 'US', + membership: [], + url: '/api/team/v0/teams/' + editTeamID + }, + { + parse: true + } + ); + } - var createTeamEditView = function() { return new TeamEditView({ teamEvents: TeamSpecHelpers.teamEvents, el: $('.teams-content'), + action: teamAction, + model: teamModel, teamParams: { teamsUrl: teamsUrl, courseID: "a/b/c", topicID: 'awesomeness', topicName: 'Awesomeness', - languages: [['a', 'aaa'], ['b', 'bbb']], - countries: [['c', 'ccc'], ['d', 'ddd']] + languages: [['aa', 'Afar'], ['fr', 'French'], ['en', 'English']], + countries: [['af', 'Afghanistan'], ['CA', 'Canada'], ['US', 'United States']], + teamsDetailUrl: teamModel.url } }).render(); }; @@ -68,15 +99,15 @@ define([ spyOn(Backbone.history, 'navigate'); }); - it('can render itself correctly', function () { + var assertFormRendersCorrectly = function() { var fieldClasses = [ - '.u-field-name', - '.u-field-description', - '.u-field-optional_description', - '.u-field-language', - '.u-field-country' - ], - teamEditView = createTeamEditView(); + '.u-field-name', + '.u-field-description', + '.u-field-optional_description', + '.u-field-language', + '.u-field-country' + ], + teamEditView = createEditTeamView(); _.each(fieldClasses, function (fieldClass) { expect(teamEditView.$el.find(fieldClass).length).toBe(1); @@ -84,32 +115,46 @@ define([ expect(teamEditView.$('.create-team.form-actions .action-primary').length).toBe(1); expect(teamEditView.$('.create-team.form-actions .action-cancel').length).toBe(1); - }); - it('can create a team', function () { - var requests = AjaxHelpers.requests(this), - teamEditView = createTeamEditView(); + if (teamAction === 'edit') { + expect(teamEditView.$el.find('.u-field-name input').val()).toBe('Avengers'); + expect(teamEditView.$el.find('.u-field-description textarea').val()).toBe('Team of dumbs'); + expect(teamEditView.$el.find('.u-field-language select option:selected').text()).toBe('English'); + expect(teamEditView.$el.find('.u-field-country select option:selected').text()).toBe('United States'); + } + }; + + var requestMethod = function() { + return teamAction === 'create' ? 'POST' : 'PATCH'; + }; + + var assertTeamCreateUpdateInfo = function(that, teamsData, teamsUrl, expectedUrl) { + var requests = AjaxHelpers.requests(that), + teamEditView = createEditTeamView(); teamEditView.$('.u-field-name input').val(teamsData.name); teamEditView.$('.u-field-textarea textarea').val(teamsData.description); - teamEditView.$('.u-field-language select').val('a').attr("selected", "selected"); - teamEditView.$('.u-field-country select').val('c').attr("selected", "selected"); + teamEditView.$('.u-field-language select').val(teamsData.language).attr("selected", "selected"); + teamEditView.$('.u-field-country select').val(teamsData.country).attr("selected", "selected"); teamEditView.$('.create-team.form-actions .action-primary').click(); - AjaxHelpers.expectJsonRequest(requests, 'POST', teamsUrl, teamsData); - AjaxHelpers.respondWithJson(requests, _.extend(_.extend({}, teamsData), { id: '123'})); + + AjaxHelpers.expectJsonRequest(requests, requestMethod(), teamsUrl, teamsData); + AjaxHelpers.respondWithJson(requests, _.extend(_.extend({}, teamsData), teamAction === 'create' ? {id: '123'} : {})); expect(teamEditView.$('.create-team.wrapper-msg .copy').text().trim().length).toBe(0); - expect(Backbone.history.navigate.calls[0].args).toContain('teams/awesomeness/123'); - }); + expect(Backbone.history.navigate.calls[0].args).toContain(expectedUrl); + }; + + var assertValidationMessagesWhenFieldsEmpty = function(that) { + var requests = AjaxHelpers.requests(that), + teamEditView = createEditTeamView(); - it('shows validation error message when field is empty', function () { - var requests = AjaxHelpers.requests(this), - teamEditView = createTeamEditView(); verifyValidation(requests, teamEditView, [ ['.u-field-name input', 'Name', 'success'], ['.u-field-textarea textarea', '', 'error'] ]); + teamEditView.render(); verifyValidation(requests, teamEditView, [ ['.u-field-name input', '', 'error'], @@ -120,13 +165,13 @@ define([ ['.u-field-name input', '', 'error'], ['.u-field-textarea textarea', '', 'error'] ]); - }); + }; - it('shows validation error message when field value length exceeded the limit', function () { - var requests = AjaxHelpers.requests(this), - teamEditView = createTeamEditView(), - teamName = new Array(500 + 1).join( '$'), - teamDescription = new Array(500 + 1).join( '$' ); + var assertValidationMessagesWhenInvalidData = function(that) { + var requests = AjaxHelpers.requests(that), + teamEditView = createEditTeamView(), + teamName = new Array(500 + 1).join('$'), + teamDescription = new Array(500 + 1).join('$'); verifyValidation(requests, teamEditView, [ ['.u-field-name input', teamName, 'error'], @@ -142,48 +187,114 @@ define([ ['.u-field-name input', teamName, 'error'], ['.u-field-textarea textarea', teamDescription, 'error'] ]); - }); + }; - it("shows an error message for HTTP 500", function () { - var teamEditView = createTeamEditView(), - requests = AjaxHelpers.requests(this); + var assertShowMessageOnError = function(that, teamsData, teamsUrl, errorCode) { + var teamEditView = createEditTeamView(), + requests = AjaxHelpers.requests(that); teamEditView.$('.u-field-name input').val(teamsData.name); teamEditView.$('.u-field-textarea textarea').val(teamsData.description); teamEditView.$('.create-team.form-actions .action-primary').click(); - teamsData.country = ''; - teamsData.language = ''; - AjaxHelpers.expectJsonRequest(requests, 'POST', teamsUrl, teamsData); - AjaxHelpers.respondWithError(requests); - expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("An error occurred. Please try again."); - }); + if (teamAction === 'create') { + teamsData.country = ''; + teamsData.language = ''; + } + AjaxHelpers.expectJsonRequest(requests, requestMethod(), teamsUrl, teamsData); - it("shows correct error message when server returns an error", function () { - var requests = AjaxHelpers.requests(this), - teamEditView = createTeamEditView(); + if (errorCode < 500) { + AjaxHelpers.respondWithError( + requests, + errorCode, + {'user_message': 'User message', 'developer_message': 'Developer message'} + ); + expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("User message"); + } else { + AjaxHelpers.respondWithError(requests); + expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("An error occurred. Please try again."); + } + }; - teamEditView.$('.u-field-name input').val(teamsData.name); - teamEditView.$('.u-field-textarea textarea').val(teamsData.description); - - teamEditView.$('.create-team.form-actions .action-primary').click(); - teamsData.country = ''; - teamsData.language = ''; - AjaxHelpers.expectJsonRequest(requests, 'POST', teamsUrl, teamsData); - AjaxHelpers.respondWithError( - requests, - 400, - {'user_message': 'User message', 'developer_message': 'Developer message'} - ); - - expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("User message"); - }); - - it("changes route on cancel click", function () { - var teamEditView = createTeamEditView(); + var assertRedirectsToCorrectUrlOnCancel = function(expectedUrl) { + var teamEditView = createEditTeamView(); teamEditView.$('.create-team.form-actions .action-cancel').click(); - expect(Backbone.history.navigate.calls[0].args).toContain('topics/awesomeness'); + expect(Backbone.history.navigate.calls[0].args).toContain(expectedUrl); + }; + + describe('NewTeam', function () { + + beforeEach(function() { + teamAction = 'create'; + }); + + it('can render itself correctly', function () { + assertFormRendersCorrectly(); + }); + + it('can create a team', function () { + assertTeamCreateUpdateInfo(this, createTeamData, teamsUrl, 'teams/awesomeness/123'); + }); + + it('shows validation error message when field is empty', function () { + assertValidationMessagesWhenFieldsEmpty(this); + }); + + it('shows validation error message when field value length exceeded the limit', function () { + assertValidationMessagesWhenInvalidData(this); + }); + + it("shows an error message for HTTP 500", function () { + assertShowMessageOnError(this, createTeamData, teamsUrl, 500); + }); + + it("shows correct error message when server returns an error", function () { + assertShowMessageOnError(this, createTeamData, teamsUrl, 400); + }); + + it("changes route on cancel click", function () { + assertRedirectsToCorrectUrlOnCancel('topics/awesomeness'); + }); + }); + + describe('EditTeam', function () { + + beforeEach(function() { + teamAction = 'edit'; + }); + + it('can render itself correctly', function () { + assertFormRendersCorrectly(); + }); + + it('can edit a team', function () { + var copyTeamsData = _.clone(editTeamData); + copyTeamsData.country = 'CA'; + copyTeamsData.language = 'fr'; + + assertTeamCreateUpdateInfo(this, copyTeamsData, teamsUrl + editTeamID + '?expand=user', 'teams/awesomeness/' + editTeamID); + }); + + it('shows validation error message when field is empty', function () { + assertValidationMessagesWhenFieldsEmpty(this); + }); + + it('shows validation error message when field value length exceeded the limit', function () { + assertValidationMessagesWhenInvalidData(this); + }); + + it("shows an error message for HTTP 500", function () { + assertShowMessageOnError(this, editTeamData, teamsUrl + editTeamID + '?expand=user', 500); + }); + + it("shows correct error message when server returns an error", function () { + assertShowMessageOnError(this, editTeamData, teamsUrl + editTeamID + '?expand=user', 400); + }); + + it("changes route on cancel click", function () { + assertRedirectsToCorrectUrlOnCancel('teams/awesomeness/' + editTeamID); + }); }); }); }); diff --git a/lms/djangoapps/teams/static/teams/js/spec/views/team_join_spec.js b/lms/djangoapps/teams/static/teams/js/spec/views/team_join_spec.js deleted file mode 100644 index 93f8adb6a1..0000000000 --- a/lms/djangoapps/teams/static/teams/js/spec/views/team_join_spec.js +++ /dev/null @@ -1,218 +0,0 @@ -define([ - 'backbone', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'teams/js/models/team', - 'teams/js/views/team_join', 'teams/js/spec_helpers/team_spec_helpers' -], function (Backbone, _, AjaxHelpers, TeamModel, TeamJoinView, TeamSpecHelpers) { - 'use strict'; - describe('TeamJoinView', function () { - var createTeamsUrl, - createTeamModelData, - createMembershipData, - createJoinView, - verifyErrorMessage, - ACCOUNTS_API_URL = '/api/user/v1/accounts/', - TEAMS_URL = '/api/team/v0/teams/', - TEAMS_MEMBERSHIP_URL = '/api/team/v0/team_membership/'; - - beforeEach(function () { - setFixtures( - '
' - ); - }); - - verifyErrorMessage = function (requests, errorMessage, expectedMessage, joinTeam) { - var view = createJoinView(1, 'ma', createTeamModelData('teamA', 'teamAlpha', [])); - if (joinTeam) { - // if we want the error to return when user try to join team, respond with no membership - AjaxHelpers.respondWithJson(requests, {"count": 0}); - view.$('.action.action-primary').click(); - } - AjaxHelpers.respondWithTextError(requests, 400, errorMessage); - expect($('.msg-content .copy').text().trim()).toBe(expectedMessage); - }; - - createTeamsUrl = function (teamId) { - return TEAMS_URL + teamId + '?expand=user'; - }; - - createTeamModelData = function (teamId, teamName, membership) { - return { - id: teamId, - name: teamName, - membership: membership - }; - }; - - createMembershipData = function (username) { - return [ - { - "user": { - "username": username, - "url": ACCOUNTS_API_URL + username - } - } - ]; - }; - - createJoinView = function(maxTeamSize, currentUsername, teamModelData, teamId) { - teamId = teamId || 'teamA'; - - var model = new TeamModel(teamModelData, { parse: true }); - model.url = createTeamsUrl(teamId); - - var teamJoinView = new TeamJoinView( - { - courseID: TeamSpecHelpers.testCourseID, - teamEvents: TeamSpecHelpers.teamEvents, - model: model, - teamsUrl: createTeamsUrl(teamId), - maxTeamSize: maxTeamSize, - currentUsername: currentUsername, - teamMembershipsUrl: TEAMS_MEMBERSHIP_URL - } - ); - return teamJoinView.render(); - }; - - it('can render itself', function () { - var teamModelData = createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma')); - var view = createJoinView(1, 'ma', teamModelData); - - expect(view.$('.join-team').length).toEqual(1); - }); - - it('can join team successfully', function () { - var requests = AjaxHelpers.requests(this); - var currentUsername = 'ma1'; - var teamId = 'teamA'; - var teamName = 'teamAlpha'; - var teamModelData = createTeamModelData(teamId, teamName, []); - var view = createJoinView(1, currentUsername, teamModelData); - - // a get request will be sent to get user membership info - // because current user is not member of current team - AjaxHelpers.expectRequest( - requests, - 'GET', - TEAMS_MEMBERSHIP_URL + '?' + $.param({ - 'username': currentUsername, 'course_id': TeamSpecHelpers.testCourseID - }) - ); - - // current user is not a member of any team so we should see the Join Team button - AjaxHelpers.respondWithJson(requests, {"count": 0}); - expect(view.$('.action.action-primary').length).toEqual(1); - - // a post request will be sent to add current user to current team - view.$('.action.action-primary').click(); - AjaxHelpers.expectRequest( - requests, - 'POST', - TEAMS_MEMBERSHIP_URL, - $.param({'username': currentUsername, 'team_id': teamId}) - ); - AjaxHelpers.respondWithJson(requests, {}); - - // on success, team model will be fetched and - // join team view and team profile will be re-rendered - AjaxHelpers.expectRequest( - requests, - 'GET', - createTeamsUrl(teamId) - ); - AjaxHelpers.respondWithJson( - requests, createTeamModelData(teamId, teamName, createMembershipData(currentUsername)) - ); - - // current user is now member of the current team then there should be no button and no message - expect(view.$('.action.action-primary').length).toEqual(0); - expect(view.$('.join-team-message').length).toEqual(0); - }); - - it('shows already member message', function () { - var requests = AjaxHelpers.requests(this); - var currentUsername = 'ma1'; - var view = createJoinView(1, currentUsername, createTeamModelData('teamA', 'teamAlpha', [])); - - // a get request will be sent to get user membership info - // because current user is not member of current team - AjaxHelpers.expectRequest( - requests, - 'GET', - TEAMS_MEMBERSHIP_URL + '?' + $.param({ - 'username': currentUsername, 'course_id': TeamSpecHelpers.testCourseID - }) - ); - - // current user is a member of another team so we should see the correct message - AjaxHelpers.respondWithJson(requests, {"count": 1}); - expect(view.$('.action.action-primary').length).toEqual(0); - expect(view.$('.join-team-message').text().trim()).toBe(view.alreadyMemberMessage); - }); - - it('shows team full message', function () { - var requests = AjaxHelpers.requests(this); - var view = createJoinView( - 1, - 'ma1', - createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma')) - ); - - // team has no space and current user is a not member of - // current team so we should see the correct message - expect(view.$('.action.action-primary').length).toEqual(0); - expect(view.$('.join-team-message').text().trim()).toBe(view.teamFullMessage); - - // there should be no request made - expect(requests.length).toBe(0); - }); - - it('shows correct error message if user fails to join team', function () { - var requests = AjaxHelpers.requests(this); - - // verify user_message - verifyErrorMessage( - requests, - JSON.stringify({'user_message': "Can't be made member"}), - "Can't be made member", - true - ); - - // verify generic error message - verifyErrorMessage( - requests, - '', - 'An error occurred. Try again.', - true - ); - - // verify error message when json parsing succeeded but error message format is incorrect - verifyErrorMessage( - requests, - JSON.stringify({'blah': "Can't be made member"}), - 'An error occurred. Try again.', - true - ); - }); - - it('shows correct error message if initializing the view fails', function () { - // Rendering the view sometimes require fetching user's memberships. This may fail. - var requests = AjaxHelpers.requests(this); - - // verify user_message - verifyErrorMessage( - requests, - JSON.stringify({'user_message': "Can't return user memberships"}), - "Can't return user memberships", - false - ); - - // verify generic error message - verifyErrorMessage( - requests, - '', - 'An error occurred. Try again.', - false - ); - }); - }); -}); diff --git a/lms/djangoapps/teams/static/teams/js/spec/views/team_profile_header_actions_spec.js b/lms/djangoapps/teams/static/teams/js/spec/views/team_profile_header_actions_spec.js new file mode 100644 index 0000000000..5fc597c7f1 --- /dev/null +++ b/lms/djangoapps/teams/static/teams/js/spec/views/team_profile_header_actions_spec.js @@ -0,0 +1,254 @@ +define([ + 'backbone', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'teams/js/models/team', + 'teams/js/views/team_profile_header_actions', 'teams/js/spec_helpers/team_spec_helpers' +], function (Backbone, _, AjaxHelpers, TeamModel, TeamProfileHeaderActionsView, TeamSpecHelpers) { + 'use strict'; + + describe('TeamProfileHeaderActionsView', function () { + var createTeamsUrl, + createTeamModelData, + createMembershipData, + createHeaderActionsView, + verifyErrorMessage, + ACCOUNTS_API_URL = '/api/user/v1/accounts/', + TEAMS_URL = '/api/team/v0/teams/', + TEAMS_MEMBERSHIP_URL = '/api/team/v0/team_membership/'; + + createTeamsUrl = function (teamId) { + return TEAMS_URL + teamId + '?expand=user'; + }; + + createTeamModelData = function (teamId, teamName, membership) { + return { + id: teamId, + name: teamName, + membership: membership, + url: createTeamsUrl(teamId) + }; + }; + + createHeaderActionsView = function(maxTeamSize, currentUsername, teamModelData, showEditButton) { + var teamId = 'teamA'; + + var model = new TeamModel(teamModelData, { parse: true }); + + return new TeamProfileHeaderActionsView( + { + courseID: TeamSpecHelpers.testCourseID, + teamEvents: TeamSpecHelpers.teamEvents, + model: model, + teamsUrl: createTeamsUrl(teamId), + maxTeamSize: maxTeamSize, + currentUsername: currentUsername, + teamMembershipsUrl: TEAMS_MEMBERSHIP_URL, + topicID: '', + showEditButton: showEditButton + } + ).render(); + }; + + createMembershipData = function (username) { + return [ + { + "user": { + "username": username, + "url": ACCOUNTS_API_URL + username + } + } + ]; + }; + + describe('JoinButton', function () { + + beforeEach(function () { + setFixtures( + '
' + ); + }); + + verifyErrorMessage = function (requests, errorMessage, expectedMessage, joinTeam) { + var view = createHeaderActionsView(1, 'ma', createTeamModelData('teamA', 'teamAlpha', [])); + if (joinTeam) { + // if we want the error to return when user try to join team, respond with no membership + AjaxHelpers.respondWithJson(requests, {"count": 0}); + view.$('.action.action-primary').click(); + } + AjaxHelpers.respondWithTextError(requests, 400, errorMessage); + expect($('.msg-content .copy').text().trim()).toBe(expectedMessage); + }; + + it('can render itself', function () { + var teamModelData = createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma')); + var view = createHeaderActionsView(1, 'ma', teamModelData); + + expect(view.$('.join-team').length).toEqual(1); + }); + + it('can join team successfully', function () { + var requests = AjaxHelpers.requests(this); + var currentUsername = 'ma1'; + var teamId = 'teamA'; + var teamName = 'teamAlpha'; + var teamModelData = createTeamModelData(teamId, teamName, []); + var view = createHeaderActionsView(1, currentUsername, teamModelData); + + // a get request will be sent to get user membership info + // because current user is not member of current team + AjaxHelpers.expectRequest( + requests, + 'GET', + TEAMS_MEMBERSHIP_URL + '?' + $.param({ + 'username': currentUsername, 'course_id': TeamSpecHelpers.testCourseID + }) + ); + + // current user is not a member of any team so we should see the Join Team button + AjaxHelpers.respondWithJson(requests, {"count": 0}); + expect(view.$('.action.action-primary').length).toEqual(1); + + // a post request will be sent to add current user to current team + view.$('.action.action-primary').click(); + AjaxHelpers.expectRequest( + requests, + 'POST', + TEAMS_MEMBERSHIP_URL, + $.param({'username': currentUsername, 'team_id': teamId}) + ); + AjaxHelpers.respondWithJson(requests, {}); + + // on success, team model will be fetched and + // join team view and team profile will be re-rendered + AjaxHelpers.expectRequest( + requests, + 'GET', + createTeamsUrl(teamId) + ); + AjaxHelpers.respondWithJson( + requests, createTeamModelData(teamId, teamName, createMembershipData(currentUsername)) + ); + + // current user is now member of the current team then there should be no button and no message + expect(view.$('.action.action-primary').length).toEqual(0); + expect(view.$('.join-team-message').length).toEqual(0); + }); + + it('shows already member message', function () { + var requests = AjaxHelpers.requests(this); + var currentUsername = 'ma1'; + var view = createHeaderActionsView(1, currentUsername, createTeamModelData('teamA', 'teamAlpha', [])); + + // a get request will be sent to get user membership info + // because current user is not member of current team + AjaxHelpers.expectRequest( + requests, + 'GET', + TEAMS_MEMBERSHIP_URL + '?' + $.param({ + 'username': currentUsername, 'course_id': TeamSpecHelpers.testCourseID + }) + ); + + // current user is a member of another team so we should see the correct message + AjaxHelpers.respondWithJson(requests, {"count": 1}); + expect(view.$('.action.action-primary').length).toEqual(0); + expect(view.$('.join-team-message').text().trim()).toBe(view.alreadyMemberMessage); + }); + + it('shows team full message', function () { + var requests = AjaxHelpers.requests(this); + var view = createHeaderActionsView( + 1, + 'ma1', + createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma')) + ); + + // team has no space and current user is a not member of + // current team so we should see the correct message + expect(view.$('.action.action-primary').length).toEqual(0); + expect(view.$('.join-team-message').text().trim()).toBe(view.teamFullMessage); + + // there should be no request made + expect(requests.length).toBe(0); + }); + + it('shows correct error message if user fails to join team', function () { + var requests = AjaxHelpers.requests(this); + + // verify user_message + verifyErrorMessage( + requests, + JSON.stringify({'user_message': "Can't be made member"}), + "Can't be made member", + true + ); + + // verify generic error message + verifyErrorMessage( + requests, + '', + 'An error occurred. Try again.', + true + ); + + // verify error message when json parsing succeeded but error message format is incorrect + verifyErrorMessage( + requests, + JSON.stringify({'blah': "Can't be made member"}), + 'An error occurred. Try again.', + true + ); + }); + + it('shows correct error message if initializing the view fails', function () { + // Rendering the view sometimes require fetching user's memberships. This may fail. + var requests = AjaxHelpers.requests(this); + + // verify user_message + verifyErrorMessage( + requests, + JSON.stringify({'user_message': "Can't return user memberships"}), + "Can't return user memberships", + false + ); + + // verify generic error message + verifyErrorMessage( + requests, + '', + 'An error occurred. Try again.', + false + ); + }); + }); + + describe('EditButton', function() { + var teamModelData, + view, + createAndAssertView; + + createAndAssertView = function(showEditButton) { + teamModelData = createTeamModelData('aveA', 'avengers', createMembershipData('ma')); + view = createHeaderActionsView(1, 'ma', teamModelData, showEditButton); + expect(view.$('.action-edit-team').length).toEqual(showEditButton ? 1 : 0); + }; + + it('renders when option showEditButton is true', function () { + createAndAssertView(true); + }); + + it('does not render when option showEditButton is false', function () { + createAndAssertView(false); + }); + + it("can navigate to correct url", function () { + spyOn(Backbone.history, 'navigate'); + createAndAssertView(true); + var editButton = view.$('.action-edit-team'); + + expect(editButton.length).toEqual(1); + $(editButton).click(); + + expect(Backbone.history.navigate.calls[0].args[0]).toContain('/edit-team'); + }); + }); + }); +}); diff --git a/lms/djangoapps/teams/static/teams/js/views/edit_team.js b/lms/djangoapps/teams/static/teams/js/views/edit_team.js index ff12c86d4a..5f8b3f2462 100644 --- a/lms/djangoapps/teams/static/teams/js/views/edit_team.js +++ b/lms/djangoapps/teams/static/teams/js/views/edit_team.js @@ -14,9 +14,9 @@ maxTeamDescriptionLength: 300, events: { - 'click .action-primary': 'createTeam', - 'submit form': 'createTeam', - 'click .action-cancel': 'goBackToTopic' + 'click .action-primary': 'createOrUpdateTeam', + 'submit form': 'createOrUpdateTeam', + 'click .action-cancel': 'cancelAndGoBack' }, initialize: function(options) { @@ -27,12 +27,20 @@ this.teamsUrl = options.teamParams.teamsUrl; this.languages = options.teamParams.languages; this.countries = options.teamParams.countries; - this.primaryButtonTitle = options.primaryButtonTitle || 'Submit'; + this.teamsDetailUrl = options.teamParams.teamsDetailUrl; + this.action = options.action; - _.bindAll(this, 'goBackToTopic', 'createTeam'); + _.bindAll(this, 'cancelAndGoBack', 'createOrUpdateTeam'); - this.teamModel = new TeamModel({}); - this.teamModel.url = this.teamsUrl; + if (this.action === 'create') { + this.teamModel = new TeamModel({}); + this.teamModel.url = this.teamsUrl; + this.primaryButtonTitle = 'Create'; + } else if(this.action === 'edit' ) { + this.teamModel = options.model; + this.teamModel.url = this.teamsDetailUrl.replace('team_id', options.model.get('id')) + '?expand=user'; + this.primaryButtonTitle = 'Update'; + } this.teamNameField = new FieldViews.TextFieldView({ model: this.teamModel, @@ -74,7 +82,11 @@ }, render: function() { - this.$el.html(_.template(editTeamTemplate)({primaryButtonTitle: this.primaryButtonTitle})); + this.$el.html(_.template(editTeamTemplate) ({ + primaryButtonTitle: this.primaryButtonTitle, + action: this.action, + totalMembers: _.isUndefined(this.teamModel) ? 0 : this.teamModel.get('membership').length + })); this.set(this.teamNameField, '.team-required-fields'); this.set(this.teamDescriptionField, '.team-required-fields'); this.set(this.teamLanguageField, '.team-optional-fields'); @@ -91,20 +103,28 @@ } }, - createTeam: function (event) { + createOrUpdateTeam: function (event) { event.preventDefault(); var view = this, teamLanguage = this.teamLanguageField.fieldValue(), - teamCountry = this.teamCountryField.fieldValue(); + teamCountry = this.teamCountryField.fieldValue(), + data = { + name: this.teamNameField.fieldValue(), + description: this.teamDescriptionField.fieldValue(), + language: _.isNull(teamLanguage) ? '' : teamLanguage, + country: _.isNull(teamCountry) ? '' : teamCountry + }, + saveOptions = { + wait: true + }; - var data = { - course_id: this.courseID, - topic_id: this.topicID, - name: this.teamNameField.fieldValue(), - description: this.teamDescriptionField.fieldValue(), - language: _.isNull(teamLanguage) ? '' : teamLanguage, - country: _.isNull(teamCountry) ? '' : teamCountry - }; + if (this.action === 'create') { + data.course_id = this.courseID; + data.topic_id = this.topicID; + } else if (this.action === 'edit' ) { + saveOptions.patch = true; + saveOptions.contentType = 'application/merge-patch+json'; + } var validationResult = this.validateTeamData(data); if (validationResult.status === false) { @@ -112,10 +132,10 @@ return; } - this.teamModel.save(data, { wait: true }) + this.teamModel.save(data, saveOptions) .done(function(result) { view.teamEvents.trigger('teams:update', { - action: 'create', + action: view.action, team: result }); Backbone.history.navigate( @@ -186,8 +206,15 @@ } }, - goBackToTopic: function () { - Backbone.history.navigate('topics/' + this.topicID, {trigger: true}); + cancelAndGoBack: function (event) { + event.preventDefault(); + var url; + if (this.action === 'create') { + url = 'topics/' + this.topicID; + } else if (this.action === 'edit' ) { + url = 'teams/' + this.topicID + '/' + this.teamModel.get('id'); + } + Backbone.history.navigate(url, {trigger: true}); } }); }); diff --git a/lms/djangoapps/teams/static/teams/js/views/team_join.js b/lms/djangoapps/teams/static/teams/js/views/team_profile_header_actions.js similarity index 79% rename from lms/djangoapps/teams/static/teams/js/views/team_join.js rename to lms/djangoapps/teams/static/teams/js/views/team_profile_header_actions.js index c9de1e2838..a6b6390034 100644 --- a/lms/djangoapps/teams/static/teams/js/views/team_join.js +++ b/lms/djangoapps/teams/static/teams/js/views/team_profile_header_actions.js @@ -5,8 +5,8 @@ 'underscore', 'gettext', 'teams/js/views/team_utils', - 'text!teams/templates/team-join.underscore'], - function (Backbone, _, gettext, TeamUtils, teamJoinTemplate) { + 'text!teams/templates/team-profile-header-actions.underscore'], + function (Backbone, _, gettext, TeamUtils, teamProfileHeaderActionsTemplate) { return Backbone.View.extend({ errorMessage: gettext("An error occurred. Try again."), @@ -14,31 +14,34 @@ teamFullMessage: gettext("This team is full."), events: { - "click .action-primary": "joinTeam" + "click .action-primary": "joinTeam", + "click .action-edit-team": "editTeam" }, initialize: function(options) { this.teamEvents = options.teamEvents; - this.template = _.template(teamJoinTemplate); + this.template = _.template(teamProfileHeaderActionsTemplate); this.courseID = options.courseID; this.maxTeamSize = options.maxTeamSize; this.currentUsername = options.currentUsername; this.teamMembershipsUrl = options.teamMembershipsUrl; - _.bindAll(this, 'render', 'joinTeam', 'getUserTeamInfo'); + this.showEditButton = options.showEditButton; + this.topicID = options.topicID; + _.bindAll(this, 'render', 'joinTeam','editTeam', 'getUserTeamInfo'); this.listenTo(this.model, "change", this.render); }, render: function() { var view = this, message, - showButton, + showJoinButton, teamHasSpace; this.getUserTeamInfo(this.currentUsername, view.maxTeamSize).done(function (info) { teamHasSpace = info.teamHasSpace; // if user is the member of current team then we wouldn't show anything if (!info.memberOfCurrentTeam) { - showButton = !info.alreadyMember && teamHasSpace; + showJoinButton = !info.alreadyMember && teamHasSpace; if (info.alreadyMember) { message = info.memberOfCurrentTeam ? '' : view.alreadyMemberMessage; @@ -47,7 +50,11 @@ } } - view.$el.html(view.template({showButton: showButton, message: message})); + view.$el.html(view.template({ + showJoinButton: showJoinButton, + message: message, + showEditButton: view.showEditButton + })); }); return view; }, @@ -108,6 +115,10 @@ } return deferred.promise(); + }, + editTeam: function (event) { + event.preventDefault(); + Backbone.history.navigate('topics/' + this.topicID + '/' + this.model.get('id') +'/edit-team', {trigger: true}); } }); }); 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 704a967b6d..860447510b 100644 --- a/lms/djangoapps/teams/static/teams/js/views/teams_tab.js +++ b/lms/djangoapps/teams/static/teams/js/views/teams_tab.js @@ -17,12 +17,12 @@ 'teams/js/views/my_teams', 'teams/js/views/topic_teams', 'teams/js/views/edit_team', - 'teams/js/views/team_join', + 'teams/js/views/team_profile_header_actions', 'text!teams/templates/teams_tab.underscore'], function (Backbone, _, gettext, HeaderView, HeaderModel, TabbedView, TopicModel, TopicCollection, TeamModel, TeamCollection, TeamMembershipCollection, TopicsView, TeamProfileView, MyTeamsView, TopicTeamsView, TeamEditView, - TeamJoinView, teamsTemplate) { + TeamProfileHeaderActionsView, teamsTemplate) { var TeamsHeaderModel = HeaderModel.extend({ initialize: function (attributes) { _.extend(this.defaults, {nav_aria_label: gettext('teams')}); @@ -52,6 +52,7 @@ this.topics = options.topics; this.topicUrl = options.topicUrl; this.teamsUrl = options.teamsUrl; + this.teamsDetailUrl = options.teamsDetailUrl; this.teamMembershipsUrl = options.teamMembershipsUrl; this.teamMembershipDetailUrl = options.teamMembershipDetailUrl; this.maxTeamSize = options.maxTeamSize; @@ -74,6 +75,7 @@ }, this)], ['topics/:topic_id(/)', _.bind(this.browseTopic, this)], ['topics/:topic_id/create-team(/)', _.bind(this.newTeam, this)], + ['topics/:topic_id/:team_id/edit-team(/)', _.bind(this.editTeam, this)], ['teams/:topic_id/:team_id(/)', _.bind(this.browseTeam, this)], [new RegExp('^(browse)\/?$'), _.bind(this.goToTab, this)], [new RegExp('^(my-teams)\/?$'), _.bind(this.goToTab, this)] @@ -208,16 +210,53 @@ }) }), main: new TeamEditView({ + action: 'create', teamEvents: self.teamEvents, - tagName: 'create-new-team', - teamParams: teamsView.main.teamParams, - primaryButtonTitle: 'Create' + teamParams: teamsView.main.teamParams }) }); self.render(); }); }, + /** + * Render the edit team form. + */ + editTeam: function (topicID, teamID) { + var self = this, + editViewWithHeader; + this.getTopic(topicID).done(function (topic) { + self.getTeam(teamID, false).done(function(team) { + var view = new TeamEditView({ + action: 'edit', + teamEvents: self.teamEvents, + teamParams: { + courseID: self.courseID, + topicID: topic.get('id'), + teamsUrl: self.teamsUrl, + topicName: topic.get('name'), + languages: self.languages, + countries: self.countries, + teamsDetailUrl: self.teamsDetailUrl + }, + model: team + }); + editViewWithHeader = self.createViewWithHeader({ + mainView: view, + subject: { + name: gettext("Edit Team"), + description: gettext("If you make significant changes, make sure you notify members of the team before making these changes.") + }, + parentTeam: team, + parentTopic: topic + } + ); + self.mainView = editViewWithHeader; + self.render(); + }); + }); + }, + /** * Return a promise for the TeamsView for the given topic ID. */ @@ -227,7 +266,7 @@ var self = this, router = this.router, deferred = $.Deferred(); - if (this.teamsCollection && this.teamsCollection.topic_id === topicID) { + if (this.teamsCollection && this.teamsCollection.topic_id === topicID && this.teamsView) { deferred.resolve(this.teamsView); } else { this.getTopic(topicID) @@ -254,7 +293,8 @@ teamsUrl: self.teamsUrl, topicName: topic.get('name'), languages: self.languages, - countries: self.countries + countries: self.countries, + teamsDetailUrl: self.teamsDetailUrl } }); deferred.resolve( @@ -311,14 +351,17 @@ teamMembershipDetailUrl: self.teamMembershipDetailUrl, setFocusToHeaderFunc: self.setFocusToHeader }); - var teamJoinView = new TeamJoinView({ + + var TeamProfileActionsView = new TeamProfileHeaderActionsView({ teamEvents: self.teamEvents, - courseID: courseID, - model: team, + courseID: courseID, + model: team, teamsUrl: self.teamsUrl, maxTeamSize: self.maxTeamSize, currentUsername: self.userInfo.username, - teamMembershipsUrl: self.teamMembershipsUrl + teamMembershipsUrl: self.teamMembershipsUrl, + topicID: topicID, + showEditButton: self.userInfo.privileged || self.userInfo.staff }); deferred.resolve( self.createViewWithHeader( @@ -326,7 +369,7 @@ mainView: view, subject: team, parentTopic: topic, - headerActionsView: teamJoinView + headerActionsView: TeamProfileActionsView } ) ); @@ -337,7 +380,8 @@ createViewWithHeader: function (options) { var router = this.router, - breadcrumbs, headerView; + breadcrumbs, headerView, + viewDescription, viewTitle; breadcrumbs = [{ title: gettext('All Topics'), url: '#browse' @@ -348,10 +392,25 @@ url: '#topics/' + options.parentTopic.id }); } + if (options.parentTeam) { + breadcrumbs.push({ + title: options.parentTeam.get('name'), + url: '#teams/' + options.parentTopic.id + '/' + options.parentTeam.id + }); + } + if (options.subject instanceof Backbone.Model) { + viewDescription = options.subject.get('description'); + viewTitle = options.subject.get('name'); + + } else if (options.subject) { + viewDescription = options.subject.description; + viewTitle = options.subject.name; + } + headerView = new HeaderView({ model: new TeamsHeaderModel({ - description: options.subject.get('description'), - title: options.subject.get('name'), + description: viewDescription, + title: viewTitle, breadcrumbs: breadcrumbs }), headerActionsView: options.headerActionsView, diff --git a/lms/djangoapps/teams/static/teams/templates/edit-team.underscore b/lms/djangoapps/teams/static/teams/templates/edit-team.underscore index 5fbf6bf633..bea9c1a5dd 100644 --- a/lms/djangoapps/teams/static/teams/templates/edit-team.underscore +++ b/lms/djangoapps/teams/static/teams/templates/edit-team.underscore @@ -1,8 +1,14 @@
-