1988 lines
82 KiB
Python
1988 lines
82 KiB
Python
"""
|
|
Acceptance tests for the teams feature.
|
|
"""
|
|
|
|
|
|
import json
|
|
import random
|
|
import time
|
|
from uuid import uuid4
|
|
|
|
import ddt
|
|
from dateutil.parser import parse
|
|
from selenium.common.exceptions import TimeoutException
|
|
from six.moves import map, range
|
|
|
|
from common.test.acceptance.fixtures import LMS_BASE_URL
|
|
from common.test.acceptance.fixtures.course import CourseFixture
|
|
from common.test.acceptance.fixtures.discussion import ForumsConfigMixin, MultipleThreadFixture, Thread
|
|
from common.test.acceptance.pages.common.auto_auth import AutoAuthPage
|
|
from common.test.acceptance.pages.common.utils import confirm_prompt
|
|
from common.test.acceptance.pages.lms.course_home import CourseHomePage
|
|
from common.test.acceptance.pages.lms.learner_profile import LearnerProfilePage
|
|
from common.test.acceptance.pages.lms.tab_nav import TabNavPage
|
|
from common.test.acceptance.pages.lms.teams import (
|
|
BrowseTeamsPage,
|
|
BrowseTopicsPage,
|
|
EditMembershipPage,
|
|
MyTeamsPage,
|
|
TeamManagementPage,
|
|
TeamPage,
|
|
TeamsPage
|
|
)
|
|
from common.test.acceptance.tests.helpers import EventsTestMixin, UniqueCourseTest, get_modal_alert
|
|
from openedx.core.lib.tests import attr
|
|
|
|
TOPICS_PER_PAGE = 12
|
|
|
|
|
|
class TeamsTabBase(EventsTestMixin, ForumsConfigMixin, UniqueCourseTest):
|
|
"""Base class for Teams Tab tests"""
|
|
def setUp(self):
|
|
super(TeamsTabBase, self).setUp()
|
|
self.tab_nav = TabNavPage(self.browser)
|
|
self.course_home_page = CourseHomePage(self.browser, self.course_id)
|
|
self.teams_page = TeamsPage(self.browser, self.course_id)
|
|
# TODO: Refactor so resetting events database is not necessary
|
|
self.reset_event_tracking()
|
|
|
|
self.enable_forums()
|
|
|
|
def create_topics(self, num_topics):
|
|
"""Create `num_topics` test topics."""
|
|
return [{u"description": i, u"name": i, u"id": i} for i in map(str, range(num_topics))]
|
|
|
|
def create_teams(self, topic, num_teams, time_between_creation=0):
|
|
"""Create `num_teams` teams belonging to `topic`."""
|
|
teams = []
|
|
for i in range(num_teams):
|
|
team = {
|
|
'course_id': self.course_id,
|
|
'topic_id': topic['id'],
|
|
'name': u'Team {}'.format(i),
|
|
'description': u'Description {}'.format(i),
|
|
'language': 'aa',
|
|
'country': 'AF'
|
|
}
|
|
teams.append(self.post_team_data(team))
|
|
# Sadly, this sleep is necessary in order to ensure that
|
|
# sorting by last_activity_at works correctly when running
|
|
# in Jenkins.
|
|
# THIS IS AN ANTI-PATTERN - DO NOT COPY.
|
|
time.sleep(time_between_creation)
|
|
return teams
|
|
|
|
def post_team_data(self, team_data):
|
|
"""Given a JSON representation of a team, post it to the server."""
|
|
response = self.course_fixture.session.post(
|
|
LMS_BASE_URL + '/api/team/v0/teams/',
|
|
data=json.dumps(team_data),
|
|
headers=self.course_fixture.headers
|
|
)
|
|
self.assertEqual(response.status_code, 200)
|
|
return json.loads(response.text)
|
|
|
|
def create_memberships(self, num_memberships, team_id):
|
|
"""Create `num_memberships` users and assign them to `team_id`. The
|
|
last user created becomes the current user."""
|
|
memberships = []
|
|
for __ in range(num_memberships):
|
|
user_info = AutoAuthPage(self.browser, course_id=self.course_id).visit().user_info
|
|
memberships.append(user_info)
|
|
self.create_membership(user_info['username'], team_id)
|
|
#pylint: disable=attribute-defined-outside-init
|
|
self.user_info = memberships[-1]
|
|
return memberships
|
|
|
|
def create_membership(self, username, team_id):
|
|
"""Assign `username` to `team_id`."""
|
|
response = self.course_fixture.session.post(
|
|
LMS_BASE_URL + '/api/team/v0/team_membership/',
|
|
data=json.dumps({'username': username, 'team_id': team_id}),
|
|
headers=self.course_fixture.headers
|
|
)
|
|
return json.loads(response.text)
|
|
|
|
def set_team_configuration(self, configuration_data, enroll_in_course=True, global_staff=False):
|
|
"""
|
|
Sets team configuration on the course and calls auto-auth on the user.
|
|
"""
|
|
#pylint: disable=attribute-defined-outside-init
|
|
self.course_fixture = CourseFixture(**self.course_info)
|
|
if configuration_data:
|
|
self.course_fixture.add_advanced_settings(
|
|
{u"teams_configuration": {u"value": configuration_data}}
|
|
)
|
|
self.course_fixture.install()
|
|
|
|
enroll_course_id = self.course_id if enroll_in_course else None
|
|
#pylint: disable=attribute-defined-outside-init
|
|
self.user_info = AutoAuthPage(self.browser, course_id=enroll_course_id, staff=global_staff).visit().user_info
|
|
self.course_home_page.visit()
|
|
|
|
def verify_teams_present(self, present):
|
|
"""
|
|
Verifies whether or not the teams tab is present. If it should be present, also
|
|
checks the text on the page (to ensure view is working).
|
|
"""
|
|
if present:
|
|
self.assertIn("Teams", self.tab_nav.tab_names)
|
|
self.teams_page.visit()
|
|
self.assertEqual(self.teams_page.active_tab(), 'browse')
|
|
else:
|
|
self.assertNotIn("Teams", self.tab_nav.tab_names)
|
|
|
|
def verify_teams(self, page, expected_teams):
|
|
"""Verify that the list of team cards on the current page match the expected teams in order."""
|
|
|
|
def assert_team_equal(expected_team, team_card_name, team_card_description):
|
|
"""
|
|
Helper to assert that a single team card has the expected name and
|
|
description.
|
|
"""
|
|
self.assertEqual(expected_team['name'], team_card_name)
|
|
self.assertEqual(expected_team['description'], team_card_description)
|
|
|
|
team_card_names = page.team_names
|
|
team_card_descriptions = page.team_descriptions
|
|
list(map(assert_team_equal, expected_teams, team_card_names, team_card_descriptions))
|
|
|
|
def verify_my_team_count(self, expected_number_of_teams):
|
|
""" Verify the number of teams shown on "My Team". """
|
|
|
|
# We are doing these operations on this top-level page object to avoid reloading the page.
|
|
self.teams_page.verify_my_team_count(expected_number_of_teams)
|
|
|
|
def only_team_events(self, event):
|
|
"""Filter out all non-team events."""
|
|
return event['event_type'].startswith('edx.team.')
|
|
|
|
|
|
@ddt.ddt
|
|
@attr(shard=5)
|
|
class TeamsTabTest(TeamsTabBase):
|
|
"""
|
|
Tests verifying when the Teams tab is present.
|
|
"""
|
|
def test_teams_not_enabled(self):
|
|
"""
|
|
Scenario: teams tab should not be present if no team configuration is set
|
|
Given I am enrolled in a course without team configuration
|
|
When I view the course info page
|
|
Then I should not see the Teams tab
|
|
"""
|
|
self.set_team_configuration(None)
|
|
self.verify_teams_present(False)
|
|
|
|
def test_teams_not_enabled_no_topics(self):
|
|
"""
|
|
Scenario: teams tab should not be present if team configuration does not specify topics
|
|
Given I am enrolled in a course with no topics in the team configuration
|
|
When I view the course info page
|
|
Then I should not see the Teams tab
|
|
"""
|
|
self.set_team_configuration({u"max_team_size": 10, u"topics": []})
|
|
self.verify_teams_present(False)
|
|
|
|
def test_teams_enabled(self):
|
|
"""
|
|
Scenario: teams tab should be present if user is enrolled in the course and it has team configuration
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I view the course info page
|
|
Then I should see the Teams tab
|
|
And the correct content should be on the page
|
|
"""
|
|
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(1)})
|
|
self.verify_teams_present(True)
|
|
|
|
def test_teams_enabled_global_staff(self):
|
|
"""
|
|
Scenario: teams tab should be present if user is not enrolled in the course, but is global staff
|
|
Given there is a course with team configuration
|
|
And I am not enrolled in that course, but am global staff
|
|
When I view the course info page
|
|
Then I should see the Teams tab
|
|
And the correct content should be on the page
|
|
"""
|
|
self.set_team_configuration(
|
|
{u"max_team_size": 10, u"topics": self.create_topics(1)},
|
|
enroll_in_course=False,
|
|
global_staff=True
|
|
)
|
|
self.verify_teams_present(True)
|
|
|
|
@ddt.data(
|
|
'topics/{topic_id}',
|
|
'topics/{topic_id}/search',
|
|
'teams/{topic_id}/{team_id}/edit-team',
|
|
'teams/{topic_id}/{team_id}'
|
|
)
|
|
def test_unauthorized_error_message(self, route):
|
|
"""Ensure that an error message is shown to the user if they attempt
|
|
to take an action which makes an AJAX request while not signed
|
|
in.
|
|
"""
|
|
topics = self.create_topics(1)
|
|
topic = topics[0]
|
|
self.set_team_configuration(
|
|
{u'max_team_size': 10, u'topics': topics},
|
|
global_staff=True
|
|
)
|
|
team = self.create_teams(topic, 1)[0]
|
|
self.teams_page.visit()
|
|
self.browser.delete_cookie('sessionid')
|
|
url = self.browser.current_url.split('#')[0]
|
|
self.browser.get(
|
|
'{url}#{route}'.format(
|
|
url=url,
|
|
route=route.format(
|
|
topic_id=topic['id'],
|
|
team_id=team['id']
|
|
)
|
|
)
|
|
)
|
|
self.teams_page.wait_for_ajax()
|
|
self.assertEqual(
|
|
self.teams_page.warning_message,
|
|
u"Your request could not be completed. Reload the page and try again."
|
|
)
|
|
|
|
@ddt.data(
|
|
('browse', '.topics-list'),
|
|
# TODO: find a reliable way to match the "My Teams" tab
|
|
# ('my-teams', 'div.teams-list'),
|
|
('teams/{topic_id}/{team_id}', 'div.discussion-module'),
|
|
('topics/{topic_id}/create-team', 'div.create-team-instructions'),
|
|
('topics/{topic_id}', '.teams-list'),
|
|
('not-a-real-route', 'div.warning')
|
|
)
|
|
@ddt.unpack
|
|
def test_url_routing(self, route, selector):
|
|
"""Ensure that navigating to a URL route correctly updates the page
|
|
content.
|
|
"""
|
|
topics = self.create_topics(1)
|
|
topic = topics[0]
|
|
self.set_team_configuration({
|
|
u'max_team_size': 10,
|
|
u'topics': topics
|
|
})
|
|
team = self.create_teams(topic, 1)[0]
|
|
self.teams_page.visit()
|
|
|
|
# Get the base URL (the URL without any trailing fragment)
|
|
url = self.browser.current_url
|
|
fragment_index = url.find('#')
|
|
if fragment_index >= 0:
|
|
url = url[0:fragment_index]
|
|
|
|
self.browser.get(
|
|
'{url}#{route}'.format(
|
|
url=url,
|
|
route=route.format(
|
|
topic_id=topic['id'],
|
|
team_id=team['id']
|
|
))
|
|
)
|
|
self.teams_page.wait_for_page()
|
|
self.teams_page.wait_for_ajax()
|
|
self.assertTrue(self.teams_page.q(css=selector).present)
|
|
self.assertTrue(self.teams_page.q(css=selector).visible)
|
|
|
|
|
|
@attr(shard=5)
|
|
class MyTeamsTest(TeamsTabBase):
|
|
"""
|
|
Tests for the "My Teams" tab of the Teams page.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super(MyTeamsTest, self).setUp()
|
|
self.topic = {u"name": u"Example Topic", u"id": "example_topic", u"description": "Description"}
|
|
self.set_team_configuration({'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]})
|
|
self.my_teams_page = MyTeamsPage(self.browser, self.course_id)
|
|
self.page_viewed_event = {
|
|
'event_type': 'edx.team.page_viewed',
|
|
'event': {
|
|
'page_name': 'my-teams',
|
|
'topic_id': None,
|
|
'team_id': None
|
|
}
|
|
}
|
|
|
|
def test_not_member_of_any_teams(self):
|
|
"""
|
|
Scenario: Visiting the My Teams page when user is not a member of any team should not display any teams.
|
|
Given I am enrolled in a course with a team configuration and a topic but am not a member of a team
|
|
When I visit the My Teams page
|
|
And I should see no teams
|
|
And I should see a message that I belong to no teams.
|
|
"""
|
|
with self.assert_events_match_during(self.only_team_events, expected_events=[self.page_viewed_event]):
|
|
self.my_teams_page.visit()
|
|
self.assertEqual(len(self.my_teams_page.team_cards), 0, msg='Expected to see no team cards')
|
|
self.assertEqual(
|
|
self.my_teams_page.q(css='.page-content-main').text,
|
|
[u'You are not currently a member of any team.']
|
|
)
|
|
|
|
def test_member_of_a_team(self):
|
|
"""
|
|
Scenario: Visiting the My Teams page when user is a member of a team should display the teams.
|
|
Given I am enrolled in a course with a team configuration and a topic and am a member of a team
|
|
When I visit the My Teams page
|
|
Then I should see a pagination header showing the number of teams
|
|
And I should see all the expected team cards
|
|
And I should not see a pagination footer
|
|
"""
|
|
teams = self.create_teams(self.topic, 1)
|
|
self.create_membership(self.user_info['username'], teams[0]['id'])
|
|
with self.assert_events_match_during(self.only_team_events, expected_events=[self.page_viewed_event]):
|
|
self.my_teams_page.visit()
|
|
self.verify_teams(self.my_teams_page, teams)
|
|
|
|
def test_multiple_team_members(self):
|
|
"""
|
|
Scenario: Visiting the My Teams page when user is a member of a team should display the teams.
|
|
Given I am a member of a team with multiple members
|
|
When I visit the My Teams page
|
|
Then I should see the correct number of team members on my membership
|
|
"""
|
|
teams = self.create_teams(self.topic, 1)
|
|
self.create_memberships(4, teams[0]['id'])
|
|
self.my_teams_page.visit()
|
|
self.assertEqual(self.my_teams_page.team_memberships[0], '4 / 10 Members')
|
|
|
|
|
|
@attr(shard=5)
|
|
@ddt.ddt
|
|
class BrowseTopicsTest(TeamsTabBase):
|
|
"""
|
|
Tests for the Browse tab of the Teams page.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super(BrowseTopicsTest, self).setUp()
|
|
self.topics_page = BrowseTopicsPage(self.browser, self.course_id)
|
|
|
|
@ddt.data(('name', False), ('team_count', True))
|
|
@ddt.unpack
|
|
def test_sort_topics(self, sort_order, reverse):
|
|
"""
|
|
Scenario: the user should be able to sort the list of topics by name or team count
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
Then I should see a list of topics for the course
|
|
When I choose a sort order
|
|
Then I should see the paginated list of topics in that order
|
|
"""
|
|
topics = self.create_topics(TOPICS_PER_PAGE + 1)
|
|
self.set_team_configuration({u"max_team_size": 100, u"topics": topics})
|
|
for i, topic in enumerate(random.sample(topics, len(topics))):
|
|
self.create_teams(topic, i)
|
|
topic['team_count'] = i
|
|
self.topics_page.visit()
|
|
self.topics_page.sort_topics_by(sort_order)
|
|
topic_names = self.topics_page.topic_names
|
|
self.assertEqual(len(topic_names), TOPICS_PER_PAGE)
|
|
self.assertEqual(
|
|
topic_names,
|
|
[t['name'] for t in sorted(topics, key=lambda t: t[sort_order], reverse=reverse)][:TOPICS_PER_PAGE]
|
|
)
|
|
|
|
def test_sort_topics_update(self):
|
|
"""
|
|
Scenario: the list of topics should remain sorted after updates
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse topics and choose a sort order
|
|
Then I should see the paginated list of topics in that order
|
|
When I create a team in one of those topics
|
|
And I return to the topics list
|
|
Then I should see the topics in the correct sorted order
|
|
"""
|
|
topics = self.create_topics(3)
|
|
self.set_team_configuration({u"max_team_size": 100, u"topics": topics})
|
|
self.topics_page.visit()
|
|
self.topics_page.sort_topics_by('team_count')
|
|
topic_name = self.topics_page.topic_names[-1]
|
|
topic = [t for t in topics if t['name'] == topic_name][0]
|
|
self.topics_page.browse_teams_for_topic(topic_name)
|
|
browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, topic)
|
|
browse_teams_page.wait_for_page()
|
|
browse_teams_page.click_create_team_link()
|
|
create_team_page = TeamManagementPage(self.browser, self.course_id, topic)
|
|
create_team_page.create_team()
|
|
|
|
team_page = TeamPage(self.browser, self.course_id)
|
|
team_page.wait_for_page()
|
|
|
|
team_page.click_all_topics()
|
|
self.topics_page.wait_for_page()
|
|
self.topics_page.wait_for_ajax()
|
|
self.assertEqual(topic_name, self.topics_page.topic_names[0])
|
|
|
|
def test_list_topics(self):
|
|
"""
|
|
Scenario: a list of topics should be visible in the "Browse" tab
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
Then I should see a list of topics for the course
|
|
"""
|
|
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(2)})
|
|
self.topics_page.visit()
|
|
self.assertEqual(len(self.topics_page.topic_cards), 2)
|
|
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 1-2 out of 2 total'))
|
|
self.assertFalse(self.topics_page.pagination_controls_visible())
|
|
self.assertFalse(self.topics_page.is_previous_page_button_enabled())
|
|
self.assertFalse(self.topics_page.is_next_page_button_enabled())
|
|
|
|
def test_topic_pagination(self):
|
|
"""
|
|
Scenario: a list of topics should be visible in the "Browse" tab, paginated 12 per page
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
Then I should see only the first 12 topics
|
|
"""
|
|
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(20)})
|
|
self.topics_page.visit()
|
|
self.assertEqual(len(self.topics_page.topic_cards), TOPICS_PER_PAGE)
|
|
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 1-12 out of 20 total'))
|
|
self.assertTrue(self.topics_page.pagination_controls_visible())
|
|
self.assertFalse(self.topics_page.is_previous_page_button_enabled())
|
|
self.assertTrue(self.topics_page.is_next_page_button_enabled())
|
|
|
|
def test_go_to_numbered_page(self):
|
|
"""
|
|
Scenario: topics should be able to be navigated by page number
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
And I enter a valid page number in the page number input
|
|
Then I should see that page of topics
|
|
"""
|
|
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(25)})
|
|
self.topics_page.visit()
|
|
self.topics_page.go_to_page(3)
|
|
self.assertEqual(len(self.topics_page.topic_cards), 1)
|
|
self.assertTrue(self.topics_page.is_previous_page_button_enabled())
|
|
self.assertFalse(self.topics_page.is_next_page_button_enabled())
|
|
|
|
def test_go_to_invalid_page(self):
|
|
"""
|
|
Scenario: browsing topics should not respond to invalid page numbers
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
And I enter an invalid page number in the page number input
|
|
Then I should stay on the current page
|
|
"""
|
|
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(13)})
|
|
self.topics_page.visit()
|
|
self.topics_page.go_to_page(3)
|
|
self.assertEqual(self.topics_page.get_current_page_number(), 1)
|
|
|
|
def test_page_navigation_buttons(self):
|
|
"""
|
|
Scenario: browsing topics should not respond to invalid page numbers
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
When I press the next page button
|
|
Then I should move to the next page
|
|
When I press the previous page button
|
|
Then I should move to the previous page
|
|
"""
|
|
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(13)})
|
|
self.topics_page.visit()
|
|
self.topics_page.press_next_page_button()
|
|
self.assertEqual(len(self.topics_page.topic_cards), 1)
|
|
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 13-13 out of 13 total'))
|
|
self.topics_page.press_previous_page_button()
|
|
self.assertEqual(len(self.topics_page.topic_cards), TOPICS_PER_PAGE)
|
|
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 1-12 out of 13 total'))
|
|
|
|
def test_topic_pagination_one_page(self):
|
|
"""
|
|
Scenario: Browsing topics when there are fewer topics than the page size i.e. 12
|
|
all topics should show on one page
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
And I should see corrected number of topic cards
|
|
And I should see the correct page header
|
|
And I should not see a pagination footer
|
|
"""
|
|
self.set_team_configuration({u"max_team_size": 10, u"topics": self.create_topics(10)})
|
|
self.topics_page.visit()
|
|
self.assertEqual(len(self.topics_page.topic_cards), 10)
|
|
self.assertTrue(self.topics_page.get_pagination_header_text().startswith('Showing 1-10 out of 10 total'))
|
|
self.assertFalse(self.topics_page.pagination_controls_visible())
|
|
|
|
def test_topic_description_truncation(self):
|
|
"""
|
|
Scenario: excessively long topic descriptions should be truncated so
|
|
as to fit within a topic card.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
with a long description
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
Then I should see a truncated topic description
|
|
"""
|
|
initial_description = "A" + " really" * 50 + " long description"
|
|
self.set_team_configuration(
|
|
{
|
|
u"max_team_size": 1,
|
|
u"topics": [
|
|
{"id": "long-description-topic", "description": initial_description},
|
|
]
|
|
}
|
|
)
|
|
self.topics_page.visit()
|
|
truncated_description = self.topics_page.topic_descriptions[0]
|
|
self.assertLess(len(truncated_description), len(initial_description))
|
|
self.assertTrue(truncated_description.endswith('...'))
|
|
self.assertIn(truncated_description.split('...')[0], initial_description)
|
|
|
|
def test_go_to_teams_list(self):
|
|
"""
|
|
Scenario: Clicking on a Topic Card should take you to the
|
|
teams list for that Topic.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Teams page
|
|
And I browse topics
|
|
And I click on the arrow link to view teams for the first topic
|
|
Then I should be on the browse teams page
|
|
"""
|
|
topic = {u"name": u"Example Topic", u"id": u"example_topic", u"description": "Description"}
|
|
self.set_team_configuration(
|
|
{u"max_team_size": 1, u"topics": [topic]}
|
|
)
|
|
self.topics_page.visit()
|
|
self.topics_page.browse_teams_for_topic('Example Topic')
|
|
browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, topic)
|
|
browse_teams_page.wait_for_page()
|
|
self.assertEqual(browse_teams_page.header_name, 'Example Topic')
|
|
self.assertEqual(browse_teams_page.header_description, 'Description')
|
|
|
|
def test_page_viewed_event(self):
|
|
"""
|
|
Scenario: Visiting the browse topics page should fire a page viewed event.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the browse topics page
|
|
Then my browser should post a page viewed event
|
|
"""
|
|
topic = {u"name": u"Example Topic", u"id": u"example_topic", u"description": "Description"}
|
|
self.set_team_configuration(
|
|
{u"max_team_size": 1, u"topics": [topic]}
|
|
)
|
|
events = [{
|
|
'event_type': 'edx.team.page_viewed',
|
|
'event': {
|
|
'page_name': 'browse',
|
|
'topic_id': None,
|
|
'team_id': None
|
|
}
|
|
}]
|
|
with self.assert_events_match_during(self.only_team_events, expected_events=events):
|
|
self.topics_page.visit()
|
|
|
|
|
|
@attr(shard=4)
|
|
@ddt.ddt
|
|
class BrowseTeamsWithinTopicTest(TeamsTabBase):
|
|
"""
|
|
Tests for browsing Teams within a Topic on the Teams page.
|
|
"""
|
|
TEAMS_PAGE_SIZE = 10
|
|
|
|
def setUp(self):
|
|
super(BrowseTeamsWithinTopicTest, self).setUp()
|
|
self.topic = {u"name": u"Example Topic", u"id": "example_topic", u"description": "Description"}
|
|
self.max_team_size = 10
|
|
self.set_team_configuration({
|
|
'course_id': self.course_id,
|
|
'max_team_size': self.max_team_size,
|
|
'topics': [self.topic]
|
|
})
|
|
self.browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, self.topic)
|
|
self.topics_page = BrowseTopicsPage(self.browser, self.course_id)
|
|
|
|
def teams_with_default_sort_order(self, teams):
|
|
"""Return a list of teams sorted according to the default ordering
|
|
(last_activity_at, with a secondary sort by open slots).
|
|
"""
|
|
return sorted(
|
|
sorted(teams, key=lambda t: len(t['membership']), reverse=True),
|
|
key=lambda t: parse(t['last_activity_at']).replace(microsecond=0),
|
|
reverse=True
|
|
)
|
|
|
|
def verify_page_header(self):
|
|
"""Verify that the page header correctly reflects the current topic's name and description."""
|
|
self.assertEqual(self.browse_teams_page.header_name, self.topic['name'])
|
|
self.assertEqual(self.browse_teams_page.header_description, self.topic['description'])
|
|
|
|
def verify_search_header(self, search_results_page, search_query):
|
|
"""Verify that the page header correctly reflects the current topic's name and description."""
|
|
self.assertEqual(search_results_page.header_name, 'Team Search')
|
|
self.assertEqual(
|
|
search_results_page.header_description,
|
|
u'Showing results for "{search_query}"'.format(search_query=search_query)
|
|
)
|
|
|
|
def verify_on_page(self, teams_page, page_num, total_teams, pagination_header_text, footer_visible):
|
|
"""
|
|
Verify that we are on the correct team list page.
|
|
|
|
Arguments:
|
|
teams_page (BaseTeamsPage): The teams page object that should be the current page.
|
|
page_num (int): The one-indexed page number that we expect to be on
|
|
total_teams (list): An unsorted list of all the teams for the
|
|
current topic
|
|
pagination_header_text (str): Text we expect to see in the
|
|
pagination header.
|
|
footer_visible (bool): Whether we expect to see the pagination
|
|
footer controls.
|
|
"""
|
|
sorted_teams = self.teams_with_default_sort_order(total_teams)
|
|
self.assertTrue(teams_page.get_pagination_header_text().startswith(pagination_header_text))
|
|
self.verify_teams(
|
|
teams_page,
|
|
sorted_teams[(page_num - 1) * self.TEAMS_PAGE_SIZE:page_num * self.TEAMS_PAGE_SIZE]
|
|
)
|
|
self.assertEqual(
|
|
teams_page.pagination_controls_visible(),
|
|
footer_visible,
|
|
msg='Expected paging footer to be ' + 'visible' if footer_visible else 'invisible'
|
|
)
|
|
|
|
@ddt.data(
|
|
('open_slots', 'last_activity_at', True),
|
|
('last_activity_at', 'open_slots', True)
|
|
)
|
|
@ddt.unpack
|
|
def test_sort_teams(self, sort_order, secondary_sort_order, reverse):
|
|
"""
|
|
Scenario: the user should be able to sort the list of teams by open slots or last activity
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse teams within a topic
|
|
Then I should see a list of teams for that topic
|
|
When I choose a sort order
|
|
Then I should see the paginated list of teams in that order
|
|
"""
|
|
teams = self.create_teams(self.topic, self.TEAMS_PAGE_SIZE + 1)
|
|
for i, team in enumerate(random.sample(teams, len(teams))):
|
|
for _ in range(i):
|
|
user_info = AutoAuthPage(self.browser, course_id=self.course_id).visit().user_info
|
|
self.create_membership(user_info['username'], team['id'])
|
|
team['open_slots'] = self.max_team_size - i
|
|
|
|
# Re-authenticate as staff after creating users
|
|
AutoAuthPage(
|
|
self.browser,
|
|
course_id=self.course_id,
|
|
staff=True
|
|
).visit()
|
|
self.browse_teams_page.visit()
|
|
self.browse_teams_page.sort_teams_by(sort_order)
|
|
team_names = self.browse_teams_page.team_names
|
|
self.assertEqual(len(team_names), self.TEAMS_PAGE_SIZE)
|
|
sorted_teams = [
|
|
team['name']
|
|
for team in sorted(
|
|
sorted(teams, key=lambda t: t[secondary_sort_order], reverse=reverse),
|
|
key=lambda t: t[sort_order],
|
|
reverse=reverse
|
|
)
|
|
][:self.TEAMS_PAGE_SIZE]
|
|
self.assertEqual(team_names, sorted_teams)
|
|
|
|
def test_default_sort_order(self):
|
|
"""
|
|
Scenario: the list of teams should be sorted by last activity by default
|
|
Given I am enrolled in a course with team configuration and topics
|
|
When I visit the Teams page
|
|
And I browse teams within a topic
|
|
Then I should see a list of teams for that topic, sorted by last activity
|
|
"""
|
|
self.create_teams(self.topic, self.TEAMS_PAGE_SIZE + 1)
|
|
self.browse_teams_page.visit()
|
|
self.assertEqual(self.browse_teams_page.sort_order, 'last activity')
|
|
|
|
def test_no_teams(self):
|
|
"""
|
|
Scenario: Visiting a topic with no teams should not display any teams.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Teams page for that topic
|
|
Then I should see the correct page header
|
|
And I should see a pagination header showing no teams
|
|
And I should see no teams
|
|
And I should see a button to add a team
|
|
And I should not see a pagination footer
|
|
"""
|
|
self.browse_teams_page.visit()
|
|
self.verify_page_header()
|
|
self.assertTrue(self.browse_teams_page.get_pagination_header_text().startswith('Showing 0 out of 0 total'))
|
|
self.assertEqual(len(self.browse_teams_page.team_cards), 0, msg='Expected to see no team cards')
|
|
self.assertFalse(
|
|
self.browse_teams_page.pagination_controls_visible(),
|
|
msg='Expected paging footer to be invisible'
|
|
)
|
|
|
|
def test_teams_one_page(self):
|
|
"""
|
|
Scenario: Visiting a topic with fewer teams than the page size should
|
|
all those teams on one page.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Teams page for that topic
|
|
Then I should see the correct page header
|
|
And I should see a pagination header showing the number of teams
|
|
And I should see all the expected team cards
|
|
And I should see a button to add a team
|
|
And I should not see a pagination footer
|
|
"""
|
|
teams = self.teams_with_default_sort_order(
|
|
self.create_teams(self.topic, self.TEAMS_PAGE_SIZE, time_between_creation=1)
|
|
)
|
|
self.browse_teams_page.visit()
|
|
self.verify_page_header()
|
|
self.assertTrue(self.browse_teams_page.get_pagination_header_text().startswith('Showing 1-10 out of 10 total'))
|
|
self.verify_teams(self.browse_teams_page, teams)
|
|
self.assertFalse(
|
|
self.browse_teams_page.pagination_controls_visible(),
|
|
msg='Expected paging footer to be invisible'
|
|
)
|
|
|
|
def test_teams_navigation_buttons(self):
|
|
"""
|
|
Scenario: The user should be able to page through a topic's team list
|
|
using navigation buttons when it is longer than the page size.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Teams page for that topic
|
|
Then I should see the correct page header
|
|
And I should see that I am on the first page of results
|
|
When I click on the next page button
|
|
Then I should see that I am on the second page of results
|
|
And when I click on the previous page button
|
|
Then I should see that I am on the first page of results
|
|
"""
|
|
teams = self.create_teams(self.topic, self.TEAMS_PAGE_SIZE + 1, time_between_creation=1)
|
|
self.browse_teams_page.visit()
|
|
self.verify_page_header()
|
|
self.verify_on_page(self.browse_teams_page, 1, teams, 'Showing 1-10 out of 11 total', True)
|
|
self.browse_teams_page.press_next_page_button()
|
|
self.verify_on_page(self.browse_teams_page, 2, teams, 'Showing 11-11 out of 11 total', True)
|
|
self.browse_teams_page.press_previous_page_button()
|
|
self.verify_on_page(self.browse_teams_page, 1, teams, 'Showing 1-10 out of 11 total', True)
|
|
|
|
def test_teams_page_input(self):
|
|
"""
|
|
Scenario: The user should be able to page through a topic's team list
|
|
using the page input when it is longer than the page size.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Teams page for that topic
|
|
Then I should see the correct page header
|
|
And I should see that I am on the first page of results
|
|
When I input the second page
|
|
Then I should see that I am on the second page of results
|
|
When I input the first page
|
|
Then I should see that I am on the first page of results
|
|
"""
|
|
teams = self.create_teams(self.topic, self.TEAMS_PAGE_SIZE + 10, time_between_creation=1)
|
|
self.browse_teams_page.visit()
|
|
self.verify_page_header()
|
|
self.verify_on_page(self.browse_teams_page, 1, teams, 'Showing 1-10 out of 20 total', True)
|
|
self.browse_teams_page.go_to_page(2)
|
|
self.verify_on_page(self.browse_teams_page, 2, teams, 'Showing 11-20 out of 20 total', True)
|
|
self.browse_teams_page.go_to_page(1)
|
|
self.verify_on_page(self.browse_teams_page, 1, teams, 'Showing 1-10 out of 20 total', True)
|
|
|
|
def test_browse_team_topics(self):
|
|
"""
|
|
Scenario: User should be able to navigate to "browse all teams" and "search team description" links.
|
|
Given I am enrolled in a course with teams enabled
|
|
When I visit the Teams page for a topic
|
|
Then I should see the correct page header
|
|
And I should see the link to "browse teams in other topics"
|
|
When I should navigate to that link
|
|
Then I should see the topic browse page
|
|
"""
|
|
self.browse_teams_page.visit()
|
|
self.verify_page_header()
|
|
|
|
self.browse_teams_page.click_browse_all_teams_link()
|
|
self.topics_page.wait_for_page()
|
|
|
|
def test_search(self):
|
|
"""
|
|
Scenario: User should be able to search for a team
|
|
Given I am enrolled in a course with teams enabled
|
|
When I visit the Teams page for that topic
|
|
And I search for 'banana'
|
|
Then I should see the search result page
|
|
And the search header should be shown
|
|
And 0 results should be shown
|
|
And my browser should fire a page viewed event for the search page
|
|
And a searched event should have been fired
|
|
"""
|
|
# Note: all searches will return 0 results with the mock search server
|
|
# used by Bok Choy.
|
|
search_text = 'banana'
|
|
self.create_teams(self.topic, 5)
|
|
self.browse_teams_page.visit()
|
|
events = [{
|
|
'event_type': 'edx.team.page_viewed',
|
|
'event': {
|
|
'page_name': 'search-teams',
|
|
'topic_id': self.topic['id'],
|
|
'team_id': None
|
|
}
|
|
}, {
|
|
'event_type': 'edx.team.searched',
|
|
'event': {
|
|
'search_text': search_text,
|
|
'topic_id': self.topic['id'],
|
|
'number_of_results': 0
|
|
}
|
|
}]
|
|
with self.assert_events_match_during(self.only_team_events, expected_events=events, in_order=False):
|
|
search_results_page = self.browse_teams_page.search(search_text)
|
|
self.verify_search_header(search_results_page, search_text)
|
|
self.assertTrue(search_results_page.get_pagination_header_text().startswith('Showing 0 out of 0 total'))
|
|
|
|
def test_page_viewed_event(self):
|
|
"""
|
|
Scenario: Visiting the browse page should fire a page viewed event.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Teams page
|
|
Then my browser should post a page viewed event for the teams page
|
|
"""
|
|
self.create_teams(self.topic, 5)
|
|
events = [{
|
|
'event_type': 'edx.team.page_viewed',
|
|
'event': {
|
|
'page_name': 'single-topic',
|
|
'topic_id': self.topic['id'],
|
|
'team_id': None
|
|
}
|
|
}]
|
|
with self.assert_events_match_during(self.only_team_events, expected_events=events):
|
|
self.browse_teams_page.visit()
|
|
|
|
def test_team_name_xss(self):
|
|
"""
|
|
Scenario: Team names should be HTML-escaped on the teams page
|
|
Given I am enrolled in a course with teams enabled
|
|
When I visit the Teams page for a topic, with a team name containing JS code
|
|
Then I should not see any alerts
|
|
"""
|
|
self.post_team_data({
|
|
'course_id': self.course_id,
|
|
'topic_id': self.topic['id'],
|
|
'name': '<script>alert("XSS")</script>',
|
|
'description': 'Description',
|
|
'language': 'aa',
|
|
'country': 'AF'
|
|
})
|
|
with self.assertRaises(TimeoutException):
|
|
self.browser.get(self.browse_teams_page.url)
|
|
alert = get_modal_alert(self.browser)
|
|
alert.accept()
|
|
|
|
|
|
class TeamFormActions(TeamsTabBase):
|
|
"""
|
|
Base class for create, edit, and delete team.
|
|
"""
|
|
TEAM_DESCRIPTION = 'The Avengers are a fictional team of superheroes.'
|
|
|
|
topic = {'name': 'Example Topic', 'id': 'example_topic', 'description': 'Description'}
|
|
TEAMS_NAME = 'Avengers'
|
|
|
|
def setUp(self):
|
|
super(TeamFormActions, self).setUp()
|
|
self.team_management_page = TeamManagementPage(self.browser, self.course_id, self.topic)
|
|
|
|
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.team_management_page.header_page_name, title)
|
|
self.assertEqual(self.team_management_page.header_page_description, description)
|
|
self.assertEqual(self.team_management_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 an existing team to join, '
|
|
'or if you would like to learn with friends you know.',
|
|
breadcrumbs=u'All Topics {topic_name}'.format(topic_name=self.topic['name'])
|
|
)
|
|
|
|
def verify_and_navigate_to_edit_team_page(self):
|
|
"""Navigates to the edit team page and verifies."""
|
|
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.team_management_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=u'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."""
|
|
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.team_management_page.value_for_text_field(
|
|
field_id='name',
|
|
value=self.TEAMS_NAME,
|
|
press_enter=False
|
|
)
|
|
self.team_management_page.set_value_for_textarea_field(
|
|
field_id='description',
|
|
value=self.TEAM_DESCRIPTION
|
|
)
|
|
self.team_management_page.value_for_dropdown_field(field_id='language', value='English')
|
|
self.team_management_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.team_management_page.message_for_field('name'),
|
|
'A name that identifies your team (maximum 255 characters).'
|
|
)
|
|
self.assertEqual(
|
|
self.team_management_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.team_management_page.message_for_field('country'),
|
|
'The country that team members primarily identify with.'
|
|
)
|
|
self.assertEqual(
|
|
self.team_management_page.message_for_field('language'),
|
|
'The language that team members primarily use to communicate with each other.'
|
|
)
|
|
|
|
|
|
@attr(shard=4)
|
|
@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.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):
|
|
"""
|
|
Scenario: The user should be able to see error message in case of missing required field.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Create Team page for that topic
|
|
Then I should see the Create Team header and form
|
|
And When I click create team button without filling required fields
|
|
Then I should see the error message and highlighted fields.
|
|
"""
|
|
self.verify_and_navigate_to_create_team_page()
|
|
|
|
# `submit_form` clicks on a button, but that button doesn't always
|
|
# have the click event handler registered on it in time. That's why
|
|
# this test is flaky. Unfortunately, I don't know of a straightforward
|
|
# way to write something that waits for that event handler to be bound
|
|
# to the button element. So I used time.sleep as well, even though
|
|
# the bok choy docs explicitly ask us not to:
|
|
# https://bok-choy.readthedocs.io/en/latest/guidelines.html
|
|
# Sorry! For the story to address this anti-pattern, see TNL-5820
|
|
time.sleep(0.5)
|
|
self.team_management_page.submit_form()
|
|
self.team_management_page.wait_for(
|
|
lambda: self.team_management_page.validation_message_text,
|
|
"Validation message text never loaded."
|
|
)
|
|
self.assertEqual(
|
|
self.team_management_page.validation_message_text,
|
|
'Check the highlighted fields below and try again.'
|
|
)
|
|
self.assertTrue(self.team_management_page.error_for_field(field_id='name'))
|
|
self.assertTrue(self.team_management_page.error_for_field(field_id='description'))
|
|
|
|
def test_user_can_see_error_message_for_incorrect_data(self):
|
|
"""
|
|
Scenario: The user should be able to see error message in case of increasing length for required fields.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Create Team page for that topic
|
|
Then I should see the Create Team header and form
|
|
When I add text > than 255 characters for name field
|
|
And I click Create button
|
|
Then I should see the error message for exceeding length.
|
|
"""
|
|
self.verify_and_navigate_to_create_team_page()
|
|
|
|
# Fill the name field with >255 characters to see validation message.
|
|
self.team_management_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 '
|
|
'audience, some at no charge. It also conducts research into learning based on how '
|
|
'people use its platform. EdX was created for students and institutions that seek to'
|
|
'transform themselves through cutting-edge technologies, innovative pedagogy, and '
|
|
'rigorous courses. More than 70 schools, nonprofits, corporations, and international'
|
|
'organizations offer or plan to offer courses on the edX website. As of 22 October 2014,'
|
|
'edX has more than 4 million users taking more than 500 courses online.',
|
|
press_enter=False
|
|
)
|
|
self.team_management_page.submit_form()
|
|
|
|
self.assertEqual(
|
|
self.team_management_page.validation_message_text,
|
|
'Check the highlighted fields below and try again.'
|
|
)
|
|
self.assertTrue(self.team_management_page.error_for_field(field_id='name'))
|
|
|
|
def test_user_can_create_new_team_successfully(self):
|
|
"""
|
|
Scenario: The user should be able to create new team.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Create Team page for that topic
|
|
Then I should see the Create Team header and form
|
|
When I fill all the fields present with appropriate data
|
|
And I click Create button
|
|
Then I expect analytics events to be emitted
|
|
And I should see the page for my team
|
|
And I should see the message that says "You are member of this team"
|
|
And the new team should be added to the list of teams within the topic
|
|
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_or_edit_form()
|
|
|
|
expected_events = [
|
|
{
|
|
'event_type': 'edx.team.created'
|
|
},
|
|
{
|
|
'event_type': 'edx.team.learner_added',
|
|
'event': {
|
|
'add_method': 'added_on_create',
|
|
}
|
|
}
|
|
]
|
|
with self.assert_events_match_during(event_filter=self.only_team_events, expected_events=expected_events):
|
|
self.team_management_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.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
|
|
self.teams_page.click_specific_topic("Example Topic")
|
|
self.teams_page.verify_topic_team_count(1)
|
|
|
|
self.teams_page.click_all_topics()
|
|
self.teams_page.verify_team_count_in_first_topic(1)
|
|
|
|
# Verify that if one switches to "My Team" without reloading the page, the newly created team is shown.
|
|
self.verify_my_team_count(1)
|
|
|
|
def test_user_can_cancel_the_team_creation(self):
|
|
"""
|
|
Scenario: The user should be able to cancel the creation of new team.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the Create Team page for that topic
|
|
Then I should see the Create Team header and form
|
|
When I click Cancel button
|
|
Then I should see teams list page without any new team.
|
|
And if I switch to "My Team", it shows no teams
|
|
"""
|
|
self.assertTrue(self.browse_teams_page.get_pagination_header_text().startswith('Showing 0 out of 0 total'))
|
|
|
|
self.verify_and_navigate_to_create_team_page()
|
|
|
|
# We add a sleep here to allow time for the click event handler to bind
|
|
# to the cancel button. Using time.sleep in bok-choy tests is,
|
|
# generally, an anti-pattern. So don't copy this :).
|
|
# For the story to address this anti-pattern, see TNL-5820
|
|
time.sleep(0.5)
|
|
|
|
self.team_management_page.cancel_team()
|
|
|
|
self.browse_teams_page.wait_for_page()
|
|
self.assertTrue(self.browse_teams_page.get_pagination_header_text().startswith('Showing 0 out of 0 total'))
|
|
|
|
self.teams_page.click_all_topics()
|
|
self.teams_page.verify_team_count_in_first_topic(0)
|
|
|
|
self.verify_my_team_count(0)
|
|
|
|
def test_page_viewed_event(self):
|
|
"""
|
|
Scenario: Visiting the create team page should fire a page viewed event.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the create team page
|
|
Then my browser should post a page viewed event
|
|
"""
|
|
events = [{
|
|
'event_type': 'edx.team.page_viewed',
|
|
'event': {
|
|
'page_name': 'new-team',
|
|
'topic_id': self.topic['id'],
|
|
'team_id': None
|
|
}
|
|
}]
|
|
with self.assert_events_match_during(self.only_team_events, expected_events=events):
|
|
self.verify_and_navigate_to_create_team_page()
|
|
|
|
|
|
@attr(shard=21)
|
|
@ddt.ddt
|
|
class DeleteTeamTest(TeamFormActions):
|
|
"""
|
|
Tests for deleting teams.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super(DeleteTeamTest, self).setUp()
|
|
|
|
self.set_team_configuration(
|
|
{'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]},
|
|
global_staff=True
|
|
)
|
|
|
|
self.team = self.create_teams(self.topic, num_teams=1)[0]
|
|
self.team_page = TeamPage(self.browser, self.course_id, team=self.team)
|
|
|
|
#need to have a membership to confirm it gets deleted as well
|
|
self.create_membership(self.user_info['username'], self.team['id'])
|
|
|
|
self.team_page.visit()
|
|
|
|
def test_cancel_delete(self):
|
|
"""
|
|
Scenario: The user should be able to cancel the Delete Team dialog
|
|
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 Delete Team button
|
|
When I click the delete team button
|
|
And I cancel the prompt
|
|
And I refresh the page
|
|
Then I should still see the team
|
|
"""
|
|
self.delete_team(cancel=True)
|
|
self.team_management_page.wait_for_page()
|
|
self.browser.refresh()
|
|
self.team_management_page.wait_for_page()
|
|
self.assertEqual(
|
|
' '.join(('All Topics', self.topic['name'], self.team['name'])),
|
|
self.team_management_page.header_page_breadcrumbs
|
|
)
|
|
|
|
@ddt.data('Moderator', 'Community TA', 'Administrator', None)
|
|
def test_delete_team(self, role):
|
|
"""
|
|
Scenario: The user should be able to see and navigate to the delete 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 Delete Team button
|
|
When I click the delete team button
|
|
And I confirm the prompt
|
|
Then I should see the browse teams page
|
|
And the team should not be present
|
|
"""
|
|
# If role is None, remain logged in as global staff
|
|
if role is not None:
|
|
AutoAuthPage(
|
|
self.browser,
|
|
course_id=self.course_id,
|
|
staff=False,
|
|
roles=role
|
|
).visit()
|
|
self.team_page.visit()
|
|
self.delete_team(require_notification=False)
|
|
browse_teams_page = BrowseTeamsPage(self.browser, self.course_id, self.topic)
|
|
browse_teams_page.wait_for_page()
|
|
self.assertNotIn(self.team['name'], browse_teams_page.team_names)
|
|
|
|
def delete_team(self, **kwargs):
|
|
"""
|
|
Delete a team. Passes `kwargs` to `confirm_prompt`.
|
|
Expects edx.team.deleted event to be emitted, with correct course_id.
|
|
Also expects edx.team.learner_removed event to be emitted for the
|
|
membership that is removed as a part of the delete operation.
|
|
"""
|
|
|
|
self.team_page.click_edit_team_button()
|
|
self.team_management_page.wait_for_page()
|
|
self.team_management_page.delete_team_button.click()
|
|
|
|
if 'cancel' in kwargs and kwargs['cancel'] is True:
|
|
confirm_prompt(self.team_management_page, **kwargs)
|
|
else:
|
|
expected_events = [
|
|
{
|
|
'event_type': 'edx.team.deleted',
|
|
'event': {
|
|
'team_id': self.team['id']
|
|
}
|
|
},
|
|
{
|
|
'event_type': 'edx.team.learner_removed',
|
|
'event': {
|
|
'team_id': self.team['id'],
|
|
'remove_method': 'team_deleted',
|
|
'user_id': self.user_info['user_id']
|
|
}
|
|
}
|
|
]
|
|
with self.assert_events_match_during(
|
|
event_filter=self.only_team_events, expected_events=expected_events
|
|
):
|
|
confirm_prompt(self.team_management_page, **kwargs)
|
|
|
|
def test_delete_team_updates_topics(self):
|
|
"""
|
|
Scenario: Deleting a team should update the team count on the topics page
|
|
Given I am staff user for a course with a team
|
|
And I delete a team
|
|
When I navigate to the browse topics page
|
|
Then the team count for the deletd team's topic should be updated
|
|
"""
|
|
self.delete_team(require_notification=False)
|
|
BrowseTeamsPage(self.browser, self.course_id, self.topic).click_all_topics()
|
|
topics_page = BrowseTopicsPage(self.browser, self.course_id)
|
|
topics_page.wait_for_page()
|
|
self.teams_page.verify_topic_team_count(0)
|
|
|
|
|
|
@attr(shard=17)
|
|
@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.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
|
|
And an analytics event should be fired
|
|
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()
|
|
|
|
expected_events = [
|
|
{
|
|
'event_type': 'edx.team.changed',
|
|
'event': {
|
|
'team_id': self.team['id'],
|
|
'field': 'country',
|
|
'old': 'AF',
|
|
'new': 'PK',
|
|
'truncated': [],
|
|
}
|
|
},
|
|
{
|
|
'event_type': 'edx.team.changed',
|
|
'event': {
|
|
'team_id': self.team['id'],
|
|
'field': 'name',
|
|
'old': self.team['name'],
|
|
'new': self.TEAMS_NAME,
|
|
'truncated': [],
|
|
}
|
|
},
|
|
{
|
|
'event_type': 'edx.team.changed',
|
|
'event': {
|
|
'team_id': self.team['id'],
|
|
'field': 'language',
|
|
'old': 'aa',
|
|
'new': 'en',
|
|
'truncated': [],
|
|
}
|
|
},
|
|
{
|
|
'event_type': 'edx.team.changed',
|
|
'event': {
|
|
'team_id': self.team['id'],
|
|
'field': 'description',
|
|
'old': self.team['description'],
|
|
'new': self.TEAM_DESCRIPTION,
|
|
'truncated': [],
|
|
}
|
|
},
|
|
]
|
|
with self.assert_events_match_during(
|
|
event_filter=self.only_team_events,
|
|
expected_events=expected_events,
|
|
):
|
|
self.team_management_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.team_management_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.team_management_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_page_viewed_event(self):
|
|
"""
|
|
Scenario: Visiting the edit team page should fire a page viewed event.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the edit team page
|
|
Then my browser should post a page viewed event
|
|
"""
|
|
events = [{
|
|
'event_type': 'edx.team.page_viewed',
|
|
'event': {
|
|
'page_name': 'edit-team',
|
|
'topic_id': self.topic['id'],
|
|
'team_id': self.team['id']
|
|
}
|
|
}]
|
|
with self.assert_events_match_during(self.only_team_events, expected_events=events):
|
|
self.verify_and_navigate_to_edit_team_page()
|
|
|
|
|
|
@attr(shard=17)
|
|
@ddt.ddt
|
|
class EditMembershipTest(TeamFormActions):
|
|
"""
|
|
Tests for administrating from the team membership page
|
|
"""
|
|
|
|
def setUp(self):
|
|
super(EditMembershipTest, self).setUp()
|
|
|
|
self.set_team_configuration(
|
|
{'course_id': self.course_id, 'max_team_size': 10, 'topics': [self.topic]},
|
|
global_staff=True
|
|
)
|
|
self.team_management_page = TeamManagementPage(self.browser, self.course_id, self.topic)
|
|
self.team = self.create_teams(self.topic, num_teams=1)[0]
|
|
|
|
#make sure a user exists on this team so we can edit the membership
|
|
self.create_membership(self.user_info['username'], self.team['id'])
|
|
|
|
self.edit_membership_page = EditMembershipPage(self.browser, self.course_id, self.team)
|
|
self.team_page = TeamPage(self.browser, self.course_id, team=self.team)
|
|
|
|
def edit_membership_helper(self, role, cancel=False):
|
|
"""
|
|
Helper for common functionality in edit membership tests.
|
|
Checks for all relevant assertions about membership being removed,
|
|
including verify edx.team.learner_removed events are emitted.
|
|
"""
|
|
if role is not None:
|
|
AutoAuthPage(
|
|
self.browser,
|
|
course_id=self.course_id,
|
|
staff=False,
|
|
roles=role
|
|
).visit()
|
|
|
|
self.team_page.visit()
|
|
self.team_page.click_edit_team_button()
|
|
self.team_management_page.wait_for_page()
|
|
|
|
self.assertTrue(
|
|
self.team_management_page.membership_button_present
|
|
)
|
|
|
|
self.team_management_page.click_membership_button()
|
|
self.edit_membership_page.wait_for_page()
|
|
self.edit_membership_page.click_first_remove()
|
|
if cancel:
|
|
self.edit_membership_page.cancel_delete_membership_dialog()
|
|
self.assertEqual(self.edit_membership_page.team_members, 1)
|
|
else:
|
|
expected_events = [
|
|
{
|
|
'event_type': 'edx.team.learner_removed',
|
|
'event': {
|
|
'team_id': self.team['id'],
|
|
'remove_method': 'removed_by_admin',
|
|
'user_id': self.user_info['user_id']
|
|
}
|
|
}
|
|
]
|
|
with self.assert_events_match_during(
|
|
event_filter=self.only_team_events, expected_events=expected_events
|
|
):
|
|
self.edit_membership_page.confirm_delete_membership_dialog()
|
|
self.assertEqual(self.edit_membership_page.team_members, 0)
|
|
self.edit_membership_page.wait_for_page()
|
|
|
|
@ddt.data('Moderator', 'Community TA', 'Administrator', None)
|
|
def test_remove_membership(self, role):
|
|
"""
|
|
Scenario: The user should be able to remove a membership
|
|
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 Membership button
|
|
And When I click the edit membership button
|
|
Then I should see the edit membership page
|
|
And When I click the remove button and confirm the dialog
|
|
Then my membership should be removed, and I should remain on the page
|
|
"""
|
|
self.edit_membership_helper(role, cancel=False)
|
|
|
|
@ddt.data('Moderator', 'Community TA', 'Administrator', None)
|
|
def test_cancel_remove_membership(self, role):
|
|
"""
|
|
Scenario: The user should be able to remove a membership
|
|
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 Membership button
|
|
And When I click the edit membership button
|
|
Then I should see the edit membership page
|
|
And When I click the remove button and cancel the dialog
|
|
Then my membership should not be removed, and I should remain on the page
|
|
"""
|
|
self.edit_membership_helper(role, cancel=True)
|
|
|
|
|
|
@attr(shard=17)
|
|
@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"}
|
|
|
|
def _set_team_configuration_and_membership(
|
|
self,
|
|
max_team_size=10,
|
|
membership_team_index=0,
|
|
visit_team_index=0,
|
|
create_membership=True,
|
|
another_user=False):
|
|
"""
|
|
Set team configuration.
|
|
|
|
Arguments:
|
|
max_team_size (int): number of users a team can have
|
|
membership_team_index (int): index of team user will join
|
|
visit_team_index (int): index of team user will visit
|
|
create_membership (bool): whether to create membership or not
|
|
another_user (bool): another user to visit a team
|
|
"""
|
|
#pylint: disable=attribute-defined-outside-init
|
|
self.set_team_configuration(
|
|
{'course_id': self.course_id, 'max_team_size': max_team_size, 'topics': [self.topic]}
|
|
)
|
|
self.teams = self.create_teams(self.topic, 2)
|
|
|
|
if create_membership:
|
|
self.create_membership(self.user_info['username'], self.teams[membership_team_index]['id'])
|
|
|
|
if another_user:
|
|
AutoAuthPage(self.browser, course_id=self.course_id).visit()
|
|
|
|
self.team_page = TeamPage(self.browser, self.course_id, self.teams[visit_team_index])
|
|
|
|
def setup_thread(self):
|
|
"""
|
|
Create and return a thread for this test's discussion topic.
|
|
"""
|
|
thread = Thread(
|
|
id="test_thread_{}".format(uuid4().hex),
|
|
commentable_id=self.teams[0]['discussion_topic_id'],
|
|
body="Dummy text body.",
|
|
context="standalone",
|
|
)
|
|
thread_fixture = MultipleThreadFixture([thread])
|
|
thread_fixture.push()
|
|
return thread
|
|
|
|
def setup_discussion_user(self, role=None, staff=False):
|
|
"""Set this test's user to have the given role in its
|
|
discussions. Role is one of 'Community TA', 'Moderator',
|
|
'Administrator', or 'Student'.
|
|
"""
|
|
kwargs = {
|
|
'course_id': self.course_id,
|
|
'staff': staff
|
|
}
|
|
if role is not None:
|
|
kwargs['roles'] = role
|
|
#pylint: disable=attribute-defined-outside-init
|
|
self.user_info = AutoAuthPage(self.browser, **kwargs).visit().user_info
|
|
|
|
def verify_teams_discussion_permissions(self, should_have_permission):
|
|
"""Verify that the teams discussion component is in the correct state
|
|
for the test user. If `should_have_permission` is True, assert that
|
|
the user can see controls for posting replies, voting, editing, and
|
|
deleting. Otherwise, assert that those controls are hidden.
|
|
"""
|
|
thread = self.setup_thread()
|
|
self.team_page.visit()
|
|
self.assertEqual(self.team_page.discussion_id, self.teams[0]['discussion_topic_id'])
|
|
discussion_page = self.team_page.discussion_page
|
|
discussion_page.wait_for_page()
|
|
self.assertTrue(discussion_page.is_discussion_expanded())
|
|
self.assertEqual(discussion_page.get_num_displayed_threads(), 1)
|
|
discussion_page.show_thread(thread['id'])
|
|
thread_page = discussion_page.thread_page
|
|
assertion = self.assertTrue if should_have_permission else self.assertFalse
|
|
assertion(thread_page.q(css='.post-header-actions').present)
|
|
assertion(thread_page.q(css='.add-response').present)
|
|
|
|
def test_discussion_on_my_team_page(self):
|
|
"""
|
|
Scenario: Team Page renders a discussion for a team to which I belong.
|
|
Given I am enrolled in a course with a team configuration, a topic,
|
|
and a team belonging to that topic of which I am a member
|
|
When the team has a discussion with a thread
|
|
And I visit the Team page for that team
|
|
Then I should see a discussion with the correct discussion_id
|
|
And I should see the existing thread
|
|
And I should see controls to change the state of the discussion
|
|
"""
|
|
self._set_team_configuration_and_membership()
|
|
self.verify_teams_discussion_permissions(True)
|
|
|
|
@ddt.data(True, False)
|
|
def test_discussion_on_other_team_page(self, is_staff):
|
|
"""
|
|
Scenario: Team Page renders a team discussion for a team to which I do
|
|
not belong.
|
|
Given I am enrolled in a course with a team configuration, a topic,
|
|
and a team belonging to that topic of which I am not a member
|
|
When the team has a discussion with a thread
|
|
And I visit the Team page for that team
|
|
Then I should see a discussion with the correct discussion_id
|
|
And I should see the team's thread
|
|
And I should not see controls to change the state of the discussion
|
|
"""
|
|
self._set_team_configuration_and_membership(create_membership=False)
|
|
self.setup_discussion_user(staff=is_staff)
|
|
self.verify_teams_discussion_permissions(False)
|
|
|
|
@ddt.data('Moderator', 'Community TA', 'Administrator')
|
|
def test_discussion_privileged(self, role):
|
|
self._set_team_configuration_and_membership(create_membership=False)
|
|
self.setup_discussion_user(role=role)
|
|
self.verify_teams_discussion_permissions(True)
|
|
|
|
def assert_team_details(self, num_members, is_member=True, max_size=10):
|
|
"""
|
|
Verifies that user can see all the information, present on detail page according to their membership status.
|
|
|
|
Arguments:
|
|
num_members (int): number of users in a team
|
|
is_member (bool) default True: True if request user is member else False
|
|
max_size (int): number of users a team can have
|
|
"""
|
|
self.assertEqual(
|
|
self.team_page.team_capacity_text,
|
|
self.team_page.format_capacity_text(num_members, max_size)
|
|
)
|
|
self.assertEqual(self.team_page.team_location, 'Afghanistan')
|
|
self.assertEqual(self.team_page.team_language, 'Afar')
|
|
self.assertEqual(self.team_page.team_members, num_members)
|
|
|
|
if num_members > 0:
|
|
self.assertTrue(self.team_page.team_members_present)
|
|
else:
|
|
self.assertFalse(self.team_page.team_members_present)
|
|
|
|
if is_member:
|
|
self.assertEqual(self.team_page.team_user_membership_text, 'You are a member of this team.')
|
|
self.assertTrue(self.team_page.team_leave_link_present)
|
|
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.new_post_button_present)
|
|
|
|
def test_team_member_can_see_full_team_details(self):
|
|
"""
|
|
Scenario: Team member can see full info for team.
|
|
Given I am enrolled in a course with a team configuration, a topic,
|
|
and a team belonging to that topic of which I am a member
|
|
When I visit the Team page for that team
|
|
Then I should see the full team detail
|
|
And I should see the team members
|
|
And I should see my team membership text
|
|
And I should see the language & country
|
|
And I should see the Leave Team and Invite Team
|
|
"""
|
|
self._set_team_configuration_and_membership()
|
|
self.team_page.visit()
|
|
|
|
self.assert_team_details(
|
|
num_members=1,
|
|
)
|
|
|
|
def test_other_users_can_see_limited_team_details(self):
|
|
"""
|
|
Scenario: Users who are not member of this team can only see limited info for this team.
|
|
Given I am enrolled in a course with a team configuration, a topic,
|
|
and a team belonging to that topic of which I am not a member
|
|
When I visit the Team page for that team
|
|
Then I should not see full team detail
|
|
And I should see the team members
|
|
And I should not see my team membership text
|
|
And I should not see the Leave Team and Invite Team links
|
|
"""
|
|
self._set_team_configuration_and_membership(create_membership=False)
|
|
self.team_page.visit()
|
|
|
|
self.assert_team_details(is_member=False, num_members=0)
|
|
|
|
def test_user_can_navigate_to_members_profile_page(self):
|
|
"""
|
|
Scenario: User can navigate to profile page via team member profile image.
|
|
Given I am enrolled in a course with a team configuration, a topic,
|
|
and a team belonging to that topic of which I am a member
|
|
When I visit the Team page for that team
|
|
Then I should see profile images for the team members
|
|
When I click on the first profile image
|
|
Then I should be taken to the user's profile page
|
|
And I should see the username on profile page
|
|
"""
|
|
self._set_team_configuration_and_membership()
|
|
self.team_page.visit()
|
|
|
|
learner_name = self.team_page.first_member_username
|
|
|
|
self.team_page.click_first_profile_image()
|
|
|
|
learner_profile_page = LearnerProfilePage(self.browser, learner_name)
|
|
learner_profile_page.wait_for_page()
|
|
learner_profile_page.wait_for_field('username')
|
|
self.assertTrue(learner_profile_page.field_is_visible('username'))
|
|
|
|
def test_join_team(self):
|
|
"""
|
|
Scenario: User can join a Team if not a member already..
|
|
|
|
Given I am enrolled in a course with a team configuration, a topic,
|
|
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 an analytics event should be emitted
|
|
And I should see the updated information under Team Details
|
|
And I should see New Post button
|
|
And if I switch to "My Team", the team I have joined is displayed
|
|
"""
|
|
self._set_team_configuration_and_membership(create_membership=False)
|
|
teams_page = BrowseTeamsPage(self.browser, self.course_id, self.topic)
|
|
teams_page.visit()
|
|
teams_page.view_first_team()
|
|
self.assertTrue(self.team_page.join_team_button_present)
|
|
expected_events = [
|
|
{
|
|
'event_type': 'edx.team.learner_added',
|
|
'event': {
|
|
'add_method': 'joined_from_team_view'
|
|
}
|
|
}
|
|
]
|
|
with self.assert_events_match_during(event_filter=self.only_team_events, expected_events=expected_events):
|
|
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, is_member=True)
|
|
|
|
# Verify that if one switches to "My Team" without reloading the page, the newly joined team is shown.
|
|
self.teams_page.click_all_topics()
|
|
self.verify_my_team_count(1)
|
|
|
|
def test_already_member_message(self):
|
|
"""
|
|
Scenario: User should see `You are already in a team` if user is a
|
|
member of other team.
|
|
|
|
Given I am enrolled in a course with a team configuration, a topic,
|
|
and a team belonging to that topic
|
|
And I am already a member of a team
|
|
And I visit a team other than mine
|
|
Then I should see `You are already in a team` message
|
|
"""
|
|
self._set_team_configuration_and_membership(membership_team_index=0, visit_team_index=1)
|
|
self.team_page.visit()
|
|
self.assertEqual(self.team_page.join_team_message, 'You already belong to another team in this team set.')
|
|
self.assert_team_details(num_members=0, is_member=False)
|
|
|
|
def test_team_full_message(self):
|
|
"""
|
|
Scenario: User should see `Team is full` message when team is full.
|
|
|
|
Given I am enrolled in a course with a team configuration, a topic,
|
|
and a team belonging to that topic
|
|
And team has no space left
|
|
And I am not a member of any team
|
|
And I visit the team
|
|
Then I should see `Team is full` message
|
|
"""
|
|
self._set_team_configuration_and_membership(
|
|
create_membership=True,
|
|
max_team_size=1,
|
|
membership_team_index=0,
|
|
visit_team_index=0,
|
|
another_user=True
|
|
)
|
|
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 an analytics event should be emitted
|
|
And I should see Join Team button
|
|
And I should not see New Post button
|
|
And if I switch to "My Team", the team I have left is not displayed
|
|
"""
|
|
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)
|
|
expected_events = [
|
|
{
|
|
'event_type': 'edx.team.learner_removed',
|
|
'event': {
|
|
'remove_method': 'self_removal'
|
|
}
|
|
}
|
|
]
|
|
with self.assert_events_match_during(event_filter=self.only_team_events, expected_events=expected_events):
|
|
# I think we're seeing the same problem that we're seeing in
|
|
# CreateTeamTest.test_user_can_see_error_message_for_missing_data.
|
|
# We click on the "leave team" link after it's loaded, but before
|
|
# its JavaScript event handler is added. Adding this sleep gives
|
|
# enough time for that event handler to bind to the link. Sorry!
|
|
# For the story to address this anti-pattern, see TNL-5820
|
|
time.sleep(0.5)
|
|
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)
|
|
|
|
# Verify that if one switches to "My Team" without reloading the page, the old team no longer shows.
|
|
self.teams_page.click_all_topics()
|
|
self.verify_my_team_count(0)
|
|
|
|
def test_page_viewed_event(self):
|
|
"""
|
|
Scenario: Visiting the team profile page should fire a page viewed event.
|
|
Given I am enrolled in a course with a team configuration and a topic
|
|
When I visit the team profile page
|
|
Then my browser should post a page viewed event
|
|
"""
|
|
self._set_team_configuration_and_membership()
|
|
events = [{
|
|
'event_type': 'edx.team.page_viewed',
|
|
'event': {
|
|
'page_name': 'single-team',
|
|
'topic_id': self.topic['id'],
|
|
'team_id': self.teams[0]['id']
|
|
}
|
|
}]
|
|
with self.assert_events_match_during(self.only_team_events, expected_events=events):
|
|
self.team_page.visit()
|