Skip to content

Commit 8703aaf

Browse files
committed
Merge branch 'master' into html-extracted
2 parents 8902978 + 7c2acdc commit 8703aaf

File tree

7 files changed

+208
-63
lines changed

7 files changed

+208
-63
lines changed

.github/workflows/add-remove-label-on-comment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ on:
1717
jobs:
1818
add_remove_labels:
1919
uses: openedx/.github/.github/workflows/add-remove-label-on-comment.yml@master
20+

cms/envs/mock.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ CROSS_DOMAIN_CSRF_COOKIE_DOMAIN: .localhost
246246
CROSS_DOMAIN_CSRF_COOKIE_NAME: csrftoken
247247
CSRF_COOKIE_SECURE: true
248248
CSRF_TRUSTED_ORIGINS:
249-
- .localhost
249+
- https://*.localhost
250250
CSRF_TRUSTED_ORIGINS_WITH_SCHEME:
251251
- https://*.localhost
252252
DATABASES:

lms/djangoapps/courseware/tests/helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ def setUp(self):
138138
self.setup_course()
139139
self.initialize_module(metadata=self.METADATA, data=self.DATA)
140140

141-
def get_url(self, dispatch):
141+
def get_url(self, dispatch, handler_name='xmodule_handler'):
142142
"""Return item url with dispatch."""
143143
return reverse(
144144
'xblock_handler',
145-
args=(str(self.course.id), quote_slashes(self.item_url), 'xmodule_handler', dispatch)
145+
args=(str(self.course.id), quote_slashes(self.item_url), handler_name, dispatch)
146146
)
147147

148148

lms/djangoapps/courseware/tests/test_word_cloud.py

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
11
"""Word cloud integration tests using mongo modulestore."""
2-
3-
4-
import pytest
5-
2+
import importlib
63
import json
4+
import re
75
from operator import itemgetter
6+
from unittest.mock import patch
7+
from uuid import UUID
88

9+
import pytest
10+
from django.conf import settings
11+
from django.test import override_settings
12+
from xblock import plugin
13+
14+
from common.djangoapps.student.tests.factories import RequestFactoryNoCsrf
15+
from xmodule import word_cloud_block
916
# noinspection PyUnresolvedReferences
10-
from xmodule.tests.helpers import override_descriptor_system # pylint: disable=unused-import
17+
from xmodule.tests.helpers import override_descriptor_system, mock_render_template # pylint: disable=unused-import
1118
from xmodule.x_module import STUDENT_VIEW
12-
1319
from .helpers import BaseTestXmodule
1420

1521

1622
@pytest.mark.usefixtures("override_descriptor_system")
17-
class TestWordCloud(BaseTestXmodule):
23+
class _TestWordCloudBase(BaseTestXmodule):
1824
"""Integration test for Word Cloud Block."""
25+
__test__ = False
1926
CATEGORY = "word_cloud"
2027

