Commit 0fb11365 by dcadams

Merge branch 'master' of github.com:edx/edx-platform into feature-dcadams-usermanagement

parents a8ec1ca9 468dfe34
...@@ -74,3 +74,4 @@ Jason Bau <jbau@stanford.edu> ...@@ -74,3 +74,4 @@ Jason Bau <jbau@stanford.edu>
Frances Botsford <frances@edx.org> Frances Botsford <frances@edx.org>
Jonah Stanley <Jonah_Stanley@brown.edu> Jonah Stanley <Jonah_Stanley@brown.edu>
Slater Victoroff <slater.r.victoroff@gmail.com> Slater Victoroff <slater.r.victoroff@gmail.com>
Peter Fogg <peter.p.fogg@gmail.com>
...@@ -11,8 +11,6 @@ Feature: Advanced (manual) course policy ...@@ -11,8 +11,6 @@ Feature: Advanced (manual) course policy
Given I am on the Advanced Course Settings page in Studio Given I am on the Advanced Course Settings page in Studio
Then the settings are alphabetized Then the settings are alphabetized
# Skipped because Ubuntu ChromeDriver cannot click notification "Cancel"
@skip
Scenario: Test cancel editing key value Scenario: Test cancel editing key value
Given I am on the Advanced Course Settings page in Studio Given I am on the Advanced Course Settings page in Studio
When I edit the value of a policy key When I edit the value of a policy key
...@@ -21,8 +19,6 @@ Feature: Advanced (manual) course policy ...@@ -21,8 +19,6 @@ Feature: Advanced (manual) course policy
And I reload the page And I reload the page
Then the policy key value is unchanged Then the policy key value is unchanged
# Skipped because Ubuntu ChromeDriver cannot click notification "Save"
@skip
Scenario: Test editing key value Scenario: Test editing key value
Given I am on the Advanced Course Settings page in Studio Given I am on the Advanced Course Settings page in Studio
When I edit the value of a policy key and save When I edit the value of a policy key and save
...@@ -30,8 +26,6 @@ Feature: Advanced (manual) course policy ...@@ -30,8 +26,6 @@ Feature: Advanced (manual) course policy
And I reload the page And I reload the page
Then the policy key value is changed Then the policy key value is changed
# Skipped because Ubuntu ChromeDriver cannot edit CodeMirror input
@skip
Scenario: Test how multi-line input appears Scenario: Test how multi-line input appears
Given I am on the Advanced Course Settings page in Studio Given I am on the Advanced Course Settings page in Studio
When I create a JSON object as a value When I create a JSON object as a value
...@@ -39,8 +33,6 @@ Feature: Advanced (manual) course policy ...@@ -39,8 +33,6 @@ Feature: Advanced (manual) course policy
And I reload the page And I reload the page
Then it is displayed as formatted Then it is displayed as formatted
# Skipped because Ubuntu ChromeDriver cannot edit CodeMirror input
@skip
Scenario: Test automatic quoting of non-JSON values Scenario: Test automatic quoting of non-JSON values
Given I am on the Advanced Course Settings page in Studio Given I am on the Advanced Course Settings page in Studio
When I create a non-JSON value not in quotes When I create a non-JSON value not in quotes
......
...@@ -42,8 +42,9 @@ def edit_the_value_of_a_policy_key(step): ...@@ -42,8 +42,9 @@ def edit_the_value_of_a_policy_key(step):
It is hard to figure out how to get into the CodeMirror It is hard to figure out how to get into the CodeMirror
area, so cheat and do it from the policy key field :) area, so cheat and do it from the policy key field :)
""" """
e = world.css_find(KEY_CSS)[get_index_of(DISPLAY_NAME_KEY)] world.css_find(".CodeMirror")[get_index_of(DISPLAY_NAME_KEY)].click()
e._element.send_keys(Keys.TAB, Keys.END, Keys.ARROW_LEFT, ' ', 'X') g = world.css_find("div.CodeMirror.CodeMirror-focused > div > textarea")
g._element.send_keys(Keys.ARROW_LEFT, ' ', 'X')
@step(u'I edit the value of a policy key and save$') @step(u'I edit the value of a policy key and save$')
...@@ -123,10 +124,12 @@ def get_display_name_value(): ...@@ -123,10 +124,12 @@ def get_display_name_value():
def change_display_name_value(step, new_value): def change_display_name_value(step, new_value):
e = world.css_find(KEY_CSS)[get_index_of(DISPLAY_NAME_KEY)]
world.css_find(".CodeMirror")[get_index_of(DISPLAY_NAME_KEY)].click()
g = world.css_find("div.CodeMirror.CodeMirror-focused > div > textarea")
display_name = get_display_name_value() display_name = get_display_name_value()
for count in range(len(display_name)): for count in range(len(display_name)):
e._element.send_keys(Keys.TAB, Keys.END, Keys.BACK_SPACE) g._element.send_keys(Keys.END, Keys.BACK_SPACE)
# Must delete "" before typing the JSON value # Must delete "" before typing the JSON value
e._element.send_keys(Keys.TAB, Keys.END, Keys.BACK_SPACE, Keys.BACK_SPACE, new_value) g._element.send_keys(Keys.END, Keys.BACK_SPACE, Keys.BACK_SPACE, new_value)
press_the_notification_button(step, "Save") press_the_notification_button(step, "Save")
...@@ -161,3 +161,11 @@ def i_created_a_video_component(step): ...@@ -161,3 +161,11 @@ def i_created_a_video_component(step):
'i4x://edx/templates/video/default', 'i4x://edx/templates/video/default',
'.xmodule_VideoModule' '.xmodule_VideoModule'
) )
@step('I have clicked the new unit button')
def open_new_unit(step):
step.given('I have opened a new course section in Studio')
step.given('I have added a new subsection')
step.given('I expand the first section')
world.css_click('a.new-unit-item')
...@@ -14,20 +14,27 @@ def create_component_instance(step, component_button_css, instance_id, expected_ ...@@ -14,20 +14,27 @@ def create_component_instance(step, component_button_css, instance_id, expected_
@world.absorb @world.absorb
def click_new_component_button(step, component_button_css): def click_new_component_button(step, component_button_css):
step.given('I have opened a new course section in Studio') step.given('I have clicked the new unit button')
step.given('I have added a new subsection')
step.given('I expand the first section')
world.css_click('a.new-unit-item')
world.css_click(component_button_css) world.css_click(component_button_css)
@world.absorb @world.absorb
def click_component_from_menu(instance_id, expected_css): def click_component_from_menu(instance_id, expected_css):
"""
Creates a component from `instance_id`. For components with more
than one template, clicks on `elem_css` to create the new
component. Components with only one template are created as soon
as the user clicks the appropriate button, so we assert that the
expected component is present.
"""
elem_css = "a[data-location='%s']" % instance_id elem_css = "a[data-location='%s']" % instance_id
assert_equal(1, len(world.css_find(elem_css))) elements = world.css_find(elem_css)
world.css_click(elem_css) assert(len(elements) == 1)
if elements[0]['id'] == instance_id: # If this is a component with multiple templates
world.css_click(elem_css)
assert_equal(1, len(world.css_find(expected_css))) assert_equal(1, len(world.css_find(expected_css)))
@world.absorb @world.absorb
def edit_component_and_select_settings(): def edit_component_and_select_settings():
world.css_click('a.edit-button') world.css_click('a.edit-button')
......
...@@ -11,3 +11,7 @@ Feature: Discussion Component Editor ...@@ -11,3 +11,7 @@ Feature: Discussion Component Editor
And I edit and select Settings And I edit and select Settings
Then I can modify the display name Then I can modify the display name
And my display name change is persisted on save And my display name change is persisted on save
Scenario: Creating a discussion takes a single click
Given I have clicked the new unit button
Then creating a discussion takes a single click
...@@ -21,3 +21,10 @@ def i_see_only_the_settings_and_values(step): ...@@ -21,3 +21,10 @@ def i_see_only_the_settings_and_values(step):
['Display Name', "Discussion Tag", True], ['Display Name', "Discussion Tag", True],
['Subcategory', "Topic-Level Student-Visible Label", True] ['Subcategory', "Topic-Level Student-Visible Label", True]
]) ])
@step('creating a discussion takes a single click')
def discussion_takes_a_single_click(step):
assert(not world.is_css_present('.xmodule_DiscussionModule'))
world.css_click("a[data-location='i4x://edx/templates/discussion/Discussion_Tag']")
assert(world.is_css_present('.xmodule_DiscussionModule'))
...@@ -4,3 +4,7 @@ Feature: Video Component ...@@ -4,3 +4,7 @@ Feature: Video Component
Scenario: Autoplay is disabled in Studio Scenario: Autoplay is disabled in Studio
Given I have created a Video component Given I have created a Video component
Then when I view the video it does not have autoplay enabled Then when I view the video it does not have autoplay enabled
Scenario: Creating a video takes a single click
Given I have clicked the new unit button
Then creating a video takes a single click
...@@ -9,3 +9,10 @@ from lettuce import world, step ...@@ -9,3 +9,10 @@ from lettuce import world, step
def does_not_autoplay(step): def does_not_autoplay(step):
assert world.css_find('.video')[0]['data-autoplay'] == 'False' assert world.css_find('.video')[0]['data-autoplay'] == 'False'
assert world.css_find('.video_control')[0].has_class('play') assert world.css_find('.video_control')[0].has_class('play')
@step('creating a video takes a single click')
def video_takes_a_single_click(step):
assert(not world.is_css_present('.xmodule_VideoModule'))
world.css_click("a[data-location='i4x://edx/templates/video/default']")
assert(world.is_css_present('.xmodule_VideoModule'))
...@@ -77,14 +77,25 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -77,14 +77,25 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
self.client = Client() self.client = Client()
self.client.login(username=uname, password=password) self.client.login(username=uname, password=password)
def test_advanced_components_in_edit_unit(self): def check_components_on_page(self, component_types, expected_types):
"""
Ensure that the right types end up on the page.
component_types is the list of advanced components.
expected_types is the list of elements that should appear on the page.
expected_types and component_types should be similar, but not
exactly the same -- for example, 'videoalpha' in
component_types should cause 'Video Alpha' to be present.
"""
store = modulestore('direct') store = modulestore('direct')
import_from_xml(store, 'common/test/data/', ['simple']) import_from_xml(store, 'common/test/data/', ['simple'])
course = store.get_item(Location(['i4x', 'edX', 'simple', course = store.get_item(Location(['i4x', 'edX', 'simple',
'course', '2012_Fall', None]), depth=None) 'course', '2012_Fall', None]), depth=None)
course.advanced_modules = ADVANCED_COMPONENT_TYPES course.advanced_modules = component_types
store.update_metadata(course.location, own_metadata(course)) store.update_metadata(course.location, own_metadata(course))
...@@ -94,13 +105,20 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase): ...@@ -94,13 +105,20 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
resp = self.client.get(reverse('edit_unit', kwargs={'location': descriptor.location.url()})) resp = self.client.get(reverse('edit_unit', kwargs={'location': descriptor.location.url()}))
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
for expected in expected_types:
self.assertIn(expected, resp.content)
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 # This could be made better, but for now let's just assert that we see the advanced modules mentioned in the page
# response HTML # response HTML
self.assertIn('Video Alpha', resp.content) self.check_components_on_page(ADVANCED_COMPONENT_TYPES, ['Video Alpha',
self.assertIn('Word cloud', resp.content) 'Word cloud',
self.assertIn('Annotation', resp.content) 'Annotation',
self.assertIn('Open Ended Response', resp.content) 'Open Ended Response',
self.assertIn('Peer Grading Interface', resp.content) 'Peer Grading Interface'])
def test_advanced_components_require_two_clicks(self):
self.check_components_on_page(['videoalpha'], ['Video Alpha'])
def check_edit_unit(self, test_course_name): def check_edit_unit(self, test_course_name):
import_from_xml(modulestore('direct'), 'common/test/data/', [test_course_name]) import_from_xml(modulestore('direct'), 'common/test/data/', [test_course_name])
......
...@@ -91,6 +91,12 @@ CACHES = ENV_TOKENS['CACHES'] ...@@ -91,6 +91,12 @@ CACHES = ENV_TOKENS['CACHES']
SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN') SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN')
# allow for environments to specify what cookie name our login subsystem should use
# this is to fix a bug regarding simultaneous logins between edx.org and edge.edx.org which can
# happen with some browsers (e.g. Firefox)
if ENV_TOKENS.get('SESSION_COOKIE_NAME', None):
SESSION_COOKIE_NAME = ENV_TOKENS.get('SESSION_COOKIE_NAME')
#Email overrides #Email overrides
DEFAULT_FROM_EMAIL = ENV_TOKENS.get('DEFAULT_FROM_EMAIL', DEFAULT_FROM_EMAIL) DEFAULT_FROM_EMAIL = ENV_TOKENS.get('DEFAULT_FROM_EMAIL', DEFAULT_FROM_EMAIL)
DEFAULT_FEEDBACK_EMAIL = ENV_TOKENS.get('DEFAULT_FEEDBACK_EMAIL', DEFAULT_FEEDBACK_EMAIL) DEFAULT_FEEDBACK_EMAIL = ENV_TOKENS.get('DEFAULT_FEEDBACK_EMAIL', DEFAULT_FEEDBACK_EMAIL)
......
class CMS.Views.UnitEdit extends Backbone.View class CMS.Views.UnitEdit extends Backbone.View
events: events:
'click .new-component .new-component-type a': 'showComponentTemplates' 'click .new-component .new-component-type a.multiple-templates': 'showComponentTemplates'
'click .new-component .new-component-type a.single-template': 'saveNewComponent'
'click .new-component .cancel-button': 'closeNewComponent' 'click .new-component .cancel-button': 'closeNewComponent'
'click .new-component-templates .new-component-template a': 'saveNewComponent' 'click .new-component-templates .new-component-template a': 'saveNewComponent'
'click .new-component-templates .cancel-button': 'closeNewComponent' 'click .new-component-templates .cancel-button': 'closeNewComponent'
......
...@@ -53,9 +53,15 @@ ...@@ -53,9 +53,15 @@
<div class="new-component"> <div class="new-component">
<h5>Add New Component</h5> <h5>Add New Component</h5>
<ul class="new-component-type"> <ul class="new-component-type">
% for type in sorted(component_templates.keys()): % for type, templates in sorted(component_templates.items()):
<li> <li>
<a href="#" data-type="${type}"> % if type == 'advanced' or len(templates) > 1:
<a href="#" class="multiple-templates" data-type="${type}">
% else:
% for _, location, _ in templates:
<a href="#" class="single-template" data-type="${type}" data-location="${location}">
% endfor
% endif
<span class="large-template-icon large-${type}-icon"></span> <span class="large-template-icon large-${type}-icon"></span>
<span class="name">${type}</span> <span class="name">${type}</span>
</a> </a>
...@@ -64,50 +70,52 @@ ...@@ -64,50 +70,52 @@
</ul> </ul>
</div> </div>
% for type, templates in sorted(component_templates.items()): % for type, templates in sorted(component_templates.items()):
% if len(templates) > 1 or type == 'advanced':
<div class="new-component-templates new-component-${type}"> <div class="new-component-templates new-component-${type}">
% if type == "problem": % if type == "problem":
<div class="tab-group tabs"> <div class="tab-group tabs">
<ul class="problem-type-tabs nav-tabs"> <ul class="problem-type-tabs nav-tabs">
<li class="current"> <li class="current">
<a class="link-tab" href="#tab1">Common Problem Types</a> <a class="link-tab" href="#tab1">Common Problem Types</a>
</li> </li>
<li> <li>
<a class="link-tab" href="#tab2">Advanced</a> <a class="link-tab" href="#tab2">Advanced</a>
</li> </li>
</ul>
% endif
<div class="tab current" id="tab1">
<ul class="new-component-template">
% for name, location, has_markdown in templates:
% if has_markdown or type != "problem":
<li class="editor-md">
<a href="#" id="${location}" data-location="${location}">
<span class="name"> ${name}</span>
</a>
</li>
% endif
%endfor
</ul>
</div>
% if type == "problem":
<div class="tab" id="tab2">
<ul class="new-component-template">
% for name, location, has_markdown in templates:
% if not has_markdown:
<li class="editor-manual">
<a href="#" id="${location}" data-location="${location}">
<span class="name"> ${name}</span>
</a>
</li>
% endif
% endfor
</ul> </ul>
</div> % endif
</div> <div class="tab current" id="tab1">
% endif <ul class="new-component-template">
<a href="#" class="cancel-button">Cancel</a> % for name, location, has_markdown in templates:
</div> % if has_markdown or type != "problem":
<li class="editor-md">
<a href="#" id="${location}" data-location="${location}">
<span class="name"> ${name}</span>
</a>
</li>
% endif
%endfor
</ul>
</div>
% if type == "problem":
<div class="tab" id="tab2">
<ul class="new-component-template">
% for name, location, has_markdown in templates:
% if not has_markdown:
<li class="editor-manual">
<a href="#" id="${location}" data-location="${location}">
<span class="name"> ${name}</span>
</a>
</li>
% endif
% endfor
</ul>
</div>
</div>
% endif
<a href="#" class="cancel-button">Cancel</a>
</div>
% endif
% endfor % endfor
</li> </li>
</ol> </ol>
......
...@@ -44,7 +44,11 @@ class MakoLoader(object): ...@@ -44,7 +44,11 @@ class MakoLoader(object):
if source.startswith("## mako\n"): if source.startswith("## mako\n"):
# This is a mako template # This is a mako template
template = Template(filename=file_path, module_directory=self.module_directory, uri=template_name) template = Template(filename=file_path,
module_directory=self.module_directory,
input_encoding='utf-8',
output_encoding='utf-8',
uri=template_name)
return template, None return template, None
else: else:
# This is a regular template # This is a regular template
......
...@@ -14,6 +14,7 @@ from django.core.management.base import NoArgsCommand ...@@ -14,6 +14,7 @@ from django.core.management.base import NoArgsCommand
from django.conf import settings from django.conf import settings
from mako.template import Template from mako.template import Template
import textwrap
class Command(NoArgsCommand): class Command(NoArgsCommand):
""" """
...@@ -61,5 +62,12 @@ class Command(NoArgsCommand): ...@@ -61,5 +62,12 @@ class Command(NoArgsCommand):
result in `outfile`. result in `outfile`.
""" """
with open(outfile, "w") as _outfile: with open(outfile, "w") as _outfile:
_outfile.write(textwrap.dedent("""\
/*
* This file is dynamically generated and ignored by Git.
* DO NOT MAKE CHANGES HERE. Instead, go edit its template:
* %s
*/
""" % infile))
_outfile.write(Template(filename=str(infile)).render(env=self.__context())) _outfile.write(Template(filename=str(infile)).render(env=self.__context()))
...@@ -87,8 +87,8 @@ def reset_data(scenario): ...@@ -87,8 +87,8 @@ def reset_data(scenario):
LOGGER.debug("Flushing the test database...") LOGGER.debug("Flushing the test database...")
call_command('flush', interactive=False) call_command('flush', interactive=False)
# Uncomment below to trigger a screenshot on error
@after.each_scenario # @after.each_scenario
def screenshot_on_error(scenario): def screenshot_on_error(scenario):
""" """
Save a screenshot to help with debugging. Save a screenshot to help with debugging.
......
...@@ -121,7 +121,7 @@ class ErrorDescriptor(ErrorFields, JSONEditingDescriptor): ...@@ -121,7 +121,7 @@ class ErrorDescriptor(ErrorFields, JSONEditingDescriptor):
def from_descriptor(cls, descriptor, error_msg='Error not available'): def from_descriptor(cls, descriptor, error_msg='Error not available'):
return cls._construct( return cls._construct(
descriptor.system, descriptor.system,
descriptor._model_data, str(descriptor),
error_msg, error_msg,
location=descriptor.location, location=descriptor.location,
) )
......
...@@ -32,6 +32,8 @@ class HtmlModule(HtmlFields, XModule): ...@@ -32,6 +32,8 @@ class HtmlModule(HtmlFields, XModule):
css = {'scss': [resource_string(__name__, 'css/html/display.scss')]} css = {'scss': [resource_string(__name__, 'css/html/display.scss')]}
def get_html(self): def get_html(self):
if self.system.anonymous_student_id:
return self.data.replace("%%USER_ID%%", self.system.anonymous_student_id)
return self.data return self.data
......
...@@ -4,6 +4,9 @@ Tests for ErrorModule and NonStaffErrorModule ...@@ -4,6 +4,9 @@ Tests for ErrorModule and NonStaffErrorModule
import unittest import unittest
from xmodule.tests import test_system from xmodule.tests import test_system
import xmodule.error_module as error_module import xmodule.error_module as error_module
from xmodule.modulestore import Location
from xmodule.x_module import XModuleDescriptor
from mock import MagicMock
class TestErrorModule(unittest.TestCase): class TestErrorModule(unittest.TestCase):
...@@ -14,22 +17,33 @@ class TestErrorModule(unittest.TestCase): ...@@ -14,22 +17,33 @@ class TestErrorModule(unittest.TestCase):
self.system = test_system() self.system = test_system()
self.org = "org" self.org = "org"
self.course = "course" self.course = "course"
self.fake_xml = "<problem />" self.location = Location(['i4x', self.org, self.course, None, None])
self.valid_xml = "<problem />"
self.broken_xml = "<problem>" self.broken_xml = "<problem>"
self.error_msg = "Error" self.error_msg = "Error"
def test_error_module_create(self): def test_error_module_xml_rendering(self):
descriptor = error_module.ErrorDescriptor.from_xml( descriptor = error_module.ErrorDescriptor.from_xml(
self.fake_xml, self.system, self.org, self.course) self.valid_xml, self.system, self.org, self.course, self.error_msg)
self.assertTrue(isinstance(descriptor, error_module.ErrorDescriptor)) self.assertTrue(isinstance(descriptor, error_module.ErrorDescriptor))
def test_error_module_rendering(self):
descriptor = error_module.ErrorDescriptor.from_xml(
self.fake_xml, self.system, self.org, self.course, self.error_msg)
module = descriptor.xmodule(self.system) module = descriptor.xmodule(self.system)
rendered_html = module.get_html() rendered_html = module.get_html()
self.assertIn(self.error_msg, rendered_html) self.assertIn(self.error_msg, rendered_html)
self.assertIn(self.fake_xml, rendered_html) self.assertIn(self.valid_xml, rendered_html)
def test_error_module_from_descriptor(self):
descriptor = MagicMock([XModuleDescriptor],
system=self.system,
location=self.location,
_model_data=self.valid_xml)
error_descriptor = error_module.ErrorDescriptor.from_descriptor(
descriptor, self.error_msg)
self.assertTrue(isinstance(error_descriptor, error_module.ErrorDescriptor))
module = error_descriptor.xmodule(self.system)
rendered_html = module.get_html()
self.assertIn(self.error_msg, rendered_html)
self.assertIn(str(descriptor), rendered_html)
class TestNonStaffErrorModule(TestErrorModule): class TestNonStaffErrorModule(TestErrorModule):
...@@ -39,13 +53,27 @@ class TestNonStaffErrorModule(TestErrorModule): ...@@ -39,13 +53,27 @@ class TestNonStaffErrorModule(TestErrorModule):
def test_non_staff_error_module_create(self): def test_non_staff_error_module_create(self):
descriptor = error_module.NonStaffErrorDescriptor.from_xml( descriptor = error_module.NonStaffErrorDescriptor.from_xml(
self.fake_xml, self.system, self.org, self.course) self.valid_xml, self.system, self.org, self.course)
self.assertTrue(isinstance(descriptor, error_module.NonStaffErrorDescriptor)) self.assertTrue(isinstance(descriptor, error_module.NonStaffErrorDescriptor))
def test_non_staff_error_module_rendering(self): def test_from_xml_render(self):
descriptor = error_module.NonStaffErrorDescriptor.from_xml( descriptor = error_module.NonStaffErrorDescriptor.from_xml(
self.fake_xml, self.system, self.org, self.course) self.valid_xml, self.system, self.org, self.course)
module = descriptor.xmodule(self.system) module = descriptor.xmodule(self.system)
rendered_html = module.get_html() rendered_html = module.get_html()
self.assertNotIn(self.error_msg, rendered_html) self.assertNotIn(self.error_msg, rendered_html)
self.assertNotIn(self.fake_xml, rendered_html) self.assertNotIn(self.valid_xml, rendered_html)
def test_error_module_from_descriptor(self):
descriptor = MagicMock([XModuleDescriptor],
system=self.system,
location=self.location,
_model_data=self.valid_xml)
error_descriptor = error_module.NonStaffErrorDescriptor.from_descriptor(
descriptor, self.error_msg)
self.assertTrue(isinstance(error_descriptor, error_module.ErrorDescriptor))
module = error_descriptor.xmodule(self.system)
rendered_html = module.get_html()
self.assertNotIn(self.error_msg, rendered_html)
self.assertNotIn(str(descriptor), rendered_html)
import unittest
from mock import Mock
from xmodule.html_module import HtmlModule
from xmodule.modulestore import Location
from . import test_system
class HtmlModuleSubstitutionTestCase(unittest.TestCase):
location = Location(["i4x", "edX", "toy", "html", "simple_html"])
descriptor = Mock()
def test_substitution_works(self):
sample_xml = '''%%USER_ID%%'''
module_data = {'data': sample_xml}
module_system = test_system()
module = HtmlModule(module_system, self.location, self.descriptor, module_data)
self.assertEqual(module.get_html(), str(module_system.anonymous_student_id))
def test_substitution_without_magic_string(self):
sample_xml = '''
<html>
<p>Hi USER_ID!11!</p>
</html>
'''
module_data = {'data': sample_xml}
module = HtmlModule(test_system(), self.location, self.descriptor, module_data)
self.assertEqual(module.get_html(), sample_xml)
def test_substitution_without_anonymous_student_id(self):
sample_xml = '''%%USER_ID%%'''
module_data = {'data': sample_xml}
module_system = test_system()
module_system.anonymous_student_id = None
module = HtmlModule(module_system, self.location, self.descriptor, module_data)
self.assertEqual(module.get_html(), sample_xml)
...@@ -335,9 +335,8 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours ...@@ -335,9 +335,8 @@ def get_module_for_descriptor(user, request, descriptor, model_data_cache, cours
else: else:
err_descriptor_class = NonStaffErrorDescriptor err_descriptor_class = NonStaffErrorDescriptor
err_descriptor = err_descriptor_class.from_xml( err_descriptor = err_descriptor_class.from_descriptor(
str(descriptor), descriptor.system, descriptor,
org=descriptor.location.org, course=descriptor.location.course,
error_msg=exc_info_to_str(sys.exc_info()) error_msg=exc_info_to_str(sys.exc_info())
) )
......
...@@ -102,6 +102,12 @@ with open(ENV_ROOT / CONFIG_PREFIX + "env.json") as env_file: ...@@ -102,6 +102,12 @@ with open(ENV_ROOT / CONFIG_PREFIX + "env.json") as env_file:
SITE_NAME = ENV_TOKENS['SITE_NAME'] SITE_NAME = ENV_TOKENS['SITE_NAME']
SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN') SESSION_COOKIE_DOMAIN = ENV_TOKENS.get('SESSION_COOKIE_DOMAIN')
# allow for environments to specify what cookie name our login subsystem should use
# this is to fix a bug regarding simultaneous logins between edx.org and edge.edx.org which can
# happen with some browsers (e.g. Firefox)
if ENV_TOKENS.get('SESSION_COOKIE_NAME', None):
SESSION_COOKIE_NAME = ENV_TOKENS.get('SESSION_COOKIE_NAME')
BOOK_URL = ENV_TOKENS['BOOK_URL'] BOOK_URL = ENV_TOKENS['BOOK_URL']
MEDIA_URL = ENV_TOKENS['MEDIA_URL'] MEDIA_URL = ENV_TOKENS['MEDIA_URL']
LOG_DIR = ENV_TOKENS['LOG_DIR'] LOG_DIR = ENV_TOKENS['LOG_DIR']
......
...@@ -62,7 +62,8 @@ nav.course-material { ...@@ -62,7 +62,8 @@ nav.course-material {
header.global.slim { header.global.slim {
@include box-shadow(0 1px 2px rgba(0, 0, 0, .1)); @include box-shadow(0 1px 2px rgba(0, 0, 0, .1));
height: 50px; height: auto;
padding: 5px 0 10px 0;
border-bottom: 1px solid $outer-border-color; border-bottom: 1px solid $outer-border-color;
background: $white; background: $white;
...@@ -106,16 +107,15 @@ header.global.slim { ...@@ -106,16 +107,15 @@ header.global.slim {
padding-top: 5px; padding-top: 5px;
} }
h1.logo { h1.logo {
margin-left: 13px; margin: 0 10px 0 13px;
margin-right: 20px;
padding-right: 20px; padding-right: 20px;
&:before { &:before {
@extend .faded-vertical-divider; @extend .faded-vertical-divider;
content: ""; content: "";
display: block; display: block;
height: 40px; height: 35px;
position: absolute; position: absolute;
right: 3px; right: 3px;
top: 0; top: 0;
...@@ -126,12 +126,16 @@ header.global.slim { ...@@ -126,12 +126,16 @@ header.global.slim {
@extend .faded-vertical-divider-light; @extend .faded-vertical-divider-light;
content: ""; content: "";
display: block; display: block;
height: 40px; height: 35px;
position: absolute; position: absolute;
right: 0px; right: 0;
top: 0; top: 0;
width: 1px; width: 1px;
} }
img {
height: 30px;
}
} }
.nav-global { .nav-global {
...@@ -147,6 +151,7 @@ header.global.slim { ...@@ -147,6 +151,7 @@ header.global.slim {
color: #777; color: #777;
letter-spacing: 0; letter-spacing: 0;
margin-top: 9px; margin-top: 9px;
margin-bottom: 0;
text-transform: none; text-transform: none;
text-shadow: 0 1px 0 #fff; text-shadow: 0 1px 0 #fff;
white-space: nowrap; white-space: nowrap;
...@@ -168,4 +173,4 @@ header.global.slim { ...@@ -168,4 +173,4 @@ header.global.slim {
font-weight: bold; font-weight: bold;
letter-spacing: 0; letter-spacing: 0;
} }
} }
\ No newline at end of file
...@@ -39,7 +39,7 @@ site_status_msg = get_site_status_msg(course_id) ...@@ -39,7 +39,7 @@ site_status_msg = get_site_status_msg(course_id)
<h1 class="logo"> <h1 class="logo">
<a href="${marketing_link('ROOT')}"> <a href="${marketing_link('ROOT')}">
<img src="${static.url(branding.get_logo_url(request.META.get('HTTP_HOST')))}"/> <img src="${static.url(branding.get_logo_url(request.META.get('HTTP_HOST')))}" alt="edX home" />
</a></h1> </a></h1>
% if course: % if course:
......
...@@ -27,10 +27,11 @@ def coffee_cmd(watch=false, debug=false) ...@@ -27,10 +27,11 @@ def coffee_cmd(watch=false, debug=false)
# #
# Ref: https://github.com/joyent/node/issues/2479 # Ref: https://github.com/joyent/node/issues/2479
# #
# Instead, watch 50 files per process in parallel # Rather than watching all of the directories in one command
# watch each static files subdirectory separately
cmds = [] cmds = []
Dir['*/static/**/*.coffee'].each_slice(50) do |coffee_files| ['lms/static/coffee', 'cms/static/coffee', 'common/static/coffee', 'common/static/xmodule'].each do |coffee_folder|
cmds << "node_modules/.bin/coffee --watch --compile #{coffee_files.join(' ')}" cmds << "node_modules/.bin/coffee --watch --compile #{coffee_folder}"
end end
cmds cmds
else else
...@@ -119,12 +120,15 @@ namespace :assets do ...@@ -119,12 +120,15 @@ namespace :assets do
namespace :sass do namespace :sass do
# In watch mode, sass doesn't immediately compile out of date files, # In watch mode, sass doesn't immediately compile out of date files,
# so force a recompile first # so force a recompile first
task :_watch => 'assets:sass:debug' # Also force xmodule files to be generated before we start watching anything
task :_watch => ['assets:sass:debug', 'assets:xmodule']
multitask :debug => 'assets:xmodule:debug' multitask :debug => 'assets:xmodule:debug'
end end
multitask :coffee => 'assets:xmodule' multitask :coffee => 'assets:xmodule'
namespace :coffee do namespace :coffee do
# Force xmodule files to be generated before we start watching anything
task :_watch => 'assets:xmodule'
multitask :debug => 'assets:xmodule:debug' multitask :debug => 'assets:xmodule:debug'
end end
end end
......
numpy==1.6.2 numpy==1.6.2
networkx==1.7
sympy==0.7.1
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment