1
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-10-02 16:28:26 +02:00

Switch from QUnit to Jasmine.

This commit is contained in:
Johann-S
2019-03-13 16:23:50 +02:00
parent 08d81c8437
commit c8c2074650
22 changed files with 1435 additions and 1092 deletions

14
js/src/.eslintrc.json Normal file
View File

@@ -0,0 +1,14 @@
{
"root": true,
"extends": [
"../../.eslintrc.json"
],
"overrides": [
{
"files": ["**/*.spec.js"],
"env": {
"jasmine": true
}
}
]
}

View File

@@ -11,10 +11,10 @@ import {
emulateTransitionEnd,
getSelectorFromElement,
getTransitionDurationFromElement
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import SelectorEngine from './dom/selector-engine'
} from '../util/index'
import Data from '../dom/data'
import EventHandler from '../dom/event-handler'
import SelectorEngine from '../dom/selector-engine'
/**
* ------------------------------------------------------------------------
@@ -53,6 +53,7 @@ const ClassName = {
class Alert {
constructor(element) {
this._element = element
if (this._element) {
Data.setData(element, DATA_KEY, this)
}
@@ -118,7 +119,7 @@ class Alert {
const transitionDuration = getTransitionDurationFromElement(element)
EventHandler
.one(element, TRANSITION_END, event => this._destroyElement(element, event))
.one(element, TRANSITION_END, () => this._destroyElement(element))
emulateTransitionEnd(element, transitionDuration)
}
@@ -176,6 +177,7 @@ EventHandler
* add .alert to jQuery only if jQuery is present
*/
/* istanbul ignore if */
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Alert._jQueryInterface

127
js/src/alert/alert.spec.js Normal file
View File

@@ -0,0 +1,127 @@
import Alert from './alert'
import { makeArray, getTransitionDurationFromElement } from '../util/index'
/** Test helpers */
import { getFixture, clearFixture } from '../../tests/helpers/fixture'
describe('Alert', () => {
let fixtureEl
beforeAll(() => {
fixtureEl = getFixture()
})
afterEach(() => {
clearFixture()
})
it('should return version', () => {
expect(typeof Alert.VERSION).toEqual('string')
})
describe('data-api', () => {
it('should close an alert without instanciate it manually', () => {
fixtureEl.innerHTML = [
'<div class="alert">',
' <button type="button" data-dismiss="alert">x</button>',
'</div>'
].join('')
const button = document.querySelector('button')
button.click()
expect(makeArray(document.querySelectorAll('.alert')).length).toEqual(0)
})
it('should close an alert without instanciate it manually with the parent selector', () => {
fixtureEl.innerHTML = [
'<div class="alert">',
' <button type="button" data-target=".alert" data-dismiss="alert">x</button>',
'</div>'
].join('')
const button = document.querySelector('button')
button.click()
expect(makeArray(document.querySelectorAll('.alert')).length).toEqual(0)
})
})
describe('close', () => {
it('should close an alert', done => {
const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
fixtureEl.innerHTML = '<div class="alert"></div>'
const alertEl = document.querySelector('.alert')
const alert = new Alert(alertEl)
alertEl.addEventListener('closed.bs.alert', () => {
expect(makeArray(document.querySelectorAll('.alert')).length).toEqual(0)
expect(spy).not.toHaveBeenCalled()
done()
})
alert.close()
})
it('should close alert with fade class', done => {
fixtureEl.innerHTML = '<div class="alert fade"></div>'
const alertEl = document.querySelector('.alert')
const alert = new Alert(alertEl)
alertEl.addEventListener('transitionend', () => {
expect().nothing()
})
alertEl.addEventListener('closed.bs.alert', () => {
expect(makeArray(document.querySelectorAll('.alert')).length).toEqual(0)
done()
})
alert.close()
})
it('should not remove alert if close event is prevented', done => {
fixtureEl.innerHTML = '<div class="alert"></div>'
const alertEl = document.querySelector('.alert')
const alert = new Alert(alertEl)
const endTest = () => {
setTimeout(() => {
expect(alert._removeElement).not.toHaveBeenCalled()
done()
}, 10)
}
spyOn(alert, '_removeElement')
alertEl.addEventListener('close.bs.alert', event => {
event.preventDefault()
endTest()
})
alertEl.addEventListener('closed.bs.alert', () => {
endTest()
})
alert.close()
})
})
describe('dispose', () => {
it('should dispose an alert', () => {
fixtureEl.innerHTML = '<div class="alert"></div>'
const alertEl = document.querySelector('.alert')
const alert = new Alert(alertEl)
expect(Alert._getInstance(alertEl)).toBeDefined()
alert.dispose()
expect(Alert._getInstance(alertEl)).toBeNull()
})
})
})

