the widget can be overridden with an attribute class customjsonfieldjsonfield widget textinput field customjsonfield self assertisinstancefield widget textinput def testconvertedvalueself field jsonfieldrequiredfalse tests a b c a 1 b 2 1 1 5 foo true false null for jsonstring in tests with self subtestjsonstringjsonstring val field cleanjsonstring self assertequalfield cleanval val def testhaschangedself field jsonfield self assertisfield haschangeda true a 1 true self assertisfield haschangeda 1 b 2 b 2 a 1 false def testcustomencoderdecoderself class customdecoderjson jsondecoder def initself objecthooknone args kwargs return super initobjecthookself asuuid args kwargs def asuuidself dct if uuid in dct dctuuid uuid uuiddctuuid return dct value uuid uuid uuidc141e15265504172a78405448d98204b encodedvalue uuid c141e15265504172a78405448d98204b field jsonfieldencoderdjangojsonencoder decodercustomdecoder self assertequalfield preparevaluevalue encodedvalue self assertequalfield cleanencodedvalue value def testformfielddisabledself class jsonformform jsonfield jsonfielddisabledtrue form jsonformjsonfield bar initialjsonfield foo self assertinquot fooquot textarea form asp def testredisplaynoneinputself class jsonformform jsonfield jsonfieldrequiredtrue tests jsonfield none for data in tests with self subtestdatadata form jsonformdata self assertequalformjsonfield value null self assertinnulltextarea form asp self assertequalform errorsjsonfield this field is required def testredisplaywronginputself class jsonformform name charfieldmaxlength2 jsonfield jsonfield jsonfield input is valid name is too long form jsonformname xyz jsonfield foo self assertnotinjsonfield form errors self assertinquot fooquot textarea form asp invalid jsonfield form jsonformname xy jsonfield foo self assertequalform errorsjsonfield enter a valid json self assertinquot fooquot textarea form asp the widget can be overridden with an attribute displaying a bound form typically due to invalid input the form should not overquote jsonfield inputs jsonfield input is valid name is too long invalid jsonfield
import json
import uuid

from django.core.serializers.json import DjangoJSONEncoder
from django.forms import (
    CharField,
    Form,
    JSONField,
    Textarea,
    TextInput,
    ValidationError,
)
from django.test import SimpleTestCase


class JSONFieldTest(SimpleTestCase):
    def test_valid(self):
        field = JSONField()
        value = field.clean('{"a": "b"}')
        self.assertEqual(value, {"a": "b"})

    def test_valid_empty(self):
        field = JSONField(required=False)
        self.assertIsNone(field.clean(""))
        self.assertIsNone(field.clean(None))

    def test_invalid(self):
        field = JSONField()
        with self.assertRaisesMessage(ValidationError, "Enter a valid JSON."):
            field.clean("{some badly formed: json}")

    def test_prepare_value(self):
        field = JSONField()
        self.assertEqual(field.prepare_value({"a": "b"}), '{"a": "b"}')
        self.assertEqual(field.prepare_value(None), "null")
        self.assertEqual(field.prepare_value("foo"), '"foo"')
        self.assertEqual(field.prepare_value("你好，世界"), '"你好，世界"')
        self.assertEqual(field.prepare_value({"a": "😀🐱"}), '{"a": "😀🐱"}')
        self.assertEqual(
            field.prepare_value(["你好，世界", "jaźń"]),
            '["你好，世界", "jaźń"]',
        )

    def test_widget(self):
        field = JSONField()
        self.assertIsInstance(field.widget, Textarea)

    def test_custom_widget_kwarg(self):
        field = JSONField(widget=TextInput)
        self.assertIsInstance(field.widget, TextInput)

    def test_custom_widget_attribute(self):
        

        class CustomJSONField(JSONField):
            widget = TextInput

        field = CustomJSONField()
        self.assertIsInstance(field.widget, TextInput)

    def test_converted_value(self):
        field = JSONField(required=False)
        tests = [
            '["a", "b", "c"]',
            '{"a": 1, "b": 2}',
            "1",
            "1.5",
            '"foo"',
            "true",
            "false",
            "null",
        ]
        for json_string in tests:
            with self.subTest(json_string=json_string):
                val = field.clean(json_string)
                self.assertEqual(field.clean(val), val)

    def test_has_changed(self):
        field = JSONField()
        self.assertIs(field.has_changed({"a": True}, '{"a": 1}'), True)
        self.assertIs(field.has_changed({"a": 1, "b": 2}, '{"b": 2, "a": 1}'), False)

    def test_custom_encoder_decoder(self):
        class CustomDecoder(json.JSONDecoder):
            def __init__(self, object_hook=None, *args, **kwargs):
                return super().__init__(object_hook=self.as_uuid, *args, **kwargs)

            def as_uuid(self, dct):
                if "uuid" in dct:
                    dct["uuid"] = uuid.UUID(dct["uuid"])
                return dct

        value = {"uuid": uuid.UUID("{c141e152-6550-4172-a784-05448d98204b}")}
        encoded_value = '{"uuid": "c141e152-6550-4172-a784-05448d98204b"}'
        field = JSONField(encoder=DjangoJSONEncoder, decoder=CustomDecoder)
        self.assertEqual(field.prepare_value(value), encoded_value)
        self.assertEqual(field.clean(encoded_value), value)

    def test_formfield_disabled(self):
        class JSONForm(Form):
            json_field = JSONField(disabled=True)

        form = JSONForm({"json_field": '["bar"]'}, initial={"json_field": ["foo"]})
        self.assertIn("[&quot;foo&quot;]</textarea>", form.as_p())

    def test_redisplay_none_input(self):
        class JSONForm(Form):
            json_field = JSONField(required=True)

        tests = [
            {},
            {"json_field": None},
        ]
        for data in tests:
            with self.subTest(data=data):
                form = JSONForm(data)
                self.assertEqual(form["json_field"].value(), "null")
                self.assertIn("null</textarea>", form.as_p())
                self.assertEqual(form.errors["json_field"], ["This field is required."])

    def test_redisplay_wrong_input(self):
        

        class JSONForm(Form):
            name = CharField(max_length=2)
            json_field = JSONField()

        
        form = JSONForm({"name": "xyz", "json_field": '["foo"]'})
        self.assertNotIn("json_field", form.errors)
        self.assertIn("[&quot;foo&quot;]</textarea>", form.as_p())
        
        form = JSONForm({"name": "xy", "json_field": '{"foo"}'})
        self.assertEqual(form.errors["json_field"], ["Enter a valid JSON."])
        self.assertIn("{&quot;foo&quot;}</textarea>", form.as_p())