Fixed #32556 -- Fixed handling empty string as non-boolean attributes value by assertHTMLEqual().
This commit is contained in:
parent
98abf80cde
commit
41e6b2a3c5
|
@ -9,6 +9,16 @@ from django.utils.regex_helper import _lazy_re_compile
|
||||||
# https://infra.spec.whatwg.org/#ascii-whitespace
|
# https://infra.spec.whatwg.org/#ascii-whitespace
|
||||||
ASCII_WHITESPACE = _lazy_re_compile(r'[\t\n\f\r ]+')
|
ASCII_WHITESPACE = _lazy_re_compile(r'[\t\n\f\r ]+')
|
||||||
|
|
||||||
|
# https://html.spec.whatwg.org/#attributes-3
|
||||||
|
BOOLEAN_ATTRIBUTES = {
|
||||||
|
'allowfullscreen', 'async', 'autofocus', 'autoplay', 'checked', 'controls',
|
||||||
|
'default', 'defer ', 'disabled', 'formnovalidate', 'hidden', 'ismap',
|
||||||
|
'itemscope', 'loop', 'multiple', 'muted', 'nomodule', 'novalidate', 'open',
|
||||||
|
'playsinline', 'readonly', 'required', 'reversed', 'selected',
|
||||||
|
# Attributes for deprecated tags.
|
||||||
|
'truespeed',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def normalize_whitespace(string):
|
def normalize_whitespace(string):
|
||||||
return ASCII_WHITESPACE.sub(' ', string)
|
return ASCII_WHITESPACE.sub(' ', string)
|
||||||
|
@ -23,11 +33,14 @@ def normalize_attributes(attributes):
|
||||||
value = ' '.join(sorted(
|
value = ' '.join(sorted(
|
||||||
value for value in ASCII_WHITESPACE.split(value) if value
|
value for value in ASCII_WHITESPACE.split(value) if value
|
||||||
))
|
))
|
||||||
# Attributes without a value is same as attribute with value that
|
# Boolean attributes without a value is same as attribute with value
|
||||||
# equals the attributes name:
|
# that equals the attributes name. For example:
|
||||||
# <input checked> == <input checked="checked">
|
# <input checked> == <input checked="checked">
|
||||||
if not value or value == name:
|
if name in BOOLEAN_ATTRIBUTES:
|
||||||
value = None
|
if not value or value == name:
|
||||||
|
value = None
|
||||||
|
elif value is None:
|
||||||
|
value = ''
|
||||||
normalized.append((name, value))
|
normalized.append((name, value))
|
||||||
return normalized
|
return normalized
|
||||||
|
|
||||||
|
@ -131,7 +144,7 @@ class Element:
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
output = '<%s' % self.name
|
output = '<%s' % self.name
|
||||||
for key, value in self.attributes:
|
for key, value in self.attributes:
|
||||||
if value:
|
if value is not None:
|
||||||
output += ' %s="%s"' % (key, value)
|
output += ' %s="%s"' % (key, value)
|
||||||
else:
|
else:
|
||||||
output += ' %s' % key
|
output += ' %s' % key
|
||||||
|
|
|
@ -362,6 +362,10 @@ Miscellaneous
|
||||||
``EmailMessage.attach()`` with an invalid ``content`` or ``mimetype``
|
``EmailMessage.attach()`` with an invalid ``content`` or ``mimetype``
|
||||||
arguments now raise ``ValueError`` instead of ``AssertionError``.
|
arguments now raise ``ValueError`` instead of ``AssertionError``.
|
||||||
|
|
||||||
|
* :meth:`~django.test.SimpleTestCase.assertHTMLEqual` no longer considers a
|
||||||
|
non-boolean attribute without a value equal to an attribute with the same
|
||||||
|
name and value.
|
||||||
|
|
||||||
.. _deprecated-features-4.0:
|
.. _deprecated-features-4.0:
|
||||||
|
|
||||||
Features deprecated in 4.0
|
Features deprecated in 4.0
|
||||||
|
|
|
@ -1596,8 +1596,8 @@ your test suite.
|
||||||
closed or the HTML document ends.
|
closed or the HTML document ends.
|
||||||
* Empty tags are equivalent to their self-closing version.
|
* Empty tags are equivalent to their self-closing version.
|
||||||
* The ordering of attributes of an HTML element is not significant.
|
* The ordering of attributes of an HTML element is not significant.
|
||||||
* Attributes without an argument are equal to attributes that equal in
|
* Boolean attributes (like ``checked``) without an argument are equal to
|
||||||
name and value (see the examples).
|
attributes that equal in name and value (see the examples).
|
||||||
* Text, character references, and entity references that refer to the same
|
* Text, character references, and entity references that refer to the same
|
||||||
character are equivalent.
|
character are equivalent.
|
||||||
|
|
||||||
|
@ -1620,6 +1620,12 @@ your test suite.
|
||||||
|
|
||||||
Output in case of error can be customized with the ``msg`` argument.
|
Output in case of error can be customized with the ``msg`` argument.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.0
|
||||||
|
|
||||||
|
In older versions, any attribute (not only boolean attributes) without
|
||||||
|
a value was considered equal to an attribute with the same name and
|
||||||
|
value.
|
||||||
|
|
||||||
.. method:: SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)
|
.. method:: SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)
|
||||||
|
|
||||||
Asserts that the strings ``html1`` and ``html2`` are *not* equal. The
|
Asserts that the strings ``html1`` and ``html2`` are *not* equal. The
|
||||||
|
|
|
@ -715,10 +715,25 @@ class HTMLEqualTests(SimpleTestCase):
|
||||||
self.assertHTMLEqual(html1, html2)
|
self.assertHTMLEqual(html1, html2)
|
||||||
|
|
||||||
def test_boolean_attribute(self):
|
def test_boolean_attribute(self):
|
||||||
html1 = '<input attr>'
|
html1 = '<input checked>'
|
||||||
html2 = '<input attr="">'
|
html2 = '<input checked="">'
|
||||||
|
html3 = '<input checked="checked">'
|
||||||
self.assertHTMLEqual(html1, html2)
|
self.assertHTMLEqual(html1, html2)
|
||||||
self.assertEqual(parse_html(html1), parse_html(html2))
|
self.assertHTMLEqual(html1, html3)
|
||||||
|
self.assertHTMLEqual(html2, html3)
|
||||||
|
self.assertHTMLNotEqual(html1, '<input checked="invalid">')
|
||||||
|
self.assertEqual(str(parse_html(html1)), '<input checked>')
|
||||||
|
self.assertEqual(str(parse_html(html2)), '<input checked>')
|
||||||
|
self.assertEqual(str(parse_html(html3)), '<input checked>')
|
||||||
|
|
||||||
|
def test_non_boolean_attibutes(self):
|
||||||
|
html1 = '<input value>'
|
||||||
|
html2 = '<input value="">'
|
||||||
|
html3 = '<input value="value">'
|
||||||
|
self.assertHTMLEqual(html1, html2)
|
||||||
|
self.assertHTMLNotEqual(html1, html3)
|
||||||
|
self.assertEqual(str(parse_html(html1)), '<input value="">')
|
||||||
|
self.assertEqual(str(parse_html(html2)), '<input value="">')
|
||||||
|
|
||||||
def test_normalize_refs(self):
|
def test_normalize_refs(self):
|
||||||
pairs = [
|
pairs = [
|
||||||
|
|
Loading…
Reference in New Issue