aboutsummaryrefslogtreecommitdiff
path: root/js/tests/unit/tooltip.spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/tests/unit/tooltip.spec.js')
-rw-r--r--js/tests/unit/tooltip.spec.js230
1 files changed, 187 insertions, 43 deletions
diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js
index 84f5abcda..22a7edd01 100644
--- a/js/tests/unit/tooltip.spec.js
+++ b/js/tests/unit/tooltip.spec.js
@@ -3,7 +3,7 @@ import EventHandler from '../../src/dom/event-handler'
import { noop } from '../../src/util/index'
/** Test helpers */
-import { getFixture, clearFixture, jQueryMock, createEvent } from '../helpers/fixture'
+import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
describe('Tooltip', () => {
let fixtureEl
@@ -16,7 +16,7 @@ describe('Tooltip', () => {
clearFixture()
document.querySelectorAll('.tooltip').forEach(tooltipEl => {
- document.body.removeChild(tooltipEl)
+ tooltipEl.remove()
})
})
@@ -63,13 +63,24 @@ describe('Tooltip', () => {
})
describe('constructor', () => {
+ it('should take care of element either passed as a CSS selector or DOM element', () => {
+ fixtureEl.innerHTML = '<a href="#" id="tooltipEl" rel="tooltip" title="Nice and short title">'
+
+ const tooltipEl = fixtureEl.querySelector('#tooltipEl')
+ const tooltipBySelector = new Tooltip('#tooltipEl')
+ const tooltipByElement = new Tooltip(tooltipEl)
+
+ expect(tooltipBySelector._element).toEqual(tooltipEl)
+ expect(tooltipByElement._element).toEqual(tooltipEl)
+ })
+
it('should not take care of disallowed data attributes', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-sanitize="false" title="Another tooltip">'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- expect(tooltip.config.sanitize).toEqual(true)
+ expect(tooltip._config.sanitize).toEqual(true)
})
it('should convert title and content to string if numbers', () => {
@@ -81,8 +92,8 @@ describe('Tooltip', () => {
content: 7
})
- expect(tooltip.config.title).toEqual('1')
- expect(tooltip.config.content).toEqual('7')
+ expect(tooltip._config.title).toEqual('1')
+ expect(tooltip._config.content).toEqual('7')
})
it('should enable selector delegation', done => {
@@ -183,7 +194,7 @@ describe('Tooltip', () => {
tooltip.enable()
tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeDefined()
+ expect(document.querySelector('.tooltip')).not.toBeNull()
done()
})
@@ -256,7 +267,7 @@ describe('Tooltip', () => {
const tooltip = new Tooltip(tooltipEl)
tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeDefined()
+ expect(document.querySelector('.tooltip')).not.toBeNull()
done()
})
@@ -375,7 +386,7 @@ describe('Tooltip', () => {
const tooltip = new Tooltip(tooltipEl)
tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeDefined()
+ expect(document.querySelector('.tooltip')).not.toBeNull()
tooltip.dispose()
@@ -397,7 +408,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tooltipShown = document.querySelector('.tooltip')
- expect(tooltipShown).toBeDefined()
+ expect(tooltipShown).not.toBeNull()
expect(tooltipEl.getAttribute('aria-describedby')).toEqual(tooltipShown.getAttribute('id'))
expect(tooltipShown.getAttribute('id')).toContain('tooltip')
done()
@@ -435,11 +446,11 @@ describe('Tooltip', () => {
const tooltip = new Tooltip(tooltipEl)
document.documentElement.ontouchstart = noop
- spyOn(EventHandler, 'on')
+ spyOn(EventHandler, 'on').and.callThrough()
tooltipEl.addEventListener('shown.bs.tooltip', () => {
expect(document.querySelector('.tooltip')).not.toBeNull()
- expect(EventHandler.on).toHaveBeenCalled()
+ expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
document.documentElement.ontouchstart = undefined
done()
})
@@ -479,7 +490,7 @@ describe('Tooltip', () => {
tooltipEl.removeEventListener('shown.bs.tooltip', firstCallback)
let tooltipShown = document.querySelector('.tooltip')
- tooltipShown.parentNode.removeChild(tooltipShown)
+ tooltipShown.remove()
tooltipEl.addEventListener('shown.bs.tooltip', () => {
tooltipShown = document.querySelector('.tooltip')
@@ -505,7 +516,7 @@ describe('Tooltip', () => {
})
tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(fixtureEl.querySelector('.tooltip')).toBeDefined()
+ expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
done()
})
@@ -524,7 +535,7 @@ describe('Tooltip', () => {
})
tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(fixtureEl.querySelector('.tooltip')).toBeDefined()
+ expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
done()
})
@@ -540,7 +551,7 @@ describe('Tooltip', () => {
})
tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(fixtureEl.querySelector('.tooltip')).toBeDefined()
+ expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
done()
})
@@ -557,7 +568,7 @@ describe('Tooltip', () => {
})
tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeDefined()
+ expect(document.querySelector('.tooltip')).not.toBeNull()
expect(spy).toHaveBeenCalled()
done()
})
@@ -576,7 +587,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tip = document.querySelector('.tooltip')
- expect(tip).toBeDefined()
+ expect(tip).not.toBeNull()
expect(tip.classList.contains('fade')).toEqual(false)
done()
})
@@ -697,6 +708,100 @@ describe('Tooltip', () => {
tooltipEl.dispatchEvent(createEvent('mouseover'))
})
+ it('should not hide tooltip if leave event occurs and interaction remains inside trigger', done => {
+ fixtureEl.innerHTML = [
+ '<a href="#" rel="tooltip" title="Another tooltip">',
+ '<b>Trigger</b>',
+ 'the tooltip',
+ '</a>'
+ ]
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+ const triggerChild = tooltipEl.querySelector('b')
+
+ spyOn(tooltip, 'hide').and.callThrough()
+
+ tooltipEl.addEventListener('mouseover', () => {
+ const moveMouseToChildEvent = createEvent('mouseout')
+ Object.defineProperty(moveMouseToChildEvent, 'relatedTarget', {
+ value: triggerChild
+ })
+
+ tooltipEl.dispatchEvent(moveMouseToChildEvent)
+ })
+
+ tooltipEl.addEventListener('mouseout', () => {
+ expect(tooltip.hide).not.toHaveBeenCalled()
+ done()
+ })
+
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
+ })
+
+ it('should properly maintain tooltip state if leave event occurs and enter event occurs during hide transition', done => {
+ // Style this tooltip to give it plenty of room for popper to do what it wants
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-placement="top" style="position:fixed;left:50%;top:50%;">Trigger</a>'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ spyOn(window, 'getComputedStyle').and.returnValue({
+ transitionDuration: '0.15s',
+ transitionDelay: '0s'
+ })
+
+ setTimeout(() => {
+ expect(tooltip._popper).not.toBeNull()
+ expect(tooltip.getTipElement().getAttribute('data-popper-placement')).toBe('top')
+ tooltipEl.dispatchEvent(createEvent('mouseout'))
+
+ setTimeout(() => {
+ expect(tooltip.getTipElement().classList.contains('show')).toEqual(false)
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
+ }, 100)
+
+ setTimeout(() => {
+ expect(tooltip._popper).not.toBeNull()
+ expect(tooltip.getTipElement().getAttribute('data-popper-placement')).toBe('top')
+ done()
+ }, 200)
+ }, 0)
+
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
+ })
+
+ it('should only trigger inserted event if a new tooltip element was created', done => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ spyOn(window, 'getComputedStyle').and.returnValue({
+ transitionDuration: '0.15s',
+ transitionDelay: '0s'
+ })
+
+ const insertedFunc = jasmine.createSpy()
+ tooltipEl.addEventListener('inserted.bs.tooltip', insertedFunc)
+
+ setTimeout(() => {
+ expect(insertedFunc).toHaveBeenCalledTimes(1)
+ tooltip.hide()
+
+ setTimeout(() => {
+ tooltip.show()
+ }, 100)
+
+ setTimeout(() => {
+ expect(insertedFunc).toHaveBeenCalledTimes(1)
+ done()
+ }, 200)
+ }, 0)
+
+ tooltip.show()
+ })
+
it('should show a tooltip with custom class provided in data attributes', done => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-custom-class="custom-class">'
@@ -705,7 +810,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tip = document.querySelector('.tooltip')
- expect(tip).toBeDefined()
+ expect(tip).not.toBeNull()
expect(tip.classList.contains('custom-class')).toBeTrue()
done()
})
@@ -723,7 +828,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tip = document.querySelector('.tooltip')
- expect(tip).toBeDefined()
+ expect(tip).not.toBeNull()
expect(tip.classList.contains('custom-class')).toBeTrue()
expect(tip.classList.contains('custom-class-2')).toBeTrue()
done()
@@ -743,7 +848,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tip = document.querySelector('.tooltip')
- expect(tip).toBeDefined()
+ expect(tip).not.toBeNull()
expect(spy).toHaveBeenCalled()
expect(tip.classList.contains('custom-class')).toBeTrue()
done()
@@ -784,7 +889,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('hidden.bs.tooltip', () => {
expect(document.querySelector('.tooltip')).toBeNull()
- expect(EventHandler.off).toHaveBeenCalled()
+ expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
document.documentElement.ontouchstart = undefined
done()
})
@@ -940,10 +1045,10 @@ describe('Tooltip', () => {
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- tooltip.setContent()
-
const tip = tooltip.getTipElement()
+ tooltip.setContent(tip)
+
expect(tip.classList.contains('show')).toEqual(false)
expect(tip.classList.contains('fade')).toEqual(false)
expect(tip.querySelector('.tooltip-inner').textContent).toEqual('Another tooltip')
@@ -1024,7 +1129,7 @@ describe('Tooltip', () => {
html: true
})
- tooltip.getTipElement().appendChild(childContent)
+ tooltip.getTipElement().append(childContent)
tooltip.setElementContent(tooltip.getTipElement(), childContent)
expect().nothing()
@@ -1158,7 +1263,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tooltipShown = document.querySelector('.tooltip')
- expect(tooltipShown).toBeDefined()
+ expect(tooltipShown).not.toBeNull()
expect(tooltipEl.getAttribute('aria-label')).toEqual('Another tooltip')
done()
})
@@ -1175,7 +1280,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tooltipShown = document.querySelector('.tooltip')
- expect(tooltipShown).toBeDefined()
+ expect(tooltipShown).not.toBeNull()
expect(tooltipEl.getAttribute('aria-label')).toEqual('Different label')
done()
})
@@ -1192,7 +1297,7 @@ describe('Tooltip', () => {
tooltipEl.addEventListener('shown.bs.tooltip', () => {
const tooltipShown = document.querySelector('.tooltip')
- expect(tooltipShown).toBeDefined()
+ expect(tooltipShown).not.toBeNull()
expect(tooltipEl.getAttribute('aria-label')).toBeNull()
done()
})
@@ -1201,6 +1306,60 @@ describe('Tooltip', () => {
})
})
+ describe('getOrCreateInstance', () => {
+ it('should return tooltip instance', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+ const tooltip = new Tooltip(div)
+
+ expect(Tooltip.getOrCreateInstance(div)).toEqual(tooltip)
+ expect(Tooltip.getInstance(div)).toEqual(Tooltip.getOrCreateInstance(div, {}))
+ expect(Tooltip.getOrCreateInstance(div)).toBeInstanceOf(Tooltip)
+ })
+
+ it('should return new instance when there is no tooltip instance', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+
+ expect(Tooltip.getInstance(div)).toEqual(null)
+ expect(Tooltip.getOrCreateInstance(div)).toBeInstanceOf(Tooltip)
+ })
+
+ it('should return new instance when there is no tooltip instance with given configuration', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+
+ expect(Tooltip.getInstance(div)).toEqual(null)
+ const tooltip = Tooltip.getOrCreateInstance(div, {
+ title: () => 'test'
+ })
+ expect(tooltip).toBeInstanceOf(Tooltip)
+
+ expect(tooltip.getTitle()).toEqual('test')
+ })
+
+ it('should return the instance when exists without given configuration', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+ const tooltip = new Tooltip(div, {
+ title: () => 'nothing'
+ })
+ expect(Tooltip.getInstance(div)).toEqual(tooltip)
+
+ const tooltip2 = Tooltip.getOrCreateInstance(div, {
+ title: () => 'test'
+ })
+ expect(tooltip).toBeInstanceOf(Tooltip)
+ expect(tooltip2).toEqual(tooltip)
+
+ expect(tooltip2.getTitle()).toEqual('nothing')
+ })
+ })
+
describe('jQueryInterface', () => {
it('should create a tooltip', () => {
fixtureEl.innerHTML = '<div></div>'
@@ -1212,7 +1371,7 @@ describe('Tooltip', () => {
jQueryMock.fn.tooltip.call(jQueryMock)
- expect(Tooltip.getInstance(div)).toBeDefined()
+ expect(Tooltip.getInstance(div)).not.toBeNull()
})
it('should not re create a tooltip', () => {
@@ -1246,21 +1405,6 @@ describe('Tooltip', () => {
expect(tooltip.show).toHaveBeenCalled()
})
- it('should do nothing when we call dispose or hide if there is no tooltip created', () => {
- fixtureEl.innerHTML = '<div></div>'
-
- const div = fixtureEl.querySelector('div')
-
- spyOn(Tooltip.prototype, 'dispose')
-
- jQueryMock.fn.tooltip = Tooltip.jQueryInterface
- jQueryMock.elements = [div]
-
- jQueryMock.fn.tooltip.call(jQueryMock, 'dispose')
-
- expect(Tooltip.prototype.dispose).not.toHaveBeenCalled()
- })
-
it('should throw error on undefined method', () => {
fixtureEl.innerHTML = '<div></div>'