Skip to content

Commit

Permalink
improve test helper's makeDOM
Browse files Browse the repository at this point in the history
  • Loading branch information
bantic committed Jul 18, 2015
1 parent e6509e4 commit f6a7c07
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 82 deletions.
41 changes: 12 additions & 29 deletions src/js/parsers/section.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@ import {

import Marker from 'content-kit-editor/models/marker';
import { MARKUP_TYPES } from 'content-kit-editor/models/marker';
import { getAttributes } from 'content-kit-editor/utils/dom-utils';
import { forEach } from 'content-kit-editor/utils/array-utils';

export default {
parse(element) {
if (!this.isSectionElement(element)) {
element = this.wrapInSectionElement(element);
}

const tagName = this.tagNameFromElement(element);
const tagName = this.sectionTagNameFromElement(element);
const section = new Section(tagName);
const state = {section, markups:[], text:''};

this.toArray(element.childNodes).forEach(el => {
forEach(element.childNodes, (el) => {
this.parseNode(el, state);
});

Expand All @@ -39,7 +41,7 @@ export default {
return parent;
},

parseNode(node, state={text:'', markups:[], section:new Section()}) {
parseNode(node, state) {
switch (node.nodeType) {
case TEXT_NODE:
this.parseTextNode(node, state);
Expand All @@ -65,11 +67,13 @@ export default {
state.markups.push(markup);
}

this.toArray(element.childNodes).forEach(node => {
forEach(element.childNodes, (node) => {
this.parseNode(node, state);
});

if (markup) {
// close the marker started for this node and pop
// its markup from the stack
let marker = new Marker(state.text, state.markups);
state.section.appendMarker(marker);
state.markups.pop();
Expand All @@ -82,7 +86,8 @@ export default {
},

isSectionElement(element) {
return SECTION_TAG_NAMES.indexOf(element.tagName) !== -1;
return element.nodeType === ELEMENT_NODE &&
SECTION_TAG_NAMES.indexOf(element.tagName.toLowerCase()) !== -1;
},

markupFromElement(element) {
Expand All @@ -91,33 +96,11 @@ export default {

return {
type: tagName,
attributes: this.attributesToObject(element.attributes)
attributes: getAttributes(element)
};
},

// convert a NamedNodeMap (`element.attributes`) to a real object with
// key-value pairs
attributesToObject(attributes=[]) {
let result = {};

for (let i=0; i<attributes.length; i++) {
let {name, value} = attributes[i];
result[name] = value;
}

return result;
},

toArray(nodeList) {
let arr = [];
for (let i=0; i<nodeList.length; i++) {
arr.push(nodeList[i]);
}

return arr;
},

tagNameFromElement(element) {
sectionTagNameFromElement(element) {
let tagName = element.tagName.toLowerCase();
if (SECTION_TAG_NAMES.indexOf(tagName) === -1) { tagName = DEFAULT_TAG_NAME; }
return tagName;
Expand Down
13 changes: 12 additions & 1 deletion src/js/utils/array-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ function detect(array, callback) {
}
}

/**
* Useful for array-like things that aren't
* actually arrays, like NodeList
*/
function forEach(enumerable, callback) {
for (let i=0; i<enumerable.length; i++) {
callback(enumerable[i]);
}
}

export {
detect
detect,
forEach
};
20 changes: 19 additions & 1 deletion src/js/utils/dom-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,27 @@ function forEachChildNode(element, callback) {
}
}

/**
* converts the element's NamedNodeMap of attrs into
* an object with key-value pairs
*/
function getAttributes(element) {
let result = {};
if (element.hasAttributes()) {
let attributes = element.attributes;

for (let i=0; i<attributes.length; i++) {
let {name, value} = attributes[i];
result[name] = value;
}
}
return result;
}

export {
detectParentNode,
containsNode,
clearChildNodes,
forEachChildNode
forEachChildNode,
getAttributes
};
30 changes: 19 additions & 11 deletions tests/helpers/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,21 +96,29 @@ function triggerKeyEvent(node, eventType, keyCode=KEY_CODES.ENTER) {
node.dispatchEvent(oEvent);
}

function makeTextNode(text) {
return document.createTextNode(text);
function _buildDOM(tagName, attributes={}, children=[]) {
const el = document.createElement(tagName);
Object.keys(attributes).forEach(k => el.setAttribute(k, attributes[k]));
children.forEach(child => el.appendChild(child));
return el;
}

_buildDOM.text = (string) => {
return document.createTextNode(string);
};

/**
* to create a text node use tagName='text', {value:'the text'}
* Usage:
* makeDOM(t =>
* t('div', attributes={}, children=[
* t('b', {}, [
* t.text('I am a bold text node')
* ])
* ])
* );
*/
function makeDOM(tagName, attributes={}, children=[]) {
if (tagName === 'text') { return makeTextNode(attributes.value); }

let el = document.createElement(tagName);
Object.keys(attributes).forEach(k => el.setAttribute(k, attributes[k]));
children.forEach(child => el.appendChild(makeDOM(...child)));

return el;
function makeDOM(tree) {
return tree(_buildDOM);
}

export default {
Expand Down
80 changes: 40 additions & 40 deletions tests/unit/parsers/section-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import Helpers from '../../test-helpers';
module('Unit: Parser: SectionParser');

test('#parse parses simple dom', (assert) => {
let element = Helpers.dom.makeDOM(
'p', {}, [
['text', {value:'hello there'}],
['b', {}, [
['text', {value: 'i am bold'}]
]]
]
let element = Helpers.dom.makeDOM(t =>
t('p', {}, [
t.text('hello there'),
t('b', {}, [
t.text('i am bold')
])
])
);

const section = SectionParser.parse(element);
Expand All @@ -26,16 +26,16 @@ test('#parse parses simple dom', (assert) => {
});

test('#parse parses nested markups', (assert) => {
let element = Helpers.dom.makeDOM(
'p', {}, [
['b', {}, [
['text', {value: 'i am bold'}],
['i', {}, [
['text', {value: 'i am bold and italic'}]
]],
['text', {value: 'i am bold again'}]
]]
]
let element = Helpers.dom.makeDOM(t =>
t('p', {}, [
t('b', {}, [
t.text('i am bold'),
t('i', {}, [
t.text('i am bold and italic')
]),
t.text('i am bold again')
])
])
);

const section = SectionParser.parse(element);
Expand All @@ -52,12 +52,12 @@ test('#parse parses nested markups', (assert) => {
});

test('#parse ignores non-markup elements like spans', (assert) => {
let element = Helpers.dom.makeDOM(
'p', {}, [
['span', {}, [
['text', {value: 'i was in span'}]
]]
]
let element = Helpers.dom.makeDOM(t =>
t('p', {}, [
t('span', {}, [
t.text('i was in span')
])
])
);

const section = SectionParser.parse(element);
Expand All @@ -69,12 +69,12 @@ test('#parse ignores non-markup elements like spans', (assert) => {
});

test('#parse reads attributes', (assert) => {
let element = Helpers.dom.makeDOM(
'p', {}, [
['a', {href: 'google.com'}, [
['text', {value: 'i am a link'}]
]]
]
let element = Helpers.dom.makeDOM(t =>
t('p', {}, [
t('a', {href: 'google.com'}, [
t.text('i am a link')
])
])
);
const section = SectionParser.parse(element);
assert.equal(section.markers.length, 1, 'has 1 markers');
Expand All @@ -85,15 +85,15 @@ test('#parse reads attributes', (assert) => {
});

test('#parse joins contiguous text nodes separated by non-markup elements', (assert) => {
let element = Helpers.dom.makeDOM(
'p', {}, [
['span', {}, [
['text', {value: 'span 1'}]
]],
['span', {}, [
['text', {value: 'span 2'}]
]]
]
let element = Helpers.dom.makeDOM(t =>
t('p', {}, [
t('span', {}, [
t.text('span 1')
]),
t('span', {}, [
t.text('span 2')
])
])
);

const section = SectionParser.parse(element);
Expand All @@ -105,8 +105,8 @@ test('#parse joins contiguous text nodes separated by non-markup elements', (ass
});

test('#parse parses a single text node', (assert) => {
let element = Helpers.dom.makeDOM(
'text', {value: 'raw text'}
let element = Helpers.dom.makeDOM(h =>
h.text('raw text')
);
const section = SectionParser.parse(element);
assert.equal(section.tagName, 'p');
Expand Down

0 comments on commit f6a7c07

Please sign in to comment.