Commit f788caa6 by muzaffaryousaf

Edit team page for Instructor only.

TNL-1914
parent 406c9bc5
...@@ -205,7 +205,7 @@ class BrowseTeamsPage(CoursePage, PaginatedUIMixin): ...@@ -205,7 +205,7 @@ class BrowseTeamsPage(CoursePage, PaginatedUIMixin):
self.wait_for_ajax() self.wait_for_ajax()
class CreateTeamPage(CoursePage, FieldsMixin): class CreateOrEditTeamPage(CoursePage, FieldsMixin):
""" """
Create team page. Create team page.
""" """
...@@ -216,7 +216,7 @@ class CreateTeamPage(CoursePage, FieldsMixin): ...@@ -216,7 +216,7 @@ class CreateTeamPage(CoursePage, FieldsMixin):
representation of a topic following the same convention as a representation of a topic following the same convention as a
course module's topic. course module's topic.
""" """
super(CreateTeamPage, self).__init__(browser, course_id) super(CreateOrEditTeamPage, self).__init__(browser, course_id)
self.topic = topic self.topic = topic
self.url_path = "teams/#topics/{topic_id}/create-team".format(topic_id=self.topic['id']) self.url_path = "teams/#topics/{topic_id}/create-team".format(topic_id=self.topic['id'])
...@@ -410,3 +410,12 @@ class TeamPage(CoursePage, PaginatedUIMixin): ...@@ -410,3 +410,12 @@ class TeamPage(CoursePage, PaginatedUIMixin):
"""Navigate to the 'All Topics' page.""" """Navigate to the 'All Topics' page."""
self.q(css='.breadcrumbs a').results[0].click() self.q(css='.breadcrumbs a').results[0].click()
self.wait_for_ajax() 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()
...@@ -20,7 +20,7 @@ from ...pages.lms.auto_auth import AutoAuthPage ...@@ -20,7 +20,7 @@ from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.course_info import CourseInfoPage from ...pages.lms.course_info import CourseInfoPage
from ...pages.lms.learner_profile import LearnerProfilePage from ...pages.lms.learner_profile import LearnerProfilePage
from ...pages.lms.tab_nav import TabNavPage 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 TOPICS_PER_PAGE = 12
...@@ -335,7 +335,7 @@ class BrowseTopicsTest(TeamsTabBase): ...@@ -335,7 +335,7 @@ class BrowseTopicsTest(TeamsTabBase):
browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, topic) browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, topic)
self.assertTrue(browse_teams_page.is_browser_on_page()) self.assertTrue(browse_teams_page.is_browser_on_page())
browse_teams_page.click_create_team_link() 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_text_field(field_id='name', value='Team Name', press_enter=False)
create_team_page.value_for_textarea_field( create_team_page.value_for_textarea_field(
field_id='description', field_id='description',
...@@ -629,78 +629,124 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase): ...@@ -629,78 +629,124 @@ class BrowseTeamsWithinTopicTest(TeamsTabBase):
@attr('shard_5') @attr('shard_5')
class CreateTeamTest(TeamsTabBase): class TeamFormActions(TeamsTabBase):
""" """
Tests for creating a new Team within a Topic on the Teams page. Base class for create & edit team.
""" """
TEAM_DESCRIPTION = 'The Avengers are a fictional team of superheroes.'
def setUp(self): topic = {'name': 'Example Topic', 'id': 'example_topic', 'description': 'Description'}
super(CreateTeamTest, self).setUp() TEAMS_NAME = 'Avengers'
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.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): def verify_page_header(self, title, description, breadcrumbs):
""" """
Verify that the page header correctly reflects the Verify that the page header correctly reflects the
create team header, description and breadcrumb. create team header, description and breadcrumb.
""" """
self.assertEqual(self.create_team_page.header_page_name, 'Create a New Team') self.assertEqual(self.create_or_edit_team_page.header_page_name, title)
self.assertEqual( self.assertEqual(self.create_or_edit_team_page.header_page_description, description)
self.create_team_page.header_page_description, self.assertEqual(self.create_or_edit_team_page.header_page_breadcrumbs, breadcrumbs)
'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): def verify_and_navigate_to_create_team_page(self):
"""Navigates to the create team page and verifies.""" """Navigates to the create team page and verifies."""
self.browse_teams_page.click_create_team_link() self.browse_teams_page.click_create_team_link()
self.verify_page_header() 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()
def fill_create_form(self): # Edit page header.
"""Fill the create team form fields with appropriate values.""" self.verify_page_header(
self.create_team_page.value_for_text_field(field_id='name', value=self.team_name, press_enter=False) title='Edit Team',
self.create_team_page.value_for_textarea_field( 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', field_id='description',
value='The Avengers are a fictional team of superheroes.' value=self.TEAM_DESCRIPTION
) )
self.create_team_page.value_for_dropdown_field(field_id='language', value='English') self.create_or_edit_team_page.value_for_dropdown_field(field_id='language', value='English')
self.create_team_page.value_for_dropdown_field(field_id='country', value='Pakistan') self.create_or_edit_team_page.value_for_dropdown_field(field_id='country', value='Pakistan')
def test_user_can_see_create_team_page(self): def verify_all_fields_exist(self):
""" """
Scenario: The user should be able to see the create team page via teams list page. Verify the fields for create/edit 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 Create Team page link on bottom
And When I click create team link
Then I should see the create team page.
And I should see the create team header
And I should also see the help messages for fields.
""" """
self.verify_and_navigate_to_create_team_page()
self.assertEqual( self.assertEqual(
self.create_team_page.message_for_field('name'), self.create_or_edit_team_page.message_for_field('name'),
'A name that identifies your team (maximum 255 characters).' 'A name that identifies your team (maximum 255 characters).'
) )
self.assertEqual( self.assertEqual(
self.create_team_page.message_for_textarea_field('description'), self.create_or_edit_team_page.message_for_textarea_field('description'),
'A short description of the team to help other learners understand ' 'A short description of the team to help other learners understand '
'the goals or direction of the team (maximum 300 characters).' 'the goals or direction of the team (maximum 300 characters).'
) )
self.assertEqual( self.assertEqual(
self.create_team_page.message_for_field('country'), self.create_or_edit_team_page.message_for_field('country'),
'The country that team members primarily identify with.' 'The country that team members primarily identify with.'
) )
self.assertEqual( self.assertEqual(
self.create_team_page.message_for_field('language'), self.create_or_edit_team_page.message_for_field('language'),
'The language that team members primarily use to communicate with each other.' '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.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()
def test_user_can_see_create_team_page(self):
"""
Scenario: The user should be able to see the create team page via teams list 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 Create Team page link on bottom
And When I click create team link
Then I should see the create team page.
And I should see the create team header
And I should also see the help messages for fields.
"""
self.verify_and_navigate_to_create_team_page()
self.verify_all_fields_exist()
def test_user_can_see_error_message_for_missing_data(self): def test_user_can_see_error_message_for_missing_data(self):
""" """
Scenario: The user should be able to see error message in case of missing required field. Scenario: The user should be able to see error message in case of missing required field.
...@@ -711,14 +757,14 @@ class CreateTeamTest(TeamsTabBase): ...@@ -711,14 +757,14 @@ class CreateTeamTest(TeamsTabBase):
Then I should see the error message and highlighted fields. Then I should see the error message and highlighted fields.
""" """
self.verify_and_navigate_to_create_team_page() 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.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.' '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'))
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='description'))
def test_user_can_see_error_message_for_incorrect_data(self): def test_user_can_see_error_message_for_incorrect_data(self):
""" """
...@@ -733,7 +779,7 @@ class CreateTeamTest(TeamsTabBase): ...@@ -733,7 +779,7 @@ class CreateTeamTest(TeamsTabBase):
self.verify_and_navigate_to_create_team_page() self.verify_and_navigate_to_create_team_page()
# Fill the name field with >255 characters to see validation message. # 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', field_id='name',
value='EdX is a massive open online course (MOOC) provider and online learning platform. ' 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 ' 'It hosts online university-level courses in a wide range of disciplines to a worldwide '
...@@ -745,13 +791,13 @@ class CreateTeamTest(TeamsTabBase): ...@@ -745,13 +791,13 @@ class CreateTeamTest(TeamsTabBase):
'edX has more than 4 million users taking more than 500 courses online.', 'edX has more than 4 million users taking more than 500 courses online.',
press_enter=False press_enter=False
) )
self.create_team_page.submit_form() self.create_or_edit_team_page.submit_form()
self.assertEqual( 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.' '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): def test_user_can_create_new_team_successfully(self):
""" """
...@@ -767,16 +813,19 @@ class CreateTeamTest(TeamsTabBase): ...@@ -767,16 +813,19 @@ class CreateTeamTest(TeamsTabBase):
And the number of teams should be updated on the topic card 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 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.verify_and_navigate_to_create_team_page()
self.fill_create_form() self.fill_create_or_edit_form()
self.create_team_page.submit_form() self.create_or_edit_team_page.submit_form()
# Verify that the page is shown for the new team # Verify that the page is shown for the new team
team_page = TeamPage(self.browser, self.course_id) team_page = TeamPage(self.browser, self.course_id)
team_page.wait_for_page() team_page.wait_for_page()
self.assertEqual(team_page.team_name, self.team_name) self.assertEqual(team_page.team_name, self.TEAMS_NAME)
self.assertEqual(team_page.team_description, 'The Avengers are a fictional team of superheroes.') self.assertEqual(team_page.team_description, self.TEAM_DESCRIPTION)
self.assertEqual(team_page.team_user_membership_text, 'You are a member of this team.') 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 # Verify the new team was added to the topic list
...@@ -802,7 +851,7 @@ class CreateTeamTest(TeamsTabBase): ...@@ -802,7 +851,7 @@ class CreateTeamTest(TeamsTabBase):
self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 0 out of 0 total') 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.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.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') self.assertEqual(self.browse_teams_page.get_pagination_header_text(), 'Showing 0 out of 0 total')
...@@ -813,6 +862,157 @@ class CreateTeamTest(TeamsTabBase): ...@@ -813,6 +862,157 @@ class CreateTeamTest(TeamsTabBase):
self.verify_my_team_count(0) 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') @attr('shard_5')
@ddt.ddt @ddt.ddt
class TeamPageTest(TeamsTabBase): class TeamPageTest(TeamsTabBase):
......
...@@ -4,13 +4,14 @@ define([ ...@@ -4,13 +4,14 @@ define([
'backbone', 'backbone',
'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/ajax_helpers',
'teams/js/views/edit_team', 'teams/js/views/edit_team',
'teams/js/models/team',
'teams/js/spec_helpers/team_spec_helpers' 'teams/js/spec_helpers/team_spec_helpers'
], function ($, _, Backbone, AjaxHelpers, TeamEditView, TeamSpecHelpers) { ], function ($, _, Backbone, AjaxHelpers, TeamEditView, TeamModel, TeamSpecHelpers) {
'use strict'; 'use strict';
describe('EditTeam', function () { describe('CreateEditTeam', function() {
var teamsUrl = '/api/team/v0/teams/', var teamsUrl = '/api/team/v0/teams/',
teamsData = { createTeamData = {
id: null, id: null,
name: "TeamName", name: "TeamName",
is_active: null, is_active: null,
...@@ -18,11 +19,17 @@ define([ ...@@ -18,11 +19,17 @@ define([
topic_id: "awesomeness", topic_id: "awesomeness",
date_created: "", date_created: "",
description: "TeamDescription", description: "TeamDescription",
country: "c", country: "US",
language: "a", language: "en",
membership: [], membership: [],
last_activity_at: '' last_activity_at: ''
}, },
editTeamData = {
name: "UpdatedAvengers",
description: "We do not discuss about avengers.",
country: "US",
language: "en"
},
verifyValidation = function (requests, teamEditView, fieldsData) { verifyValidation = function (requests, teamEditView, fieldsData) {
_.each(fieldsData, function (fieldData) { _.each(fieldsData, function (fieldData) {
teamEditView.$(fieldData[0]).val(fieldData[1]); teamEditView.$(fieldData[0]).val(fieldData[1]);
...@@ -32,33 +39,57 @@ define([ ...@@ -32,33 +39,57 @@ define([
var message = teamEditView.$('.wrapper-msg'); var message = teamEditView.$('.wrapper-msg');
expect(message.hasClass('is-hidden')).toBeFalsy(); 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( expect(message.find('.copy').text().trim()).toBe(
"Check the highlighted fields below and try again." "Check the highlighted fields below and try again."
); );
_.each(fieldsData, function (fieldData) { _.each(fieldsData, function (fieldData) {
if(fieldData[2] === 'error') { if (fieldData[2] === 'error') {
expect(teamEditView.$(fieldData[0].split(" ")[0] + '.error').length).toBe(1); 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(teamEditView.$(fieldData[0].split(" ")[0] + '.error').length).toBe(0);
} }
}); });
expect(requests.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({ return new TeamEditView({
teamEvents: TeamSpecHelpers.teamEvents, teamEvents: TeamSpecHelpers.teamEvents,
el: $('.teams-content'), el: $('.teams-content'),
action: teamAction,
model: teamModel,
teamParams: { teamParams: {
teamsUrl: teamsUrl, teamsUrl: teamsUrl,
courseID: "a/b/c", courseID: "a/b/c",
topicID: 'awesomeness', topicID: 'awesomeness',
topicName: 'Awesomeness', topicName: 'Awesomeness',
languages: [['a', 'aaa'], ['b', 'bbb']], languages: [['aa', 'Afar'], ['fr', 'French'], ['en', 'English']],
countries: [['c', 'ccc'], ['d', 'ddd']] countries: [['af', 'Afghanistan'], ['CA', 'Canada'], ['US', 'United States']],
teamsDetailUrl: teamModel.url
} }
}).render(); }).render();
}; };
...@@ -68,15 +99,15 @@ define([ ...@@ -68,15 +99,15 @@ define([
spyOn(Backbone.history, 'navigate'); spyOn(Backbone.history, 'navigate');
}); });
it('can render itself correctly', function () { var assertFormRendersCorrectly = function() {
var fieldClasses = [ var fieldClasses = [
'.u-field-name', '.u-field-name',
'.u-field-description', '.u-field-description',
'.u-field-optional_description', '.u-field-optional_description',
'.u-field-language', '.u-field-language',
'.u-field-country' '.u-field-country'
], ],
teamEditView = createTeamEditView(); teamEditView = createEditTeamView();
_.each(fieldClasses, function (fieldClass) { _.each(fieldClasses, function (fieldClass) {
expect(teamEditView.$el.find(fieldClass).length).toBe(1); expect(teamEditView.$el.find(fieldClass).length).toBe(1);
...@@ -84,32 +115,46 @@ define([ ...@@ -84,32 +115,46 @@ define([
expect(teamEditView.$('.create-team.form-actions .action-primary').length).toBe(1); expect(teamEditView.$('.create-team.form-actions .action-primary').length).toBe(1);
expect(teamEditView.$('.create-team.form-actions .action-cancel').length).toBe(1); expect(teamEditView.$('.create-team.form-actions .action-cancel').length).toBe(1);
});
it('can create a team', function () { if (teamAction === 'edit') {
var requests = AjaxHelpers.requests(this), expect(teamEditView.$el.find('.u-field-name input').val()).toBe('Avengers');
teamEditView = createTeamEditView(); 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-name input').val(teamsData.name);
teamEditView.$('.u-field-textarea textarea').val(teamsData.description); teamEditView.$('.u-field-textarea textarea').val(teamsData.description);
teamEditView.$('.u-field-language select').val('a').attr("selected", "selected"); teamEditView.$('.u-field-language select').val(teamsData.language).attr("selected", "selected");
teamEditView.$('.u-field-country select').val('c').attr("selected", "selected"); teamEditView.$('.u-field-country select').val(teamsData.country).attr("selected", "selected");
teamEditView.$('.create-team.form-actions .action-primary').click(); 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(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, [ verifyValidation(requests, teamEditView, [
['.u-field-name input', 'Name', 'success'], ['.u-field-name input', 'Name', 'success'],
['.u-field-textarea textarea', '', 'error'] ['.u-field-textarea textarea', '', 'error']
]); ]);
teamEditView.render(); teamEditView.render();
verifyValidation(requests, teamEditView, [ verifyValidation(requests, teamEditView, [
['.u-field-name input', '', 'error'], ['.u-field-name input', '', 'error'],
...@@ -120,13 +165,13 @@ define([ ...@@ -120,13 +165,13 @@ define([
['.u-field-name input', '', 'error'], ['.u-field-name input', '', 'error'],
['.u-field-textarea textarea', '', 'error'] ['.u-field-textarea textarea', '', 'error']
]); ]);
}); };
it('shows validation error message when field value length exceeded the limit', function () { var assertValidationMessagesWhenInvalidData = function(that) {
var requests = AjaxHelpers.requests(this), var requests = AjaxHelpers.requests(that),
teamEditView = createTeamEditView(), teamEditView = createEditTeamView(),
teamName = new Array(500 + 1).join( '$'), teamName = new Array(500 + 1).join('$'),
teamDescription = new Array(500 + 1).join( '$' ); teamDescription = new Array(500 + 1).join('$');
verifyValidation(requests, teamEditView, [ verifyValidation(requests, teamEditView, [
['.u-field-name input', teamName, 'error'], ['.u-field-name input', teamName, 'error'],
...@@ -142,48 +187,114 @@ define([ ...@@ -142,48 +187,114 @@ define([
['.u-field-name input', teamName, 'error'], ['.u-field-name input', teamName, 'error'],
['.u-field-textarea textarea', teamDescription, 'error'] ['.u-field-textarea textarea', teamDescription, 'error']
]); ]);
}); };
it("shows an error message for HTTP 500", function () { var assertShowMessageOnError = function(that, teamsData, teamsUrl, errorCode) {
var teamEditView = createTeamEditView(), var teamEditView = createEditTeamView(),
requests = AjaxHelpers.requests(this); requests = AjaxHelpers.requests(that);
teamEditView.$('.u-field-name input').val(teamsData.name); teamEditView.$('.u-field-name input').val(teamsData.name);
teamEditView.$('.u-field-textarea textarea').val(teamsData.description); teamEditView.$('.u-field-textarea textarea').val(teamsData.description);
teamEditView.$('.create-team.form-actions .action-primary').click(); 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 () { if (errorCode < 500) {
var requests = AjaxHelpers.requests(this), AjaxHelpers.respondWithError(
teamEditView = createTeamEditView(); 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); var assertRedirectsToCorrectUrlOnCancel = function(expectedUrl) {
teamEditView.$('.u-field-textarea textarea').val(teamsData.description); var teamEditView = createEditTeamView();
teamEditView.$('.create-team.form-actions .action-cancel').click();
expect(Backbone.history.navigate.calls[0].args).toContain(expectedUrl);
};
teamEditView.$('.create-team.form-actions .action-primary').click(); describe('NewTeam', function () {
teamsData.country = '';
teamsData.language = ''; beforeEach(function() {
AjaxHelpers.expectJsonRequest(requests, 'POST', teamsUrl, teamsData); teamAction = 'create';
AjaxHelpers.respondWithError( });
requests,
400, it('can render itself correctly', function () {
{'user_message': 'User message', 'developer_message': 'Developer message'} assertFormRendersCorrectly();
); });
expect(teamEditView.$('.wrapper-msg .copy').text().trim()).toBe("User message"); 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');
});
}); });
it("changes route on cancel click", function () { describe('EditTeam', function () {
var teamEditView = createTeamEditView();
teamEditView.$('.create-team.form-actions .action-cancel').click(); beforeEach(function() {
expect(Backbone.history.navigate.calls[0].args).toContain('topics/awesomeness'); 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);
});
}); });
}); });
}); });
define([ define([
'backbone', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'teams/js/models/team', 'backbone', 'underscore', 'common/js/spec_helpers/ajax_helpers', 'teams/js/models/team',
'teams/js/views/team_join', 'teams/js/spec_helpers/team_spec_helpers' 'teams/js/views/team_profile_header_actions', 'teams/js/spec_helpers/team_spec_helpers'
], function (Backbone, _, AjaxHelpers, TeamModel, TeamJoinView, TeamSpecHelpers) { ], function (Backbone, _, AjaxHelpers, TeamModel, TeamProfileHeaderActionsView, TeamSpecHelpers) {
'use strict'; 'use strict';
describe('TeamJoinView', function () {
describe('TeamProfileHeaderActionsView', function () {
var createTeamsUrl, var createTeamsUrl,
createTeamModelData, createTeamModelData,
createMembershipData, createMembershipData,
createJoinView, createHeaderActionsView,
verifyErrorMessage, verifyErrorMessage,
ACCOUNTS_API_URL = '/api/user/v1/accounts/', ACCOUNTS_API_URL = '/api/user/v1/accounts/',
TEAMS_URL = '/api/team/v0/teams/', TEAMS_URL = '/api/team/v0/teams/',
TEAMS_MEMBERSHIP_URL = '/api/team/v0/team_membership/'; TEAMS_MEMBERSHIP_URL = '/api/team/v0/team_membership/';
beforeEach(function () {
setFixtures(
'<div class="teams-content"><div class="msg-content"><div class="copy"></div></div><div class="header-action-view"></div></div>'
);
});
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) { createTeamsUrl = function (teamId) {
return TEAMS_URL + teamId + '?expand=user'; return TEAMS_URL + teamId + '?expand=user';
}; };
...@@ -38,28 +22,17 @@ define([ ...@@ -38,28 +22,17 @@ define([
return { return {
id: teamId, id: teamId,
name: teamName, name: teamName,
membership: membership membership: membership,
url: createTeamsUrl(teamId)
}; };
}; };
createMembershipData = function (username) { createHeaderActionsView = function(maxTeamSize, currentUsername, teamModelData, showEditButton) {
return [ var teamId = 'teamA';
{
"user": {
"username": username,
"url": ACCOUNTS_API_URL + username
}
}
];
};
createJoinView = function(maxTeamSize, currentUsername, teamModelData, teamId) {
teamId = teamId || 'teamA';
var model = new TeamModel(teamModelData, { parse: true }); var model = new TeamModel(teamModelData, { parse: true });
model.url = createTeamsUrl(teamId);
var teamJoinView = new TeamJoinView( return new TeamProfileHeaderActionsView(
{ {
courseID: TeamSpecHelpers.testCourseID, courseID: TeamSpecHelpers.testCourseID,
teamEvents: TeamSpecHelpers.teamEvents, teamEvents: TeamSpecHelpers.teamEvents,
...@@ -67,152 +40,215 @@ define([ ...@@ -67,152 +40,215 @@ define([
teamsUrl: createTeamsUrl(teamId), teamsUrl: createTeamsUrl(teamId),
maxTeamSize: maxTeamSize, maxTeamSize: maxTeamSize,
currentUsername: currentUsername, currentUsername: currentUsername,
teamMembershipsUrl: TEAMS_MEMBERSHIP_URL teamMembershipsUrl: TEAMS_MEMBERSHIP_URL,
topicID: '',
showEditButton: showEditButton
} }
); ).render();
return teamJoinView.render();
}; };
it('can render itself', function () { createMembershipData = function (username) {
var teamModelData = createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma')); return [
var view = createJoinView(1, 'ma', teamModelData); {
"user": {
"username": username,
"url": ACCOUNTS_API_URL + username
}
}
];
};
expect(view.$('.join-team').length).toEqual(1); describe('JoinButton', function () {
});
it('can join team successfully', function () { beforeEach(function () {
var requests = AjaxHelpers.requests(this); setFixtures(
var currentUsername = 'ma1'; '<div class="teams-content"><div class="msg-content"><div class="copy"></div></div><div class="header-action-view"></div></div>'
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 () { verifyErrorMessage = function (requests, errorMessage, expectedMessage, joinTeam) {
var requests = AjaxHelpers.requests(this); var view = createHeaderActionsView(1, 'ma', createTeamModelData('teamA', 'teamAlpha', []));
var currentUsername = 'ma1'; if (joinTeam) {
var view = createJoinView(1, currentUsername, createTeamModelData('teamA', 'teamAlpha', [])); // if we want the error to return when user try to join team, respond with no membership
AjaxHelpers.respondWithJson(requests, {"count": 0});
// a get request will be sent to get user membership info view.$('.action.action-primary').click();
// because current user is not member of current team }
AjaxHelpers.expectRequest( AjaxHelpers.respondWithTextError(requests, 400, errorMessage);
requests, expect($('.msg-content .copy').text().trim()).toBe(expectedMessage);
'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 () { it('can render itself', function () {
var requests = AjaxHelpers.requests(this); var teamModelData = createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma'));
var view = createJoinView( var view = createHeaderActionsView(1, 'ma', teamModelData);
1,
'ma1', expect(view.$('.join-team').length).toEqual(1);
createTeamModelData('teamA', 'teamAlpha', createMembershipData('ma')) });
);
it('can join team successfully', function () {
// team has no space and current user is a not member of var requests = AjaxHelpers.requests(this);
// current team so we should see the correct message var currentUsername = 'ma1';
expect(view.$('.action.action-primary').length).toEqual(0); var teamId = 'teamA';
expect(view.$('.join-team-message').text().trim()).toBe(view.teamFullMessage); var teamName = 'teamAlpha';
var teamModelData = createTeamModelData(teamId, teamName, []);
// there should be no request made var view = createHeaderActionsView(1, currentUsername, teamModelData);
expect(requests.length).toBe(0);
}); // 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);
it('shows correct error message if user fails to join team', function () { // a post request will be sent to add current user to current team
var requests = AjaxHelpers.requests(this); view.$('.action.action-primary').click();
AjaxHelpers.expectRequest(
// verify user_message requests,
verifyErrorMessage( 'POST',
requests, TEAMS_MEMBERSHIP_URL,
JSON.stringify({'user_message': "Can't be made member"}), $.param({'username': currentUsername, 'team_id': teamId})
"Can't be made member", );
true AjaxHelpers.respondWithJson(requests, {});
);
// on success, team model will be fetched and
// verify generic error message // join team view and team profile will be re-rendered
verifyErrorMessage( AjaxHelpers.expectRequest(
requests, requests,
'', 'GET',
'An error occurred. Try again.', createTeamsUrl(teamId)
true );
); AjaxHelpers.respondWithJson(
requests, createTeamModelData(teamId, teamName, createMembershipData(currentUsername))
// verify error message when json parsing succeeded but error message format is incorrect );
verifyErrorMessage(
requests, // current user is now member of the current team then there should be no button and no message
JSON.stringify({'blah': "Can't be made member"}), expect(view.$('.action.action-primary').length).toEqual(0);
'An error occurred. Try again.', expect(view.$('.join-team-message').length).toEqual(0);
true });
);
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
);
});
}); });
it('shows correct error message if initializing the view fails', function () { describe('EditButton', function() {
// Rendering the view sometimes require fetching user's memberships. This may fail. var teamModelData,
var requests = AjaxHelpers.requests(this); view,
createAndAssertView;
// verify user_message
verifyErrorMessage( createAndAssertView = function(showEditButton) {
requests, teamModelData = createTeamModelData('aveA', 'avengers', createMembershipData('ma'));
JSON.stringify({'user_message': "Can't return user memberships"}), view = createHeaderActionsView(1, 'ma', teamModelData, showEditButton);
"Can't return user memberships", expect(view.$('.action-edit-team').length).toEqual(showEditButton ? 1 : 0);
false };
);
it('renders when option showEditButton is true', function () {
// verify generic error message createAndAssertView(true);
verifyErrorMessage( });
requests,
'', it('does not render when option showEditButton is false', function () {
'An error occurred. Try again.', createAndAssertView(false);
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');
});
}); });
}); });
}); });
...@@ -14,9 +14,9 @@ ...@@ -14,9 +14,9 @@
maxTeamDescriptionLength: 300, maxTeamDescriptionLength: 300,
events: { events: {
'click .action-primary': 'createTeam', 'click .action-primary': 'createOrUpdateTeam',
'submit form': 'createTeam', 'submit form': 'createOrUpdateTeam',
'click .action-cancel': 'goBackToTopic' 'click .action-cancel': 'cancelAndGoBack'
}, },
initialize: function(options) { initialize: function(options) {
...@@ -27,12 +27,20 @@ ...@@ -27,12 +27,20 @@
this.teamsUrl = options.teamParams.teamsUrl; this.teamsUrl = options.teamParams.teamsUrl;
this.languages = options.teamParams.languages; this.languages = options.teamParams.languages;
this.countries = options.teamParams.countries; 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({ this.teamNameField = new FieldViews.TextFieldView({
model: this.teamModel, model: this.teamModel,
...@@ -74,7 +82,11 @@ ...@@ -74,7 +82,11 @@
}, },
render: function() { 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.teamNameField, '.team-required-fields');
this.set(this.teamDescriptionField, '.team-required-fields'); this.set(this.teamDescriptionField, '.team-required-fields');
this.set(this.teamLanguageField, '.team-optional-fields'); this.set(this.teamLanguageField, '.team-optional-fields');
...@@ -91,20 +103,28 @@ ...@@ -91,20 +103,28 @@
} }
}, },
createTeam: function (event) { createOrUpdateTeam: function (event) {
event.preventDefault(); event.preventDefault();
var view = this, var view = this,
teamLanguage = this.teamLanguageField.fieldValue(), teamLanguage = this.teamLanguageField.fieldValue(),
teamCountry = this.teamCountryField.fieldValue(); teamCountry = this.teamCountryField.fieldValue(),
data = {
var data = { name: this.teamNameField.fieldValue(),
course_id: this.courseID, description: this.teamDescriptionField.fieldValue(),
topic_id: this.topicID, language: _.isNull(teamLanguage) ? '' : teamLanguage,
name: this.teamNameField.fieldValue(), country: _.isNull(teamCountry) ? '' : teamCountry
description: this.teamDescriptionField.fieldValue(), },
language: _.isNull(teamLanguage) ? '' : teamLanguage, saveOptions = {
country: _.isNull(teamCountry) ? '' : teamCountry wait: true
}; };
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); var validationResult = this.validateTeamData(data);
if (validationResult.status === false) { if (validationResult.status === false) {
...@@ -112,10 +132,10 @@ ...@@ -112,10 +132,10 @@
return; return;
} }
this.teamModel.save(data, { wait: true }) this.teamModel.save(data, saveOptions)
.done(function(result) { .done(function(result) {
view.teamEvents.trigger('teams:update', { view.teamEvents.trigger('teams:update', {
action: 'create', action: view.action,
team: result team: result
}); });
Backbone.history.navigate( Backbone.history.navigate(
...@@ -186,8 +206,15 @@ ...@@ -186,8 +206,15 @@
} }
}, },
goBackToTopic: function () { cancelAndGoBack: function (event) {
Backbone.history.navigate('topics/' + this.topicID, {trigger: true}); 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});
} }
}); });
}); });
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
'underscore', 'underscore',
'gettext', 'gettext',
'teams/js/views/team_utils', 'teams/js/views/team_utils',
'text!teams/templates/team-join.underscore'], 'text!teams/templates/team-profile-header-actions.underscore'],
function (Backbone, _, gettext, TeamUtils, teamJoinTemplate) { function (Backbone, _, gettext, TeamUtils, teamProfileHeaderActionsTemplate) {
return Backbone.View.extend({ return Backbone.View.extend({
errorMessage: gettext("An error occurred. Try again."), errorMessage: gettext("An error occurred. Try again."),
...@@ -14,31 +14,34 @@ ...@@ -14,31 +14,34 @@
teamFullMessage: gettext("This team is full."), teamFullMessage: gettext("This team is full."),
events: { events: {
"click .action-primary": "joinTeam" "click .action-primary": "joinTeam",
"click .action-edit-team": "editTeam"
}, },
initialize: function(options) { initialize: function(options) {
this.teamEvents = options.teamEvents; this.teamEvents = options.teamEvents;
this.template = _.template(teamJoinTemplate); this.template = _.template(teamProfileHeaderActionsTemplate);
this.courseID = options.courseID; this.courseID = options.courseID;
this.maxTeamSize = options.maxTeamSize; this.maxTeamSize = options.maxTeamSize;
this.currentUsername = options.currentUsername; this.currentUsername = options.currentUsername;
this.teamMembershipsUrl = options.teamMembershipsUrl; 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); this.listenTo(this.model, "change", this.render);
}, },
render: function() { render: function() {
var view = this, var view = this,
message, message,
showButton, showJoinButton,
teamHasSpace; teamHasSpace;
this.getUserTeamInfo(this.currentUsername, view.maxTeamSize).done(function (info) { this.getUserTeamInfo(this.currentUsername, view.maxTeamSize).done(function (info) {
teamHasSpace = info.teamHasSpace; teamHasSpace = info.teamHasSpace;
// if user is the member of current team then we wouldn't show anything // if user is the member of current team then we wouldn't show anything
if (!info.memberOfCurrentTeam) { if (!info.memberOfCurrentTeam) {
showButton = !info.alreadyMember && teamHasSpace; showJoinButton = !info.alreadyMember && teamHasSpace;
if (info.alreadyMember) { if (info.alreadyMember) {
message = info.memberOfCurrentTeam ? '' : view.alreadyMemberMessage; message = info.memberOfCurrentTeam ? '' : view.alreadyMemberMessage;
...@@ -47,7 +50,11 @@ ...@@ -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; return view;
}, },
...@@ -108,6 +115,10 @@ ...@@ -108,6 +115,10 @@
} }
return deferred.promise(); return deferred.promise();
},
editTeam: function (event) {
event.preventDefault();
Backbone.history.navigate('topics/' + this.topicID + '/' + this.model.get('id') +'/edit-team', {trigger: true});
} }
}); });
}); });
......
...@@ -17,12 +17,12 @@ ...@@ -17,12 +17,12 @@
'teams/js/views/my_teams', 'teams/js/views/my_teams',
'teams/js/views/topic_teams', 'teams/js/views/topic_teams',
'teams/js/views/edit_team', 'teams/js/views/edit_team',
'teams/js/views/team_join', 'teams/js/views/team_profile_header_actions',
'text!teams/templates/teams_tab.underscore'], 'text!teams/templates/teams_tab.underscore'],
function (Backbone, _, gettext, HeaderView, HeaderModel, TabbedView, function (Backbone, _, gettext, HeaderView, HeaderModel, TabbedView,
TopicModel, TopicCollection, TeamModel, TeamCollection, TeamMembershipCollection, TopicModel, TopicCollection, TeamModel, TeamCollection, TeamMembershipCollection,
TopicsView, TeamProfileView, MyTeamsView, TopicTeamsView, TeamEditView, TopicsView, TeamProfileView, MyTeamsView, TopicTeamsView, TeamEditView,
TeamJoinView, teamsTemplate) { TeamProfileHeaderActionsView, teamsTemplate) {
var TeamsHeaderModel = HeaderModel.extend({ var TeamsHeaderModel = HeaderModel.extend({
initialize: function (attributes) { initialize: function (attributes) {
_.extend(this.defaults, {nav_aria_label: gettext('teams')}); _.extend(this.defaults, {nav_aria_label: gettext('teams')});
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
this.topics = options.topics; this.topics = options.topics;
this.topicUrl = options.topicUrl; this.topicUrl = options.topicUrl;
this.teamsUrl = options.teamsUrl; this.teamsUrl = options.teamsUrl;
this.teamsDetailUrl = options.teamsDetailUrl;
this.teamMembershipsUrl = options.teamMembershipsUrl; this.teamMembershipsUrl = options.teamMembershipsUrl;
this.teamMembershipDetailUrl = options.teamMembershipDetailUrl; this.teamMembershipDetailUrl = options.teamMembershipDetailUrl;
this.maxTeamSize = options.maxTeamSize; this.maxTeamSize = options.maxTeamSize;
...@@ -74,6 +75,7 @@ ...@@ -74,6 +75,7 @@
}, this)], }, this)],
['topics/:topic_id(/)', _.bind(this.browseTopic, this)], ['topics/:topic_id(/)', _.bind(this.browseTopic, this)],
['topics/:topic_id/create-team(/)', _.bind(this.newTeam, 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)], ['teams/:topic_id/:team_id(/)', _.bind(this.browseTeam, this)],
[new RegExp('^(browse)\/?$'), _.bind(this.goToTab, this)], [new RegExp('^(browse)\/?$'), _.bind(this.goToTab, this)],
[new RegExp('^(my-teams)\/?$'), _.bind(this.goToTab, this)] [new RegExp('^(my-teams)\/?$'), _.bind(this.goToTab, this)]
...@@ -208,10 +210,9 @@ ...@@ -208,10 +210,9 @@
}) })
}), }),
main: new TeamEditView({ main: new TeamEditView({
action: 'create',
teamEvents: self.teamEvents, teamEvents: self.teamEvents,
tagName: 'create-new-team', teamParams: teamsView.main.teamParams
teamParams: teamsView.main.teamParams,
primaryButtonTitle: 'Create'
}) })
}); });
self.render(); self.render();
...@@ -219,6 +220,44 @@ ...@@ -219,6 +220,44 @@
}, },
/** /**
* 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. * Return a promise for the TeamsView for the given topic ID.
*/ */
getTeamsView: function (topicID) { getTeamsView: function (topicID) {
...@@ -227,7 +266,7 @@ ...@@ -227,7 +266,7 @@
var self = this, var self = this,
router = this.router, router = this.router,
deferred = $.Deferred(); deferred = $.Deferred();
if (this.teamsCollection && this.teamsCollection.topic_id === topicID) { if (this.teamsCollection && this.teamsCollection.topic_id === topicID && this.teamsView) {
deferred.resolve(this.teamsView); deferred.resolve(this.teamsView);
} else { } else {
this.getTopic(topicID) this.getTopic(topicID)
...@@ -254,7 +293,8 @@ ...@@ -254,7 +293,8 @@
teamsUrl: self.teamsUrl, teamsUrl: self.teamsUrl,
topicName: topic.get('name'), topicName: topic.get('name'),
languages: self.languages, languages: self.languages,
countries: self.countries countries: self.countries,
teamsDetailUrl: self.teamsDetailUrl
} }
}); });
deferred.resolve( deferred.resolve(
...@@ -311,14 +351,17 @@ ...@@ -311,14 +351,17 @@
teamMembershipDetailUrl: self.teamMembershipDetailUrl, teamMembershipDetailUrl: self.teamMembershipDetailUrl,
setFocusToHeaderFunc: self.setFocusToHeader setFocusToHeaderFunc: self.setFocusToHeader
}); });
var teamJoinView = new TeamJoinView({
var TeamProfileActionsView = new TeamProfileHeaderActionsView({
teamEvents: self.teamEvents, teamEvents: self.teamEvents,
courseID: courseID, courseID: courseID,
model: team, model: team,
teamsUrl: self.teamsUrl, teamsUrl: self.teamsUrl,
maxTeamSize: self.maxTeamSize, maxTeamSize: self.maxTeamSize,
currentUsername: self.userInfo.username, currentUsername: self.userInfo.username,
teamMembershipsUrl: self.teamMembershipsUrl teamMembershipsUrl: self.teamMembershipsUrl,
topicID: topicID,
showEditButton: self.userInfo.privileged || self.userInfo.staff
}); });
deferred.resolve( deferred.resolve(
self.createViewWithHeader( self.createViewWithHeader(
...@@ -326,7 +369,7 @@ ...@@ -326,7 +369,7 @@
mainView: view, mainView: view,
subject: team, subject: team,
parentTopic: topic, parentTopic: topic,
headerActionsView: teamJoinView headerActionsView: TeamProfileActionsView
} }
) )
); );
...@@ -337,7 +380,8 @@ ...@@ -337,7 +380,8 @@
createViewWithHeader: function (options) { createViewWithHeader: function (options) {
var router = this.router, var router = this.router,
breadcrumbs, headerView; breadcrumbs, headerView,
viewDescription, viewTitle;
breadcrumbs = [{ breadcrumbs = [{
title: gettext('All Topics'), title: gettext('All Topics'),
url: '#browse' url: '#browse'
...@@ -348,10 +392,25 @@ ...@@ -348,10 +392,25 @@
url: '#topics/' + options.parentTopic.id 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({ headerView = new HeaderView({
model: new TeamsHeaderModel({ model: new TeamsHeaderModel({
description: options.subject.get('description'), description: viewDescription,
title: options.subject.get('name'), title: viewTitle,
breadcrumbs: breadcrumbs breadcrumbs: breadcrumbs
}), }),
headerActionsView: options.headerActionsView, headerActionsView: options.headerActionsView,
......
<form> <form>
<div class="create-team wrapper-msg is-incontext urgency-low warning is-hidden" tabindex="-1"> <div class="create-team wrapper-msg is-incontext urgency-low error is-hidden" tabindex="-1">
<div class="msg"> <div class="msg">
<div class="msg-content"> <div class="msg-content">
<h3 class="title"><%- gettext("Your team could not be created!") %></h3> <h3 class="title">
<% if (action === 'create') { %>
<%- gettext("Your team could not be created.") %>
<% } else if (action === 'edit') { %>
<%- gettext("Your team could not be updated.") %>
<% } %>
</h3>
<span class="screen-reader-message sr"></span> <span class="screen-reader-message sr"></span>
<div class="copy"> <div class="copy">
<p></p> <p></p>
...@@ -11,15 +17,16 @@ ...@@ -11,15 +17,16 @@
</div> </div>
</div> </div>
<div class="form-instructions create-team-instructions"> <% if (action === 'create') { %>
<p class="copy"> <div class="form-instructions create-team-instructions">
<%- gettext("Enter information to describe your team. You cannot change these details after you create the team.") %></p> <p class="copy">
</div> <%- gettext("Enter information to describe your team. You cannot change these details after you create the team.") %></p>
</div>
<% } %>
<div class="team-edit-fields"> <div class="team-edit-fields">
<div class="team-required-fields"> <div class="team-required-fields">
</div> </div>
<div class="team-optional-fields"> <div class="team-optional-fields">
<fieldset> <fieldset>
<div class="u-field u-field-optional_description"> <div class="u-field u-field-optional_description">
...@@ -38,7 +45,7 @@ ...@@ -38,7 +45,7 @@
<button class="action action-primary"> <button class="action action-primary">
<%= <%=
interpolate_text( interpolate_text(
_.escape(gettext("{primaryButtonTitle} {span_start}a new team{span_end}")), _.escape(gettext("{primaryButtonTitle} {span_start}a team{span_end}")),
{ {
'primaryButtonTitle': primaryButtonTitle, 'span_start': '<span class="sr">', 'span_end': '</span>' 'primaryButtonTitle': primaryButtonTitle, 'span_start': '<span class="sr">', 'span_end': '</span>'
} }
...@@ -48,9 +55,10 @@ ...@@ -48,9 +55,10 @@
<button class="action action-cancel"> <button class="action action-cancel">
<%= <%=
interpolate_text( interpolate_text(
_.escape(gettext("Cancel {span_start}a new team{span_end}")), _.escape(gettext("Cancel {span_start} {action} team {span_end}")),
{ {
'span_start': '<span class="sr">', 'span_end': '</span>' 'span_start': '<span class="sr">', 'span_end': '</span>',
'action': action === 'create' ? 'creating' : 'updating'
} }
) )
%> %>
......
<div class="join-team form-actions"> <div class="join-team form-actions">
<% if (showButton) {%> <% if (showJoinButton) {%>
<button class="action action-primary"> <button class="action action-primary">
<%- gettext("Join Team") %> <%- gettext("Join Team") %>
</button> </button>
<% } else if (message) { %> <% } else if (message) { %>
<p class="join-team-message"><%- message %></p> <p class="join-team-message"><%- message %></p>
<% } %> <% } %>
<% if (showEditButton) { %>
<button class="btn btn-secondary action-edit-team"><%- gettext("Edit Team") %></button>
<% } %>
</div> </div>
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
<% if (isMember) { %> <% if (isMember) { %>
<div class="leave-team"> <div class="leave-team">
<button class="btn btn-link btn-base btn-secondary leave-team-link"><%- gettext("Leave Team") %></button> <button class="btn btn-link leave-team-link"><%- gettext("Leave Team") %></button>
</div> </div>
<div class="divider-lv1"></div> <div class="divider-lv1"></div>
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
topicUrl: '${ topic_url }', topicUrl: '${ topic_url }',
topicsUrl: '${ topics_url }', topicsUrl: '${ topics_url }',
teamsUrl: '${ teams_url }', teamsUrl: '${ teams_url }',
teamsDetailUrl: '${ teams_detail_url }',
teamMembershipsUrl: '${ team_memberships_url }', teamMembershipsUrl: '${ team_memberships_url }',
teamMembershipDetailUrl: '${ team_membership_detail_url }', teamMembershipDetailUrl: '${ team_membership_detail_url }',
maxTeamSize: ${ course.teams_max_size }, maxTeamSize: ${ course.teams_max_size },
......
...@@ -117,6 +117,7 @@ class TeamsDashboardView(View): ...@@ -117,6 +117,7 @@ class TeamsDashboardView(View):
), ),
"topics_url": reverse('topics_list', request=request), "topics_url": reverse('topics_list', request=request),
"teams_url": reverse('teams_list', request=request), "teams_url": reverse('teams_list', request=request),
"teams_detail_url": reverse('teams_detail', args=['team_id']),
"team_memberships_url": reverse('team_membership_list', request=request), "team_memberships_url": reverse('team_membership_list', request=request),
"team_membership_detail_url": reverse('team_membership_detail', args=['team_id', user.username]), "team_membership_detail_url": reverse('team_membership_detail', args=['team_id', user.username]),
"languages": settings.ALL_LANGUAGES, "languages": settings.ALL_LANGUAGES,
......
...@@ -802,7 +802,7 @@ ...@@ -802,7 +802,7 @@
'lms/include/teams/js/spec/views/topic_card_spec.js', 'lms/include/teams/js/spec/views/topic_card_spec.js',
'lms/include/teams/js/spec/views/topic_teams_spec.js', 'lms/include/teams/js/spec/views/topic_teams_spec.js',
'lms/include/teams/js/spec/views/topics_spec.js', 'lms/include/teams/js/spec/views/topics_spec.js',
'lms/include/teams/js/spec/views/team_join_spec.js' 'lms/include/teams/js/spec/views/team_profile_header_actions_spec.js'
]); ]);
}).call(this, requirejs, define); }).call(this, requirejs, define);
...@@ -404,7 +404,7 @@ ...@@ -404,7 +404,7 @@
} }
} }
.btn-link { .btn-secondary {
@extend %btn-pl-secondary-base; @extend %btn-pl-secondary-base;
background-image: none; background-image: none;
...@@ -416,3 +416,24 @@ ...@@ -416,3 +416,24 @@
color: $link-color; color: $link-color;
} }
} }
.btn-link {
@extend %shame-link-text;
// reset of inherited buttons
border-radius: 0;
border-color: $transparent;
padding: 1px;
background: $transparent;
background-image: none;
@extend %t-action3;
@extend %t-strong;
&:focus,
&:hover {
background-image: none !important;
background-color: $transparent !important;
box-shadow: none !important;
}
}
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