describe('Core htmx Shadow DOM Tests', function() {
// Skip these tests if browser doesn't support shadow DOM
if (typeof window.ShadowRoot === 'undefined') return
before(function() {
this.initialWorkArea = getWorkArea().outerHTML
})
after(function() {
getWorkArea().outerHTML = this.initialWorkArea
})
beforeEach(function() {
this.server = makeServer()
clearWorkArea()
var workArea = getWorkArea()
if (!workArea.shadowRoot) workArea.attachShadow({ mode: 'open' })
workArea.shadowRoot.innerHTML = ''
})
afterEach(function() {
this.server.restore()
clearWorkArea()
getWorkArea().shadowRoot.innerHTML = ''
})
// Locally redefine the `byId` and `make` functions to use shadow DOM
function byId(id) {
return getWorkArea().shadowRoot.getElementById(id) || document.getElementById(id)
}
function make(htmlStr) {
htmlStr = htmlStr.trim()
var makeFn = function() {
var range = document.createRange()
var fragment = range.createContextualFragment(htmlStr)
var wa = getWorkArea().shadowRoot
var child = null
var children = fragment.children || fragment.childNodes // IE
var appendedChildren = []
while (children.length > 0) {
child = children[0]
wa.appendChild(child)
appendedChildren.push(child)
}
htmx.process(wa)
return child // return last added element
}
if (getWorkArea()) {
return makeFn()
} else {
ready(makeFn)
}
}
// special target selector extensions
it('properly retrieves shadow root for extended selector', function() {
var div = make('
')
htmx.defineExtension('test/shadowdom.js', {
init: function(api) {
api.getTarget(div).should.equal(getWorkArea().shadowRoot)
}
})
})
it('properly escapes shadow root for extended selector', function() {
var div = make('')
htmx.defineExtension('test/shadowdom.js', {
init: function(api) {
api.getTarget(div).should.equal(getWorkArea())
}
})
})
// bootstrap test
it('issues a GET request on click and swaps content', function() {
this.server.respondWith('GET', '/test', 'Clicked!')
var btn = make('')
btn.click()
this.server.respond()
btn.innerHTML.should.equal('Clicked!')
})
it('processes inner content properly', function() {
this.server.respondWith('GET', '/test', 'Click Me')
this.server.respondWith('GET', '/test2', 'Clicked!')
var div = make('')
div.click()
this.server.respond()
div.innerHTML.should.equal('Click Me')
var a = div.querySelector('a')
a.click()
this.server.respond()
a.innerHTML.should.equal('Clicked!')
})
it('handles swap outerHTML properly', function() {
this.server.respondWith('GET', '/test', 'Click Me')
this.server.respondWith('GET', '/test2', 'Clicked!')
var div = make('')
div.click()
should.equal(byId('d1'), div)
this.server.respond()
should.equal(byId('d1'), null)
byId('a1').click()
this.server.respond()
byId('a1').innerHTML.should.equal('Clicked!')
})
it('handles beforebegin properly', function() {
var i = 0
this.server.respondWith('GET', '/test', function(xhr) {
i++
xhr.respond(200, {}, '' + i + '')
})
this.server.respondWith('GET', '/test2', '*')
// extra wrapping div here because `ShadowRoot` doesn't support `innerText` or `child.parentElement`
var div = make('
*
').children[0]
var parent = div.parentElement
div.click()
this.server.respond()
div.innerText.should.equal('*')
removeWhiteSpace(parent.innerText).should.equal('1*')
byId('a1').click()
this.server.respond()
removeWhiteSpace(parent.innerText).should.equal('**')
div.click()
this.server.respond()
div.innerText.should.equal('*')
removeWhiteSpace(parent.innerText).should.equal('*2*')
byId('a2').click()
this.server.respond()
removeWhiteSpace(parent.innerText).should.equal('***')
})
it('handles afterbegin properly', function() {
var i = 0
this.server.respondWith('GET', '/test', function(xhr) {
i++
xhr.respond(200, {}, '' + i)
})
var div = make('
*
')
div.click()
this.server.respond()
div.innerText.should.equal('1*')
div.click()
this.server.respond()
div.innerText.should.equal('21*')
div.click()
this.server.respond()
div.innerText.should.equal('321*')
})
it('handles afterbegin properly with no initial content', function() {
var i = 0
this.server.respondWith('GET', '/test', function(xhr) {
i++
xhr.respond(200, {}, '' + i)
})
var div = make('')
div.click()
this.server.respond()
div.innerText.should.equal('1')
div.click()
this.server.respond()
div.innerText.should.equal('21')
div.click()
this.server.respond()
div.innerText.should.equal('321')
})
it('handles afterend properly', function() {
var i = 0
this.server.respondWith('GET', '/test', function(xhr) {
i++
xhr.respond(200, {}, '' + i + '')
})
this.server.respondWith('GET', '/test2', '*')
// extra wrapping div here because `ShadowRoot` doesn't support `innerText` or `child.parentElement`
var div = make('
*
').children[0]
var parent = div.parentElement
div.click()
this.server.respond()
div.innerText.should.equal('*')
removeWhiteSpace(parent.innerText).should.equal('*1')
byId('a1').click()
this.server.respond()
removeWhiteSpace(parent.innerText).should.equal('**')
div.click()
this.server.respond()
div.innerText.should.equal('*')
removeWhiteSpace(parent.innerText).should.equal('*2*')
byId('a2').click()
this.server.respond()
removeWhiteSpace(parent.innerText).should.equal('***')
})
it('handles beforeend properly', function() {
var i = 0
this.server.respondWith('GET', '/test', function(xhr) {
i++
xhr.respond(200, {}, '' + i)
})
var div = make('
*
')
div.click()
this.server.respond()
div.innerText.should.equal('*1')
div.click()
this.server.respond()
div.innerText.should.equal('*12')
div.click()
this.server.respond()
div.innerText.should.equal('*123')
})
it('handles beforeend properly with no initial content', function() {
var i = 0
this.server.respondWith('GET', '/test', function(xhr) {
i++
xhr.respond(200, {}, '' + i)
})
var div = make('')
div.click()
this.server.respond()
div.innerText.should.equal('1')
div.click()
this.server.respond()
div.innerText.should.equal('12')
div.click()
this.server.respond()
div.innerText.should.equal('123')
})
it('handles hx-target properly', function() {
this.server.respondWith('GET', '/test', 'Clicked!')
var btn = make('')
var target = make('Initial')
btn.click()
target.innerHTML.should.equal('Initial')
this.server.respond()
target.innerHTML.should.equal('Clicked!')
})
it('handles 204 NO CONTENT responses properly', function() {
this.server.respondWith('GET', '/test', [204, {}, 'No Content!'])
var btn = make('')
btn.click()
btn.innerHTML.should.equal('Click Me!')
this.server.respond()
btn.innerHTML.should.equal('Click Me!')
})
it('handles 304 NOT MODIFIED responses properly', function() {
this.server.respondWith('GET', '/test-1', [200, {}, 'Content for Tab 1'])
this.server.respondWith('GET', '/test-2', [200, {}, 'Content for Tab 2'])
var target = make('')
var btn1 = make('')
var btn2 = make('')
btn1.click()
target.innerHTML.should.equal('')
this.server.respond()
target.innerHTML.should.equal('Content for Tab 1')
btn2.click()
this.server.respond()
target.innerHTML.should.equal('Content for Tab 2')
this.server.respondWith('GET', '/test-1', [304, {}, 'Content for Tab 1'])
this.server.respondWith('GET', '/test-2', [304, {}, 'Content for Tab 2'])
btn1.click()
this.server.respond()
target.innerHTML.should.equal('Content for Tab 1')
btn2.click()
this.server.respond()
target.innerHTML.should.equal('Content for Tab 2')
})
it('handles hx-trigger with non-default value', function() {
this.server.respondWith('GET', '/test', 'Clicked!')
var form = make('')
form.click()
form.innerHTML.should.equal('Click Me!')
this.server.respond()
form.innerHTML.should.equal('Clicked!')
})
it('handles hx-trigger with load event', function() {
this.server.respondWith('GET', '/test', 'Loaded!')
var div = make('
Load Me!
')
div.innerHTML.should.equal('Load Me!')
this.server.respond()
div.innerHTML.should.equal('Loaded!')
})
it('sets the content type of the request properly', function(done) {
this.server.respondWith('GET', '/test', function(xhr) {
xhr.respond(200, {}, 'done')
xhr.overriddenMimeType.should.equal('text/html')
done()
})
var div = make('
Click Me!
')
div.click()
this.server.respond()
})
it('issues two requests when clicked twice before response', function() {
var i = 1
this.server.respondWith('GET', '/test', function(xhr) {
xhr.respond(200, {}, 'click ' + i)
i++
})
var div = make('')
div.click()
div.click()
this.server.respond()
div.innerHTML.should.equal('click 1')
this.server.respond()
div.innerHTML.should.equal('click 2')
})
it('issues two requests when clicked three times before response', function() {
var i = 1
this.server.respondWith('GET', '/test', function(xhr) {
xhr.respond(200, {}, 'click ' + i)
i++
})
var div = make('')
div.click()
div.click()
div.click()
this.server.respondAll()
div.innerHTML.should.equal('click 2')
})
it('properly handles hx-select for basic situation', function() {
var i = 1
this.server.respondWith('GET', '/test', "
foo
bar
")
var div = make('')
div.click()
this.server.respond()
div.innerHTML.should.equal('
foo
')
})
it('properly handles hx-select for full html document situation', function() {
this.server.respondWith('GET', '/test', "
foo
bar
")
var div = make('')
div.click()
this.server.respond()
div.innerHTML.should.equal('