AA-289: rewrite how course updates behave when dismissed
Previously, dismissing one course update would disable all future updates. But that feels a bit limiting. Instead, these are the new rules: - If the newest update has not been dismissed yet, it gets displayed. - If the newest update has been dismissed, we display nothing. - Editing the newest update will cause it to be displayed again. - New updates get displayed, even if previous updates were dismissed. This commit includes a light refactoring and addition of some utility methods to help manage the above. It also notes that the learning MFE does not use the "latest_update" waffle option, which controls the labeling of the update message.
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
Common test code for course_experience, like shared base classes.
|
||||
"""
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from lms.djangoapps.courseware.courses import get_course_info_usage_key
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
|
||||
class BaseCourseUpdatesTestCase(SharedModuleStoreTestCase):
|
||||
"""Base class for working with course updates."""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Set up the simplest course possible."""
|
||||
# pylint: disable=super-method-not-called
|
||||
with super().setUpClassAndTestData():
|
||||
with cls.store.default_store(ModuleStoreEnum.Type.split):
|
||||
cls.course = CourseFactory.create()
|
||||
with cls.store.bulk_operations(cls.course.id):
|
||||
# Create a basic course structure
|
||||
chapter = ItemFactory.create(category='chapter', parent_location=cls.course.location)
|
||||
section = ItemFactory.create(category='sequential', parent_location=chapter.location)
|
||||
ItemFactory.create(category='vertical', parent_location=section.location)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Set up and enroll our fake user in the course."""
|
||||
super().setUpTestData()
|
||||
cls.user = UserFactory(password=cls.TEST_PASSWORD)
|
||||
CourseEnrollment.enroll(cls.user, cls.course.id)
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.client.login(username=self.user.username, password=self.TEST_PASSWORD)
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_course_updates()
|
||||
super().tearDown()
|
||||
|
||||
def remove_course_updates(self, user=None, course=None):
|
||||
"""Remove any course updates in the specified course."""
|
||||
user = user or self.user
|
||||
course = course or self.course
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
try:
|
||||
course_updates = modulestore().get_item(updates_usage_key)
|
||||
modulestore().delete_item(course_updates.location, user.id)
|
||||
except (ItemNotFoundError, ValueError):
|
||||
pass
|
||||
|
||||
def edit_course_update(self, index, content=None, course=None, user=None, date=None, deleted=None):
|
||||
"""Edits a course update item. Only changes explicitly provided parameters."""
|
||||
user = user or self.user
|
||||
course = course or self.course
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
course_updates = modulestore().get_item(updates_usage_key)
|
||||
for item in course_updates.items:
|
||||
if item['id'] == index:
|
||||
if date is not None:
|
||||
item['date'] = date
|
||||
if content is not None:
|
||||
item['content'] = content
|
||||
if deleted is not None:
|
||||
item['status'] = 'deleted' if deleted else 'visible'
|
||||
break
|
||||
modulestore().update_item(course_updates, user.id)
|
||||
|
||||
def create_course_update(self, content, course=None, user=None, date='December 31, 1999', deleted=False):
|
||||
"""Creates a test welcome message for the specified course."""
|
||||
user = user or self.user
|
||||
course = course or self.course
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
try:
|
||||
course_updates = modulestore().get_item(updates_usage_key)
|
||||
except ItemNotFoundError:
|
||||
course_updates = self.create_course_updates_block(course=course, user=user)
|
||||
item = {
|
||||
'id': len(course_updates.items) + 1,
|
||||
'date': date,
|
||||
'content': content,
|
||||
'status': 'deleted' if deleted else 'visible',
|
||||
}
|
||||
course_updates.items.append(item)
|
||||
modulestore().update_item(course_updates, user.id)
|
||||
return item
|
||||
|
||||
def create_course_updates_block(self, course=None, user=None):
|
||||
"""Create a course updates block."""
|
||||
user = user or self.user
|
||||
course = course or self.course
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
course_updates = modulestore().create_item(
|
||||
user.id,
|
||||
updates_usage_key.course_key,
|
||||
updates_usage_key.block_type,
|
||||
block_id=updates_usage_key.block_id
|
||||
)
|
||||
course_updates.data = ''
|
||||
return course_updates
|
||||
|
||||
121
openedx/features/course_experience/tests/test_course_updates.py
Normal file
121
openedx/features/course_experience/tests/test_course_updates.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
Tests for the course updates utility methods.
|
||||
"""
|
||||
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from openedx.core.djangoapps.user_api.course_tag.api import get_course_tag, set_course_tag
|
||||
from openedx.features.course_experience.course_updates import (
|
||||
dismiss_current_update_for_user, get_current_update_for_user, get_ordered_updates,
|
||||
)
|
||||
from openedx.features.course_experience.tests import BaseCourseUpdatesTestCase
|
||||
|
||||
|
||||
class TestCourseUpdatesUtils(BaseCourseUpdatesTestCase):
|
||||
"""Tests for the course update utility methods."""
|
||||
|
||||
UPDATES_TAG = 'view-welcome-message'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super().setUpTestData()
|
||||
cls.request = RequestFactory().get('/')
|
||||
cls.request.user = cls.user
|
||||
|
||||
def test_update_structure(self):
|
||||
"""Test that returned item dictionary is as we expect."""
|
||||
content = '<em>HTML Content</em>'
|
||||
date = 'January 1, 2000'
|
||||
self.create_course_update(content, date=date)
|
||||
updates = get_ordered_updates(self.request, self.course)
|
||||
self.assertListEqual(updates, [{
|
||||
'id': 1,
|
||||
'content': content,
|
||||
'date': date,
|
||||
'status': 'visible',
|
||||
}])
|
||||
|
||||
def test_ordered_updates(self):
|
||||
"""Test that order of returned items follows our rules."""
|
||||
first = self.create_course_update('2000', date='January 1, 2000')
|
||||
second = self.create_course_update('2017', date='January 1, 2017')
|
||||
third = self.create_course_update('Also 2017', date='January 1, 2017')
|
||||
injected = self.create_course_update('Injected out of order', date='January 1, 2010')
|
||||
ill_formed = self.create_course_update('Ill-formed date is parsed as now()', date='foobar')
|
||||
self.create_course_update('Deleted is ignored', deleted=True)
|
||||
updates = get_ordered_updates(self.request, self.course)
|
||||
self.assertListEqual(updates, [ill_formed, third, second, injected, first])
|
||||
|
||||
def test_replace_urls(self):
|
||||
"""We should be replacing static URLs with course specific ones."""
|
||||
self.create_course_update("<img src='/static/img.png'>")
|
||||
updates = get_ordered_updates(self.request, self.course)
|
||||
expected = "<img src='/asset-v1:{org}+{course}+{run}+type@asset+block/img.png'>".format(
|
||||
org=self.course.id.org,
|
||||
course=self.course.id.course,
|
||||
run=self.course.id.run,
|
||||
)
|
||||
self.assertEqual(updates[0]['content'], expected)
|
||||
|
||||
def test_ordered_update_includes_dismissed_updates(self):
|
||||
"""Ordered update list should still have dismissed updates."""
|
||||
self.create_course_update('Dismissed')
|
||||
dismiss_current_update_for_user(self.request, self.course)
|
||||
updates = get_ordered_updates(self.request, self.course)
|
||||
self.assertEqual(len(updates), 1)
|
||||
|
||||
def test_get_current_update_is_newest(self):
|
||||
"""Tests that the current update is also the newest."""
|
||||
self.create_course_update('Oldest', date='January 1, 1900')
|
||||
self.create_course_update('New', date='January 1, 2017')
|
||||
self.create_course_update('Oldish', date='January 1, 2000')
|
||||
self.assertEqual(get_current_update_for_user(self.request, self.course), 'New')
|
||||
|
||||
def test_get_current_update_when_dismissed(self):
|
||||
"""Tests that a dismissed update is not returned."""
|
||||
self.create_course_update('Dismissed')
|
||||
dismiss_current_update_for_user(self.request, self.course)
|
||||
self.assertIsNone(get_current_update_for_user(self.request, self.course))
|
||||
|
||||
def test_get_current_update_when_dismissed_but_edited(self):
|
||||
"""Tests that a dismissed but edited update is returned."""
|
||||
self.create_course_update('Original')
|
||||
dismiss_current_update_for_user(self.request, self.course)
|
||||
self.assertIsNone(get_current_update_for_user(self.request, self.course))
|
||||
self.edit_course_update(1, content='Edited')
|
||||
self.assertIsNotNone(get_current_update_for_user(self.request, self.course))
|
||||
|
||||
def test_get_current_update_remembers_dismissals(self):
|
||||
"""Tests that older dismissed updates are remembered."""
|
||||
self.create_course_update('First')
|
||||
self.create_course_update('Second')
|
||||
dismiss_current_update_for_user(self.request, self.course)
|
||||
self.create_course_update('Third')
|
||||
dismiss_current_update_for_user(self.request, self.course)
|
||||
self.create_course_update('Fourth')
|
||||
|
||||
self.assertEqual(get_current_update_for_user(self.request, self.course), 'Fourth')
|
||||
self.edit_course_update(4, deleted=True)
|
||||
self.assertIsNone(get_current_update_for_user(self.request, self.course))
|
||||
self.edit_course_update(3, deleted=True)
|
||||
self.assertIsNone(get_current_update_for_user(self.request, self.course))
|
||||
self.edit_course_update(2, deleted=True)
|
||||
self.assertEqual(get_current_update_for_user(self.request, self.course), 'First')
|
||||
|
||||
def test_legacy_ignore_all_support(self):
|
||||
"""Storing 'False' as the dismissal ignores all updates."""
|
||||
self.create_course_update('First')
|
||||
self.assertEqual(get_current_update_for_user(self.request, self.course), 'First')
|
||||
|
||||
set_course_tag(self.user, self.course.id, self.UPDATES_TAG, 'False')
|
||||
self.assertIsNone(get_current_update_for_user(self.request, self.course))
|
||||
|
||||
def test_dismissal_hashing(self):
|
||||
"""Confirm that the stored dismissal values are what we expect, to catch accidentally changing formats."""
|
||||
self.create_course_update('First')
|
||||
dismiss_current_update_for_user(self.request, self.course)
|
||||
self.create_course_update('Second')
|
||||
dismiss_current_update_for_user(self.request, self.course)
|
||||
|
||||
tag = get_course_tag(self.user, self.course.id, self.UPDATES_TAG)
|
||||
self.assertEqual(tag, '7fb55ed0b7a30342ba6da306428cae04,c22cf8376b1893dcfcef0649fe1a7d87')
|
||||
@@ -54,6 +54,7 @@ from openedx.features.course_experience import (
|
||||
SHOW_REVIEWS_TOOL_FLAG,
|
||||
SHOW_UPGRADE_MSG_ON_COURSE_HOME
|
||||
)
|
||||
from openedx.features.course_experience.tests import BaseCourseUpdatesTestCase
|
||||
from openedx.features.discounts.applicability import get_discount_expiration_date
|
||||
from openedx.features.discounts.utils import REV1008_EXPERIMENT_ID, format_strikeout_price
|
||||
from common.djangoapps.student.models import CourseEnrollment, FBEEnrollmentExclusion
|
||||
@@ -61,12 +62,11 @@ from common.djangoapps.student.tests.factories import UserFactory
|
||||
from common.djangoapps.util.date_utils import strftime_localized
|
||||
from xmodule.course_module import COURSE_VISIBILITY_PRIVATE, COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import CourseUserType, ModuleStoreTestCase, SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.django_utils import CourseUserType, ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls
|
||||
|
||||
from ... import COURSE_PRE_START_ACCESS_FLAG, ENABLE_COURSE_GOALS
|
||||
from .helpers import add_course_mode, remove_course_mode
|
||||
from .test_course_updates import create_course_update, remove_course_updates
|
||||
|
||||
TEST_PASSWORD = 'test'
|
||||
TEST_CHAPTER_NAME = 'Test Chapter'
|
||||
@@ -113,7 +113,7 @@ def course_home_url_from_string(course_key_string):
|
||||
)
|
||||
|
||||
|
||||
class CourseHomePageTestCase(SharedModuleStoreTestCase):
|
||||
class CourseHomePageTestCase(BaseCourseUpdatesTestCase):
|
||||
"""
|
||||
Base class for testing the course home page.
|
||||
"""
|
||||
@@ -154,10 +154,8 @@ class CourseHomePageTestCase(SharedModuleStoreTestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Set up and enroll our fake user in the course."""
|
||||
super(CourseHomePageTestCase, cls).setUpTestData()
|
||||
super().setUpTestData()
|
||||
cls.staff_user = StaffFactory(course_key=cls.course.id, password=TEST_PASSWORD)
|
||||
cls.user = UserFactory(password=TEST_PASSWORD)
|
||||
CourseEnrollment.enroll(cls.user, cls.course.id)
|
||||
|
||||
def create_future_course(self, specific_date=None):
|
||||
"""
|
||||
@@ -170,17 +168,9 @@ class CourseHomePageTestCase(SharedModuleStoreTestCase):
|
||||
|
||||
|
||||
class TestCourseHomePage(CourseHomePageTestCase):
|
||||
def setUp(self):
|
||||
super(TestCourseHomePage, self).setUp()
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
|
||||
def tearDown(self):
|
||||
remove_course_updates(self.user, self.course)
|
||||
super(TestCourseHomePage, self).tearDown()
|
||||
|
||||
def test_welcome_message_when_unified(self):
|
||||
# Create a welcome message
|
||||
create_course_update(self.course, self.user, TEST_WELCOME_MESSAGE)
|
||||
self.create_course_update(TEST_WELCOME_MESSAGE)
|
||||
|
||||
url = course_home_url(self.course)
|
||||
response = self.client.get(url)
|
||||
@@ -189,7 +179,7 @@ class TestCourseHomePage(CourseHomePageTestCase):
|
||||
@override_waffle_flag(DISABLE_UNIFIED_COURSE_TAB_FLAG, active=True)
|
||||
def test_welcome_message_when_not_unified(self):
|
||||
# Create a welcome message
|
||||
create_course_update(self.course, self.user, TEST_WELCOME_MESSAGE)
|
||||
self.create_course_update(TEST_WELCOME_MESSAGE)
|
||||
|
||||
url = course_home_url(self.course)
|
||||
response = self.client.get(url)
|
||||
@@ -204,7 +194,7 @@ class TestCourseHomePage(CourseHomePageTestCase):
|
||||
response = self.client.get(url)
|
||||
self.assertNotContains(response, TEST_COURSE_UPDATES_TOOL, status_code=200)
|
||||
|
||||
create_course_update(self.course, self.user, TEST_UPDATE_MESSAGE)
|
||||
self.create_course_update(TEST_UPDATE_MESSAGE)
|
||||
url = course_home_url(self.course)
|
||||
response = self.client.get(url)
|
||||
self.assertContains(response, TEST_COURSE_UPDATES_TOOL, status_code=200)
|
||||
@@ -249,18 +239,15 @@ class TestCourseHomePageAccess(CourseHomePageTestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCourseHomePageAccess, self).setUp()
|
||||
super().setUp()
|
||||
self.client.logout() # start with least access and add access back in the various test cases
|
||||
|
||||
# Make this a verified course so that an upgrade message might be shown
|
||||
add_course_mode(self.course, mode_slug=CourseMode.AUDIT)
|
||||
add_course_mode(self.course)
|
||||
|
||||
# Add a welcome message
|
||||
create_course_update(self.course, self.staff_user, TEST_WELCOME_MESSAGE)
|
||||
|
||||
def tearDown(self):
|
||||
remove_course_updates(self.staff_user, self.course)
|
||||
super(TestCourseHomePageAccess, self).tearDown()
|
||||
self.create_course_update(TEST_WELCOME_MESSAGE)
|
||||
|
||||
@override_waffle_flag(SHOW_REVIEWS_TOOL_FLAG, active=True)
|
||||
@ddt.data(
|
||||
|
||||
@@ -2,25 +2,14 @@
|
||||
Tests for the course updates page.
|
||||
"""
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import six
|
||||
from django.urls import reverse
|
||||
|
||||
from lms.djangoapps.courseware.courses import get_course_info_usage_key
|
||||
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
from openedx.features.course_experience.views.course_updates import STATUS_VISIBLE
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls
|
||||
|
||||
TEST_PASSWORD = 'test'
|
||||
from openedx.features.course_experience.tests import BaseCourseUpdatesTestCase
|
||||
from xmodule.modulestore.tests.factories import check_mongo_calls
|
||||
|
||||
QUERY_COUNT_TABLE_BLACKLIST = WAFFLE_TABLES
|
||||
|
||||
@@ -32,93 +21,18 @@ def course_updates_url(course):
|
||||
return reverse(
|
||||
'openedx.course_experience.course_updates',
|
||||
kwargs={
|
||||
'course_id': six.text_type(course.id),
|
||||
'course_id': str(course.id),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def create_course_update(course, user, content, date='December 31, 1999'):
|
||||
"""
|
||||
Creates a test welcome message for the specified course.
|
||||
"""
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
try:
|
||||
course_updates = modulestore().get_item(updates_usage_key)
|
||||
except ItemNotFoundError:
|
||||
course_updates = create_course_updates_block(course, user)
|
||||
course_updates.items.append({
|
||||
"id": len(course_updates.items) + 1,
|
||||
"date": date,
|
||||
"content": content,
|
||||
"status": STATUS_VISIBLE
|
||||
})
|
||||
modulestore().update_item(course_updates, user.id)
|
||||
|
||||
|
||||
def create_course_updates_block(course, user):
|
||||
"""
|
||||
Create a course updates block.
|
||||
"""
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
course_updates = modulestore().create_item(
|
||||
user.id,
|
||||
updates_usage_key.course_key,
|
||||
updates_usage_key.block_type,
|
||||
block_id=updates_usage_key.block_id
|
||||
)
|
||||
course_updates.data = ''
|
||||
return course_updates
|
||||
|
||||
|
||||
def remove_course_updates(user, course):
|
||||
"""
|
||||
Remove any course updates in the specified course.
|
||||
"""
|
||||
updates_usage_key = get_course_info_usage_key(course, 'updates')
|
||||
try:
|
||||
course_updates = modulestore().get_item(updates_usage_key)
|
||||
modulestore().delete_item(course_updates.location, user.id)
|
||||
except (ItemNotFoundError, ValueError):
|
||||
pass
|
||||
|
||||
|
||||
class TestCourseUpdatesPage(SharedModuleStoreTestCase):
|
||||
class TestCourseUpdatesPage(BaseCourseUpdatesTestCase):
|
||||
"""
|
||||
Test the course updates page.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Set up the simplest course possible."""
|
||||
# pylint: disable=super-method-not-called
|
||||
with super(TestCourseUpdatesPage, cls).setUpClassAndTestData():
|
||||
with cls.store.default_store(ModuleStoreEnum.Type.split):
|
||||
cls.course = CourseFactory.create()
|
||||
with cls.store.bulk_operations(cls.course.id):
|
||||
# Create a basic course structure
|
||||
chapter = ItemFactory.create(category='chapter', parent_location=cls.course.location)
|
||||
section = ItemFactory.create(category='sequential', parent_location=chapter.location)
|
||||
ItemFactory.create(category='vertical', parent_location=section.location)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
"""Set up and enroll our fake user in the course."""
|
||||
cls.user = UserFactory(password=TEST_PASSWORD)
|
||||
CourseEnrollment.enroll(cls.user, cls.course.id)
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up for the tests.
|
||||
"""
|
||||
super(TestCourseUpdatesPage, self).setUp()
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
|
||||
def tearDown(self):
|
||||
remove_course_updates(self.user, self.course)
|
||||
super(TestCourseUpdatesPage, self).tearDown()
|
||||
|
||||
def test_view(self):
|
||||
create_course_update(self.course, self.user, 'First Message')
|
||||
create_course_update(self.course, self.user, 'Second Message')
|
||||
self.create_course_update('First Message')
|
||||
self.create_course_update('Second Message')
|
||||
url = course_updates_url(self.course)
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
@@ -127,7 +41,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase):
|
||||
|
||||
def test_queries(self):
|
||||
ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1))
|
||||
create_course_update(self.course, self.user, 'First Message')
|
||||
self.create_course_update('First Message')
|
||||
|
||||
# Pre-fetch the view to populate any caches
|
||||
course_updates_url(self.course)
|
||||
|
||||
@@ -2,21 +2,10 @@
|
||||
Tests for course welcome messages.
|
||||
"""
|
||||
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.urls import reverse
|
||||
|
||||
from common.djangoapps.student.models import CourseEnrollment
|
||||
from common.djangoapps.student.tests.factories import UserFactory
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
from .test_course_updates import create_course_update, remove_course_updates
|
||||
|
||||
TEST_PASSWORD = 'test'
|
||||
TEST_WELCOME_MESSAGE = '<h2>Welcome!</h2>'
|
||||
from openedx.features.course_experience.tests import BaseCourseUpdatesTestCase
|
||||
|
||||
|
||||
def welcome_message_url(course):
|
||||
@@ -26,7 +15,7 @@ def welcome_message_url(course):
|
||||
return reverse(
|
||||
'openedx.course_experience.welcome_message_fragment_view',
|
||||
kwargs={
|
||||
'course_id': six.text_type(course.id),
|
||||
'course_id': str(course.id),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -38,7 +27,7 @@ def latest_update_url(course):
|
||||
return reverse(
|
||||
'openedx.course_experience.latest_update_fragment_view',
|
||||
kwargs={
|
||||
'course_id': six.text_type(course.id),
|
||||
'course_id': str(course.id),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -50,58 +39,28 @@ def dismiss_message_url(course):
|
||||
return reverse(
|
||||
'openedx.course_experience.dismiss_welcome_message',
|
||||
kwargs={
|
||||
'course_id': six.text_type(course.id),
|
||||
'course_id': str(course.id),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestWelcomeMessageView(ModuleStoreTestCase):
|
||||
class TestWelcomeMessageView(BaseCourseUpdatesTestCase):
|
||||
"""
|
||||
Tests for the course welcome message fragment view.
|
||||
|
||||
Also tests the LatestUpdate view because the functionality is similar.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""Set up the simplest course possible, then set up and enroll our fake user in the course."""
|
||||
super(TestWelcomeMessageView, self).setUp()
|
||||
with self.store.default_store(ModuleStoreEnum.Type.split):
|
||||
self.course = CourseFactory.create()
|
||||
with self.store.bulk_operations(self.course.id):
|
||||
# Create a basic course structure
|
||||
chapter = ItemFactory.create(category='chapter', parent_location=self.course.location)
|
||||
section = ItemFactory.create(category='sequential', parent_location=chapter.location)
|
||||
ItemFactory.create(category='vertical', parent_location=section.location)
|
||||
self.user = UserFactory(password=TEST_PASSWORD)
|
||||
CourseEnrollment.enroll(self.user, self.course.id)
|
||||
self.client.login(username=self.user.username, password=TEST_PASSWORD)
|
||||
|
||||
def tearDown(self):
|
||||
remove_course_updates(self.user, self.course)
|
||||
super(TestWelcomeMessageView, self).tearDown()
|
||||
|
||||
@ddt.data(welcome_message_url, latest_update_url)
|
||||
def test_message_display(self, url_generator):
|
||||
create_course_update(self.course, self.user, 'First Update', date='January 1, 2000')
|
||||
create_course_update(self.course, self.user, 'Second Update', date='January 1, 2017')
|
||||
create_course_update(self.course, self.user, 'Retroactive Update', date='January 1, 2010')
|
||||
self.create_course_update('First Update', date='January 1, 2000')
|
||||
self.create_course_update('Second Update', date='January 1, 2017')
|
||||
self.create_course_update('Retroactive Update', date='January 1, 2010')
|
||||
response = self.client.get(url_generator(self.course))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'Second Update')
|
||||
self.assertContains(response, 'Dismiss')
|
||||
|
||||
@ddt.data(welcome_message_url, latest_update_url)
|
||||
def test_replace_urls(self, url_generator):
|
||||
img_url = 'img.png'
|
||||
create_course_update(self.course, self.user, u"<img src='/static/{url}'>".format(url=img_url))
|
||||
response = self.client.get(url_generator(self.course))
|
||||
self.assertContains(response, "/asset-v1:{org}+{course}+{run}+type@asset+block/{url}".format(
|
||||
org=self.course.id.org,
|
||||
course=self.course.id.course,
|
||||
run=self.course.id.run,
|
||||
url=img_url,
|
||||
))
|
||||
|
||||
@ddt.data(welcome_message_url, latest_update_url)
|
||||
def test_empty_message(self, url_generator):
|
||||
response = self.client.get(url_generator(self.course))
|
||||
@@ -109,7 +68,7 @@ class TestWelcomeMessageView(ModuleStoreTestCase):
|
||||
|
||||
def test_dismiss_welcome_message(self):
|
||||
# Latest update is dimssed in JS and has no server/backend component.
|
||||
create_course_update(self.course, self.user, 'First Update', date='January 1, 2017')
|
||||
self.create_course_update('First Update')
|
||||
|
||||
response = self.client.get(welcome_message_url(self.course))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
Reference in New Issue
Block a user