131
js/src/dom/data.spec.js Normal file
View File

@@ -0,0 +1,131 @@
import Data from './data'
/** Test helpers */
import { getFixture, clearFixture } from '../../tests/helpers/fixture'
describe('Data', () => {
let fixtureEl
beforeAll(() => {
fixtureEl = getFixture()
})
afterEach(() => {
clearFixture()
})
describe('setData', () => {
it('should set data in an element by adding a key attribute', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const data = {
test: 'bsData'
}
Data.setData(div, 'test', data)
expect(div.key).toBeDefined()
})
it('should change data if something is already stored', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const data = {
test: 'bsData'
}
Data.setData(div, 'test', data)
data.test = 'bsData2'
Data.setData(div, 'test', data)
expect(div.key).toBeDefined()
})
})
describe('getData', () => {
it('should return stored data', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const data = {
test: 'bsData'
}
Data.setData(div, 'test', data)
expect(Data.getData(div, 'test')).toEqual(data)
})
it('should return null on undefined element', () => {
expect(Data.getData(null)).toEqual(null)
expect(Data.getData(undefined)).toEqual(null)
})
it('should return null when an element have nothing stored', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
expect(Data.getData(div, 'test')).toEqual(null)
})
it('should return null when an element have nothing stored with the provided key', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const data = {
test: 'bsData'
}
Data.setData(div, 'test', data)
expect(Data.getData(div, 'test2')).toEqual(null)
})
})
describe('removeData', () => {
it('should do nothing when an element have nothing stored', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
Data.removeData(div, 'test')
expect().nothing()
})
it('should should do nothing if it\'s not a valid key provided', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const data = {
test: 'bsData'
}
Data.setData(div, 'test', data)
expect(div.key).toBeDefined()
Data.removeData(div, 'test2')
expect(div.key).toBeDefined()
})
it('should remove data if something is stored', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const data = {
test: 'bsData'
}
Data.setData(div, 'test', data)
expect(div.key).toBeDefined()
Data.removeData(div, 'test')
expect(div.key).toBeUndefined()
})
})
})

View File

@@ -192,7 +192,9 @@ function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
}
const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))
const fn = delegation ? bootstrapDelegationHandler(element, handler, delegationFn) : bootstrapHandler(element, handler)
const fn = delegation ?
bootstrapDelegationHandler(element, handler, delegationFn) :
bootstrapHandler(element, handler)
fn.delegationSelector = delegation ? handler : null
fn.originalHandler = originalHandler

View File

