Files
edx-platform/cms/djangoapps/contentstore/views/tests/test_tabs.py
Dillon Dumesnil 61b93d953d feat: Add backfill course tabs management command
Previously, course tabs would only be created once and never try to
update the default tabs again. This leads to an issue if you ever want
to add a new tab. With this command, you can now update the default tabs
for all existing courses and new courses will pick it up upon creation
when CourseTabList.initialize_default is called.
2022-03-11 11:16:18 -05:00

219 lines
8.0 KiB
Python

""" Tests for tab functions (just primitive). """
import json
import random
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
from cms.djangoapps.contentstore.views import tabs
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.tabs import CourseTabList # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.x_module import STUDENT_VIEW # lint-amnesty, pylint: disable=wrong-import-order
class TabsPageTests(CourseTestCase):
"""Test cases for Tabs (a.k.a Pages) page"""
def setUp(self):
"""Common setup for tests"""
# call super class to setup course, etc.
super().setUp()
# Set the URL for tests
self.url = reverse_course_url('tabs_handler', self.course.id)
# add 4 static tabs to the course, for code coverage
self.test_tabs = []
for i in range(1, 5):
tab = ItemFactory.create(
parent_location=self.course.location,
category="static_tab",
display_name=f"Static_{i}"
)
self.test_tabs.append(tab)
self.reload_course()
def check_invalid_tab_id_response(self, resp):
"""Verify response is an error listing the invalid_tab_id"""
assert resp.status_code == 400
resp_content = json.loads(resp.content.decode('utf-8'))
assert "error" in resp_content
def test_not_implemented(self):
"""Verify not implemented errors"""
# JSON GET request not supported
with self.assertRaises(NotImplementedError):
self.client.get(self.url)
# JSON POST request not supported
with self.assertRaises(NotImplementedError):
self.client.ajax_post(
self.url,
data=json.dumps({
'tab_id_locator': {'tab_id': 'courseware'},
'unsupported_request': None,
}),
)
# invalid JSON POST request
with self.assertRaises(NotImplementedError):
self.client.ajax_post(
self.url,
data={'invalid_request': None},
)
def test_view_index(self):
"""Basic check that the Pages page responds correctly"""
resp = self.client.get_html(self.url)
self.assertContains(resp, 'course-nav-list')
def test_reorder_tabs(self):
"""Test re-ordering of tabs"""
# get the original tabs
course_tabs = list(self.course.tabs)
num_orig_tabs = len(self.course.tabs)
# make sure we have enough tabs to play around with
assert num_orig_tabs >= 5
# Randomise the order of static tabs, leave the rest intact
course_tabs.sort(key=lambda tab: (100 + random.random()) if tab.type == 'static_tab' else tab.priority)
tabs_data = [
{'tab_locator': str(self.course.id.make_usage_key("static_tab", tab.url_slug))}
for tab in course_tabs
if tab.type == 'static_tab'
]
# Remove one tab randomly. This shouldn't delete the tab.
tabs_data.pop()
# post the request with the reordered static tabs only
resp = self.client.ajax_post(
self.url,
data={
'tabs': tabs_data
},
)
assert resp.status_code == 204
# reload the course and verify the new tab order
self.reload_course()
reordered_tab_ids = [tab.tab_id for tab in course_tabs]
new_tab_ids = [tab.tab_id for tab in self.course.tabs]
assert new_tab_ids == reordered_tab_ids
def test_reorder_tabs_invalid_tab(self):
"""Test re-ordering of tabs with invalid tab"""
invalid_tab_ids = ['courseware', 'info', 'invalid_tab_id']
# post the request
resp = self.client.ajax_post(
self.url,
data={'tabs': [{'tab_id': tab_id} for tab_id in invalid_tab_ids]},
)
self.check_invalid_tab_id_response(resp)
def check_toggle_tab_visiblity(self, tab_type, new_is_hidden_setting):
"""Helper method to check changes in tab visibility"""
# find the tab
old_tab = CourseTabList.get_tab_by_type(self.course.tabs, tab_type)
# visibility should be different from new setting
assert old_tab.is_hidden != new_is_hidden_setting
# post the request
resp = self.client.ajax_post(
self.url,
data=json.dumps({
'tab_id_locator': {'tab_id': old_tab.tab_id},
'is_hidden': new_is_hidden_setting,
}),
)
assert resp.status_code == 204
# reload the course and verify the new visibility setting
self.reload_course()
new_tab = CourseTabList.get_tab_by_type(self.course.tabs, tab_type)
assert new_tab.is_hidden == new_is_hidden_setting
def test_toggle_tab_visibility(self):
"""Test toggling of tab visibility"""
self.check_toggle_tab_visiblity('wiki', False)
self.check_toggle_tab_visiblity('wiki', True)
def test_toggle_invalid_tab_visibility(self):
"""Test toggling visibility of an invalid tab"""
# post the request
resp = self.client.ajax_post(
self.url,
data=json.dumps({
'tab_id_locator': {'tab_id': 'invalid_tab_id'}
}),
)
self.check_invalid_tab_id_response(resp)
def test_tab_preview_html(self):
"""
Verify that the static tab renders itself with the correct HTML
"""
preview_url = f'/xblock/{self.test_tabs[0].location}/{STUDENT_VIEW}'
resp = self.client.get(preview_url, HTTP_ACCEPT='application/json')
assert resp.status_code == 200
resp_content = json.loads(resp.content.decode('utf-8'))
html = resp_content['html']
# Verify that the HTML contains the expected elements
assert '<span class="action-button-text">Edit</span>' in html
assert '<span class="sr">Duplicate this component</span>' in html
assert '<span class="sr">Delete this component</span>' in html
assert '<span data-tooltip="Drag to reorder" class="drag-handle action"></span>' in html
class PrimitiveTabEdit(ModuleStoreTestCase):
"""Tests for the primitive tab edit data manipulations"""
def test_delete(self):
"""Test primitive tab deletion."""
course = CourseFactory.create()
with self.assertRaises(ValueError):
tabs.primitive_delete(course, 0)
with self.assertRaises(ValueError):
tabs.primitive_delete(course, 1)
with self.assertRaises(IndexError):
tabs.primitive_delete(course, 7)
assert course.tabs[2] != {'type': 'dates', 'name': 'Dates'}
tabs.primitive_delete(course, 2)
assert {'type': 'progress'} not in course.tabs
# Check that dates has shifted up
assert course.tabs[2] == {'type': 'dates', 'name': 'Dates'}
def test_insert(self):
"""Test primitive tab insertion."""
course = CourseFactory.create()
tabs.primitive_insert(course, 2, 'pdf_textbooks', 'aname')
assert course.tabs[2] == {'type': 'pdf_textbooks', 'name': 'aname'}
with self.assertRaises(ValueError):
tabs.primitive_insert(course, 0, 'pdf_textbooks', 'aname')
with self.assertRaises(ValueError):
tabs.primitive_insert(course, 3, 'static_tab', 'aname')
def test_save(self):
"""Test course saving."""
course = CourseFactory.create()
tabs.primitive_insert(course, 3, 'pdf_textbooks', 'aname')
course2 = modulestore().get_course(course.id)
assert course2.tabs[3] == {'type': 'pdf_textbooks', 'name': 'aname'}