feat!: Flip Studio MFE Waffle Flags to be On-By-Default (#36495)

This makes nearly all of Studio React-by-default by replacing the
"opt-in-to-React" flags with a set of parallel
"opt-out-of-React-and-use-the-legacy-experience" flags. Here is the
mapping:

* `contentstore.new_studio_mfe.use_new_unit_page` ->
  `!legacy_studio.unit_editor`
* `new_core_editors.use_new_problem_editor` ->
  `!legacy_studio.problem_editor`
* `new_core_editors.use_new_text_editor` ->
  `!legacy_studio.text_editor`
* `new_core_editors.use_new_video_editor` ->
  `!legacy_studio.video_editor`
* `new_studio_mfe.use_new_home_page` ->
  `!legacy_studio.home`
* `contentstore.new_studio_mfe.use_new_custom_pages` ->
  `!legacy_studio.custom_pages`
* `contentstore.new_studio_mfe.use_new_schedule_details_page` ->
  `!legacy_studio.schedule_details`
* `contentstore.new_studio_mfe.use_new_advanced_settings_page` ->
  `!legacy_studio.advanced_settings`
* `contentstore.new_studio_mfe.use_new_grading_page` ->
  `!legacy_studio.grading`
* `contentstore.new_studio_mfe.use_new_updates_page` ->
  `!legacy_studio.updates`
* `contentstore.new_studio_mfe.use_new_import_page` ->
  `!legacy_studio.import`
* `contentstore.new_studio_mfe.use_new_export_page` ->
  `!legacy_studio.export`
* `contentstore.new_studio_mfe.use_new_files_uploads_page` ->
  `!legacy_studio.files_uploads`
* `contentstore.new_studio_mfe.use_new_course_outline_page` ->
  `!legacy_studio.course_outline`
* `contentstore.new_studio_mfe.use_new_course_team_page` ->
  `!legacy_studio.course_team`
* `contentstore.new_studio_mfe.use_new_certificates_page` ->
  `!legacy_studio.certificates`
* `contentstore.new_studio_mfe.use_new_textbooks_page` ->
  `!legacy_studio.textbooks`
* `contentstore.new_studio_mfe.use_new_group_configurations_page` ->
  `!legacy_studio.configurations`

Part of: https://github.com/openedx/edx-platform/issues/36275
This commit is contained in:
Kyle McCormick
2025-04-24 12:34:35 -04:00
committed by GitHub
parent 1b15ae0cd9
commit 8e9f94424d
31 changed files with 436 additions and 387 deletions

View File

@@ -6,14 +6,11 @@ import json
import ddt
from django.test import override_settings
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from milestones.tests.utils import MilestonesTestCaseMixin
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.toggles import ENABLE_NEW_STUDIO_ADVANCED_SETTINGS_PAGE
@override_waffle_flag(ENABLE_NEW_STUDIO_ADVANCED_SETTINGS_PAGE, active=True)
@ddt.ddt
class CourseAdvanceSettingViewTest(CourseTestCase, MilestonesTestCaseMixin):
"""

View File

@@ -8,15 +8,12 @@ from urllib.parse import urlencode
import ddt
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from xmodule.modulestore.tests.factories import BlockFactory
from xmodule.tabs import CourseTabList
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.toggles import ENABLE_NEW_STUDIO_CUSTOM_PAGES
@override_waffle_flag(ENABLE_NEW_STUDIO_CUSTOM_PAGES, active=True)
@ddt.ddt
class TabsAPITests(CourseTestCase):
"""

View File

@@ -22,6 +22,7 @@ class CourseIndexViewTest(CourseTestCase, PermissionAccessMixin):
"""
Tests for CourseIndexView.
"""
maxDiff = None # Show the entire dictionary in the diff
def setUp(self):
super().setUp()
@@ -74,7 +75,10 @@ class CourseIndexViewTest(CourseTestCase, PermissionAccessMixin):
},
"language_code": "en",
"lms_link": get_lms_link_for_item(self.course.location),
"mfe_proctored_exam_settings_url": "",
"mfe_proctored_exam_settings_url": (
f"http://course-authoring-mfe/course/{self.course.id}"
"/pages-and-resources/proctoring/settings"
),
"notification_dismiss_url": None,
"proctoring_errors": [],
"reindex_link": f"/course/{self.course.id}/search_reindex",
@@ -121,7 +125,10 @@ class CourseIndexViewTest(CourseTestCase, PermissionAccessMixin):
},
"language_code": "en",
"lms_link": get_lms_link_for_item(self.course.location),
"mfe_proctored_exam_settings_url": "",
"mfe_proctored_exam_settings_url": (
f"http://course-authoring-mfe/course/{self.course.id}"
"/pages-and-resources/proctoring/settings"
),
"notification_dismiss_url": None,
"proctoring_errors": [],
"reindex_link": f"/course/{self.course.id}/search_reindex",
@@ -151,6 +158,6 @@ class CourseIndexViewTest(CourseTestCase, PermissionAccessMixin):
"""
Test to check number of queries made to mysql and mongo
"""
with self.assertNumQueries(32, table_ignorelist=WAFFLE_TABLES):
with self.assertNumQueries(33, table_ignorelist=WAFFLE_TABLES):
with check_mongo_calls(3):
self.client.get(self.url)

View File