@@ -0,0 +1,327 @@
import EventHandler from './event-handler'
/** Test helpers */
import { getFixture, clearFixture } from '../../tests/helpers/fixture'
describe('EventHandler', () => {
let fixtureEl
beforeAll(() => {
fixtureEl = getFixture()
})
afterEach(() => {
clearFixture()
})
describe('on', () => {
it('should not add event listener if the event is not a string', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
EventHandler.on(div, null, () => {})
EventHandler.on(null, 'click', () => {})
expect().nothing()
})
it('should add event listener', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
EventHandler.on(div, 'click', () => {
expect().nothing()
done()
})
div.click()
})
it('should add namespaced event listener', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
EventHandler.on(div, 'bs.namespace', () => {
expect().nothing()
done()
})
EventHandler.trigger(div, 'bs.namespace')
})
it('should add native namespaced event listener', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
EventHandler.on(div, 'click.namespace', () => {
expect().nothing()
done()
})
EventHandler.trigger(div, 'click')
})
it('should handle event delegation', done => {
EventHandler.on(document, 'click', '.test', () => {
expect().nothing()
done()
})
fixtureEl.innerHTML = '<div class="test"></div>'
const div = fixtureEl.querySelector('div')
div.click()
})
})
describe('one', () => {
it('should call listener just one', done => {
fixtureEl.innerHTML = '<div></div>'
let called = 0
const div = fixtureEl.querySelector('div')
const obj = {
oneListener() {
called++
}
}
EventHandler.one(div, 'bootstrap', obj.oneListener)
EventHandler.trigger(div, 'bootstrap')
EventHandler.trigger(div, 'bootstrap')
setTimeout(() => {
expect(called).toEqual(1)
done()
}, 20)
})
})
describe('off', () => {
it('should not remove a listener', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
EventHandler.off(div, null, () => {})
EventHandler.off(null, 'click', () => {})
expect().nothing()
})
it('should remove a listener', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
let called = 0
const handler = () => {
called++
}
EventHandler.on(div, 'foobar', handler)
EventHandler.trigger(div, 'foobar')
EventHandler.off(div, 'foobar', handler)
EventHandler.trigger(div, 'foobar')
setTimeout(() => {
expect(called).toEqual(1)
done()
}, 20)
})
it('should remove all the events', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
let called = 0
EventHandler.on(div, 'foobar', () => {
called++
})
EventHandler.on(div, 'foobar', () => {
called++
})
EventHandler.trigger(div, 'foobar')
EventHandler.off(div, 'foobar')
EventHandler.trigger(div, 'foobar')
setTimeout(() => {
expect(called).toEqual(2)
done()
}, 20)
})
it('should remove all the namespaced listeners if namespace is passed', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
let called = 0
EventHandler.on(div, 'foobar.namespace', () => {
called++
})
EventHandler.on(div, 'foofoo.namespace', () => {
called++
})
EventHandler.trigger(div, 'foobar.namespace')
EventHandler.trigger(div, 'foofoo.namespace')
EventHandler.off(div, '.namespace')
EventHandler.trigger(div, 'foobar.namespace')
EventHandler.trigger(div, 'foofoo.namespace')
setTimeout(() => {
expect(called).toEqual(2)
done()
}, 20)
})
it('should remove the namespaced listeners', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
let calledCallback1 = 0
let calledCallback2 = 0
EventHandler.on(div, 'foobar.namespace', () => {
calledCallback1++
})
EventHandler.on(div, 'foofoo.namespace', () => {
calledCallback2++
})
EventHandler.trigger(div, 'foobar.namespace')
EventHandler.off(div, 'foobar.namespace')
EventHandler.trigger(div, 'foobar.namespace')
EventHandler.trigger(div, 'foofoo.namespace')
setTimeout(() => {
expect(calledCallback1).toEqual(1)
expect(calledCallback2).toEqual(1)
done()
}, 20)
})
it('should remove the all the namespaced listeners for native events', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
let called = 0
EventHandler.on(div, 'click.namespace', () => {
called++
})
EventHandler.on(div, 'click.namespace2', () => {
called++
})
EventHandler.trigger(div, 'click')
EventHandler.off(div, 'click')
EventHandler.trigger(div, 'click')
setTimeout(() => {
expect(called).toEqual(2)
done()
}, 20)
})
it('should remove the specified namespaced listeners for native events', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
let called1 = 0
let called2 = 0
EventHandler.on(div, 'click.namespace', () => {
called1++
})
EventHandler.on(div, 'click.namespace2', () => {
called2++
})
EventHandler.trigger(div, 'click')
EventHandler.off(div, 'click.namespace')
EventHandler.trigger(div, 'click')
setTimeout(() => {
expect(called1).toEqual(1)
expect(called2).toEqual(2)
done()
}, 20)
})
it('should remove a listener registered by .one', done => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const handler = () => {
throw new Error('called')
}
EventHandler.one(div, 'foobar', handler)
EventHandler.off(div, 'foobar', handler)
EventHandler.trigger(div, 'foobar')
setTimeout(() => {
expect().nothing()
done()
}, 20)
})
it('should remove the correct delegated event listener', () => {
const element = document.createElement('div')
const subelement = document.createElement('span')
element.appendChild(subelement)
const anchor = document.createElement('a')
element.appendChild(anchor)
let i = 0
const handler = () => {
i++
}
EventHandler.on(element, 'click', 'a', handler)
EventHandler.on(element, 'click', 'span', handler)
fixtureEl.appendChild(element)
EventHandler.trigger(anchor, 'click')
EventHandler.trigger(subelement, 'click')
// first listeners called
expect(i === 2).toEqual(true)
EventHandler.off(element, 'click', 'span', handler)
EventHandler.trigger(subelement, 'click')
// removed listener not called
expect(i === 2).toEqual(true)
EventHandler.trigger(anchor, 'click')
// not removed listener called
expect(i === 3).toEqual(true)
EventHandler.on(element, 'click', 'span', handler)
EventHandler.trigger(anchor, 'click')
EventHandler.trigger(subelement, 'click')
// listener re-registered
expect(i === 5).toEqual(true)
EventHandler.off(element, 'click', 'span')
EventHandler.trigger(subelement, 'click')
// listener removed again
expect(i === 5).toEqual(true)
})
})
})

