Jasmine specs for the frontend often need some DOM elements to work with. Because creating them is such a common task, we should have an efficient way to do it.
Let's say I need this HTML structure:
<ul type="square">
<li>item 1</li>
<li>item 2</li>
</ul>
This card compares various approaches to fabricating DOM elements for testing.
While you can use standard DOM functions to individually create and append elements, this is extremely verbose:
let list = document.createElement('ul')
list.type = 'square'
jasmine.fixtures.appendChild(ul)
let item1 = document.createElement('li')
item1.innerText = 'item 1'
list.appendChild(item1)
let item2 = document.createElement('li')
list.appendChild(item2)
For a reader it is hard to follow which DOM structure is being created.
{ innerHTML }
An alternative is to set the { innerHTML }
property of a container element. This approach makes it very clear which DOM structure is being created:
jasmine.fixtures.innerHTML = `
<ul type="square">
<li>item 1</li>
<li>item 2</li>
</ul>
`
Note
I'm using a global
jasmine.fixtures
container, which is cleared automatically after each test.
One drawback is that you don't get variable references for the individual elements. For this you need to rediscover elements after setting { innerHTML }
:
jasmine.fixtures.innerHTML = ...
let list = jasmine.fixtures.children[0]
let [item0, item1] = list.querySelectorAll('li')
A quick way to fabricate DOM elements is to have library create them from a CSS selector. There are several libraries that let you pass a selector like "span.foo"
and turn it into a <span class="foo">
element for you:
Here is the example above using Unpoly's
up.element.affix()
Show archive.org snapshot
function:
let list = up.element.affix(jasmine.fixtures, 'ul[type=square]')
up.element.affix(list, 'li', text: 'item 1')
up.element.affix(list, 'li', text: 'item 2')
Note how every call to up.element.affix()
returns a reference to the newly created element. I do not need to re-discover the individual elements after creation.
I often define a function jasmine.fixture()
that takes a CSS selector and appends a matching element to my jasmine.fixtures
container:
jasmine.fixture = function(...args) {
let root = args[0] instanceof Element ? args.shift() : jasmine.fixtures
return up.element.affix(root, ...args)
}
This lets me shorten the example some more:
let list = jasmine.fixture('ul[type=square]')
jasmine.fixture(list, 'li', text: 'item 1')
jasmine.fixture(list, 'li', text: 'item 2')