@@ -1,134 +1,61 @@
"""
Unit tests for the course waffle flags view
"""
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import status
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel
User = get_user_model()
class CourseWaffleFlagsViewTest(CourseTestCase):
"""
Tests for the CourseWaffleFlagsView endpoint, which returns waffle flag states
Basic test for the CourseWaffleFlagsView endpoint, which returns waffle flag states
for a specific course or globally if no course ID is provided.
"""
maxDiff = None # Show the whole dictionary in the diff
course_waffle_flags = [
"use_new_custom_pages",
"use_new_schedule_details_page",
"use_new_advanced_settings_page",
"use_new_grading_page",
"use_new_updates_page",
"use_new_import_page",
"use_new_export_page",
"use_new_files_uploads_page",
"use_new_video_uploads_page",
"use_new_course_outline_page",
"use_new_unit_page",
"use_new_course_team_page",
"use_new_certificates_page",
"use_new_textbooks_page",
"use_new_group_configurations_page",
"use_react_markdown_editor"
]
other_expected_waffle_flags = ["enable_course_optimizer"]
defaults = {
'enable_course_optimizer': False,
'use_new_advanced_settings_page': True,
'use_new_certificates_page': True,
'use_new_course_outline_page': True,
'use_new_course_team_page': True,
'use_new_custom_pages': True,
'use_new_export_page': True,
'use_new_files_uploads_page': True,
'use_new_grading_page': True,
'use_new_group_configurations_page': True,
'use_new_home_page': True,
'use_new_import_page': True,
'use_new_schedule_details_page': True,
'use_new_textbooks_page': True,
'use_new_unit_page': True,
'use_new_updates_page': True,
'use_new_video_uploads_page': False,
'use_react_markdown_editor': False,
}
def setUp(self):
"""
Set up test data and state before each test method.
This method initializes the endpoint URL and creates a set of waffle flags
for the test course, setting each flag's value to `True`.
"""
super().setUp()
self.url = reverse("cms.djangoapps.contentstore:v1:course_waffle_flags")
self.create_waffle_flags(self.course_waffle_flags)
self.create_custom_waffle_flags()
def create_custom_waffle_flags(self, enabled=True):
"""
Helper method to create waffle flags that are not part of `course_waffle_flags` and have
a different format.
"""
WaffleFlagCourseOverrideModel.objects.create(
waffle_flag="contentstore.enable_course_optimizer",
waffle_flag=toggles.ENABLE_COURSE_OPTIMIZER.name,
course_id=self.course.id,
enabled=enabled,
enabled=True,
)
def create_waffle_flags(self, flags, enabled=True):
"""
Helper method to create waffle flag entries in the database for the test course.
def test_global_defaults(self):
url = reverse("cms.djangoapps.contentstore:v1:course_waffle_flags")
response = self.client.get(url)
assert response.data == self.defaults
Args:
flags (list): A list of flag names to set up.
enabled (bool): The value to set for each flag's enabled state.
"""
for flag in flags:
WaffleFlagCourseOverrideModel.objects.create(
waffle_flag=(
f"contentstore.new_studio_mfe.{flag}"
if flag != "use_react_markdown_editor"
else "contentstore.use_react_markdown_editor"
),
course_id=self.course.id,
enabled=enabled,
)
def expected_response(self, enabled=False):
"""
Generate an expected response dictionary based on the enabled flag.
Args:
enabled (bool): State to assign to each waffle flag in the response.
Returns:
dict: A dictionary with each flag set to the value of `enabled`.
"""
res = {flag: enabled for flag in self.course_waffle_flags}
for flag in self.other_expected_waffle_flags:
res[flag] = enabled
return res
def test_get_course_waffle_flags_with_course_id(self):
"""
Test that waffle flags for a specific course are correctly returned when
a valid course ID is provided.
Expected Behavior:
- The response should return HTTP 200 status.
- Each flag returned should be `True` as set up in the `setUp` method.
"""
course_url = reverse(
def test_course_override(self):
url = reverse(
"cms.djangoapps.contentstore:v1:course_waffle_flags",
kwargs={"course_id": self.course.id},
)
expected_response = self.expected_response(enabled=True)
expected_response["use_new_home_page"] = False
response = self.client.get(course_url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)
def test_get_course_waffle_flags_without_course_id(self):
"""
Test that the default waffle flag states are returned when no course ID is provided.
Expected Behavior:
- The response should return HTTP 200 status.
- Each flag returned should default to `False`, representing the global
default state for each flag.
"""
expected_response = self.expected_response(enabled=False)
expected_response["use_new_home_page"] = False
response = self.client.get(self.url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertDictEqual(expected_response, response.data)
response = self.client.get(url)
assert response.data == {
**self.defaults,
"enable_course_optimizer": True,
}

View File

@@ -1,5 +1,9 @@
# lint-amnesty, pylint: disable=missing-module-docstring
# TODO: Rewrite several of these assertions so that they check the output of the REST or Python
# APIs rather than parsing HTML from the deprecated legacy frontend pages. In particular, any
# test case using override_waffle_flag(toggles.LEGACY_STUDIO_*, True) will need to be fixed.
# Part of https://github.com/openedx/edx-platform/issues/36275.
import copy
import re
@@ -17,7 +21,7 @@ from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.test import TestCase
from django.test.utils import override_settings
from edx_toggles.toggles.testutils import override_waffle_switch
from edx_toggles.toggles.testutils import override_waffle_switch, override_waffle_flag
from edxval.api import create_video, get_videos_for_course
from fs.osfs import OSFS
from lxml import etree
@@ -43,6 +47,7 @@ from xmodule.modulestore.xml_importer import import_course_from_xml, perform_xli
from xmodule.seq_block import SequenceBlock
from xmodule.video_block import VideoBlock
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.config import waffle
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient, CourseTestCase, get_url, parse_json
from cms.djangoapps.contentstore.utils import (
@@ -587,6 +592,7 @@ class MiscCourseTests(ContentStoreTestCase):
for expected in expected_types:
self.assertContains(resp, expected)
@override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True)
@ddt.data("<script>alert(1)</script>", "alert('hi')", "</script><script>alert(1)</script>")
def test_container_handler_xss_prevent(self, malicious_code):
"""
@@ -596,6 +602,7 @@ class MiscCourseTests(ContentStoreTestCase):
# Test that malicious code does not appear in html
self.assertNotContains(resp, malicious_code)
@override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True)
def test_advanced_components_in_edit_unit(self):
# This could be made better, but for now let's just assert that we see the advanced modules mentioned in the
# page response HTML
@@ -697,9 +704,11 @@ class MiscCourseTests(ContentStoreTestCase):
# Remove tempdir
shutil.rmtree(root_dir)
@override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True)
def test_advanced_components_require_two_clicks(self):
self.check_components_on_page(['word_cloud'], ['Word cloud'])
@override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True)
def test_edit_unit(self):
"""Verifies rendering the editor in all the verticals in the given test course"""
self._check_verticals([self.vert_loc])
@@ -1379,6 +1388,7 @@ class ContentStoreTest(ContentStoreTestCase):
resp = self.client.ajax_post('/course/', self.course_data)
self.assertEqual(resp.status_code, 403)
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
def test_course_index_view_with_no_courses(self):
"""Test viewing the index page with no courses"""
resp = self.client.get_html('/home/')
@@ -1400,6 +1410,7 @@ class ContentStoreTest(ContentStoreTestCase):
item = BlockFactory.create(parent_location=course.location)
self.assertIsInstance(item, SequenceBlock)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_OUTLINE, True)
def test_course_overview_view_with_course(self):
"""Test viewing the course overview page with an existing course"""
course = CourseFactory.create()
@@ -1499,7 +1510,8 @@ class ContentStoreTest(ContentStoreTestCase):
)
course_key = course_items[0].id
resp = self._show_course_overview(course_key)
with override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_OUTLINE, True):
resp = self._show_course_overview(course_key)
# course_handler raise 404 for old mongo course
if course_key.deprecated:
@@ -1510,20 +1522,31 @@ class ContentStoreTest(ContentStoreTestCase):
self.assertContains(resp, 'Chapter 2')
# go to various pages
test_get_html('import_handler')
test_get_html('export_handler')
test_get_html('course_team_handler')
test_get_html('course_info_handler')
test_get_html('assets_handler')
test_get_html('tabs_handler')
test_get_html('settings_handler')
test_get_html('grading_handler')
test_get_html('advanced_settings_handler')
test_get_html('textbooks_list_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_IMPORT, True):
test_get_html('import_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_EXPORT, True):
test_get_html('export_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True):
test_get_html('course_team_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_UPDATES, True):
test_get_html('course_info_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_FILES_UPLOADS, True):
test_get_html('assets_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_CUSTOM_PAGES, True):
test_get_html('tabs_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True):
test_get_html('settings_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True):
test_get_html('grading_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_ADVANCED_SETTINGS, True):
test_get_html('advanced_settings_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_TEXTBOOKS, True):
test_get_html('textbooks_list_handler')
# go look at the Edit page
unit_key = course_key.make_usage_key('vertical', 'test_vertical')
resp = self.client.get_html(get_url('container_handler', unit_key))
with override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True):
resp = self.client.get_html(get_url('container_handler', unit_key))
self.assertEqual(resp.status_code, 200)
def delete_item(category, name):
@@ -1856,20 +1879,23 @@ class RerunCourseTest(ContentStoreTestCase):
"""
Asserts that the given course key is NOT in the unsucceeded course action section of the html.
"""
course_listing = lxml.html.fromstring(self.client.get_html('/home/').content)
with override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True):
course_listing = lxml.html.fromstring(self.client.get_html('/home/').content)
self.assertEqual(len(self.get_unsucceeded_course_action_elements(course_listing, course_key)), 0)
def assertInUnsucceededCourseActions(self, course_key):
"""
Asserts that the given course key is in the unsucceeded course action section of the html.
"""
course_listing = lxml.html.fromstring(self.client.get_html('/home/').content)
with override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True):
course_listing = lxml.html.fromstring(self.client.get_html('/home/').content)
self.assertEqual(len(self.get_unsucceeded_course_action_elements(course_listing, course_key)), 1)
def verify_rerun_course(self, source_course_key, destination_course_key, destination_display_name):
"""
Verify the contents of the course rerun action
"""
rerun_state = CourseRerunState.objects.find_first(course_key=destination_course_key)
expected_states = {
'state': CourseRerunUIStateManager.State.SUCCEEDED,

View File

@@ -10,8 +10,10 @@ import ddt
from ccx_keys.locator import CCXLocator
from django.conf import settings
from django.test import RequestFactory
from edx_toggles.toggles.testutils import override_waffle_flag
from opaque_keys.edx.locations import CourseLocator
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient
from cms.djangoapps.contentstore.utils import delete_course
from cms.djangoapps.contentstore.views.course import (
@@ -87,6 +89,7 @@ class TestCourseListing(ModuleStoreTestCase):
self.client.logout()
ModuleStoreTestCase.tearDown(self) # pylint: disable=non-parent-method-called
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
def test_empty_course_listing(self):
"""
Test on empty course listing, studio name is properly displayed

View File

@@ -1,7 +1,15 @@
"""
Tests for Studio Course Settings.
"""
# TODO: Remove each `override_waffle_flag(toggles.LEGACY_STUDIO_*)` by Ulmo. For each occurance:
# * If the test case is just testing the legacy frontend, and we've got the underlying
# functionality tested elsewhere, then just delete the whole test case.
# * Otherwise (i.e., the test is using the legacy UI test a unique backend behavior), then
# rewrite the test to make assertions about the output of the Python API (preferred) or
# REST API (if necessary) so that we can delete the legacy UI without sacrificing the test
# coverage.
# Part of https://github.com/openedx/edx-platform/issues/36275.
"""
import copy
import datetime
@@ -20,6 +28,7 @@ from milestones.models import MilestoneRelationshipType
from milestones.tests.utils import MilestonesTestCaseMixin
from pytz import UTC
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_usage_url
from cms.djangoapps.models.settings.course_grading import (
GRADING_POLICY_CHANGED_EVENT_TYPE,
@@ -115,6 +124,7 @@ class CourseAdvanceSettingViewTest(CourseTestCase, MilestonesTestCaseMixin):
CourseStaffRole(self.course.id).add_users(self.nonstaff)
@override_settings(FEATURES={'DISABLE_MOBILE_COURSE_AVAILABLE': True})
@override_waffle_flag(toggles.LEGACY_STUDIO_ADVANCED_SETTINGS, True)
def test_mobile_field_available(self):
"""
@@ -137,6 +147,7 @@ class CourseAdvanceSettingViewTest(CourseTestCase, MilestonesTestCaseMixin):
(False, True, True)
)
@ddt.unpack
@override_waffle_flag(toggles.LEGACY_STUDIO_ADVANCED_SETTINGS, True)
def test_discussion_fields_available(self, is_pages_and_resources_enabled,
is_legacy_discussion_setting_enabled, fields_visible):
"""
@@ -152,6 +163,16 @@ class CourseAdvanceSettingViewTest(CourseTestCase, MilestonesTestCaseMixin):
self.assertEqual('discussion_topics' in response, fields_visible)
@ddt.data(False, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_ADVANCED_SETTINGS, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_IMPORT, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_EXPORT, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_UPDATES, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_FILES_UPLOADS, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_CUSTOM_PAGES, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_TEXTBOOKS, True)
def test_disable_advanced_settings_feature(self, disable_advanced_settings):
"""
If this feature is enabled, only Django Staff/Superuser should be able to access the "Advanced Settings" page.
@@ -292,6 +313,7 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
(True, True),
)
@ddt.unpack
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_upgrade_deadline(self, has_verified_mode, has_expiration_date):
if has_verified_mode:
deadline = None
@@ -310,6 +332,7 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
self.assertEqual(b"Upgrade Deadline Date" in response.content, has_expiration_date and has_verified_mode)
@mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_PREREQUISITE_COURSES': True})
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_pre_requisite_course_list_present(self):
settings_details_url = get_url(self.course.id)
response = self.client.get_html(settings_details_url)
@@ -370,6 +393,7 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
(False, True, False),
(True, True, True),
)
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_visibility_of_entrance_exam_section(self, feature_flags):
"""
Tests entrance exam section is available if ENTRANCE_EXAMS feature is enabled no matter any other
@@ -386,6 +410,7 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
b'<h3 id="heading-entrance-exam">' in resp.content
)
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_marketing_site_fetch(self):
settings_details_url = get_url(self.course.id)
@@ -593,6 +618,7 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
assert milestones_helpers.any_unfulfilled_milestones(self.course.id, self.user.id), \
'The entrance exam should be required.'
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_editable_short_description_fetch(self):
settings_details_url = get_url(self.course.id)
@@ -631,6 +657,7 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
self.assertEqual(response.status_code, 200)
self.assertEqual(course_details.overview, '<p>&nbsp;</p>')
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_regular_site_fetch(self):
settings_details_url = get_url(self.course.id)
@@ -1504,7 +1531,6 @@ class CourseMetadataEditingTest(CourseTestCase):
'test_proctoring_provider': {},
'proctortrack': {}
},
FEATURES={'ENABLE_EXAM_SETTINGS_HTML_VIEW': True},
)
def test_validate_update_requires_escalation_email_for_proctortrack(self, include_blank_email):
json_data = {
@@ -1552,7 +1578,6 @@ class CourseMetadataEditingTest(CourseTestCase):
'DEFAULT': 'proctortrack',
'proctortrack': {}
},
FEATURES={'ENABLE_EXAM_SETTINGS_HTML_VIEW': True},
)
def test_validate_update_cannot_unset_escalation_email_when_proctortrack_is_provider(self):
course = CourseFactory.create()
@@ -1982,6 +2007,7 @@ id=\"course-enrollment-end-time\" value=\"\" placeholder=\"HH:MM\" autocomplete=
self.assertNotContains(response, element)
@mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_PUBLISHER': False})
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_course_details_with_disabled_setting_global_staff(self):
"""
Test that user enrollment end date is editable in response.
@@ -1992,6 +2018,7 @@ id=\"course-enrollment-end-time\" value=\"\" placeholder=\"HH:MM\" autocomplete=
self._verify_editable(self._get_course_details_response(True))
@mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_PUBLISHER': False})
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_course_details_with_disabled_setting_non_global_staff(self):
"""
Test that user enrollment end date is editable in response.
@@ -2002,6 +2029,7 @@ id=\"course-enrollment-end-time\" value=\"\" placeholder=\"HH:MM\" autocomplete=
self._verify_editable(self._get_course_details_response(False))
@mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_PUBLISHER': True})
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_course_details_with_enabled_setting_global_staff(self):
"""
Test that user enrollment end date is editable in response.
@@ -2013,6 +2041,7 @@ id=\"course-enrollment-end-time\" value=\"\" placeholder=\"HH:MM\" autocomplete=
@mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_PUBLISHER': True})
@override_settings(PLATFORM_NAME='edX')
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_course_details_with_enabled_setting_non_global_staff(self):
"""
Test that user enrollment end date is not editable in response.

View File

@@ -5,6 +5,8 @@ import gettext
from unittest import mock, skip
from django.utils import translation
from edx_toggles.toggles.testutils import override_waffle_flag
from django.utils.translation import get_language
from xblock.core import XBlock
from xmodule.modulestore.django import XBlockI18nService
@@ -12,6 +14,7 @@ from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE,
from xmodule.modulestore.tests.factories import CourseFactory, BlockFactory
from xmodule.tests.test_export import PureXBlock
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient
from cms.djangoapps.contentstore.views.preview import _prepare_runtime_for_preview
from common.djangoapps.student.tests.factories import UserFactory
@@ -202,6 +205,7 @@ class InternationalizationTest(ModuleStoreTestCase):
'display_name': 'Robot Super Course',
}
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
def test_course_plain_english(self):
"""Test viewing the index page with no courses"""
self.client = AjaxEnabledTestClient() # lint-amnesty, pylint: disable=attribute-defined-outside-init
@@ -213,6 +217,7 @@ class InternationalizationTest(ModuleStoreTestCase):
status_code=200,
html=True)
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
def test_course_explicit_english(self):
"""Test viewing the index page with no courses"""
self.client = AjaxEnabledTestClient() # lint-amnesty, pylint: disable=attribute-defined-outside-init

View File

@@ -2,9 +2,11 @@
Test CRUD for authorization.
"""
import copy
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient
from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_url
from common.djangoapps.student import auth
@@ -64,10 +66,15 @@ class TestCourseAccess(ModuleStoreTestCase):
self.client.logout()
ModuleStoreTestCase.tearDown(self) # pylint: disable=non-parent-method-called
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True)
def test_get_all_users(self):
"""
Test getting all authors for a course where their permissions run the gamut of allowed group
types.
TODO: Replace the call to the legacy course_team_handler with a call to the course team REST API.
The legacy page will be removed, but we still want to the test these behaviors.
Part of https://github.com/openedx/edx-platform/issues/36275.
"""
# first check the course creator.has explicit access (don't use has_access as is_staff
# will trump the actual test)

