fix merge conflict with _variables.scss

This commit is contained in:
Your Name
2013-07-29 13:32:54 -04:00
64 changed files with 1861 additions and 562 deletions

View File

@@ -0,0 +1,111 @@
from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User
from util.testing import UrlResetMixin
from mock import patch
from django.core.urlresolvers import reverse, NoReverseMatch
class AutoAuthEnabledTestCase(UrlResetMixin, TestCase):
"""
Tests for the Auto auth view that we have for load testing.
"""
@patch.dict("django.conf.settings.MITX_FEATURES", {"AUTOMATIC_AUTH_FOR_LOAD_TESTING": True})
def setUp(self):
# Patching the settings.MITX_FEATURES['AUTOMATIC_AUTH_FOR_LOAD_TESTING']
# value affects the contents of urls.py,
# so we need to call super.setUp() which reloads urls.py (because
# of the UrlResetMixin)
super(AutoAuthEnabledTestCase, self).setUp()
self.url = '/auto_auth'
self.cms_csrf_url = "signup"
self.lms_csrf_url = "signin_user"
self.client = Client()
def test_create_user(self):
"""
Test that user gets created when visiting the page.
"""
self.client.get(self.url)
qset = User.objects.all()
# assert user was created and is active
self.assertEqual(qset.count(), 1)
user = qset[0]
assert user.is_active
@patch('student.views.random.randint')
def test_create_multiple_users(self, randint):
"""
Test to make sure multiple users are created.
"""
randint.return_value = 1
self.client.get(self.url)
randint.return_value = 2
self.client.get(self.url)
qset = User.objects.all()
# make sure that USER_1 and USER_2 were created
self.assertEqual(qset.count(), 2)
@patch.dict("django.conf.settings.MITX_FEATURES", {"MAX_AUTO_AUTH_USERS": 1})
def test_login_already_created_user(self):
"""
Test that when we have reached the limit for automatic users
a subsequent request results in an already existant one being
logged in.
"""
# auto-generate 1 user (the max)
url = '/auto_auth'
self.client.get(url)
# go to the site again
self.client.get(url)
qset = User.objects.all()
# make sure it is the same user
self.assertEqual(qset.count(), 1)
class AutoAuthDisabledTestCase(UrlResetMixin, TestCase):
"""
Test that the page is inaccessible with default settings
"""
@patch.dict("django.conf.settings.MITX_FEATURES", {"AUTOMATIC_AUTH_FOR_LOAD_TESTING": False})
def setUp(self):
# Patching the settings.MITX_FEATURES['AUTOMATIC_AUTH_FOR_LOAD_TESTING']
# value affects the contents of urls.py,
# so we need to call super.setUp() which reloads urls.py (because
# of the UrlResetMixin)
super(AutoAuthDisabledTestCase, self).setUp()
self.url = '/auto_auth'
self.client = Client()
def test_auto_auth_disabled(self):
"""
Make sure automatic authentication is disabled.
"""
response = self.client.get(self.url)
self.assertEqual(response.status_code, 404)
def test_csrf_enabled(self):
"""
test that when not load testing, csrf protection is on
"""
cms_csrf_url = "signup"
lms_csrf_url = "signin_user"
self.client = Client(enforce_csrf_checks=True)
try:
csrf_protected_url = reverse(cms_csrf_url)
response = self.client.post(csrf_protected_url)
except NoReverseMatch:
csrf_protected_url = reverse(lms_csrf_url)
response = self.client.post(csrf_protected_url)
self.assertEqual(response.status_code, 403)

View File

