Posted 12 days ago. Visible to the public. Repeats.

Jasmine: Creating DOM elements efficiently

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:

Copy
<ul type="square"> <li>item 1</li> <li>item 2</li> </ul>

This card compares various approaches to fabricating DOM elements for testing.

Constructing individual elements

While you can use standard DOM functions to individually create and append elements, this is extremely verbose:

Copy
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.

Setting { 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:

Copy
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 }:

Copy
jasmine.fixtures.innerHTML = ... let list = jasmine.fixtures.children[0] let [item0, item1] = list.querySelectorAll('li')

Creating elements from CSS selectors

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() Archive function:

Copy
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.

One more refinement

I often define a function jasmine.fixture() that takes a CSS selector and appends a matching element to my jasmine.fixtures container:

Copy
jasmine.fixture = function(...args) { return up.element.affix(jasmine.fixtures, ...args) }

This lets me shorten the example some more:

Copy
let list = jasmine.fixture('ul[type=square]') up.element.affix(list, 'li', text: 'item 1') up.element.affix(list, 'li', text: 'item 2')

Does your version of Ruby on Rails still receive security updates?
Rails LTS provides security patches for unsupported versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2).

Owner of this card:

Avatar
Henning Koch
Last edit:
8 days ago
by Henning Koch
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Henning Koch to makandra dev
This website uses short-lived cookies to improve usability.
Accept or learn more