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