@@ -19,6 +19,7 @@ from django.core.context_processors import csrf
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
from django.core.validators import validate_email, validate_slug, ValidationError
from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError, transaction
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotAllowed, Http404
from django.shortcuts import redirect
@@ -674,18 +675,20 @@ def create_account(request, post_override=None):
subject = ''.join(subject.splitlines())
message = render_to_string('emails/activation_email.txt', d)
try:
if settings.MITX_FEATURES.get('REROUTE_ACTIVATION_EMAIL'):
dest_addr = settings.MITX_FEATURES['REROUTE_ACTIVATION_EMAIL']
message = ("Activation for %s (%s): %s\n" % (user, user.email, profile.name) +
'-' * 80 + '\n\n' + message)
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [dest_addr], fail_silently=False)
elif not settings.GENERATE_RANDOM_USER_CREDENTIALS:
res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except:
log.warning('Unable to send activation email to user', exc_info=True)
js['value'] = _('Could not send activation e-mail.')
return HttpResponse(json.dumps(js))
# dont send email if we are doing load testing or random user generation for some reason
if not (settings.MITX_FEATURES.get('AUTOMATIC_AUTH_FOR_LOAD_TESTING')):
try:
if settings.MITX_FEATURES.get('REROUTE_ACTIVATION_EMAIL'):
dest_addr = settings.MITX_FEATURES['REROUTE_ACTIVATION_EMAIL']
message = ("Activation for %s (%s): %s\n" % (user, user.email, profile.name) +
'-' * 80 + '\n\n' + message)
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [dest_addr], fail_silently=False)
else:
res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except:
log.warning('Unable to send activation email to user', exc_info=True)
js['value'] = _('Could not send activation e-mail.')
return HttpResponse(json.dumps(js))
# Immediately after a user creates an account, we log them in. They are only
# logged in until they close the browser. They can't log in again until they click
@@ -902,32 +905,51 @@ def create_exam_registration(request, post_override=None):
return HttpResponse(json.dumps(js), mimetype="application/json")
def get_random_post_override():
def auto_auth(request):
"""
Return a dictionary suitable for passing to post_vars of _do_create_account or post_override
of create_account, with random user info.
Automatically logs the user in with a generated random credentials
This view is only accessible when
settings.MITX_SETTINGS['AUTOMATIC_AUTH_FOR_LOAD_TESTING'] is true.
"""
def id_generator(size=6, chars=string.ascii_uppercase + string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for x in range(size))
return {'username': "random_" + id_generator(),
'email': id_generator(size=10, chars=string.ascii_lowercase) + "_dummy_test@mitx.mit.edu",
'password': id_generator(),
'name': (id_generator(size=5, chars=string.ascii_lowercase) + " " +
id_generator(size=7, chars=string.ascii_lowercase)),
'honor_code': u'true',
'terms_of_service': u'true', }
def get_dummy_post_data(username, password):
"""
Return a dictionary suitable for passing to post_vars of _do_create_account or post_override
of create_account, with specified username and password.
"""
return {'username': username,
'email': username + "_dummy_test@mitx.mit.edu",
'password': password,
'name': username + " " + username,
'honor_code': u'true',
'terms_of_service': u'true', }
def create_random_account(create_account_function):
def inner_create_random_account(request):
return create_account_function(request, post_override=get_random_post_override())
# generate random user ceredentials from a small name space (determined by settings)
name_base = 'USER_'
pass_base = 'PASS_'
return inner_create_random_account
max_users = settings.MITX_FEATURES.get('MAX_AUTO_AUTH_USERS', 200)
number = random.randint(1, max_users)
# TODO (vshnayder): do we need GENERATE_RANDOM_USER_CREDENTIALS for anything?
if settings.GENERATE_RANDOM_USER_CREDENTIALS:
create_account = create_random_account(create_account)
username = name_base + str(number)
password = pass_base + str(number)
# if they already are a user, log in
try:
user = User.objects.get(username=username)
user = authenticate(username=username, password=password)
login(request, user)
# else create and activate account info
except ObjectDoesNotExist:
post_override = get_dummy_post_data(username, password)
create_account(request, post_override=post_override)
request.user.is_active = True
request.user.save()
# return empty success
return HttpResponse('')
@ensure_csrf_cookie

View File

@@ -75,7 +75,7 @@ def initial_setup(server):
# If we were unable to get a valid session within the limit of attempts,
# then we cannot run the tests.
if not success:
raise IOError("Could not acquire valid ChromeDriver browser session.")
raise IOError("Could not acquire valid {driver} browser session.".format(driver=browser_driver))
# Set the browser size to 1280x1024
world.browser.driver.set_window_size(1280, 1024)

View File

@@ -216,22 +216,19 @@ def save_the_html(path='/tmp'):
@world.absorb
def click_course_content():
course_content_css = 'li.nav-course-courseware'
if world.browser.is_element_present_by_css(course_content_css):
world.css_click(course_content_css)
world.css_click(course_content_css)
@world.absorb
def click_course_settings():
course_settings_css = 'li.nav-course-settings'
if world.browser.is_element_present_by_css(course_settings_css):
world.css_click(course_settings_css)
world.css_click(course_settings_css)
@world.absorb
def click_tools():
tools_css = 'li.nav-course-tools'
if world.browser.is_element_present_by_css(tools_css):
world.css_click(tools_css)
world.css_click(tools_css)
@world.absorb

View File

