Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
b9cf31f4
Commit
b9cf31f4
authored
Aug 13, 2015
by
Usman Khalid
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #9299 from edx/ammar/tnl-1910-support-leaving-a-team
Support leaving a team.
parents
1319602a
5fc61207
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
264 additions
and
90 deletions
+264
-90
common/test/acceptance/pages/lms/teams.py
+10
-0
common/test/acceptance/tests/lms/test_teams.py
+34
-3
lms/djangoapps/teams/static/teams/js/spec/views/team_join_spec.js
+49
-12
lms/djangoapps/teams/static/teams/js/spec/views/team_profile_spec.js
+112
-41
lms/djangoapps/teams/static/teams/js/views/team_join.js
+2
-18
lms/djangoapps/teams/static/teams/js/views/team_profile.js
+26
-8
lms/djangoapps/teams/static/teams/js/views/team_utils.js
+22
-2
lms/djangoapps/teams/static/teams/js/views/teams_tab.js
+7
-6
lms/djangoapps/teams/templates/teams/teams.html
+1
-0
lms/djangoapps/teams/views.py
+1
-0
No files found.
common/test/acceptance/pages/lms/teams.py
View file @
b9cf31f4
...
...
@@ -284,6 +284,11 @@ class TeamPage(CoursePage, PaginatedUIMixin):
"""Verifies that team leave link is present"""
return
self
.
q
(
css
=
'.leave-team-link'
)
.
present
def
click_leave_team_link
(
self
):
""" Click on Leave Team link"""
self
.
q
(
css
=
'.leave-team-link'
)
.
first
.
click
()
self
.
wait_for_ajax
()
@property
def
team_invite_section_present
(
self
):
"""Verifies that invite section is present"""
...
...
@@ -334,3 +339,8 @@ class TeamPage(CoursePage, PaginatedUIMixin):
def
join_team_message_present
(
self
):
""" Returns True if Join Team message is present else False """
return
self
.
q
(
css
=
'.join-team .join-team-message'
)
.
present
@property
def
new_post_button_present
(
self
):
""" Returns True if New Post button is present else False """
return
self
.
q
(
css
=
'.discussion-module .new-post-btn'
)
.
present
common/test/acceptance/tests/lms/test_teams.py
View file @
b9cf31f4
...
...
@@ -750,6 +750,9 @@ class CreateTeamTest(TeamsTabBase):
@ddt.ddt
class
TeamPageTest
(
TeamsTabBase
):
"""Tests for viewing a specific team"""
SEND_INVITE_TEXT
=
'Send this link to friends so that they can join too.'
def
setUp
(
self
):
super
(
TeamPageTest
,
self
)
.
setUp
()
self
.
topic
=
{
u"name"
:
u"Example Topic"
,
u"id"
:
"example_topic"
,
u"description"
:
"Description"
}
...
...
@@ -900,10 +903,12 @@ class TeamPageTest(TeamsTabBase):
self
.
assertTrue
(
self
.
team_page
.
team_leave_link_present
)
self
.
assertTrue
(
self
.
team_page
.
team_invite_section_present
)
self
.
assertEqual
(
self
.
team_page
.
team_invite_help_text
,
invite_text
)
self
.
assertTrue
(
self
.
team_page
.
new_post_button_present
)
else
:
self
.
assertEqual
(
self
.
team_page
.
team_user_membership_text
,
''
)
self
.
assertFalse
(
self
.
team_page
.
team_leave_link_present
)
self
.
assertFalse
(
self
.
team_page
.
team_invite_section_present
)
self
.
assertFalse
(
self
.
team_page
.
new_post_button_present
)
def
test_team_member_can_see_full_team_details
(
self
):
"""
...
...
@@ -922,7 +927,7 @@ class TeamPageTest(TeamsTabBase):
self
.
assert_team_details
(
num_members
=
1
,
invite_text
=
'Send this link to friends so that they can join too.'
invite_text
=
self
.
SEND_INVITE_TEXT
)
def
test_other_users_can_see_limited_team_details
(
self
):
...
...
@@ -994,7 +999,7 @@ class TeamPageTest(TeamsTabBase):
self
.
assert_team_details
(
num_members
=
1
,
invite_text
=
'Send this link to friends so that they can join too.'
invite_text
=
self
.
SEND_INVITE_TEXT
)
self
.
assertEqual
(
self
.
team_page
.
team_invite_url
,
'{0}?invite=true'
.
format
(
self
.
team_page
.
url
))
...
...
@@ -1006,9 +1011,11 @@ class TeamPageTest(TeamsTabBase):
and a team belonging to that topic
And I visit the Team page for that team
Then I should see Join Team button
And I should not see New Post button
When I click on Join Team button
Then there should be no Join Team button and no message
And I should see the updated information under Team Details
And I should see New Post button
"""
self
.
_set_team_configuration_and_membership
(
create_membership
=
False
)
self
.
team_page
.
visit
()
...
...
@@ -1016,7 +1023,7 @@ class TeamPageTest(TeamsTabBase):
self
.
team_page
.
click_join_team_button
()
self
.
assertFalse
(
self
.
team_page
.
join_team_button_present
)
self
.
assertFalse
(
self
.
team_page
.
join_team_message_present
)
self
.
assert_team_details
(
num_members
=
1
,
i
nvite_text
=
'Send this link to friends so that they can join too.'
)
self
.
assert_team_details
(
num_members
=
1
,
i
s_member
=
True
,
invite_text
=
self
.
SEND_INVITE_TEXT
)
def
test_already_member_message
(
self
):
"""
...
...
@@ -1055,3 +1062,27 @@ class TeamPageTest(TeamsTabBase):
self
.
team_page
.
visit
()
self
.
assertEqual
(
self
.
team_page
.
join_team_message
,
'This team is full.'
)
self
.
assert_team_details
(
num_members
=
1
,
is_member
=
False
,
max_size
=
1
)
def
test_leave_team
(
self
):
"""
Scenario: User can leave a team.
Given I am enrolled in a course with a team configuration, a topic,
and a team belonging to that topic
And I am a member of team
And I visit the team
And I should not see Join Team button
And I should see New Post button
Then I should see Leave Team link
When I click on Leave Team link
Then user should be removed from team
And I should see Join Team button
And I should not see New Post button
"""
self
.
_set_team_configuration_and_membership
()
self
.
team_page
.
visit
()
self
.
assertFalse
(
self
.
team_page
.
join_team_button_present
)
self
.
assert_team_details
(
num_members
=
1
,
invite_text
=
self
.
SEND_INVITE_TEXT
)
self
.
team_page
.
click_leave_team_link
()
self
.
assert_team_details
(
num_members
=
0
,
is_member
=
False
)
self
.
assertTrue
(
self
.
team_page
.
join_team_button_present
)
lms/djangoapps/teams/static/teams/js/spec/views/team_join_spec.js
View file @
b9cf31f4
define
([
'underscore'
,
'common/js/spec_helpers/ajax_helpers'
,
'teams/js/models/team'
,
'teams/js/views/team_join'
,
'teams/js/views/team_profile'
],
function
(
_
,
AjaxHelpers
,
TeamModel
,
TeamJoinView
,
TeamProfileView
)
{
'teams/js/views/team_join'
],
function
(
_
,
AjaxHelpers
,
TeamModel
,
TeamJoinView
)
{
'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
(
'<div class="
msg-content"><div class="copy"></div></div><div class="header-action-view"
></div>'
'<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
)
{
return
TEAMS_URL
+
teamId
+
'?expand=user'
;
};
...
...
@@ -148,27 +160,52 @@ define([
expect
(
requests
.
length
).
toBe
(
0
);
});
it
(
'shows correct error message
s
'
,
function
()
{
it
(
'shows correct error message
if user fails to join team
'
,
function
()
{
var
requests
=
AjaxHelpers
.
requests
(
this
);
var
verifyErrorMessage
=
function
(
requests
,
errorMessage
,
expectedMessage
)
{
createJoinView
(
1
,
'ma'
,
createTeamModelData
(
'teamA'
,
'teamAlpha'
,
[]));
AjaxHelpers
.
respondWithTextError
(
requests
,
400
,
errorMessage
);
expect
(
$
(
'.msg-content .copy'
).
text
().
trim
()).
toBe
(
expectedMessage
);
};
// 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'
:
'Awesome! You got an error.'
}),
'Awesome! You got an error.'
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.'
'An error occurred. Try again.'
,
false
);
});
});
...
...
lms/djangoapps/teams/static/teams/js/spec/views/team_profile_spec.js
View file @
b9cf31f4
This diff is collapsed.
Click to expand it.
lms/djangoapps/teams/static/teams/js/views/team_join.js
View file @
b9cf31f4
...
...
@@ -60,12 +60,7 @@ define(['backbone',
}).
done
(
function
(
data
)
{
view
.
model
.
fetch
({});
}).
fail
(
function
(
data
)
{
try
{
var
errors
=
JSON
.
parse
(
data
.
responseText
);
view
.
showMessage
(
errors
.
user_message
);
}
catch
(
error
)
{
view
.
showMessage
(
view
.
errorMessage
);
}
TeamUtils
.
parseAndShowMessage
(
data
,
view
.
errorMessage
);
});
},
...
...
@@ -97,12 +92,7 @@ define(['backbone',
info
.
teamHasSpace
=
teamHasSpace
;
deferred
.
resolve
(
info
);
}).
fail
(
function
(
data
)
{
try
{
var
errors
=
JSON
.
parse
(
data
.
responseText
);
view
.
showMessage
(
errors
.
user_message
);
}
catch
(
error
)
{
view
.
showMessage
(
view
.
errorMessage
);
}
TeamUtils
.
parseAndShowMessage
(
data
,
view
.
errorMessage
);
deferred
.
reject
();
});
}
else
{
...
...
@@ -111,12 +101,6 @@ define(['backbone',
}
return
deferred
.
promise
();
},
showMessage
:
function
(
message
)
{
$
(
'.wrapper-msg'
).
removeClass
(
'is-hidden'
);
$
(
'.msg-content .copy'
).
text
(
message
);
$
(
'.wrapper-msg'
).
focus
();
}
});
});
...
...
lms/djangoapps/teams/static/teams/js/views/team_profile.js
View file @
b9cf31f4
...
...
@@ -6,21 +6,24 @@
define
([
'backbone'
,
'underscore'
,
'gettext'
,
'teams/js/views/team_discussion'
,
'teams/js/views/team_utils'
,
'text!teams/templates/team-profile.underscore'
,
'text!teams/templates/team-member.underscore'
],
'text!teams/templates/team-member.underscore'
],
function
(
Backbone
,
_
,
gettext
,
TeamDiscussionView
,
TeamUtils
,
teamTemplate
,
teamMemberTemplate
)
{
var
TeamProfileView
=
Backbone
.
View
.
extend
({
errorMessage
:
gettext
(
"An error occurred. Try again."
),
events
:
{
'click .invite-link-input'
:
'selectText'
'click .invite-link-input'
:
'selectText'
,
'click .leave-team-link'
:
'leaveTeam'
},
initialize
:
function
(
options
)
{
this
.
listenTo
(
this
.
model
,
"change"
,
this
.
render
);
this
.
courseID
=
options
.
courseID
;
this
.
maxTeamSize
=
options
.
maxTeamSize
;
this
.
readOnly
=
options
.
readOnly
;
this
.
requestUsername
=
options
.
requestUsername
;
this
.
isPrivileged
=
options
.
isPrivileged
;
this
.
teamInviteUrl
=
options
.
teamInviteUrl
;
this
.
teamMembershipDetailUrl
=
options
.
teamMembershipDetailUrl
;
this
.
countries
=
TeamUtils
.
selectorOptionsArrayToHashWithBlank
(
options
.
countries
);
this
.
languages
=
TeamUtils
.
selectorOptionsArrayToHashWithBlank
(
options
.
languages
);
...
...
@@ -28,16 +31,18 @@
},
render
:
function
()
{
var
memberships
=
this
.
model
.
get
(
'membership'
);
var
discussionTopicID
=
this
.
model
.
get
(
'discussion_topic_id'
);
var
memberships
=
this
.
model
.
get
(
'membership'
),
discussionTopicID
=
this
.
model
.
get
(
'discussion_topic_id'
),
isMember
=
TeamUtils
.
isUserMemberOfTeam
(
memberships
,
this
.
requestUsername
);
this
.
$el
.
html
(
_
.
template
(
teamTemplate
,
{
courseID
:
this
.
courseID
,
discussionTopicID
:
discussionTopicID
,
readOnly
:
this
.
readOnly
,
readOnly
:
!
(
this
.
isPrivileged
||
isMember
)
,
country
:
this
.
countries
[
this
.
model
.
get
(
'country'
)],
language
:
this
.
languages
[
this
.
model
.
get
(
'language'
)],
membershipText
:
TeamUtils
.
teamCapacityText
(
memberships
.
length
,
this
.
maxTeamSize
),
isMember
:
TeamUtils
.
isUserMemberOfTeam
(
memberships
,
this
.
requestUsername
)
,
isMember
:
isMember
,
hasCapacity
:
memberships
.
length
<
this
.
maxTeamSize
,
inviteLink
:
this
.
teamInviteUrl
...
...
@@ -65,6 +70,19 @@
selectText
:
function
(
event
)
{
event
.
preventDefault
();
$
(
event
.
currentTarget
).
select
();
},
leaveTeam
:
function
(
event
)
{
event
.
preventDefault
();
var
view
=
this
;
$
.
ajax
({
type
:
'DELETE'
,
url
:
view
.
teamMembershipDetailUrl
.
replace
(
'team_id'
,
view
.
model
.
get
(
'id'
))
}).
done
(
function
(
data
)
{
view
.
model
.
fetch
({});
}).
fail
(
function
(
data
)
{
TeamUtils
.
parseAndShowMessage
(
data
,
view
.
errorMessage
);
});
}
});
...
...
lms/djangoapps/teams/static/teams/js/views/team_utils.js
View file @
b9cf31f4
...
...
@@ -28,8 +28,9 @@
maxMemberCount
),
{
memberCount
:
memberCount
,
maxMemberCount
:
maxMemberCount
},
true
)
)
;
},
isUserMemberOfTeam
:
function
(
memberships
,
requestUsername
)
{
return
_
.
isObject
(
_
.
find
(
memberships
,
function
(
membership
)
...
...
@@ -37,8 +38,27 @@
return
membership
.
user
.
username
===
requestUsername
;
})
);
},
showMessage
:
function
(
message
)
{
var
messageElement
=
$
(
'.teams-content .wrapper-msg'
);
messageElement
.
removeClass
(
'is-hidden'
);
$
(
'.teams-content .msg-content .copy'
).
text
(
message
);
messageElement
.
focus
();
},
/**
* Parse `data` and show user message. If parsing fails than show `genericErrorMessage`
*/
parseAndShowMessage
:
function
(
data
,
genericErrorMessage
)
{
try
{
var
errors
=
JSON
.
parse
(
data
.
responseText
);
this
.
showMessage
(
_
.
isUndefined
(
errors
.
user_message
)
?
genericErrorMessage
:
errors
.
user_message
);
}
catch
(
error
)
{
this
.
showMessage
(
genericErrorMessage
);
}
}
}
}
;
});
}).
call
(
this
,
define
||
RequireJS
.
define
);
lms/djangoapps/teams/static/teams/js/views/teams_tab.js
View file @
b9cf31f4
...
...
@@ -53,6 +53,7 @@
this
.
topicUrl
=
options
.
topicUrl
;
this
.
teamsUrl
=
options
.
teamsUrl
;
this
.
teamMembershipsUrl
=
options
.
teamMembershipsUrl
;
this
.
teamMembershipDetailUrl
=
options
.
teamMembershipDetailUrl
;
this
.
maxTeamSize
=
options
.
maxTeamSize
;
this
.
languages
=
options
.
languages
;
this
.
countries
=
options
.
countries
;
...
...
@@ -276,16 +277,16 @@
courseID
=
this
.
courseID
;
self
.
getTopic
(
topicID
).
done
(
function
(
topic
)
{
self
.
getTeam
(
teamID
,
true
).
done
(
function
(
team
)
{
var
readOnly
=
self
.
readOnlyDiscussion
(
team
),
view
=
new
TeamProfileView
({
var
view
=
new
TeamProfileView
({
courseID
:
courseID
,
model
:
team
,
readOnly
:
readOnly
,
maxTeamSize
:
self
.
maxTeamSize
,
isPrivileged
:
self
.
userInfo
.
privileged
,
requestUsername
:
self
.
userInfo
.
username
,
countries
:
self
.
countries
,
languages
:
self
.
languages
,
teamInviteUrl
:
self
.
teamsBaseUrl
+
'#teams/'
+
topicID
+
'/'
+
teamID
+
'?invite=true'
teamInviteUrl
:
self
.
teamsBaseUrl
+
'#teams/'
+
topicID
+
'/'
+
teamID
+
'?invite=true'
,
teamMembershipDetailUrl
:
self
.
teamMembershipDetailUrl
});
var
teamJoinView
=
new
TeamJoinView
(
{
...
...
@@ -389,11 +390,11 @@
var
team
=
this
.
teamsCollection
?
this
.
teamsCollection
.
get
(
teamID
)
:
null
,
self
=
this
,
deferred
=
$
.
Deferred
(),
teamUrl
;
teamUrl
=
this
.
teamsUrl
+
teamID
+
(
expandUser
?
'?expand=user'
:
''
)
;
if
(
team
)
{
team
.
url
=
teamUrl
;
deferred
.
resolve
(
team
);
}
else
{
teamUrl
=
this
.
teamsUrl
+
teamID
+
(
expandUser
?
'?expand=user'
:
''
);
team
=
new
TeamModel
({
id
:
teamID
,
url
:
teamUrl
...
...
lms/djangoapps/teams/templates/teams/teams.html
View file @
b9cf31f4
...
...
@@ -40,6 +40,7 @@
topicsUrl: '${ topics_url }',
teamsUrl: '${ teams_url }',
teamMembershipsUrl: '${ team_memberships_url }',
teamMembershipDetailUrl: '${ team_membership_detail_url }',
maxTeamSize: ${ course.teams_max_size },
languages: ${ json.dumps(languages, cls=EscapedEdxJSONEncoder) },
countries: ${ json.dumps(countries, cls=EscapedEdxJSONEncoder) },
...
...
lms/djangoapps/teams/views.py
View file @
b9cf31f4
...
...
@@ -108,6 +108,7 @@ class TeamsDashboardView(View):
"topics_url"
:
reverse
(
'topics_list'
,
request
=
request
),
"teams_url"
:
reverse
(
'teams_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
]),
"languages"
:
settings
.
ALL_LANGUAGES
,
"countries"
:
list
(
countries
),
"disable_courseware_js"
:
True
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment