From f89371ba79c80833bc2add8185ed35034531e574 Mon Sep 17 00:00:00 2001 From: farhan Date: Wed, 19 Feb 2025 14:47:44 +0500 Subject: [PATCH 1/7] chore: Fix test cases for extracted word cloud block --- lms/djangoapps/courseware/tests/helpers.py | 4 +- .../courseware/tests/test_word_cloud.py | 87 +++++++++++++++---- openedx/envs/common.py | 2 +- xmodule/tests/test_word_cloud.py | 62 +++++++++++-- 4 files changed, 125 insertions(+), 30 deletions(-) diff --git a/lms/djangoapps/courseware/tests/helpers.py b/lms/djangoapps/courseware/tests/helpers.py index 2b5a33b2ac0f..1cd8cf89037c 100644 --- a/lms/djangoapps/courseware/tests/helpers.py +++ b/lms/djangoapps/courseware/tests/helpers.py @@ -138,11 +138,11 @@ def setUp(self): self.setup_course() self.initialize_module(metadata=self.METADATA, data=self.DATA) - def get_url(self, dispatch): + def get_url(self, dispatch, handler_name='xmodule_handler'): """Return item url with dispatch.""" return reverse( 'xblock_handler', - args=(str(self.course.id), quote_slashes(self.item_url), 'xmodule_handler', dispatch) + args=(str(self.course.id), quote_slashes(self.item_url), handler_name, dispatch) ) diff --git a/lms/djangoapps/courseware/tests/test_word_cloud.py b/lms/djangoapps/courseware/tests/test_word_cloud.py index 06217628cbca..a5d9af9c7500 100644 --- a/lms/djangoapps/courseware/tests/test_word_cloud.py +++ b/lms/djangoapps/courseware/tests/test_word_cloud.py @@ -1,15 +1,17 @@ """Word cloud integration tests using mongo modulestore.""" - - -import pytest - import json +import re from operator import itemgetter +from unittest.mock import patch +from uuid import UUID + +import pytest +from django.conf import settings +from common.djangoapps.student.tests.factories import RequestFactoryNoCsrf # noinspection PyUnresolvedReferences -from xmodule.tests.helpers import override_descriptor_system # pylint: disable=unused-import +from xmodule.tests.helpers import override_descriptor_system, mock_render_template # pylint: disable=unused-import from xmodule.x_module import STUDENT_VIEW - from .helpers import BaseTestXmodule @@ -18,6 +20,10 @@ class TestWordCloud(BaseTestXmodule): """Integration test for Word Cloud Block.""" CATEGORY = "word_cloud" + def setUp(self): + super().setUp() + self.request_factory = RequestFactoryNoCsrf() + def _get_users_state(self): """Return current state for each user: @@ -27,7 +33,18 @@ def _get_users_state(self): users_state = {} for user in self.users: - response = self.clients[user.username].post(self.get_url('get_state')) + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # The extracted Word Cloud XBlock uses @XBlock.json_handler, which expects a different + # request format and url pattern + handler_url = self.get_url('', handler_name='handle_get_state') + response = self.clients[user.username].post( + handler_url, + data=json.dumps({}), + content_type='application/json', + HTTP_X_REQUESTED_WITH='XMLHttpRequest', + ) + else: + response = self.clients[user.username].post(self.get_url('get_state')) users_state[user.username] = json.loads(response.content.decode('utf-8')) return users_state @@ -40,11 +57,22 @@ def _post_words(self, words): users_state = {} for user in self.users: - response = self.clients[user.username].post( - self.get_url('submit'), - {'student_words[]': words}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest' - ) + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # The extracted Word Cloud XBlock uses @XBlock.json_handler, which expects a different + # request format and url pattern + handler_url = self.get_url('', handler_name='handle_submit_state') + response = self.clients[user.username].post( + handler_url, + data=json.dumps({'student_words': words}), + content_type='application/json', + HTTP_X_REQUESTED_WITH='XMLHttpRequest', + ) + else: + response = self.clients[user.username].post( + self.get_url('submit'), + {'student_words[]': words}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest' + ) users_state[user.username] = json.loads(response.content.decode('utf-8')) return users_state @@ -52,7 +80,6 @@ def _post_words(self, words): def _check_response(self, response_contents, correct_jsons): """Utility function that compares correct and real responses.""" for username, content in response_contents.items(): - # Used in debugger for comparing objects. # self.maxDiff = None @@ -120,7 +147,6 @@ def test_post_words(self): correct_state = {} for index, user in enumerate(self.users): - correct_state[user.username] = { 'status': 'success', 'submitted': True, @@ -202,6 +228,14 @@ def test_handle_ajax_incorrect_dispatch(self): for user in self.users } + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # The extracted Word Cloud XBlock uses @XBlock.json_handler to handle AJAX requests, + # which automatically returns a 404 for unknown requests, so there's no need to test + # the incorrect dispatch case in this scenario. + for username, response in responses.items(): + self.assertEqual(response.status_code, 404) + return + status_codes = {response.status_code for response in responses.values()} assert status_codes.pop() == 200 @@ -214,19 +248,34 @@ def test_handle_ajax_incorrect_dispatch(self): } ) - def test_word_cloud_constructor(self): + @patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template) + def test_word_cloud_constructor(self, mock_render_django_template): """ Make sure that all parameters extracted correctly from xml. """ fragment = self.runtime.render(self.block, STUDENT_VIEW) expected_context = { - 'ajax_url': self.block.ajax_url, 'display_name': self.block.display_name, 'instructions': self.block.instructions, - 'element_class': self.block.location.block_type, - 'element_id': self.block.location.html_id(), + 'element_class': self.block.scope_ids.block_type, 'num_inputs': 5, # default value 'submitted': False, # default value, } - assert fragment.content == self.runtime.render_template('word_cloud.html', expected_context) + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # If `USE_EXTRACTED_WORD_CLOUD_BLOCK` is enabled, the `expected_context` will be different + # because in the extracted Word Cloud XBlock, the expected context: + # - contains `range_num_inputs` + # - uses `UUID` for `element_id` instead of `html_id()` + # - does not include `ajax_url` since it uses the `@XBlock.json_handler` decorator for AJAX requests + expected_context['range_num_inputs'] = range(5) + uuid_str = re.search(r"UUID\('([a-f0-9\-]+)'\)", fragment.content).group(1) + expected_context['element_id'] = UUID(uuid_str) + mock_render_django_template.assert_called_once() + # Remove i18n service + fragment_content_clean = re.sub(r"\{.*?}", "{}", fragment.content) + assert fragment_content_clean == self.runtime.render_template('templates/word_cloud.html', expected_context) + else: + expected_context['ajax_url'] = self.block.ajax_url + expected_context['element_id'] = self.block.location.html_id() + assert fragment.content == self.runtime.render_template('word_cloud.html', expected_context) diff --git a/openedx/envs/common.py b/openedx/envs/common.py index 07c538464804..406d51c789ff 100644 --- a/openedx/envs/common.py +++ b/openedx/envs/common.py @@ -680,7 +680,7 @@ def _make_locale_paths(settings): # .. toggle_warning: Not production-ready until https://github.com/openedx/edx-platform/issues/34840 is done. # .. toggle_creation_date: 2024-11-10 # .. toggle_target_removal_date: 2025-06-01 -USE_EXTRACTED_WORD_CLOUD_BLOCK = False +USE_EXTRACTED_WORD_CLOUD_BLOCK = True # .. toggle_name: USE_EXTRACTED_ANNOTATABLE_BLOCK # .. toggle_default: False diff --git a/xmodule/tests/test_word_cloud.py b/xmodule/tests/test_word_cloud.py index 9fbd02a612db..2921f2db5f00 100644 --- a/xmodule/tests/test_word_cloud.py +++ b/xmodule/tests/test_word_cloud.py @@ -1,8 +1,10 @@ """Test for Word Cloud Block functional logic.""" import json +import os from unittest.mock import Mock +from django.conf import settings from django.test import TestCase from fs.memoryfs import MemoryFS from lxml import etree @@ -10,6 +12,7 @@ from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator from webob.multidict import MultiDict from xblock.field_data import DictFieldData +from xblock.fields import ScopeIds from xmodule.word_cloud_block import WordCloudBlock from . import get_test_descriptor_system, get_test_system @@ -43,7 +46,11 @@ def test_xml_import_export_cycle(self): olx_element = etree.fromstring(original_xml) runtime.id_generator = Mock() - block = WordCloudBlock.parse_xml(olx_element, runtime, None) + + def_id = runtime.id_generator.create_definition(olx_element.tag, olx_element.get('url_name')) + keys = ScopeIds(None, olx_element.tag, def_id, runtime.id_generator.create_usage(def_id)) + block = WordCloudBlock.parse_xml(olx_element, runtime, keys) + block.location = BlockUsageLocator( CourseLocator('org', 'course', 'run', branch='revision'), 'word_cloud', 'block_id' ) @@ -54,18 +61,41 @@ def test_xml_import_export_cycle(self): assert block.num_inputs == 3 assert block.num_top_words == 100 - node = etree.Element("unknown_root") - # This will export the olx to a separate file. - block.add_xml_to_node(node) + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # For extracted XBlocks, we need to manually export the XML definition to a file to properly test the + # import/export cycle. This is because extracted XBlocks use XBlock core's `add_xml_to_node` method, + # which does not export the XML to a file like `XmlMixin.add_xml_to_node` does. + filepath = 'word_cloud/block_id.xml' + runtime.export_fs.makedirs(os.path.dirname(filepath), recreate=True) + with runtime.export_fs.open(filepath, 'wb') as fileObj: + runtime.export_to_xml(block, fileObj) + else: + node = etree.Element("unknown_root") + # This will export the olx to a separate file. + block.add_xml_to_node(node) + with runtime.export_fs.open('word_cloud/block_id.xml') as f: exported_xml = f.read() + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # For extracted XBlocks, we need to remove the `xblock-family` attribute from the exported XML to ensure + # consistency with the original XML. + # This is because extracted XBlocks use the core XBlock's `add_xml_to_node` method, which includes this + # attribute, whereas `XmlMixin.add_xml_to_node` does not. + exported_xml_tree = etree.fromstring(exported_xml.encode('utf-8')) + etree.cleanup_namespaces(exported_xml_tree) + if 'xblock-family' in exported_xml_tree.attrib: + del exported_xml_tree.attrib['xblock-family'] + exported_xml = etree.tostring(exported_xml_tree, encoding='unicode', pretty_print=True) + assert exported_xml == original_xml def test_bad_ajax_request(self): """ Make sure that answer for incorrect request is error json. """ + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + return module_system = get_test_system() block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) @@ -84,8 +114,14 @@ def test_good_ajax_request(self): module_system = get_test_system() block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) - post_data = MultiDict(('student_words[]', word) for word in ['cat', 'cat', 'dog', 'sun']) - response = json.loads(block.handle_ajax('submit', post_data)) + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # The extracted Word Cloud XBlock uses @XBlock.json_handler for handling AJAX requests. + # It expects a standard Python dictionary as POST data and returns a JSON object in response. + post_data = {'student_words': ['cat', 'cat', 'dog', 'sun']} + response = block.submit_state(post_data) + else: + post_data = MultiDict(('student_words[]', word) for word in ['cat', 'cat', 'dog', 'sun']) + response = json.loads(block.handle_ajax('submit', post_data)) assert response['status'] == 'success' assert response['submitted'] is True assert response['total_count'] == 22 @@ -128,13 +164,23 @@ def test_studio_submit_handler(self): 'num_top_words': 10, 'display_student_percents': 'False', } + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # In the extracted Word Cloud XBlock, we use StudioEditableXBlockMixin.submit_studio_edits, + # which expects a different handler name and request JSON format. + handler_name = 'submit_studio_edits' + TEST_REQUEST_JSON = { + 'values': TEST_SUBMIT_DATA, + } + else: + handler_name = 'studio_submit' + TEST_REQUEST_JSON = TEST_SUBMIT_DATA module_system = get_test_system() block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) - body = json.dumps(TEST_SUBMIT_DATA) + body = json.dumps(TEST_REQUEST_JSON) request = Request.blank('/') request.method = 'POST' request.body = body.encode('utf-8') - res = block.handle('studio_submit', request) + res = block.handle(handler_name, request) assert json.loads(res.body.decode('utf8')) == {'result': 'success'} assert block.display_name == TEST_SUBMIT_DATA['display_name'] From 8d622a457df618d7635e4226077ee4c0f2618ce0 Mon Sep 17 00:00:00 2001 From: farhan Date: Fri, 1 Aug 2025 23:40:38 +0500 Subject: [PATCH 2/7] chore: Address change requests --- xmodule/tests/test_word_cloud.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/xmodule/tests/test_word_cloud.py b/xmodule/tests/test_word_cloud.py index 2921f2db5f00..8521354e37ad 100644 --- a/xmodule/tests/test_word_cloud.py +++ b/xmodule/tests/test_word_cloud.py @@ -94,17 +94,21 @@ def test_bad_ajax_request(self): """ Make sure that answer for incorrect request is error json. """ - if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: - return - module_system = get_test_system() block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) - response = json.loads(block.handle_ajax('bad_dispatch', {})) - self.assertDictEqual(response, { - 'status': 'fail', - 'error': 'Unknown Command!' - }) + if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: + # The extracted Word Cloud XBlock uses @XBlock.json_handler for handling AJAX requests, + # which requires a different way of method invocation. + with self.assertRaises(AttributeError) as context: + json.loads(block.bad_dispatch('bad_dispatch', {})) + self.assertIn("'WordCloudBlock' object has no attribute 'bad_dispatch'", str(context.exception)) + else: + response = json.loads(block.handle_ajax('bad_dispatch', {})) + self.assertDictEqual(response, { + 'status': 'fail', + 'error': 'Unknown Command!' + }) def test_good_ajax_request(self): """ From a2d19bd6fa81e4c549ee969b4cbd81430be4885e Mon Sep 17 00:00:00 2001 From: farhan Date: Mon, 4 Aug 2025 17:32:07 +0500 Subject: [PATCH 3/7] chore: Run test cases both for builtin and extracted blocks --- .../courseware/tests/test_word_cloud.py | 23 ++++++++- xmodule/tests/test_word_cloud.py | 50 ++++++++++++++----- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/lms/djangoapps/courseware/tests/test_word_cloud.py b/lms/djangoapps/courseware/tests/test_word_cloud.py index a5d9af9c7500..8e225e1433fb 100644 --- a/lms/djangoapps/courseware/tests/test_word_cloud.py +++ b/lms/djangoapps/courseware/tests/test_word_cloud.py @@ -1,4 +1,5 @@ """Word cloud integration tests using mongo modulestore.""" +import importlib import json import re from operator import itemgetter @@ -7,8 +8,11 @@ import pytest from django.conf import settings +from django.test import override_settings +from xblock import plugin from common.djangoapps.student.tests.factories import RequestFactoryNoCsrf +from xmodule import word_cloud_block # noinspection PyUnresolvedReferences from xmodule.tests.helpers import override_descriptor_system, mock_render_template # pylint: disable=unused-import from xmodule.x_module import STUDENT_VIEW @@ -16,10 +20,17 @@ @pytest.mark.usefixtures("override_descriptor_system") -class TestWordCloud(BaseTestXmodule): +class _TestWordCloudBase(BaseTestXmodule): """Integration test for Word Cloud Block.""" + __test__ = False CATEGORY = "word_cloud" + @classmethod + def setUpClass(cls): + super().setUpClass() + plugin.PLUGIN_CACHE = {} + importlib.reload(word_cloud_block) + def setUp(self): super().setUp() self.request_factory = RequestFactoryNoCsrf() @@ -279,3 +290,13 @@ def test_word_cloud_constructor(self, mock_render_django_template): expected_context['ajax_url'] = self.block.ajax_url expected_context['element_id'] = self.block.location.html_id() assert fragment.content == self.runtime.render_template('word_cloud.html', expected_context) + + +@override_settings(USE_EXTRACTED_WORD_CLOUD_BLOCK=True) +class TestWordCloudExtracted(_TestWordCloudBase): + __test__ = True + + +@override_settings(USE_EXTRACTED_WORD_CLOUD_BLOCK=False) +class TestWordCloudBuiltIn(_TestWordCloudBase): + __test__ = True diff --git a/xmodule/tests/test_word_cloud.py b/xmodule/tests/test_word_cloud.py index 8521354e37ad..04c826c0321a 100644 --- a/xmodule/tests/test_word_cloud.py +++ b/xmodule/tests/test_word_cloud.py @@ -1,35 +1,46 @@ """Test for Word Cloud Block functional logic.""" - +import importlib import json import os from unittest.mock import Mock from django.conf import settings from django.test import TestCase +from django.test import override_settings from fs.memoryfs import MemoryFS from lxml import etree -from webob import Request from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator +from webob import Request from webob.multidict import MultiDict +from xblock import plugin from xblock.field_data import DictFieldData from xblock.fields import ScopeIds -from xmodule.word_cloud_block import WordCloudBlock +from xmodule import word_cloud_block from . import get_test_descriptor_system, get_test_system -class WordCloudBlockTest(TestCase): +class _TestWordCloudBase(TestCase): """ Logic tests for Word Cloud Block. """ - - raw_field_data = { - 'all_words': {'cat': 10, 'dog': 5, 'mom': 1, 'dad': 2}, - 'top_words': {'cat': 10, 'dog': 5, 'dad': 2}, - 'submitted': False, - 'display_name': 'Word Cloud Block', - 'instructions': 'Enter some random words that comes to your mind' - } + __test__ = False + + @classmethod + def setUpClass(cls): + super().setUpClass() + plugin.PLUGIN_CACHE = {} + importlib.reload(word_cloud_block) + + def setUp(self): + super().setUp() + self.raw_field_data = { + 'all_words': {'cat': 10, 'dog': 5, 'mom': 1, 'dad': 2}, + 'top_words': {'cat': 10, 'dog': 5, 'dad': 2}, + 'submitted': False, + 'display_name': 'Word Cloud Block', + 'instructions': 'Enter some random words that comes to your mind' + } def test_xml_import_export_cycle(self): """ @@ -49,6 +60,7 @@ def test_xml_import_export_cycle(self): def_id = runtime.id_generator.create_definition(olx_element.tag, olx_element.get('url_name')) keys = ScopeIds(None, olx_element.tag, def_id, runtime.id_generator.create_usage(def_id)) + from xmodule.word_cloud_block import WordCloudBlock block = WordCloudBlock.parse_xml(olx_element, runtime, keys) block.location = BlockUsageLocator( @@ -95,6 +107,7 @@ def test_bad_ajax_request(self): Make sure that answer for incorrect request is error json. """ module_system = get_test_system() + from xmodule.word_cloud_block import WordCloudBlock block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: @@ -116,6 +129,7 @@ def test_good_ajax_request(self): """ module_system = get_test_system() + from xmodule.word_cloud_block import WordCloudBlock block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: @@ -151,6 +165,7 @@ def test_indexibility(self): """ module_system = get_test_system() + from xmodule.word_cloud_block import WordCloudBlock block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) assert block.index_dictionary() ==\ {'content_type': 'Word Cloud', @@ -179,6 +194,7 @@ def test_studio_submit_handler(self): handler_name = 'studio_submit' TEST_REQUEST_JSON = TEST_SUBMIT_DATA module_system = get_test_system() + from xmodule.word_cloud_block import WordCloudBlock block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) body = json.dumps(TEST_REQUEST_JSON) request = Request.blank('/') @@ -192,3 +208,13 @@ def test_studio_submit_handler(self): assert block.num_inputs == TEST_SUBMIT_DATA['num_inputs'] assert block.num_top_words == TEST_SUBMIT_DATA['num_top_words'] assert block.display_student_percents == (TEST_SUBMIT_DATA['display_student_percents'] == "True") + + +@override_settings(USE_EXTRACTED_WORD_CLOUD_BLOCK=True) +class TestWordCloudExtracted(_TestWordCloudBase): + __test__ = True + + +@override_settings(USE_EXTRACTED_WORD_CLOUD_BLOCK=False) +class TestWordCloudBuiltIn(_TestWordCloudBase): + __test__ = True From ba3df94e09b9553b6176dd843569cd5a1d27a778 Mon Sep 17 00:00:00 2001 From: farhan Date: Mon, 4 Aug 2025 19:38:29 +0500 Subject: [PATCH 4/7] chore: update tests fix --- xmodule/tests/test_word_cloud.py | 23 ++++++----------------- xmodule/word_cloud_block.py | 15 +++++++++++---- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/xmodule/tests/test_word_cloud.py b/xmodule/tests/test_word_cloud.py index 04c826c0321a..bc3f18a83c54 100644 --- a/xmodule/tests/test_word_cloud.py +++ b/xmodule/tests/test_word_cloud.py @@ -1,5 +1,4 @@ """Test for Word Cloud Block functional logic.""" -import importlib import json import os from unittest.mock import Mock @@ -12,7 +11,6 @@ from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator from webob import Request from webob.multidict import MultiDict -from xblock import plugin from xblock.field_data import DictFieldData from xblock.fields import ScopeIds @@ -29,8 +27,7 @@ class _TestWordCloudBase(TestCase): @classmethod def setUpClass(cls): super().setUpClass() - plugin.PLUGIN_CACHE = {} - importlib.reload(word_cloud_block) + cls.word_cloud_class = word_cloud_block.reset_class() def setUp(self): super().setUp() @@ -46,7 +43,6 @@ def test_xml_import_export_cycle(self): """ Test the import export cycle. """ - runtime = get_test_descriptor_system() runtime.export_fs = MemoryFS() @@ -60,8 +56,7 @@ def test_xml_import_export_cycle(self): def_id = runtime.id_generator.create_definition(olx_element.tag, olx_element.get('url_name')) keys = ScopeIds(None, olx_element.tag, def_id, runtime.id_generator.create_usage(def_id)) - from xmodule.word_cloud_block import WordCloudBlock - block = WordCloudBlock.parse_xml(olx_element, runtime, keys) + block = self.word_cloud_class.parse_xml(olx_element, runtime, keys) block.location = BlockUsageLocator( CourseLocator('org', 'course', 'run', branch='revision'), 'word_cloud', 'block_id' @@ -107,8 +102,7 @@ def test_bad_ajax_request(self): Make sure that answer for incorrect request is error json. """ module_system = get_test_system() - from xmodule.word_cloud_block import WordCloudBlock - block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) + block = self.word_cloud_class(module_system, DictFieldData(self.raw_field_data), Mock()) if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: # The extracted Word Cloud XBlock uses @XBlock.json_handler for handling AJAX requests, @@ -127,10 +121,8 @@ def test_good_ajax_request(self): """ Make sure that ajax request works correctly. """ - module_system = get_test_system() - from xmodule.word_cloud_block import WordCloudBlock - block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) + block = self.word_cloud_class(module_system, DictFieldData(self.raw_field_data), Mock()) if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK: # The extracted Word Cloud XBlock uses @XBlock.json_handler for handling AJAX requests. @@ -163,10 +155,8 @@ def test_indexibility(self): """ Test indexibility of Word Cloud """ - module_system = get_test_system() - from xmodule.word_cloud_block import WordCloudBlock - block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) + block = self.word_cloud_class(module_system, DictFieldData(self.raw_field_data), Mock()) assert block.index_dictionary() ==\ {'content_type': 'Word Cloud', 'content': {'display_name': 'Word Cloud Block', @@ -194,8 +184,7 @@ def test_studio_submit_handler(self): handler_name = 'studio_submit' TEST_REQUEST_JSON = TEST_SUBMIT_DATA module_system = get_test_system() - from xmodule.word_cloud_block import WordCloudBlock - block = WordCloudBlock(module_system, DictFieldData(self.raw_field_data), Mock()) + block = self.word_cloud_class(module_system, DictFieldData(self.raw_field_data), Mock()) body = json.dumps(TEST_REQUEST_JSON) request = Request.blank('/') request.method = 'POST' diff --git a/xmodule/word_cloud_block.py b/xmodule/word_cloud_block.py index 37e82400df78..acf160afdf12 100644 --- a/xmodule/word_cloud_block.py +++ b/xmodule/word_cloud_block.py @@ -316,8 +316,15 @@ def index_dictionary(self): return xblock_body -WordCloudBlock = ( - _ExtractedWordCloudBlock if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK - else _BuiltInWordCloudBlock -) +WordCloudBlock = None + +def reset_class(): + global WordCloudBlock + WordCloudBlock = ( + _ExtractedWordCloudBlock if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK + else _BuiltInWordCloudBlock + ) + return WordCloudBlock + +reset_class() WordCloudBlock.__name__ = "WordCloudBlock" From bfea346139ea01d28c8ee6379f463de217d287bb Mon Sep 17 00:00:00 2001 From: farhan Date: Mon, 4 Aug 2025 19:44:10 +0500 Subject: [PATCH 5/7] chore: fix pylint error --- xmodule/word_cloud_block.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xmodule/word_cloud_block.py b/xmodule/word_cloud_block.py index acf160afdf12..d0d2e4b3c5fb 100644 --- a/xmodule/word_cloud_block.py +++ b/xmodule/word_cloud_block.py @@ -319,6 +319,7 @@ def index_dictionary(self): WordCloudBlock = None def reset_class(): + """Reset class as per django settings flag""" global WordCloudBlock WordCloudBlock = ( _ExtractedWordCloudBlock if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK From c2c9b9e2f298906fb61498a7d774c50be08dd95f Mon Sep 17 00:00:00 2001 From: farhan Date: Mon, 4 Aug 2025 19:56:38 +0500 Subject: [PATCH 6/7] chore: fix quality check --- xmodule/word_cloud_block.py | 1 + 1 file changed, 1 insertion(+) diff --git a/xmodule/word_cloud_block.py b/xmodule/word_cloud_block.py index d0d2e4b3c5fb..b22ecf3b7ab7 100644 --- a/xmodule/word_cloud_block.py +++ b/xmodule/word_cloud_block.py @@ -318,6 +318,7 @@ def index_dictionary(self): WordCloudBlock = None + def reset_class(): """Reset class as per django settings flag""" global WordCloudBlock From af88397048a29e7c579fe7b90882710dd023053e Mon Sep 17 00:00:00 2001 From: farhan Date: Wed, 6 Aug 2025 18:59:24 +0500 Subject: [PATCH 7/7] chore: kept USE_EXTRACTED_WORD_CLOUD_BLOCK flag False for now --- openedx/envs/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openedx/envs/common.py b/openedx/envs/common.py index 406d51c789ff..07c538464804 100644 --- a/openedx/envs/common.py +++ b/openedx/envs/common.py @@ -680,7 +680,7 @@ def _make_locale_paths(settings): # .. toggle_warning: Not production-ready until https://github.com/openedx/edx-platform/issues/34840 is done. # .. toggle_creation_date: 2024-11-10 # .. toggle_target_removal_date: 2025-06-01 -USE_EXTRACTED_WORD_CLOUD_BLOCK = True +USE_EXTRACTED_WORD_CLOUD_BLOCK = False # .. toggle_name: USE_EXTRACTED_ANNOTATABLE_BLOCK # .. toggle_default: False