28+
@classmethod
29+
def setUpClass(cls):
30+
super().setUpClass()
31+
plugin.PLUGIN_CACHE = {}
32+
importlib.reload(word_cloud_block)
33+
34+
def setUp(self):
35+
super().setUp()
36+
self.request_factory = RequestFactoryNoCsrf()
37+
2138
def _get_users_state(self):
2239
"""Return current state for each user:
2340
@@ -27,7 +44,18 @@ def _get_users_state(self):
2744
users_state = {}
2845

2946
for user in self.users:
30-
response = self.clients[user.username].post(self.get_url('get_state'))
47+
if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK:
48+
# The extracted Word Cloud XBlock uses @XBlock.json_handler, which expects a different
49+
# request format and url pattern
50+
handler_url = self.get_url('', handler_name='handle_get_state')
51+
response = self.clients[user.username].post(
52+
handler_url,
53+
data=json.dumps({}),
54+
content_type='application/json',
55+
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
56+
)
57+
else:
58+
response = self.clients[user.username].post(self.get_url('get_state'))
3159
users_state[user.username] = json.loads(response.content.decode('utf-8'))
3260

3361
return users_state
@@ -40,19 +68,29 @@ def _post_words(self, words):
4068
users_state = {}
4169

4270
for user in self.users:
43-
response = self.clients[user.username].post(
44-
self.get_url('submit'),
45-
{'student_words[]': words},
46-
HTTP_X_REQUESTED_WITH='XMLHttpRequest'
47-
)
71+
if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK:
72+
# The extracted Word Cloud XBlock uses @XBlock.json_handler, which expects a different
73+
# request format and url pattern
74+
handler_url = self.get_url('', handler_name='handle_submit_state')
75+
response = self.clients[user.username].post(
76+
handler_url,
77+
data=json.dumps({'student_words': words}),
78+
content_type='application/json',
79+
HTTP_X_REQUESTED_WITH='XMLHttpRequest',
80+
)
81+
else:
82+
response = self.clients[user.username].post(
83+
self.get_url('submit'),
84+
{'student_words[]': words},
85+
HTTP_X_REQUESTED_WITH='XMLHttpRequest'
86+
)
4887
users_state[user.username] = json.loads(response.content.decode('utf-8'))
4988

5089
return users_state
5190

5291
def _check_response(self, response_contents, correct_jsons):
5392
"""Utility function that compares correct and real responses."""
5493
for username, content in response_contents.items():
55-
5694
# Used in debugger for comparing objects.
5795
# self.maxDiff = None
5896

@@ -120,7 +158,6 @@ def test_post_words(self):
120158

121159
correct_state = {}
122160
for index, user in enumerate(self.users):
123-
124161
correct_state[user.username] = {
125162
'status': 'success',
126163
'submitted': True,
@@ -202,6 +239,14 @@ def test_handle_ajax_incorrect_dispatch(self):
202239
for user in self.users
203240
}
204241

242+
if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK:
243+
# The extracted Word Cloud XBlock uses @XBlock.json_handler to handle AJAX requests,
244+
# which automatically returns a 404 for unknown requests, so there's no need to test
245+
# the incorrect dispatch case in this scenario.
246+
for username, response in responses.items():
247+
self.assertEqual(response.status_code, 404)
248+
return
249+
205250
status_codes = {response.status_code for response in responses.values()}
206251
assert status_codes.pop() == 200
207252

@@ -214,19 +259,44 @@ def test_handle_ajax_incorrect_dispatch(self):
214259
}
215260
)
216261

217-
def test_word_cloud_constructor(self):
262+
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
263+
def test_word_cloud_constructor(self, mock_render_django_template):
218264
"""
219265
Make sure that all parameters extracted correctly from xml.
220266
"""
221267
fragment = self.runtime.render(self.block, STUDENT_VIEW)
222268
expected_context = {
223-
'ajax_url': self.block.ajax_url,
224269
'display_name': self.block.display_name,
225270
'instructions': self.block.instructions,
226-
'element_class': self.block.location.block_type,
227-
'element_id': self.block.location.html_id(),
271+
'element_class': self.block.scope_ids.block_type,
228272
'num_inputs': 5, # default value
229273
'submitted': False, # default value,
230274
}
231275

232-
assert fragment.content == self.runtime.render_template('word_cloud.html', expected_context)
276+
if settings.USE_EXTRACTED_WORD_CLOUD_BLOCK:
277+
# If `USE_EXTRACTED_WORD_CLOUD_BLOCK` is enabled, the `expected_context` will be different
278+
# because in the extracted Word Cloud XBlock, the expected context:
279+
# - contains `range_num_inputs`
280+
# - uses `UUID` for `element_id` instead of `html_id()`
281+
# - does not include `ajax_url` since it uses the `@XBlock.json_handler` decorator for AJAX requests
282+
expected_context['range_num_inputs'] = range(5)
283+
uuid_str = re.search(r"UUID\('([a-f0-9\-]+)'\)", fragment.content).group(1)
284+
expected_context['element_id'] = UUID(uuid_str)
285+
mock_render_django_template.assert_called_once()
286+
# Remove i18n service
287+
fragment_content_clean = re.sub(r"\{.*?}", "{}", fragment.content)
288+
assert fragment_content_clean == self.runtime.render_template('templates/word_cloud.html', expected_context)
289+
else:
290+
expected_context['ajax_url'] = self.block.ajax_url
291+
expected_context['element_id'] = self.block.location.html_id()
292+
assert fragment.content == self.runtime.render_template('word_cloud.html', expected_context)
293+
294+
295+
@override_settings(USE_EXTRACTED_WORD_CLOUD_BLOCK=True)
296+
class TestWordCloudExtracted(_TestWordCloudBase):
297+
__test__ = True
298+
299+
300+
@override_settings(USE_EXTRACTED_WORD_CLOUD_BLOCK=False)
301+
class TestWordCloudBuiltIn(_TestWordCloudBase):
302+
__test__ = True

lms/envs/mock.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ CROSS_DOMAIN_CSRF_COOKIE_DOMAIN: ''
329329
CROSS_DOMAIN_CSRF_COOKIE_NAME: ''
330330
CSRF_COOKIE_SECURE: true
331331
CSRF_TRUSTED_ORIGINS:
332-
- .sandbox.localhost
332+
- https://*.sandbox.localhost
333333
CSRF_TRUSTED_ORIGINS_WITH_SCHEME:
334334
- https://*.sandbox.localhost
335335
DASHBOARD_COURSE_LIMIT: 250

0 commit comments

Comments
 (0)