-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
make asides render in studio #6296
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,21 @@ | ||
""" | ||
Tests for contentstore.views.preview.py | ||
""" | ||
import re | ||
|
||
from django.test import TestCase | ||
from django.test.client import RequestFactory | ||
|
||
from xblock.core import XBlockAside | ||
from student.tests.factories import UserFactory | ||
|
||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory | ||
|
||
from contentstore.views.preview import get_preview_fragment | ||
from xmodule.modulestore import ModuleStoreEnum | ||
from xmodule.modulestore.tests.test_asides import AsideTestType | ||
from cms.djangoapps.xblock_config.models import StudioConfig | ||
from xmodule.modulestore.django import modulestore | ||
|
||
|
||
class GetPreviewHtmlTestCase(TestCase): | ||
|
@@ -18,21 +25,23 @@ class GetPreviewHtmlTestCase(TestCase): | |
Note that there are other existing test cases in test_contentstore that indirectly execute | ||
get_preview_fragment via the xblock RESTful API. | ||
""" | ||
|
||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside') | ||
def test_preview_fragment(self): | ||
""" | ||
Test for calling get_preview_html. | ||
This test used to be specifically about Locators (ensuring that they did not | ||
get translated to Locations). The test now has questionable value. | ||
Test for calling get_preview_html. Ensures data-usage-id is correctly set and | ||
asides are correctly included. | ||
""" | ||
course = CourseFactory.create() | ||
course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split) | ||
html = ItemFactory.create( | ||
parent_location=course.location, | ||
category="html", | ||
data={'data': "<html>foobar</html>"} | ||
) | ||
|
||
config = StudioConfig.current() | ||
config.enabled = True | ||
config.save() | ||
|
||
request = RequestFactory().get('/dummy-url') | ||
request.user = UserFactory() | ||
request.session = {} | ||
|
@@ -45,9 +54,50 @@ def test_preview_fragment(self): | |
html = get_preview_fragment(request, html, context).content | ||
|
||
# Verify student view html is returned, and the usage ID is as expected. | ||
html_pattern = unicode(course.id.make_usage_key('html', 'html_')).replace('html_', r'html_[0-9]*') | ||
html_pattern = re.escape(unicode(course.id.make_usage_key('html', 'replaceme'))).replace('replaceme', r'html_[0-9]*') | ||
self.assertRegexpMatches( | ||
html, | ||
'data-usage-id="{}"'.format(html_pattern) | ||
) | ||
self.assertRegexpMatches(html, '<html>foobar</html>') | ||
self.assertRegexpMatches(html, r"data-block-type=[\"\']test_aside[\"\']") | ||
self.assertRegexpMatches(html, "Aside rendered") | ||
# Now ensure the acid_aside is not in the result | ||
self.assertNotRegexpMatches(html, r"data-block-type=[\"\']acid_aside[\"\']") | ||
|
||
# Ensure about pages don't have asides | ||
about = modulestore().get_item(course.id.make_usage_key('about', 'overview')) | ||
html = get_preview_fragment(request, about, context).content | ||
self.assertNotRegexpMatches(html, r"data-block-type=[\"\']test_aside[\"\']") | ||
self.assertNotRegexpMatches(html, "Aside rendered") | ||
|
||
@XBlockAside.register_temp_plugin(AsideTestType, 'test_aside') | ||
def test_preview_no_asides(self): | ||
""" | ||
Test for calling get_preview_html. Ensures data-usage-id is correctly set and | ||
asides are correctly excluded because they are not enabled. | ||
""" | ||
course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split) | ||
html = ItemFactory.create( | ||
parent_location=course.location, | ||
category="html", | ||
data={'data': "<html>foobar</html>"} | ||
) | ||
|
||
config = StudioConfig.current() | ||
config.enabled = False | ||
config.save() | ||
|
||
request = RequestFactory().get('/dummy-url') | ||
request.user = UserFactory() | ||
request.session = {} | ||
|
||
# Call get_preview_fragment directly. | ||
context = { | ||
'reorderable_items': set(), | ||
'read_only': True | ||
} | ||
html = get_preview_fragment(request, html, context).content | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Consider factoring out the common code in this test and the last one into common code. |
||
|
||
self.assertNotRegexpMatches(html, r"data-block-type=[\"\']test_aside[\"\']") | ||
self.assertNotRegexpMatches(html, "Aside rendered") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There aren't tests here to ensure that 'about', 'course_info', or 'static_tab' don't have asides rendered - to test that the new config works properly. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
""" | ||
Django admin dashboard configuration for LMS XBlock infrastructure. | ||
""" | ||
|
||
from django.contrib import admin | ||
from config_models.admin import ConfigurationModelAdmin | ||
from cms.djangoapps.xblock_config.models import StudioConfig | ||
|
||
admin.site.register(StudioConfig, ConfigurationModelAdmin) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# -*- coding: utf-8 -*- | ||
import datetime | ||
from south.db import db | ||
from south.v2 import SchemaMigration | ||
from django.db import models | ||
|
||
|
||
class Migration(SchemaMigration): | ||
|
||
def forwards(self, orm): | ||
# Adding model 'StudioConfig' | ||
db.create_table('xblock_config_studioconfig', ( | ||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | ||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), | ||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)), | ||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)), | ||
('disabled_blocks', self.gf('django.db.models.fields.TextField')(default='about course_info static_tab')), | ||
)) | ||
db.send_create_signal('xblock_config', ['StudioConfig']) | ||
|
||
|
||
def backwards(self, orm): | ||
# Deleting model 'StudioConfig' | ||
db.delete_table('xblock_config_studioconfig') | ||
|
||
|
||
models = { | ||
'auth.group': { | ||
'Meta': {'object_name': 'Group'}, | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), | ||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) | ||
}, | ||
'auth.permission': { | ||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, | ||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) | ||
}, | ||
'auth.user': { | ||
'Meta': {'object_name': 'User'}, | ||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), | ||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), | ||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), | ||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), | ||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), | ||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) | ||
}, | ||
'contenttypes.contenttype': { | ||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, | ||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), | ||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) | ||
}, | ||
'xblock_config.studioconfig': { | ||
'Meta': {'object_name': 'StudioConfig'}, | ||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), | ||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}), | ||
'disabled_blocks': ('django.db.models.fields.TextField', [], {'default': "'about course_info static_tab'"}), | ||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) | ||
} | ||
} | ||
|
||
complete_apps = ['xblock_config'] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
""" | ||
Models used by Studio XBlock infrastructure. | ||
Includes: | ||
StudioConfig: A ConfigurationModel for managing Studio. | ||
""" | ||
|
||
from django.db.models import TextField | ||
|
||
from config_models.models import ConfigurationModel | ||
|
||
|
||
class StudioConfig(ConfigurationModel): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So - is this a Studio-wide config? Or asides-only? The docstring above implies asides-only - but this class name implies Studio-wide. The model should be called what it actually is - to avoid confusion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would have liked to see this name changed. |
||
""" | ||
Configuration for XBlockAsides. | ||
""" | ||
disabled_blocks = TextField( | ||
default="about course_info static_tab", | ||
help_text="Space-separated list of XBlocks on which XBlockAsides should never render in studio", | ||
) | ||
|
||
@classmethod | ||
def asides_enabled(cls, block_type): | ||
""" | ||
Return True if asides are enabled for this type of block in studio | ||
""" | ||
studio_config = cls.current() | ||
return studio_config.enabled and block_type not in studio_config.disabled_blocks.split() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect docstring - this is a new test.