View File

@@ -1,5 +1,10 @@
"""
This test file will test registration, login, activation, and session activity timeouts
TODO: Rewrite several of these assertions so that they check the output of the REST or Python
APIs rather than parsing HTML from the deprecated legacy frontend pages. In particular, any
test case using override_waffle_flag(toggles.LEGACY_STUDIO_*, True) will need to be fixed.
Part of https://github.com/openedx/edx-platform/issues/36275.
"""
@@ -13,8 +18,10 @@ from django.conf import settings
from django.core.cache import cache
from django.test.utils import override_settings
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from pytz import UTC
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.test_course_settings import CourseTestCase
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient, parse_json, registration, user
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
@@ -106,6 +113,7 @@ class AuthTestCase(ContentStoreTestCase):
self.assertEqual(resp.status_code, expected)
return resp
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
def test_private_pages_auth(self):
"""Make sure pages that do require login work."""
auth_pages = (
@@ -140,6 +148,7 @@ class AuthTestCase(ContentStoreTestCase):
self.check_page_get(page, expected=200)
@override_settings(SESSION_INACTIVITY_TIMEOUT_IN_SECONDS=1)
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
def test_inactive_session_timeout(self):
"""
Verify that an inactive session times out and redirects to the
@@ -249,6 +258,7 @@ class CourseKeyVerificationTestCase(CourseTestCase):
@data(('edX/test_course_key/Test_Course', 200), ('garbage:edX+test_course_key+Test_Course', 404))
@unpack
@override_waffle_flag(toggles.LEGACY_STUDIO_IMPORT, True)
def test_course_key_decorator(self, course_key, status_code):
"""
Tests for the ensure_valid_course_key decorator.

View File

@@ -67,61 +67,61 @@ def bypass_olx_failure_enabled():
return BYPASS_OLX_FAILURE.is_enabled()
# .. toggle_name: FEATURES['ENABLE_EXAM_SETTINGS_HTML_VIEW']
# .. toggle_use_cases: open_edx
# .. toggle_implementation: SettingDictToggle
# .. toggle_name: legacy_studio.exam_settings
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: When enabled, users can access the new course authoring view for proctoring exams
# .. toggle_warning: None
# .. toggle_creation_date: 2020-07-23
ENABLE_EXAM_SETTINGS_HTML_VIEW = SettingDictToggle(
"FEATURES", "ENABLE_EXAM_SETTINGS_HTML_VIEW", default=False, module_name=__name__
)
# .. toggle_description: Temporarily fall back to the old proctored exam settings view.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_EXAM_SETTINGS = CourseWaffleFlag("legacy_studio.exam_settings", __name__)
def exam_setting_view_enabled():
def exam_setting_view_enabled(course_key):
"""
Returns a boolean if proctoring exam setting mfe view is enabled.
"""
return ENABLE_EXAM_SETTINGS_HTML_VIEW.is_enabled()
return not LEGACY_STUDIO_EXAM_SETTINGS.is_enabled(course_key)
# .. toggle_name: new_core_editors.use_new_text_editor
# .. toggle_name: legacy_studio.text_editor
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new core text xblock editor
# .. toggle_description: Temporarily fall back to the old Text component (a.k.a. html block) editor.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2021-12-1
# .. toggle_target_removal_date: 2022-1-30
# .. toggle_tickets: TNL-9306
# .. toggle_warning:
ENABLE_NEW_TEXT_EDITOR_FLAG = WaffleFlag('new_core_editors.use_new_text_editor', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_TEXT_EDITOR = CourseWaffleFlag("legacy_studio.text_editor", __name__)
def use_new_text_editor():
def use_new_text_editor(course_key):
"""
Returns a boolean = true if new text editor is enabled
"""
return ENABLE_NEW_TEXT_EDITOR_FLAG.is_enabled()
return not LEGACY_STUDIO_TEXT_EDITOR.is_enabled(course_key)
# .. toggle_name: new_core_editors.use_new_video_editor
# .. toggle_name: legacy_studio.video_editor
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new core video xblock editor
# .. toggle_description: Temporarily fall back to the old Video component (a.k.a. video block) editor.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2021-12-1
# .. toggle_target_removal_date: 2022-1-30
# .. toggle_tickets: TNL-9306
# .. toggle_warning:
ENABLE_NEW_VIDEO_EDITOR_FLAG = WaffleFlag('new_core_editors.use_new_video_editor', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_VIDEO_EDITOR = CourseWaffleFlag('legacy_studio.video_editor', __name__)
def use_new_video_editor():
def use_new_video_editor(course_key):
"""
Returns a boolean = true if new video editor is enabled
"""
return ENABLE_NEW_VIDEO_EDITOR_FLAG.is_enabled()
return not LEGACY_STUDIO_VIDEO_EDITOR.is_enabled(course_key)
# .. toggle_name: new_core_editors.use_video_gallery_flow
@@ -142,23 +142,23 @@ def use_video_gallery_flow():
return ENABLE_VIDEO_GALLERY_FLOW_FLAG.is_enabled()
# .. toggle_name: new_core_editors.use_new_problem_editor
# .. toggle_name: legacy_studio.problem_editor
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new core problem xblock editor
# .. toggle_description: Temporarily fall back to the old Problem component (a.k.a. CAPA/problem block) editor.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2021-12-1
# .. toggle_target_removal_date: 2022-1-30
# .. toggle_tickets: TNL-9306
# .. toggle_warning:
ENABLE_NEW_PROBLEM_EDITOR_FLAG = WaffleFlag('new_core_editors.use_new_problem_editor', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_PROBLEM_EDITOR = CourseWaffleFlag('legacy_studio.problem_editor', __name__)
def use_new_problem_editor():
def use_new_problem_editor(course_key):
"""
Returns a boolean if new problem editor is enabled
"""
return ENABLE_NEW_PROBLEM_EDITOR_FLAG.is_enabled()
return not LEGACY_STUDIO_PROBLEM_EDITOR.is_enabled(course_key)
# .. toggle_name: contentstore.individualize_anonymous_user_id
@@ -181,43 +181,43 @@ def individualize_anonymous_user_id(course_id):
return INDIVIDUALIZE_ANONYMOUS_USER_ID.is_enabled(course_id)
# .. toggle_name: new_studio_mfe.use_new_home_page
# .. toggle_name: legacy_studio.home
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio home page mfe
# .. toggle_description: Temporarily fall back to the old Studio logged-in landing page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-9306
# .. toggle_warning:
ENABLE_NEW_STUDIO_HOME_PAGE = WaffleFlag('new_studio_mfe.use_new_home_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_HOME = WaffleFlag('legacy_studio.home', __name__)
def use_new_home_page():
"""
Returns a boolean if new studio home page mfe is enabled
"""
return ENABLE_NEW_STUDIO_HOME_PAGE.is_enabled()
return not LEGACY_STUDIO_HOME.is_enabled()
# .. toggle_name: contentstore.new_studio_mfe.use_new_custom_pages
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.custom_pages
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio custom pages mfe
# .. toggle_description: Temporarily fall back to the old Studio custom pages tab.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_CUSTOM_PAGES = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_custom_pages', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_CUSTOM_PAGES = CourseWaffleFlag("legacy_studio.custom_pages", __name__)
def use_new_custom_pages(course_key):
"""
Returns a boolean if new studio custom pages mfe is enabled
"""
return ENABLE_NEW_STUDIO_CUSTOM_PAGES.is_enabled(course_key)
return not LEGACY_STUDIO_CUSTOM_PAGES.is_enabled(course_key)
# .. toggle_name: contentstore.use_react_markdown_editor
# .. toggle_implementation: CourseWaffleFlag
@@ -236,150 +236,144 @@ def use_react_markdown_editor(course_key):
"""
return ENABLE_REACT_MARKDOWN_EDITOR.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_schedule_details_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.schedule_details
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio schedule and details mfe
# .. toggle_description: Temporarily fall back to the old Studio Schedule & Details page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_SCHEDULE_DETAILS_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_schedule_details_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_SCHEDULE_DETAILS = CourseWaffleFlag('legacy_studio.schedule_details', __name__)
def use_new_schedule_details_page(course_key):
"""
Returns a boolean if new studio schedule and details mfe is enabled
"""
return ENABLE_NEW_STUDIO_SCHEDULE_DETAILS_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_SCHEDULE_DETAILS.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_advanced_settings_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.advanced_settings
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio advanced settings page mfe
# .. toggle_description: Temporarily fall back to the old Studio Advanced Settings page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_ADVANCED_SETTINGS_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_advanced_settings_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_ADVANCED_SETTINGS = CourseWaffleFlag('legacy_studio.advanced_settings', __name__)
def use_new_advanced_settings_page(course_key):
"""
Returns a boolean if new studio advanced settings pafe mfe is enabled
"""
return ENABLE_NEW_STUDIO_ADVANCED_SETTINGS_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_ADVANCED_SETTINGS.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_grading_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.grading
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio grading page mfe
# .. toggle_description: Temporarily fall back to the old Studio Course Grading page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_GRADING_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_grading_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_GRADING = CourseWaffleFlag('legacy_studio.grading', __name__)
def use_new_grading_page(course_key):
"""
Returns a boolean if new studio grading mfe is enabled
"""
return ENABLE_NEW_STUDIO_GRADING_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_GRADING.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_updates_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.updates
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio updates page mfe
# .. toggle_description: Temporarily fall back to the old Studio Course Updates page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_UPDATES_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_updates_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_UPDATES = CourseWaffleFlag('legacy_studio.updates', __name__)
def use_new_updates_page(course_key):
"""
Returns a boolean if new studio updates mfe is enabled
"""
return ENABLE_NEW_STUDIO_UPDATES_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_UPDATES.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_import_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.import
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio import page mfe
# .. toggle_description: Temporarily fall back to the old Course Import page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_IMPORT_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_import_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_IMPORT = CourseWaffleFlag('legacy_studio.import', __name__)
def use_new_import_page(course_key):
"""
Returns a boolean if new studio import mfe is enabled
"""
return ENABLE_NEW_STUDIO_IMPORT_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_IMPORT.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_export_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.export
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio export page mfe
# .. toggle_description: Temporarily fall back to the old Course Export page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_EXPORT_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_export_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_EXPORT = CourseWaffleFlag('legacy_studio.export', __name__)
def use_new_export_page(course_key):
"""
Returns a boolean if new studio export mfe is enabled
"""
return ENABLE_NEW_STUDIO_EXPORT_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_EXPORT.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_files_uploads_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.files_uploads
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio files and uploads page mfe
# .. toggle_description: Temporarily fall back to the old Studio Files & Uploads page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_FILES_UPLOADS_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_files_uploads_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_FILES_UPLOADS = CourseWaffleFlag('legacy_studio.files_uploads', __name__)
def use_new_files_uploads_page(course_key):
"""
Returns a boolean if new studio files and uploads mfe is enabled
"""
return ENABLE_NEW_STUDIO_FILES_UPLOADS_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_FILES_UPLOADS.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_video_uploads_page
# .. toggle_name: contentstore.new_studio_mfe.use_new_files_uploads_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new video uploads page mfe
# .. toggle_description: This flag enables the use of the new studio files and uploads page mfe
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
@@ -396,124 +390,118 @@ def use_new_video_uploads_page(course_key):
return ENABLE_NEW_STUDIO_VIDEO_UPLOADS_PAGE.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_course_outline_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.course_outline
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio course outline page mfe
# .. toggle_description: Temporarily fall back to the old Studio Course Outline editor.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_COURSE_OUTLINE_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_course_outline_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_COURSE_OUTLINE = CourseWaffleFlag('legacy_studio.course_outline', __name__)
def use_new_course_outline_page(course_key):
"""
Returns a boolean if new studio course outline mfe is enabled
"""
return ENABLE_NEW_STUDIO_COURSE_OUTLINE_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_COURSE_OUTLINE.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_unit_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.unit_editor
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio course outline page mfe
# .. toggle_description: Temporarily fall back to the old Studio unit editing page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_UNIT_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_unit_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_UNIT_EDITOR = CourseWaffleFlag('legacy_studio.unit_editor', __name__)
def use_new_unit_page(course_key):
"""
Returns a boolean if new studio course outline mfe is enabled
"""
return ENABLE_NEW_STUDIO_UNIT_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_UNIT_EDITOR.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_course_team_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.course_team
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio course team page mfe
# .. toggle_description: Temporarily fall back to the old Studio Course Team page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2023-5-15
# .. toggle_target_removal_date: 2023-8-31
# .. toggle_tickets: TNL-10619
# .. toggle_warning:
ENABLE_NEW_STUDIO_COURSE_TEAM_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_course_team_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_COURSE_TEAM = CourseWaffleFlag('legacy_studio.course_team', __name__)
def use_new_course_team_page(course_key):
"""
Returns a boolean if new studio course team mfe is enabled
"""
return ENABLE_NEW_STUDIO_COURSE_TEAM_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_COURSE_TEAM.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_certificates_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.certificates
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio course certificates page mfe
# .. toggle_description: Temporarily fall back to the old Studio Course Certificates page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2024-1-18
# .. toggle_target_removal_date: 2023-4-31
# .. toggle_tickets: https://github.com/openedx/platform-roadmap/issues/317
# .. toggle_warning:
ENABLE_NEW_STUDIO_CERTIFICATES_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_certificates_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_CERTIFICATES = CourseWaffleFlag('legacy_studio.certificates', __name__)
def use_new_certificates_page(course_key):
"""
Returns a boolean if new studio certificates mfe is enabled
"""
return ENABLE_NEW_STUDIO_CERTIFICATES_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_CERTIFICATES.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_textbooks_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.textbooks
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio course textbooks page mfe
# .. toggle_description: Temporarily fall back to the old Studio Textbooks page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2024-1-18
# .. toggle_target_removal_date: 2023-4-31
# .. toggle_tickets: https://github.com/openedx/platform-roadmap/issues/319
# .. toggle_warning:
ENABLE_NEW_STUDIO_TEXTBOOKS_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_textbooks_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_TEXTBOOKS = CourseWaffleFlag('legacy_studio.textbooks', __name__)
def use_new_textbooks_page(course_key):
"""
Returns a boolean if new studio textbooks mfe is enabled
"""
return ENABLE_NEW_STUDIO_TEXTBOOKS_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_TEXTBOOKS.is_enabled(course_key)
# .. toggle_name: contentstore.new_studio_mfe.use_new_group_configurations_page
# .. toggle_implementation: CourseWaffleFlag
# .. toggle_name: legacy_studio.configurations
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: This flag enables the use of the new studio course group configurations page mfe
# .. toggle_description: Temporarily fall back to the old Studio Configurations page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2024-1-18
# .. toggle_target_removal_date: 2023-4-31
# .. toggle_tickets: https://github.com/openedx/platform-roadmap/issues/318
# .. toggle_warning:
ENABLE_NEW_STUDIO_GROUP_CONFIGURATIONS_PAGE = CourseWaffleFlag(
f'{CONTENTSTORE_NAMESPACE}.new_studio_mfe.use_new_group_configurations_page', __name__)
# .. toggle_creation_date: 2025-03-14
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_CONFIGURATIONS = CourseWaffleFlag('legacy_studio.configurations', __name__)
def use_new_group_configurations_page(course_key):
"""
Returns a boolean if new studio group configurations mfe is enabled
"""
return ENABLE_NEW_STUDIO_GROUP_CONFIGURATIONS_PAGE.is_enabled(course_key)
return not LEGACY_STUDIO_CONFIGURATIONS.is_enabled(course_key)
# .. toggle_name: contentstore.mock_video_uploads

View File

@@ -270,7 +270,7 @@ def get_proctored_exam_settings_url(course_locator) -> str:
Gets course authoring microfrontend URL for links to proctored exam settings page
"""
proctored_exam_settings_url = ''
if exam_setting_view_enabled():
if exam_setting_view_enabled(course_locator):
mfe_base_url = get_course_authoring_url(course_locator)
course_mfe_url = f'{mfe_base_url}/course/{course_locator}'
if mfe_base_url:
@@ -283,7 +283,7 @@ def get_editor_page_base_url(course_locator) -> str:
Gets course authoring microfrontend URL for links to the new base editors
"""
editor_url = None
if use_new_text_editor() or use_new_video_editor():
if use_new_text_editor(course_locator) or use_new_video_editor(course_locator):
mfe_base_url = get_course_authoring_url(course_locator)
course_mfe_url = f'{mfe_base_url}/course/{course_locator}/editor'
if mfe_base_url:

View File

@@ -370,14 +370,14 @@ def get_component_templates(courselike, library=False): # lint-amnesty, pylint:
#If using new problem editor, we select problem type inside the editor
# because of this, we only show one problem.
if category == 'problem' and use_new_problem_editor():
if category == 'problem' and use_new_problem_editor(courselike.context_key):
templates_for_category = [
template for template in templates_for_category if template['boilerplate_name'] == 'blank_common.yaml'
]
# Add any advanced problem types. Note that these are different xblocks being stored as Advanced Problems,
# currently not supported in libraries .
if category == 'problem' and not library and not use_new_problem_editor():
if category == 'problem' and not library and not use_new_problem_editor(courselike.context_key):
disabled_block_names = [block.name for block in disabled_xblocks()]
advanced_problem_types = [advanced_problem_type for advanced_problem_type in ADVANCED_PROBLEM_TYPES
if advanced_problem_type['component'] not in disabled_block_names]

View File

@@ -12,11 +12,13 @@ from unittest.mock import patch
from ddt import data, ddt
from django.conf import settings
from django.test.utils import override_settings
from edx_toggles.toggles.testutils import override_waffle_flag
from opaque_keys.edx.keys import AssetKey
from opaque_keys.edx.locator import CourseLocator
from PIL import Image
from pytz import UTC
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
from cms.djangoapps.contentstore.views import assets
@@ -84,6 +86,8 @@ class BasicAssetsTestCase(AssetsTestCase):
"""
Test getting assets via html w/o additional args
"""
@override_waffle_flag(toggles.LEGACY_STUDIO_FILES_UPLOADS, True)
def test_basic(self):
resp = self.client.get(self.url, HTTP_ACCEPT='text/html')
self.assertEqual(resp.status_code, 200)

View File

@@ -12,6 +12,7 @@ from django.http import Http404
from django.test import TestCase
from django.test.client import RequestFactory
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from openedx.core.djangoapps.video_config.toggles import PUBLIC_VIDEO_SHARE
from openedx_events.content_authoring.data import DuplicatedXBlockData
from openedx_events.content_authoring.signals import XBLOCK_DUPLICATED
@@ -56,6 +57,7 @@ from xmodule.partitions.partitions import (
from xmodule.partitions.tests.test_partitions import MockPartitionService
from xmodule.x_module import STUDENT_VIEW, STUDIO_VIEW
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import (
reverse_course_url,
@@ -2852,6 +2854,7 @@ class TestComponentHandler(TestCase):
assert mocked_get_aside_from_xblock.called is is_get_aside_called
@override_waffle_flag(toggles.LEGACY_STUDIO_PROBLEM_EDITOR, True)
class TestComponentTemplates(CourseTestCase):
"""
Unit tests for the generation of the component templates for a course.

View File

@@ -10,8 +10,10 @@ from unittest import mock
import ddt
from django.conf import settings
from django.test.utils import override_settings
from edx_toggles.toggles.testutils import override_waffle_flag
from opaque_keys.edx.keys import AssetKey
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import get_lms_link_for_certificate_web_view, reverse_course_url
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
@@ -275,6 +277,7 @@ class CertificatesListHandlerTestCase(
)
self.assertEqual(link, test_url)
@override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True)
@mock.patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True})
def test_certificate_info_in_response(self):
"""
@@ -302,6 +305,7 @@ class CertificatesListHandlerTestCase(
self.assertEqual(data[0]['version'], CERTIFICATE_SCHEMA_VERSION)
@mock.patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True})
@override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True)
def test_certificate_info_not_in_response(self):
"""
Test that certificate has not been rendered audit only course mode.
@@ -346,6 +350,7 @@ class CertificatesListHandlerTestCase(
)
self.assertContains(response, "error", status_code=403)
@override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True)
def test_audit_course_mode_is_skipped(self):
"""
Tests audit course mode is skipped when rendering certificates page.
@@ -359,6 +364,7 @@ class CertificatesListHandlerTestCase(
self.assertContains(response, 'verified')
self.assertNotContains(response, 'audit')
@override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True)
def test_audit_only_disables_cert(self):
"""
Tests audit course mode is skipped when rendering certificates page.
@@ -379,6 +385,7 @@ class CertificatesListHandlerTestCase(
['verified', 'credit'],
['professional']
)
@override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True)
def test_non_audit_enables_cert(self, slugs):
"""
Tests audit course mode is skipped when rendering certificates page.

View File

@@ -10,10 +10,12 @@ from unittest.mock import Mock, patch
from django.http import Http404
from django.test.client import RequestFactory
from django.urls import reverse
from edx_toggles.toggles.testutils import override_waffle_flag
from pytz import UTC
from urllib.parse import quote
import cms.djangoapps.contentstore.views.component as views
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase
from xmodule.modulestore import ModuleStoreEnum # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
@@ -83,6 +85,7 @@ class ContainerPageTestCase(StudioPageTestCase, LibraryTestCase):
),
)
@override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True)
def test_container_on_container_html(self):
"""
Create the scenario of an xblock with children (non-vertical) on the container page.
@@ -221,6 +224,7 @@ class ContainerPageTestCase(StudioPageTestCase, LibraryTestCase):
'cms.djangoapps.contentstore.views.component.render_to_response',
Mock(return_value=Mock(status_code=200, content=''))
)
@override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True)
def test_container_page_with_valid_and_invalid_usage_key_string(self):
"""
Check that invalid 'usage_key_string' raises Http404.
@@ -263,6 +267,7 @@ class ContainerEmbedPageTestCase(ContainerPageTestCase): # lint-amnesty, pylint
),
)
@override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True)
def test_container_on_container_html(self):
"""
Create the scenario of an xblock with children (non-vertical) on the container page.

View File

@@ -14,9 +14,11 @@ from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.test.utils import override_settings
from django.utils.translation import gettext as _
from edx_toggles.toggles.testutils import override_waffle_flag
from opaque_keys.edx.locator import CourseLocator
from search.api import perform_search
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.courseware_index import CoursewareSearchIndexer, SearchIndexingError
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import (
@@ -41,6 +43,8 @@ from ..course import _deprecated_blocks_info, course_outline_initial_state, rein
from cms.djangoapps.contentstore.xblock_storage_handlers.view_handlers import VisibilityState, create_xblock_info
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_OUTLINE, True)
class TestCourseIndex(CourseTestCase):
"""
Unit tests for getting the list of courses and the course outline.
@@ -335,6 +339,7 @@ class TestCourseIndex(CourseTestCase):
self.assertContains(response, 'display_course_number: ""')
@override_waffle_flag(toggles.LEGACY_STUDIO_HOME, True)
@ddt.ddt
class TestCourseIndexArchived(CourseTestCase):
"""
@@ -492,6 +497,7 @@ class TestCourseIndexArchived(CourseTestCase):
sql_queries=sql_queries)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_OUTLINE, True)
@ddt.ddt
class TestCourseOutline(CourseTestCase):
"""
@@ -709,11 +715,12 @@ class TestCourseOutline(CourseTestCase):
"""
Test to check number of queries made to mysql and mongo
"""
with self.assertNumQueries(29, table_ignorelist=WAFFLE_TABLES):
with self.assertNumQueries(38, table_ignorelist=WAFFLE_TABLES):
with check_mongo_calls(3):
self.client.get_html(reverse_course_url('course_handler', self.course.id))
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_OUTLINE, True)
class TestCourseReIndex(CourseTestCase):
"""
Unit tests for the course outline.

