From 0cfbc5577cd9d715e01721fbfcfa15807d6605f7 Mon Sep 17 00:00:00 2001 From: Andreas Lind Date: Fri, 6 Mar 2015 23:10:38 +0100 Subject: [PATCH 1/2] Fixed element stringification. --- lib/index.js | 50 ++++++++++++++++++++++++++++++++---------- test/unexpected-dom.js | 46 ++++++++++++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/lib/index.js b/lib/index.js index d5ce073b..2ec081fe 100644 --- a/lib/index.js +++ b/lib/index.js @@ -21,24 +21,52 @@ function getCanonicalAttributes(elm) { return result; } +function isVoidElement(elementName) { + return /(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)/i.test(elementName); +} + +// From html-minifier +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 attrValueEnumeration = enumeratedAttributeValues[attrName.toLowerCase()]; + if (!attrValueEnumeration) { + return false; + } + else { + return (-1 === attrValueEnumeration.indexOf(attrValue.toLowerCase())); + } +} + + function stringifyElement(elm) { - var openTag = ['<' + elm.nodeName.toLowerCase()]; - var closeTag = ''; + var elementName = elm.nodeName.toLowerCase(); + var str = '<' + elementName; var attrs = getCanonicalAttributes(elm); - openTag.push(Object.keys(attrs).map(function (key) { - if (typeof attrs[key] === 'boolean') { - return key; + Object.keys(attrs).forEach(function (key) { + if (isBooleanAttribute(key)) { + str += ' ' + key; } else { - return key + '="' + attrs[key] + '"'; + str += ' ' + key + '="' + attrs[key] + '"'; } - }).join(' ')); + }); - if (elm.children.length) { - return openTag.join(' ') + '>...' + closeTag; - } else { - return openTag.join(' ') + '/>'; + str += '>'; + if (!isVoidElement(elementName)) { + if (elm.children.length > 0) { + str += '...'; + } + str += ''; } + return str; } module.exports = { diff --git a/test/unexpected-dom.js b/test/unexpected-dom.js index ed31f29d..b4c2e1d4 100644 --- a/test/unexpected-dom.js +++ b/test/unexpected-dom.js @@ -6,6 +6,22 @@ var unexpected = require('unexpected'), var expect = unexpected.clone().installPlugin(unexpectedDom); expect.output.installPlugin(require('magicpen-prism')); +expect.addAssertion('to inspect as [itself]', function (expect, subject, value) { + var originalSubject = subject; + if (typeof subject === 'string') { + subject = jsdom.jsdom(' ' + subject + '').body.firstChild; + } + if (this.flags.itself) { + if (typeof originalSubject === 'string') { + expect(expect.inspect(subject).toString(), 'to equal', originalSubject); + } else { + throw new Error('subject must be given as a string when expected to inspect as itself'); + } + } else { + expect(expect.inspect(subject).toString(), 'to equal', value); + } +}); + describe('unexpected-dom', function () { beforeEach(function (done) { var self = this; @@ -18,6 +34,18 @@ describe('unexpected-dom', function () { }); }); + it('should inspect an attribute-less element correctly', function () { + expect('
', 'to inspect as itself'); + }); + + it('should inspect void elements correctly', function () { + expect('', 'to inspect as itself'); + }); + + it('should inspect simple attributes correctly', function () { + expect('', 'to inspect as itself'); + }); + it('should allow regular assertions defined for the object type to work on an HTMLElement', function () { expect(jsdom.jsdom(' ').firstChild, 'to have properties', { nodeType: 1 }); }); @@ -57,7 +85,7 @@ describe('unexpected-dom', function () { expect(function () { expect(el, 'to only have attributes', 'id'); }, 'to throw exception', function (err) { - expect(err.output.toString(), 'to be', 'expected to only have attributes \'id\''); }); }); @@ -74,7 +102,7 @@ describe('unexpected-dom', function () { expect(function () { expect(el, 'to have attributes', 'id', 'foo'); }, 'to throw exception', function (err) { - expect(err.output.toString(), 'to be', 'expected to have attributes \'id\', \'foo\''); }); }); }); @@ -93,7 +121,7 @@ describe('unexpected-dom', function () { expect(function () { expect(el, 'to only have attributes', ['id']); }, 'to throw exception', function (err) { - expect(err.output.toString(), 'to be', 'expected to only have attributes [ \'id\' ]'); }); }); @@ -110,7 +138,7 @@ describe('unexpected-dom', function () { expect(function () { expect(el, 'to have attributes', ['id', 'foo']); }, 'to throw exception', function (err) { - expect(err.output.toString(), 'to be', 'expected to have attributes [ \'id\', \'foo\' ]'); }); }); }); @@ -135,7 +163,7 @@ describe('unexpected-dom', function () { expect(el, 'to only have attributes', { id: 'foo' }); - }, 'to throw exception', /^expected