Commit 6e92ddf3 by cahrens

Cannot set String field to a dict anymore!

parent 89d00036
......@@ -28,11 +28,16 @@ Feature: Advanced (manual) course policy
Scenario: Test how multi-line input appears
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 for "discussion_topics"
Then it is displayed as formatted
And I reload the page
Then it is displayed as formatted
Scenario: Test error if value supplied is of the wrong type
Given I am on the Advanced Course Settings page in Studio
When I create a JSON object as a value for "display_name"
Then I get an error on save
Scenario: Test automatic quoting of non-JSON values
Given I am on the Advanced Course Settings page in Studio
When I create a non-JSON value not in quotes
......
......@@ -2,8 +2,7 @@
#pylint: disable=W0621
from lettuce import world, step
from common import *
from nose.tools import assert_false, assert_equal
from nose.tools import assert_false, assert_equal, assert_regexp_matches
"""
http://selenium.googlecode.com/svn/trunk/docs/api/py/webdriver/selenium.webdriver.common.keys.html
......@@ -52,9 +51,9 @@ def edit_the_value_of_a_policy_key_and_save(step):
change_display_name_value(step, '"foo"')
@step('I create a JSON object as a value$')
def create_JSON_object(step):
change_display_name_value(step, '{"key": "value", "key_2": "value_2"}')
@step('I create a JSON object as a value for "(.*)"$')
def create_JSON_object(step, key):
change_value(step, key, '{"key": "value", "key_2": "value_2"}')
@step('I create a non-JSON value not in quotes$')
......@@ -82,7 +81,12 @@ def they_are_alphabetized(step):
@step('it is displayed as formatted$')
def it_is_formatted(step):
assert_policy_entries([DISPLAY_NAME_KEY], ['{\n "key": "value",\n "key_2": "value_2"\n}'])
assert_policy_entries(['discussion_topics'], ['{\n "key": "value",\n "key_2": "value_2"\n}'])
@step('I get an error on save$')
def error_on_save(step):
assert_regexp_matches(world.css_text('#notification-error-description'), 'Incorrect setting format')
@step('it is displayed as a string')
......@@ -124,11 +128,16 @@ def get_display_name_value():
def change_display_name_value(step, new_value):
change_value(step, DISPLAY_NAME_KEY, new_value)
world.css_find(".CodeMirror")[get_index_of(DISPLAY_NAME_KEY)].click()
def change_value(step, key, new_value):
index = get_index_of(key)
world.css_find(".CodeMirror")[index].click()
g = world.css_find("div.CodeMirror.CodeMirror-focused > div > textarea")
display_name = get_display_name_value()
for count in range(len(display_name)):
current_value = world.css_find(VALUE_CSS)[index].value
g._element.send_keys(Keys.CONTROL + Keys.END)
for count in range(len(current_value)):
g._element.send_keys(Keys.END, Keys.BACK_SPACE)
# Must delete "" before typing the JSON value
g._element.send_keys(Keys.END, Keys.BACK_SPACE, Keys.BACK_SPACE, new_value)
......
......@@ -41,7 +41,9 @@ def i_see_five_settings_with_values(step):
@step('I can modify the display name')
def i_can_modify_the_display_name(step):
world.get_setting_entry(DISPLAY_NAME).find_by_css('.setting-input')[0].fill('modified')
# Verifying that the display name can be a string containing a floating point value
# (to confirm that we don't throw an error because it is of the wrong type).
world.get_setting_entry(DISPLAY_NAME).find_by_css('.setting-input')[0].fill('3.4')
verify_modified_display_name()
......@@ -172,7 +174,7 @@ def verify_modified_randomization():
def verify_modified_display_name():
world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, 'modified', True)
world.verify_setting_entry(world.get_setting_entry(DISPLAY_NAME), DISPLAY_NAME, '3.4', True)
def verify_modified_display_name_with_special_chars():
......
......@@ -401,8 +401,11 @@ def course_advanced_updates(request, org, course, name):
request_body.update({'tabs': new_tabs})
#Indicate that tabs should *not* be filtered out of the metadata
filter_tabs = False
try:
response_json = json.dumps(CourseMetadata.update_from_json(location,
request_body,
filter_tabs=filter_tabs))
except (TypeError, ValueError), e:
return HttpResponseBadRequest("Incorrect setting format. " + str(e), content_type="text/plain")
return HttpResponse(response_json, mimetype="application/json")
......@@ -188,6 +188,18 @@ def assertDeserializeEqual(field, expected, arg):
assert_equals(expected, deserialize_field(field, arg))
def assertDeserializeNonString(field):
"""
Asserts input value is returned for None or something that is not a string.
"""
assertDeserializeEqual(field, None, None)
assertDeserializeEqual(field, 3.14, 3.14)
assertDeserializeEqual(field, True, True)
assertDeserializeEqual(field, [10], [10])
assertDeserializeEqual(field, {}, {})
assertDeserializeEqual(field, [], [])
class TestSerializeInteger(unittest.TestCase):
""" Tests serialize/deserialize as related to Integer type. """
......@@ -208,7 +220,7 @@ class TestSerializeInteger(unittest.TestCase):
def test_deserialize_unsupported_types(self):
assertDeserializeEqual(Integer(), '[3]', '[3]')
self.assertRaises(TypeError, deserialize_field, None)
assertDeserializeNonString(Integer())
class FloatTest(unittest.TestCase):
......@@ -235,7 +247,7 @@ class FloatTest(unittest.TestCase):
def test_deserialize_unsupported_types(self):
assertDeserializeEqual(Float(), '[3]', '[3]')
self.assertRaises(TypeError, deserialize_field, None)
assertDeserializeNonString(Float())
class BooleanTest(unittest.TestCase):
......@@ -264,8 +276,7 @@ class BooleanTest(unittest.TestCase):
assertDeserializeEqual(Boolean(), 'fAlse', '"fAlse"')
assertDeserializeEqual(Boolean(), "TruE", '"TruE"')
def test_deserialize_unsupported_types(self):
self.assertRaises(TypeError, deserialize_field, None)
assertDeserializeNonString(Boolean())
class StringTest(unittest.TestCase):
......@@ -286,7 +297,7 @@ class StringTest(unittest.TestCase):
assertDeserializeEqual(String(), 'false', 'false')
assertDeserializeEqual(String(), '2', '2')
assertDeserializeEqual(String(), '[3]', '[3]')
self.assertRaises(TypeError, deserialize_field, None)
assertDeserializeNonString(String())
class AnyTest(unittest.TestCase):
......@@ -307,9 +318,7 @@ class AnyTest(unittest.TestCase):
assertDeserializeEqual(Any(), '[', '[')
assertDeserializeEqual(Any(), False, 'false')
assertDeserializeEqual(Any(), 3.4, '3.4')
def test_deserialize_unsupported_types(self):
self.assertRaises(TypeError, deserialize_field, None)
assertDeserializeNonString(Any())
class ListTest(unittest.TestCase):
......@@ -330,7 +339,7 @@ class ListTest(unittest.TestCase):
assertDeserializeEqual(List(), '3.4', '3.4')
assertDeserializeEqual(List(), 'false', 'false')
assertDeserializeEqual(List(), '2', '2')
self.assertRaises(TypeError, deserialize_field, None)
assertDeserializeNonString(List())
class DateTest(unittest.TestCase):
......@@ -342,9 +351,11 @@ class DateTest(unittest.TestCase):
def test_deserialize(self):
assertDeserializeEqual(Date(), '2012-12-31T23:59:59Z', "2012-12-31T23:59:59Z")
assertDeserializeEqual(Date(), '2012-12-31T23:59:59Z', '"2012-12-31T23:59:59Z"')
assertDeserializeNonString(Date())
class TimedeltaTest(unittest.TestCase):
""" Tests serialize/deserialize as related to Timedelta type. """
def test_serialize(self):
assertSerializeEqual('"1 day 12 hours 59 minutes 59 seconds"',
......@@ -355,3 +366,4 @@ class TimedeltaTest(unittest.TestCase):
'1 day 12 hours 59 minutes 59 seconds')
assertDeserializeEqual(Timedelta(), '1 day 12 hours 59 minutes 59 seconds',
'"1 day 12 hours 59 minutes 59 seconds"')
assertDeserializeNonString(Timedelta())
......@@ -95,23 +95,28 @@ def deserialize_field(field, value):
Note that this is not the same as the value returned by from_json, as model types typically store
their value internally as JSON. By default, this method will return the result of calling json.loads
on the supplied value, unless json.loads throws a TypeError, or the type of the value returned by json.loads
is not supported for this class (see 'is_type_supported'). In either of those cases, this method returns
is not supported for this class (from_json throws an Error). In either of those cases, this method returns
the input value.
"""
try:
deserialized = json.loads(value)
if deserialized is None:
return deserialized
try:
field.from_json(deserialized)
return deserialized
except (ValueError, TypeError):
# Support older serialized forms by simply returning the String representation
# Support older serialized version, which was just a string, not result of json.dumps.
# If the deserialized version cannot be converted to the type (via from_json),
# just return the original value. For example, if a string value of '3.4' was
# stored for a String field (before we started storing the result of json.dumps),
# then it would be deserialized as 3.4, but 3.4 is not supported for a String
# field. Therefore field.from_json(3.4) will throw an Error, and we should
# actually return the original value of '3.4'.
return value
except (ValueError, TypeError):
# Support older serialized version, which was just the String (not the result of json.dumps).
# Support older serialized version.
return value
......
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