From d00e763e471bae46eb7a4c544c84787dab726f3d Mon Sep 17 00:00:00 2001 From: Alex J Burke Date: Mon, 24 Sep 2018 19:50:19 +0200 Subject: [PATCH] Fix the draggable attribute & flesh out enumerated property support. The dragabble attribute was not being correctly recognised - when specified it must have only two values, the strings "true" and "false". Previously it was treated as a boolean attribute, which lead to true being accepted while false did not work at all. Change this so the only acceptable values are the two supported strings. Add some belt and braces when specifying the value in an object spec so you cannot accidentally specify an unsupported value. --- lib/index.js | 51 +++++++++++++++++++++++++++--------------- test/index.spec.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 18 deletions(-) diff --git a/lib/index.js b/lib/index.js index 58348722..ed26a05b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -79,20 +79,14 @@ var enumeratedAttributeValues = { draggable: ['true', 'false'] // defaults to 'auto' }; -function isBooleanAttribute(attrName, attrValue) { - var isSimpleBoolean = /^(?:allowfullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultchecked|defaultmuted|defaultselected|defer|disabled|enabled|formnovalidate|hidden|indeterminate|inert|ismap|itemscope|loop|multiple|muted|nohref|noresize|noshade|novalidate|nowrap|open|pauseonexit|readonly|required|reversed|scoped|seamless|selected|sortable|spellcheck|truespeed|typemustmatch|visible)$/i.test( - attrName - ); - if (isSimpleBoolean) { - return true; - } +var matchSimpleAttribute = /^(?:allowfullscreen|async|autofocus|autoplay|checked|compact|controls|declare|default|defaultchecked|defaultmuted|defaultselected|defer|disabled|enabled|formnovalidate|hidden|indeterminate|inert|ismap|itemscope|loop|multiple|muted|nohref|noresize|noshade|novalidate|nowrap|open|pauseonexit|readonly|required|reversed|scoped|seamless|selected|sortable|spellcheck|truespeed|typemustmatch|visible)$/i; - var attrValueEnumeration = enumeratedAttributeValues[attrName.toLowerCase()]; - if (!attrValueEnumeration) { - return false; - } else { - return attrValueEnumeration.indexOf(attrValue.toLowerCase()) === -1; - } +function isBooleanAttribute(attrName) { + return matchSimpleAttribute.test(attrName); +} + +function isEnumeratedAttribute(attrName) { + return attrName in enumeratedAttributeValues; } function styleStringToObject(str) { @@ -210,7 +204,7 @@ function writeAttributeToMagicPen(output, attributeName, value, isHtml) { } function stringifyAttribute(attributeName, value) { - if (isBooleanAttribute(attributeName)) { + if (isBooleanAttribute(attributeName) || isEnumeratedAttribute(attributeName)) { return attributeName; } else if (attributeName === 'class') { return 'class="' + value.join(' ') + '"'; // FIXME: entitify @@ -1042,13 +1036,34 @@ module.exports = { var expectedAttributeValue = expectedValueByAttributeName[attributeName]; promiseByKey.attributes[attributeName] = expect.promise(function() { - if (expectedAttributeValue === true) { + if (typeof expectedAttributeValue === 'undefined') { return bubbleError(function() { - expect(subject.hasAttribute(attributeName), 'to be true'); + expect(subject.hasAttribute(attributeName), 'to be false'); }); - } else if (typeof expectedAttributeValue === 'undefined') { + } else if (isEnumeratedAttribute(attributeName)) { return bubbleError(function() { - expect(subject.hasAttribute(attributeName), 'to be false'); + var indexOfEnumeratedAttributeValue = enumeratedAttributeValues[attributeName].indexOf(expectedAttributeValue); + if (indexOfEnumeratedAttributeValue === -1) { + expect.withError(function() { + expect(enumeratedAttributeValues[attributeName], 'to contain', expectedAttributeValue); + }, function(e) { + expect.fail({ + message: function(output) { + output + .text('Invalid expected value ') + .appendInspected(expectedAttributeValue) + .text('. Supported values include: ') + .appendInspected(enumeratedAttributeValues[attributeName]); + } + }); + }); + } + + expect(attributeValue, 'to satisfy', expectedAttributeValue); + }); + } else if (expectedAttributeValue === true) { + return bubbleError(function() { + expect(subject.hasAttribute(attributeName), 'to be true'); }); } else if ( attributeName === 'class' && diff --git a/test/index.spec.js b/test/index.spec.js index bd1a4929..6e4b45b1 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -1211,6 +1211,61 @@ describe('unexpected-dom', function() { ); }); + describe('with boolean attributes', function() { + describe('draggable', () => { + it('should allow "true"', function() { + body.innerHTML = '
'; + expect(body, 'to satisfy', { + children: [ + { + attributes: { + draggable: 'true' + } + } + ] + }); + }); + + it('should allow "false"', function() { + body.innerHTML = '
'; + expect(body, 'to satisfy', { + children: [ + { + attributes: { + draggable: 'false' + } + } + ] + }); + }); + + it('should error on mismatch', function() { + body.innerHTML = '
'; + expect( + function() { + expect(body, 'to satisfy', { + children: [ + { + attributes: { + draggable: false + } + } + ] + }) + }, + 'to throw', + 'expected
to satisfy { children: [ { attributes: ... } ] }\n' + + '\n' + + '\n' + + ' \n' + + '' + ); + }); + }); + }); + describe('with a textContent property', function() { it('should succeed', function() { body.innerHTML = '
foobarquux
';