297
js/src/util/index.spec.js Normal file
View File

@@ -0,0 +1,297 @@
import * as Util from './index'
/** Test helpers */
import { getFixture, clearFixture } from '../../tests/helpers/fixture'
describe('Util', () => {
let fixtureEl
beforeAll(() => {
fixtureEl = getFixture()
})
afterEach(() => {
clearFixture()
})
describe('getUID', () => {
it('should generate uid', () => {
const uid = Util.getUID('bs')
const uid2 = Util.getUID('bs')
expect(uid).not.toEqual(uid2)
})
})
describe('getSelectorFromElement', () => {
it('should get selector from data-target', () => {
fixtureEl.innerHTML = [
'<div id="test" data-target=".target"></div>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should get selector from href if no data-target set', () => {
fixtureEl.innerHTML = [
'<a id="test" href=".target"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should get selector from href if data-target equal to #', () => {
fixtureEl.innerHTML = [
'<a id="test" data-target="#" href=".target"></a>',
'<div class="target"></div>'
].join('')
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
})
it('should return null if selector not found', () => {
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
const testEl = fixtureEl.querySelector('#test')
expect(Util.getSelectorFromElement(testEl)).toBeNull()
})
})
describe('getTransitionDurationFromElement', () => {
it('should get transition from element', () => {
fixtureEl.innerHTML = '<div style="transition: all 300ms ease-out;"></div>'
expect(Util.getTransitionDurationFromElement(fixtureEl.querySelector('div'))).toEqual(300)
})
it('should return 0 if the element is undefined or null', () => {
expect(Util.getTransitionDurationFromElement(null)).toEqual(0)
expect(Util.getTransitionDurationFromElement(undefined)).toEqual(0)
})
it('should return 0 if the element do not possess transition', () => {
fixtureEl.innerHTML = '<div></div>'
expect(Util.getTransitionDurationFromElement(fixtureEl.querySelector('div'))).toEqual(0)
})
})
describe('triggerTransitionEnd', () => {
it('should trigger transitionend event', done => {
fixtureEl.innerHTML = '<div style="transition: all 300ms ease-out;"></div>'
const el = fixtureEl.querySelector('div')
el.addEventListener('transitionend', () => {
expect().nothing()
done()
})
Util.triggerTransitionEnd(el)
})
})
describe('isElement', () => {
it('should detect if the parameter is an element or not', () => {
fixtureEl.innerHTML = '<div></div>'
const el = document.querySelector('div')
expect(Util.isElement(el)).toEqual(el.nodeType)
expect(Util.isElement({})).toEqual(undefined)
})
it('should detect jQuery element', () => {
fixtureEl.innerHTML = '<div></div>'
const el = document.querySelector('div')
const fakejQuery = {
0: el
}
expect(Util.isElement(fakejQuery)).toEqual(el.nodeType)
})
})
describe('emulateTransitionEnd', () => {
it('should emulate transition end', () => {
fixtureEl.innerHTML = '<div></div>'
const el = document.querySelector('div')
const spy = spyOn(window, 'setTimeout')
Util.emulateTransitionEnd(el, 10)
expect(spy).toHaveBeenCalled()
})
it('should not emulate transition end if already triggered', done => {
fixtureEl.innerHTML = '<div></div>'
const el = fixtureEl.querySelector('div')
const spy = spyOn(el, 'removeEventListener')
Util.emulateTransitionEnd(el, 10)
Util.triggerTransitionEnd(el)
setTimeout(() => {
expect(spy).toHaveBeenCalled()
done()
}, 20)
})
})
describe('typeCheckConfig', () => {
it('should check type of the config object', () => {
const namePlugin = 'collapse'
const defaultType = {
toggle: 'boolean',
parent: '(string|element)'
}
const config = {
toggle: true,
parent: 777
}
expect(() => {
Util.typeCheckConfig(namePlugin, config, defaultType)
}).toThrow(new Error('COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".'))
})
})
describe('makeArray', () => {
it('should convert node list to array', () => {
const nodeList = document.querySelectorAll('div')
expect(Array.isArray(nodeList)).toEqual(false)
expect(Array.isArray(Util.makeArray(nodeList))).toEqual(true)
})
it('should return an empty array if the nodeList is undefined', () => {
expect(Util.makeArray(null)).toEqual([])
expect(Util.makeArray(undefined)).toEqual([])
})
})
describe('isVisible', () => {
it('should return false if the element is not defined', () => {
expect(Util.isVisible(null)).toEqual(false)
expect(Util.isVisible(undefined)).toEqual(false)
})
it('should return false if the element provided is not a dom element', () => {
expect(Util.isVisible({})).toEqual(false)
})
it('should return false if the element is not visible with display none', () => {
fixtureEl.innerHTML = '<div style="display: none;"></div>'
const div = fixtureEl.querySelector('div')
expect(Util.isVisible(div)).toEqual(false)
})
it('should return false if the element is not visible with visibility hidden', () => {
fixtureEl.innerHTML = '<div style="visibility: hidden;"></div>'
const div = fixtureEl.querySelector('div')
expect(Util.isVisible(div)).toEqual(false)
})
it('should return false if the parent element is not visible', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
expect(Util.isVisible(div)).toEqual(false)
})
it('should return true if the element is visible', () => {
fixtureEl.innerHTML = [
'<div>',
' <div id="element"></div>',
'</div>'
].join('')
const div = fixtureEl.querySelector('#element')
expect(Util.isVisible(div)).toEqual(true)
})
})
describe('findShadowRoot', () => {
it('should return null if shadow dom is not available', () => {
// Only for newer browsers
if (!document.documentElement.attachShadow) {
expect().nothing()
return
}
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
spyOn(document.documentElement, 'attachShadow').and.returnValue(null)
expect(Util.findShadowRoot(div)).toEqual(null)
})
it('should return null when we do not find a shadow root', () => {
// Only for newer browsers
if (!document.documentElement.attachShadow) {
expect().nothing()
return
}
spyOn(document, 'getRootNode').and.returnValue(undefined)
expect(Util.findShadowRoot(document)).toEqual(null)
})
it('should return the shadow root when found', () => {
// Only for newer browsers
if (!document.documentElement.attachShadow) {
expect().nothing()
return
}
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
const shadowRoot = div.attachShadow({
mode: 'open'
})
expect(Util.findShadowRoot(shadowRoot)).toEqual(shadowRoot)
shadowRoot.innerHTML = '<button>Shadow Button</button>'
expect(Util.findShadowRoot(shadowRoot.firstChild)).toEqual(shadowRoot)
})
})
describe('noop', () => {
it('should return a function', () => {
expect(typeof Util.noop()).toEqual('function')
})
})
describe('reflow', () => {
it('should return element offset height to force the reflow', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
expect(Util.reflow(div)).toEqual(0)
})
})
})

View File

@@ -0,0 +1,70 @@
import { DefaultWhitelist, sanitizeHtml } from './sanitizer'
describe('Sanitizer', () => {
describe('sanitizeHtml', () => {
it('should return the same on empty string', () => {
const empty = ''
const result = sanitizeHtml(empty, DefaultWhitelist, null)
expect(result).toEqual(empty)
})
it('should sanitize template by removing tags with XSS', () => {
const template = [
'<div>',
' <a href="javascript:alert(7)">Click me</a>',
' <span>Some content</span>',
'</div>'
].join('')
const result = sanitizeHtml(template, DefaultWhitelist, null)
expect(result.indexOf('script') === -1).toEqual(true)
})
it('should allow aria attributes and safe attributes', () => {
const template = [
'<div aria-pressed="true">',
' <span class="test">Some content</span>',
'</div>'
].join('')
const result = sanitizeHtml(template, DefaultWhitelist, null)
expect(result.indexOf('aria-pressed') !== -1).toEqual(true)
expect(result.indexOf('class="test"') !== -1).toEqual(true)
})
it('should remove not whitelist tags', () => {
const template = [
'<div>',
' <script>alert(7)</script>',
'</div>'
].join('')
const result = sanitizeHtml(template, DefaultWhitelist, null)
expect(result.indexOf('<script>') === -1).toEqual(true)
})
it('should not use native api to sanitize if a custom function passed', () => {
const template = [
'<div>',
' <span>Some content</span>',
'</div>'
].join('')
function mySanitize(htmlUnsafe) {
return htmlUnsafe
}
spyOn(DOMParser.prototype, 'parseFromString')
const result = sanitizeHtml(template, DefaultWhitelist, mySanitize)
expect(result).toEqual(template)
expect(DOMParser.prototype.parseFromString).not.toHaveBeenCalled()
})
})
})