View File

@@ -5,7 +5,9 @@ unit tests for course_info views and models.
import json
from opaque_keys.edx.keys import UsageKey
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.test_course_settings import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_usage_url
from openedx.core.lib.xblock_utils import get_course_update_items
@@ -21,6 +23,7 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
return reverse_course_url('course_info_update_handler', course_key, kwargs=kwargs)
# The do all and end all of unit test cases.
@override_waffle_flag(toggles.LEGACY_STUDIO_UPDATES, True)
def test_course_update(self):
"""Go through each interface and ensure it works."""
def get_response(content, date):

View File

@@ -4,7 +4,9 @@ Unit tests for credit eligibility UI in Studio.
from unittest import mock
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
from openedx.core.djangoapps.credit.api import get_credit_requirements
@@ -24,6 +26,7 @@ class CreditEligibilityTest(CourseTestCase):
self.course_details_url = reverse_course_url('settings_handler', str(self.course.id))
@mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_CREDIT_ELIGIBILITY': False})
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_course_details_with_disabled_setting(self):
"""
Test that user don't see credit eligibility requirements in response
@@ -35,6 +38,7 @@ class CreditEligibilityTest(CourseTestCase):
self.assertNotContains(response, "Steps required to earn course credit")
@mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_CREDIT_ELIGIBILITY': True})
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
def test_course_details_with_enabled_setting(self):
"""
Test that credit eligibility requirements are present in

