Skip to content

Commit

Permalink
Merge pull request #4 from papandreou/fixElementStringification
Browse files Browse the repository at this point in the history
Fixed element stringification.
  • Loading branch information
Munter committed Mar 7, 2015
2 parents cbbccb7 + 668d25c commit 13389bc
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 20 deletions.
50 changes: 39 additions & 11 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '</' + elm.nodeName.toLowerCase() + '>';
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].replace(/&/g, '&amp;').replace(/"/g, '&quot;') + '"';
}
}).join(' '));
});

if (elm.children.length) {
return openTag.join(' ') + '>...' + closeTag;
} else {
return openTag.join(' ') + '/>';
str += '>';
if (!isVoidElement(elementName)) {
if (elm.children.length > 0) {
str += '...';
}
str += '</' + elementName + '>';
}
return str;
}

module.exports = {
Expand Down
46 changes: 37 additions & 9 deletions test/unexpected-dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -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('<!DOCTYPE html><html><head></head><body>' + subject + '</body></html>').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;
Expand All @@ -18,6 +34,18 @@ describe('unexpected-dom', function () {
});
});

it('should inspect an attribute-less element correctly', function () {
expect('<div></div>', 'to inspect as itself');
});

it('should inspect void elements correctly', function () {
expect('<input type="text">', 'to inspect as itself');
});

it('should inspect simple attributes correctly', function () {
expect('<input disabled type="text">', 'to inspect as itself');
});

it('should allow regular assertions defined for the object type to work on an HTMLElement', function () {
expect(jsdom.jsdom('<html><head></head><body></body></html>').firstChild, 'to have properties', { nodeType: 1 });
});
Expand Down Expand Up @@ -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 <button class="bar" data-info="baz" disabled id="foo"/> to only have attributes \'id\'');
expect(err.output.toString(), 'to be', 'expected <button class="bar" data-info="baz" disabled id="foo"></button> to only have attributes \'id\'');
});
});

Expand All @@ -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 <button class="bar" data-info="baz" disabled id="foo"/> to have attributes \'id\', \'foo\'');
expect(err.output.toString(), 'to be', 'expected <button class="bar" data-info="baz" disabled id="foo"></button> to have attributes \'id\', \'foo\'');
});
});
});
Expand All @@ -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 <button class="bar" data-info="baz" disabled id="foo"/> to only have attributes [ \'id\' ]');
expect(err.output.toString(), 'to be', 'expected <button class="bar" data-info="baz" disabled id="foo"></button> to only have attributes [ \'id\' ]');
});
});

Expand All @@ -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 <button class="bar" data-info="baz" disabled id="foo"/> to have attributes [ \'id\', \'foo\' ]');
expect(err.output.toString(), 'to be', 'expected <button class="bar" data-info="baz" disabled id="foo"></button> to have attributes [ \'id\', \'foo\' ]');
});
});
});
Expand All @@ -135,7 +163,7 @@ describe('unexpected-dom', function () {
expect(el, 'to only have attributes', {
id: 'foo'
});
}, 'to throw exception', /^expected <button class="bar" data-info="baz" disabled id="foo"\/> to only have attributes/);
}, 'to throw exception', /^expected <button class="bar" data-info="baz" disabled id="foo"><\/button> to only have attributes/);
});

it('should match partial object', function () {
Expand All @@ -156,7 +184,7 @@ describe('unexpected-dom', function () {
id: 'foo',
foo: 'bar'
});
}, 'to throw exception', /expected <button class="bar" data-info="baz" disabled id="foo"\/> to have attributes/);
}, 'to throw exception', /expected <button class="bar" data-info="baz" disabled id="foo"><\/button> to have attributes/);
});
});
});
Expand All @@ -176,7 +204,7 @@ describe('unexpected-dom', function () {

expect(function () {
expect(el, 'to have no children');
}, 'to throw', /^expected <div >...<\/div> to have no children/);
}, 'to throw', /^expected <div>...<\/div> to have no children/);
});

it('should fail on element with HTMLComment children', function () {
Expand All @@ -185,7 +213,7 @@ describe('unexpected-dom', function () {

expect(function () {
expect(el, 'to have no children');
}, 'to throw', /^expected <div \/> to have no children/);
}, 'to throw', /^expected <div><\/div> to have no children/);
});

it('should fail on element with TextNode children', function () {
Expand All @@ -194,7 +222,7 @@ describe('unexpected-dom', function () {

expect(function () {
expect(el, 'to have no children');
}, 'to throw', /^expected <div \/> to have no children/);
}, 'to throw', /^expected <div><\/div> to have no children/);
});
});
});
Expand Down

0 comments on commit 13389bc

Please sign in to comment.