1. import json
    
  2. import uuid
    
  3. 
    
  4. from django.core.serializers.json import DjangoJSONEncoder
    
  5. from django.forms import (
    
  6.     CharField,
    
  7.     Form,
    
  8.     JSONField,
    
  9.     Textarea,
    
  10.     TextInput,
    
  11.     ValidationError,
    
  12. )
    
  13. from django.test import SimpleTestCase
    
  14. 
    
  15. 
    
  16. class JSONFieldTest(SimpleTestCase):
    
  17.     def test_valid(self):
    
  18.         field = JSONField()
    
  19.         value = field.clean('{"a": "b"}')
    
  20.         self.assertEqual(value, {"a": "b"})
    
  21. 
    
  22.     def test_valid_empty(self):
    
  23.         field = JSONField(required=False)
    
  24.         self.assertIsNone(field.clean(""))
    
  25.         self.assertIsNone(field.clean(None))
    
  26. 
    
  27.     def test_invalid(self):
    
  28.         field = JSONField()
    
  29.         with self.assertRaisesMessage(ValidationError, "Enter a valid JSON."):
    
  30.             field.clean("{some badly formed: json}")
    
  31. 
    
  32.     def test_prepare_value(self):
    
  33.         field = JSONField()
    
  34.         self.assertEqual(field.prepare_value({"a": "b"}), '{"a": "b"}')
    
  35.         self.assertEqual(field.prepare_value(None), "null")
    
  36.         self.assertEqual(field.prepare_value("foo"), '"foo"')
    
  37.         self.assertEqual(field.prepare_value("你好,世界"), '"你好,世界"')
    
  38.         self.assertEqual(field.prepare_value({"a": "😀🐱"}), '{"a": "😀🐱"}')
    
  39.         self.assertEqual(
    
  40.             field.prepare_value(["你好,世界", "jaźń"]),
    
  41.             '["你好,世界", "jaźń"]',
    
  42.         )
    
  43. 
    
  44.     def test_widget(self):
    
  45.         field = JSONField()
    
  46.         self.assertIsInstance(field.widget, Textarea)
    
  47. 
    
  48.     def test_custom_widget_kwarg(self):
    
  49.         field = JSONField(widget=TextInput)
    
  50.         self.assertIsInstance(field.widget, TextInput)
    
  51. 
    
  52.     def test_custom_widget_attribute(self):
    
  53.         """The widget can be overridden with an attribute."""
    
  54. 
    
  55.         class CustomJSONField(JSONField):
    
  56.             widget = TextInput
    
  57. 
    
  58.         field = CustomJSONField()
    
  59.         self.assertIsInstance(field.widget, TextInput)
    
  60. 
    
  61.     def test_converted_value(self):
    
  62.         field = JSONField(required=False)
    
  63.         tests = [
    
  64.             '["a", "b", "c"]',
    
  65.             '{"a": 1, "b": 2}',
    
  66.             "1",
    
  67.             "1.5",
    
  68.             '"foo"',
    
  69.             "true",
    
  70.             "false",
    
  71.             "null",
    
  72.         ]
    
  73.         for json_string in tests:
    
  74.             with self.subTest(json_string=json_string):
    
  75.                 val = field.clean(json_string)
    
  76.                 self.assertEqual(field.clean(val), val)
    
  77. 
    
  78.     def test_has_changed(self):
    
  79.         field = JSONField()
    
  80.         self.assertIs(field.has_changed({"a": True}, '{"a": 1}'), True)
    
  81.         self.assertIs(field.has_changed({"a": 1, "b": 2}, '{"b": 2, "a": 1}'), False)
    
  82. 
    
  83.     def test_custom_encoder_decoder(self):
    
  84.         class CustomDecoder(json.JSONDecoder):
    
  85.             def __init__(self, object_hook=None, *args, **kwargs):
    
  86.                 return super().__init__(object_hook=self.as_uuid, *args, **kwargs)
    
  87. 
    
  88.             def as_uuid(self, dct):
    
  89.                 if "uuid" in dct:
    
  90.                     dct["uuid"] = uuid.UUID(dct["uuid"])
    
  91.                 return dct
    
  92. 
    
  93.         value = {"uuid": uuid.UUID("{c141e152-6550-4172-a784-05448d98204b}")}
    
  94.         encoded_value = '{"uuid": "c141e152-6550-4172-a784-05448d98204b"}'
    
  95.         field = JSONField(encoder=DjangoJSONEncoder, decoder=CustomDecoder)
    
  96.         self.assertEqual(field.prepare_value(value), encoded_value)
    
  97.         self.assertEqual(field.clean(encoded_value), value)
    
  98. 
    
  99.     def test_formfield_disabled(self):
    
  100.         class JSONForm(Form):
    
  101.             json_field = JSONField(disabled=True)
    
  102. 
    
  103.         form = JSONForm({"json_field": '["bar"]'}, initial={"json_field": ["foo"]})
    
  104.         self.assertIn("[&quot;foo&quot;]</textarea>", form.as_p())
    
  105. 
    
  106.     def test_redisplay_none_input(self):
    
  107.         class JSONForm(Form):
    
  108.             json_field = JSONField(required=True)
    
  109. 
    
  110.         tests = [
    
  111.             {},
    
  112.             {"json_field": None},
    
  113.         ]
    
  114.         for data in tests:
    
  115.             with self.subTest(data=data):
    
  116.                 form = JSONForm(data)
    
  117.                 self.assertEqual(form["json_field"].value(), "null")
    
  118.                 self.assertIn("null</textarea>", form.as_p())
    
  119.                 self.assertEqual(form.errors["json_field"], ["This field is required."])
    
  120. 
    
  121.     def test_redisplay_wrong_input(self):
    
  122.         """
    
  123.         Displaying a bound form (typically due to invalid input). The form
    
  124.         should not overquote JSONField inputs.
    
  125.         """
    
  126. 
    
  127.         class JSONForm(Form):
    
  128.             name = CharField(max_length=2)
    
  129.             json_field = JSONField()
    
  130. 
    
  131.         # JSONField input is valid, name is too long.
    
  132.         form = JSONForm({"name": "xyz", "json_field": '["foo"]'})
    
  133.         self.assertNotIn("json_field", form.errors)
    
  134.         self.assertIn("[&quot;foo&quot;]</textarea>", form.as_p())
    
  135.         # Invalid JSONField.
    
  136.         form = JSONForm({"name": "xy", "json_field": '{"foo"}'})
    
  137.         self.assertEqual(form.errors["json_field"], ["Enter a valid JSON."])
    
  138.         self.assertIn("{&quot;foo&quot;}</textarea>", form.as_p())