View File

@@ -8,24 +8,28 @@ import ddt
import lxml
from django.conf import settings
from django.test.utils import override_settings
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import get_proctored_exam_settings_url, reverse_course_url
from common.djangoapps.util.testing import UrlResetMixin
FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy()
FEATURES_WITH_CERTS_ENABLED['CERTIFICATES_HTML_VIEW'] = True
FEATURES_WITH_EXAM_SETTINGS_ENABLED = FEATURES_WITH_CERTS_ENABLED.copy()
FEATURES_WITH_EXAM_SETTINGS_ENABLED['ENABLE_EXAM_SETTINGS_HTML_VIEW'] = True
FEATURES_WITH_EXAM_SETTINGS_ENABLED['ENABLE_PROCTORED_EXAMS'] = True
FEATURES_WITH_EXAM_SETTINGS_DISABLED = FEATURES_WITH_CERTS_ENABLED.copy()
FEATURES_WITH_EXAM_SETTINGS_DISABLED['ENABLE_EXAM_SETTINGS_HTML_VIEW'] = False
FEATURES_WITH_EXAM_SETTINGS_DISABLED['ENABLE_PROCTORED_EXAMS'] = True
@ddt.ddt
@override_settings(
FEATURES={
**settings.FEATURES,
"CERTIFICATES_HTML_VIEW": True,
"ENABLE_PROCTORED_EXAMS": True,
},
)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_OUTLINE, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_CERTIFICATES, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_CONFIGURATIONS, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_ADVANCED_SETTINGS, True)
class TestExamSettingsView(CourseTestCase, UrlResetMixin):
"""
Unit tests for the exam settings view.
@@ -46,7 +50,7 @@ class TestExamSettingsView(CourseTestCase, UrlResetMixin):
alert_node = alert_nodes[0]
return alert_node.text_content()
@override_settings(FEATURES=FEATURES_WITH_EXAM_SETTINGS_DISABLED)
@override_waffle_flag(toggles.LEGACY_STUDIO_EXAM_SETTINGS, True)
@ddt.data(
"certificates_list_handler",
"settings_handler",
@@ -64,7 +68,6 @@ class TestExamSettingsView(CourseTestCase, UrlResetMixin):
self.assertEqual(resp.status_code, 200)
self.assertNotContains(resp, 'Proctored Exam Settings')
@override_settings(FEATURES=FEATURES_WITH_EXAM_SETTINGS_ENABLED)
@ddt.data(
"certificates_list_handler",
"settings_handler",
@@ -87,7 +90,6 @@ class TestExamSettingsView(CourseTestCase, UrlResetMixin):
'DEFAULT': 'test_proctoring_provider',
'proctortrack': {}
},
FEATURES=FEATURES_WITH_EXAM_SETTINGS_ENABLED,
)
@ddt.data(
"advanced_settings_handler",
@@ -125,12 +127,12 @@ class TestExamSettingsView(CourseTestCase, UrlResetMixin):
'DEFAULT': 'test_proctoring_provider',
'proctortrack': {}
},
FEATURES=FEATURES_WITH_EXAM_SETTINGS_DISABLED,
)
@ddt.data(
"advanced_settings_handler",
"course_handler",
)
@override_waffle_flag(toggles.LEGACY_STUDIO_EXAM_SETTINGS, True)
def test_exam_settings_alert_with_exam_settings_disabled(self, page_handler):
"""
An alert should appear if current exam settings are invalid.
@@ -168,7 +170,6 @@ class TestExamSettingsView(CourseTestCase, UrlResetMixin):
'proctortrack': {},
'test_proctoring_provider': {},
},
FEATURES=FEATURES_WITH_EXAM_SETTINGS_ENABLED,
)
@ddt.data(
"advanced_settings_handler",
@@ -212,7 +213,6 @@ class TestExamSettingsView(CourseTestCase, UrlResetMixin):
alert_nodes = parsed_html.find_class('exam-settings-alert')
assert len(alert_nodes) == 0
@override_settings(FEATURES={'ENABLE_EXAM_SETTINGS_HTML_VIEW': True})
@patch('cms.djangoapps.models.settings.course_metadata.CourseMetadata.validate_proctoring_settings')
def test_proctoring_link_is_visible(self, mock_validate_proctoring_settings):

View File

@@ -6,9 +6,11 @@ Group Configuration Tests.
import json
from operator import itemgetter
from unittest.mock import patch
from edx_toggles.toggles.testutils import override_waffle_flag
import ddt
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.course_group_config import (
CONTENT_GROUP_CONFIGURATION_NAME,
ENROLLMENT_SCHEME,
@@ -256,6 +258,7 @@ class GroupConfigurationsListHandlerTestCase(CourseTestCase, GroupConfigurations
"""
return reverse_course_url('group_configurations_list_handler', self.course.id)
@override_waffle_flag(toggles.LEGACY_STUDIO_CONFIGURATIONS, True)
def test_view_index_ok(self):
"""
Basic check that the groups configuration page responds correctly.

View File

@@ -5,7 +5,9 @@ from unittest import SkipTest
from django.conf import settings
from django.test.utils import override_settings
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
from common.djangoapps.util.testing import UrlResetMixin
@@ -21,6 +23,7 @@ FEATURES_WITH_EXAM_SETTINGS_DISABLED['ENABLE_EXAM_SETTINGS_HTML_VIEW'] = False
@override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_OUTLINE, True)
class TestHeaderMenu(CourseTestCase, UrlResetMixin):
"""
Unit tests for the course header menu.
@@ -61,6 +64,7 @@ class TestHeaderMenu(CourseTestCase, UrlResetMixin):
self.assertContains(resp, '<li class="nav-item nav-course-settings-certificates">')
@override_settings(FEATURES=FEATURES_WITH_EXAM_SETTINGS_DISABLED)
@override_waffle_flag(toggles.LEGACY_STUDIO_EXAM_SETTINGS, True)
def test_header_menu_without_exam_settings_enabled(self):
"""
Tests course header menu should not have `Exam Settings` menu item

View File

@@ -23,12 +23,14 @@ from django.contrib.auth import get_user_model
from django.core.exceptions import SuspiciousOperation
from django.core.files.storage import FileSystemStorage
from django.test.utils import override_settings
from edx_toggles.toggles.testutils import override_waffle_flag
from milestones.tests.utils import MilestonesTestCaseMixin
from opaque_keys.edx.locator import LibraryLocator
from path import Path as path
from storages.backends.s3boto3 import S3Boto3Storage
from user_tasks.models import UserTaskStatus
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore import errors as import_error
from cms.djangoapps.contentstore.storage import course_import_export_storage
from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase
@@ -746,6 +748,7 @@ class ExportTestCase(CourseTestCase):
self.url = reverse_course_url('export_handler', self.course.id)
self.status_url = reverse_course_url('export_status_handler', self.course.id)
@override_waffle_flag(toggles.LEGACY_STUDIO_EXPORT, True)
def test_export_html(self):
"""
Get the HTML for the page.

View File

@@ -4,7 +4,9 @@
import json
import random
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
from cms.djangoapps.contentstore.views import tabs
@@ -66,6 +68,7 @@ class TabsPageTests(CourseTestCase):
data={'invalid_request': None},
)
@override_waffle_flag(toggles.LEGACY_STUDIO_CUSTOM_PAGES, True)
def test_view_index(self):
"""Basic check that the Pages page responds correctly"""

View File

@@ -4,6 +4,9 @@
import json
from unittest import TestCase
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
@@ -17,6 +20,7 @@ class TextbookIndexTestCase(CourseTestCase):
super().setUp()
self.url = reverse_course_url('textbooks_list_handler', self.course.id)
@override_waffle_flag(toggles.LEGACY_STUDIO_TEXTBOOKS, True)
def test_view_index(self):
"Basic check that the textbook index page responds correctly"
resp = self.client.get(self.url)

View File

@@ -1,12 +1,12 @@
"""
Tests for contentstore/views/user.py.
"""
import json
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
from common.djangoapps.student import auth
@@ -42,12 +42,14 @@ class UsersTestCase(CourseTestCase): # lint-amnesty, pylint: disable=missing-cl
kwargs={'email': email} if email else {}
)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True)
def test_index(self):
resp = self.client.get(self.index_url, HTTP_ACCEPT='text/html')
# ext_user is not currently a member of the course team, and so should
# not show up on the page.
self.assertNotContains(resp, self.ext_user.email)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True)
def test_index_member(self):
auth.add_users(self.user, CourseStaffRole(self.course.id), self.ext_user)

View File

@@ -416,7 +416,6 @@ FEATURES:
ENABLE_DISCUSSION_SERVICE: true
ENABLE_EDXNOTES: true
ENABLE_ENTERPRISE_INTEGRATION: true
ENABLE_EXAM_SETTINGS_HTML_VIEW: true
ENABLE_EXPORT_GIT: true
ENABLE_FEEDBACK_SUBMISSION: true
ENABLE_FINANCIAL_ASSISTANCE_FORM: true

View File

@@ -115,9 +115,9 @@ from openedx.core.djangolib.markup import HTML, Text
<%block name="content">
<%
use_new_editor_text = use_new_text_editor()
use_new_editor_video = use_new_video_editor()
use_new_editor_problem = use_new_problem_editor()
use_new_editor_text = use_new_text_editor(xblock_locator.course_key)
use_new_editor_video = use_new_video_editor(xblock_locator.course_key)
use_new_editor_problem = use_new_problem_editor(xblock_locator.course_key)
use_new_video_gallery_flow = use_video_gallery_flow()
%>

View File

@@ -12,9 +12,9 @@ from cms.lib.xblock.upstream_sync import UpstreamLink
from openedx.core.djangoapps.content_tagging.toggles import is_tagging_feature_disabled
%>
<%
use_new_editor_text = use_new_text_editor()
use_new_editor_video = use_new_video_editor()
use_new_editor_problem = use_new_problem_editor()
use_new_editor_text = use_new_text_editor(xblock.context_key)
use_new_editor_video = use_new_video_editor(xblock.context_key)
use_new_editor_problem = use_new_problem_editor(xblock.context_key)
use_new_video_gallery_flow = use_video_gallery_flow()
use_tagging = not is_tagging_feature_disabled()
xblock_url = xblock_studio_url(xblock)

View File

@@ -27,32 +27,6 @@ class CachedAuthMiddlewareTestCase(TestCase):
self.client.response = HttpResponse()
self.client.response.cookies = SimpleCookie() # preparing cookies
def _test_change_session_hash(self, test_url, redirect_url, target_status_code=200):
"""
Verify that if a user's session auth hash and the request's hash
differ, the user is logged out. The URL to test and the
expected redirect are passed in, since we want to test this
behavior in both LMS and CMS, but the two systems have
different URLconfs.
"""
response = self.client.get(test_url)
assert response.status_code == 200
with patch(
"openedx.core.djangoapps.cache_toolbox.middleware.set_custom_attribute"
) as mock_set_custom_attribute:
with patch.object(User, 'get_session_auth_hash', return_value='abc123', autospec=True):
# Django 3.2 has _legacy_get_session_auth_hash, and Django 4 does not
# Remove once we reach Django 4
if hasattr(User, '_legacy_get_session_auth_hash'):
with patch.object(User, '_legacy_get_session_auth_hash', return_value='abc123'):
response = self.client.get(test_url)
else:
response = self.client.get(test_url)
self.assertRedirects(response, redirect_url, target_status_code=target_status_code)
mock_set_custom_attribute.assert_any_call('failed_session_verification', True)
def _test_custom_attribute_after_changing_hash(self, test_url, mock_set_custom_attribute):
"""verify that set_custom_attribute is called with expected values"""
password = 'test-password'
@@ -102,16 +76,44 @@ class CachedAuthMiddlewareTestCase(TestCase):
@skip_unless_lms
def test_session_change_lms(self):
"""Test session verification with LMS-specific URLs."""
"""
Verify (from the LMS side) that if a user's session auth hash and the request's
hash differ, the user is logged out.
"""
dashboard_url = reverse('dashboard')
self._test_change_session_hash(dashboard_url, reverse('signin_user') + '?next=' + dashboard_url)
response = self.client.get(dashboard_url)
assert response.status_code == 200
with patch(
"openedx.core.djangoapps.cache_toolbox.middleware.set_custom_attribute"
) as mock_set_custom_attribute:
with patch.object(User, 'get_session_auth_hash', return_value='abc123', autospec=True):
response = self.client.get(dashboard_url)
redirect_url = reverse('signin_user') + '?next=' + dashboard_url
self.assertRedirects(response, redirect_url, target_status_code=200)
mock_set_custom_attribute.assert_any_call('failed_session_verification', True)
@skip_unless_cms
def test_session_change_cms(self):
"""Test session verification with CMS-specific URLs."""
"""
Verify (from the CMS side) that if a user's session auth hash and the request's
hash differ, the user is logged out.
"""
home_url = reverse('home')
# Studio login redirects to LMS login
self._test_change_session_hash(home_url, settings.LOGIN_URL + '?next=' + home_url, target_status_code=302)
response = self.client.get(home_url)
assert response.status_code == 302
assert response.url == "http://course-authoring-mfe/home"
with patch(
"openedx.core.djangoapps.cache_toolbox.middleware.set_custom_attribute"
) as mock_set_custom_attribute:
with patch.object(User, 'get_session_auth_hash', return_value='abc123', autospec=True):
response = self.client.get(home_url)
redirect_url = settings.LOGIN_URL + '?next=' + home_url
self.assertRedirects(response, redirect_url, target_status_code=302)
mock_set_custom_attribute.assert_any_call('failed_session_verification', True)
@skip_unless_lms
@patch("openedx.core.djangoapps.cache_toolbox.middleware.set_custom_attribute")