@@ -211,6 +211,8 @@ nav.sequence-nav {
@include transition(all .1s $ease-in-out-quart 0s);
white-space: pre;
z-index: 99;
visibility: hidden;
pointer-events: none;
&:empty {
background: none;
@@ -238,6 +240,7 @@ nav.sequence-nav {
display: block;
margin-top: 4px;
opacity: 1.0;
visibility: visible;
}
}
}
@@ -263,6 +266,7 @@ nav.sequence-nav {
border: 1px solid #ccc;
@include linear-gradient(top, #eee, #ddd);
box-shadow: 0 1px 0 rgba(255, 255, 255, .7) inset;
z-index: 1;
&.prev, &.next {
@@ -270,7 +274,7 @@ nav.sequence-nav {
background-position: center;
background-repeat: no-repeat;
display: block;
height: 34px;
height: 100%;
width: 40px;
text-indent: -9999px;
@include transition(all .2s $ease-in-out-quad 0s);

View File

@@ -12,10 +12,14 @@ class DiscussionFields(object):
display_name = String(
display_name="Display Name",
help="Display name for this module",
default="Discussion Tag",
scope=Scope.settings)
data = String(help="XML data for the problem", scope=Scope.content,
default="<discussion></discussion>")
default="Discussion",
scope=Scope.settings
)
data = String(
help="XML data for the problem",
scope=Scope.content,
default="<discussion></discussion>"
)
discussion_category = String(
display_name="Category",
default="Week 1",

View File

@@ -25,7 +25,7 @@ class HtmlFields(object):
scope=Scope.settings,
# it'd be nice to have a useful default but it screws up other things; so,
# use display_name_with_default for those
default="Blank HTML Page"
default="Text"
)
data = String(help="Html contents to display for this module", default=u"", scope=Scope.content)
source_code = String(help="Source code for LaTeX documents. This feature is not well-supported.", scope=Scope.settings)

View File

@@ -225,6 +225,8 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
non_draft_loc = location.replace(revision=None)
metadata_to_inherit = self.cached_metadata.get(non_draft_loc.url(), {})
inherit_metadata(module, metadata_to_inherit)
# decache any computed pending field settings
module.save()
return module
except:
log.warning("Failed to load descriptor", exc_info=True)
@@ -630,6 +632,8 @@ class MongoModuleStore(ModuleStoreBase):
definition_data = {}
dbmodel = self._create_new_model_data(location.category, location, definition_data, metadata)
xmodule = xblock_class(system, dbmodel)
# decache any pending field settings from init
xmodule.save()
return xmodule
def save_xmodule(self, xmodule):

View File

@@ -116,4 +116,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem):
module.previous_version = json_data.get('previous_version')
module.update_version = json_data.get('update_version')
module.definition_locator = self.modulestore.definition_locator(definition)
# decache any pending field settings
module.save()
return module

View File

@@ -146,7 +146,7 @@ class Progress(object):
sending Progress objects to js to limit dependencies.
'''
if progress is None:
return "NA"
return "0"
return progress.ternary_str()
@staticmethod
@@ -157,5 +157,5 @@ class Progress(object):
passing Progress objects to js to limit dependencies.
'''
if progress is None:
return "NA"
return "0"
return str(progress)

View File

@@ -90,15 +90,15 @@ class ProgressTest(unittest.TestCase):
self.assertEqual(Progress.to_js_status_str(self.not_started), "none")
self.assertEqual(Progress.to_js_status_str(self.half_done), "in_progress")
self.assertEqual(Progress.to_js_status_str(self.done), "done")
self.assertEqual(Progress.to_js_status_str(None), "NA")
self.assertEqual(Progress.to_js_status_str(None), "0")
def test_to_js_detail_str(self):
'''Test the Progress.to_js_detail_str() method'''
f = Progress.to_js_detail_str
for p in (self.not_started, self.half_done, self.done):
self.assertEqual(f(p), str(p))
# But None should be encoded as NA
self.assertEqual(f(None), "NA")
# But None should be encoded as 0
self.assertEqual(f(None), "0")
def test_add(self):
'''Test the Progress.add_counts() method'''

View File

@@ -27,11 +27,13 @@ class VideoFields(object):
scope=Scope.settings,
# it'd be nice to have a useful default but it screws up other things; so,
# use display_name_with_default for those
default="Video Title"
default="Video"
)
data = String(help="XML data for the problem",
data = String(
help="XML data for the problem",
default='',
scope=Scope.content)
scope=Scope.content
)
position = Integer(help="Current position in the video", scope=Scope.user_state, default=0)
show_captions = Boolean(help="This controls whether or not captions are shown by default.", display_name="Show Captions", scope=Scope.settings, default=True)
youtube_id_1_0 = String(help="This is the Youtube ID reference for the normal speed video.", display_name="Default Speed", scope=Scope.settings, default="OEoXaMPEzfM")

View File

@@ -537,11 +537,14 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
system: Module system
"""
return self.module_class(
# save any field changes
module = self.module_class(
system,
self,
system.xblock_model_data(self),
)
module.save()
return module
def has_dynamic_children(self):
"""
@@ -613,7 +616,13 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
new_block = system.xblock_from_json(cls, usage_id, json_data)
if parent_xblock is not None:
parent_xblock.children.append(new_block)
children = parent_xblock.children
children.append(new_block)
# trigger setter method by using top level field access
parent_xblock.children = children
# decache pending children field settings (Note, truly persisting at this point would break b/c
# persistence assumes children is a list of ids not actual xblocks)
parent_xblock.save()
return new_block
@classmethod

View File

@@ -91,7 +91,7 @@
}
&.disabled, &[disabled] {
&.disabled, &[disabled], &.is-disabled {
cursor: default;
pointer-events: none;
opacity: 0.5;