1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
from django.core.exceptions import ValidationError
from django.forms import FloatField, NumberInput
from django.test import SimpleTestCase
from django.test.selenium import SeleniumTestCase
from django.test.utils import override_settings
from django.urls import reverse
from django.utils import formats, translation
from . import FormFieldAssertionsMixin
class FloatFieldTest(FormFieldAssertionsMixin, SimpleTestCase):
def test_floatfield_1(self):
f = FloatField()
self.assertWidgetRendersTo(
f, '<input step="any" type="number" name="f" id="id_f" required>'
)
with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean("")
with self.assertRaisesMessage(ValidationError, "'This field is required.'"):
f.clean(None)
self.assertEqual(1.0, f.clean("1"))
self.assertIsInstance(f.clean("1"), float)
self.assertEqual(23.0, f.clean("23"))
self.assertEqual(3.1400000000000001, f.clean("3.14"))
self.assertEqual(3.1400000000000001, f.clean(3.14))
self.assertEqual(42.0, f.clean(42))
with self.assertRaisesMessage(ValidationError, "'Enter a number.'"):
f.clean("a")
self.assertEqual(1.0, f.clean("1.0 "))
self.assertEqual(1.0, f.clean(" 1.0"))
self.assertEqual(1.0, f.clean(" 1.0 "))
with self.assertRaisesMessage(ValidationError, "'Enter a number.'"):
f.clean("1.0a")
self.assertIsNone(f.max_value)
self.assertIsNone(f.min_value)
with self.assertRaisesMessage(ValidationError, "'Enter a number.'"):
f.clean("Infinity")
with self.assertRaisesMessage(ValidationError, "'Enter a number.'"):
f.clean("NaN")
with self.assertRaisesMessage(ValidationError, "'Enter a number.'"):
f.clean("-Inf")
def test_floatfield_2(self):
f = FloatField(required=False)
self.assertIsNone(f.clean(""))
self.assertIsNone(f.clean(None))
self.assertEqual(1.0, f.clean("1"))
self.assertIsNone(f.max_value)
self.assertIsNone(f.min_value)
def test_floatfield_3(self):
f = FloatField(max_value=1.5, min_value=0.5)
self.assertWidgetRendersTo(
f,
'<input step="any" name="f" min="0.5" max="1.5" type="number" id="id_f" '
"required>",
)
with self.assertRaisesMessage(
ValidationError, "'Ensure this value is less than or equal to 1.5.'"
):
f.clean("1.6")
with self.assertRaisesMessage(
ValidationError, "'Ensure this value is greater than or equal to 0.5.'"
):
f.clean("0.4")
self.assertEqual(1.5, f.clean("1.5"))
self.assertEqual(0.5, f.clean("0.5"))
self.assertEqual(f.max_value, 1.5)
self.assertEqual(f.min_value, 0.5)
def test_floatfield_4(self):
f = FloatField(step_size=0.02)
self.assertWidgetRendersTo(
f,
'<input name="f" step="0.02" type="number" id="id_f" required>',
)
msg = "'Ensure this value is a multiple of step size 0.02.'"
with self.assertRaisesMessage(ValidationError, msg):
f.clean("0.01")
self.assertEqual(2.34, f.clean("2.34"))
self.assertEqual(2.1, f.clean("2.1"))
self.assertEqual(-0.50, f.clean("-.5"))
self.assertEqual(-1.26, f.clean("-1.26"))
self.assertEqual(f.step_size, 0.02)
def test_floatfield_step_size_min_value(self):
f = FloatField(step_size=0.02, min_value=0.01)
msg = (
"Ensure this value is a multiple of step size 0.02, starting from 0.01, "
"e.g. 0.01, 0.03, 0.05, and so on."
)
with self.assertRaisesMessage(ValidationError, msg):
f.clean("0.02")
self.assertEqual(f.clean("2.33"), 2.33)
self.assertEqual(f.clean("0.11"), 0.11)
self.assertEqual(f.step_size, 0.02)
def test_floatfield_widget_attrs(self):
f = FloatField(widget=NumberInput(attrs={"step": 0.01, "max": 1.0, "min": 0.0}))
self.assertWidgetRendersTo(
f,
'<input step="0.01" name="f" min="0.0" max="1.0" type="number" id="id_f" '
"required>",
)
def test_floatfield_localized(self):
"""
A localized FloatField's widget renders to a text input without any
number input specific attributes.
"""
f = FloatField(localize=True)
self.assertWidgetRendersTo(f, '<input id="id_f" name="f" type="text" required>')
def test_floatfield_changed(self):
f = FloatField()
n = 4.35
self.assertFalse(f.has_changed(n, "4.3500"))
with translation.override("fr"):
f = FloatField(localize=True)
localized_n = formats.localize_input(n) # -> '4,35' in French
self.assertFalse(f.has_changed(n, localized_n))
@override_settings(DECIMAL_SEPARATOR=",")
def test_floatfield_support_decimal_separator(self):
with translation.override(None):
f = FloatField(localize=True)
self.assertEqual(f.clean("1001,10"), 1001.10)
self.assertEqual(f.clean("1001.10"), 1001.10)
@override_settings(
DECIMAL_SEPARATOR=",",
USE_THOUSAND_SEPARATOR=True,
THOUSAND_SEPARATOR=".",
)
def test_floatfield_support_thousands_separator(self):
with translation.override(None):
f = FloatField(localize=True)
self.assertEqual(f.clean("1.001,10"), 1001.10)
msg = "'Enter a number.'"
with self.assertRaisesMessage(ValidationError, msg):
f.clean("1,001.1")
@override_settings(ROOT_URLCONF="forms_tests.urls")
class FloatFieldHTMLTest(SeleniumTestCase):
available_apps = ["forms_tests"]
def test_float_field_rendering_passes_client_side_validation(self):
"""
Rendered widget allows non-integer value with the client-side
validation.
"""
from selenium.webdriver.common.by import By
self.selenium.get(self.live_server_url + reverse("form_view"))
number_input = self.selenium.find_element(By.ID, "id_number")
number_input.send_keys("0.5")
is_valid = self.selenium.execute_script(
"return document.getElementById('id_number').checkValidity()"
)
self.assertTrue(is_valid)
|