aboutsummaryrefslogtreecommitdiff
path: root/js/tests/unit/tooltip.spec.js
diff options
context:
space:
mode:
authorPatrick H. Lauke <[email protected]>2021-05-04 12:46:06 +0100
committerGitHub <[email protected]>2021-05-04 12:46:06 +0100
commit8865a8ab1c7157ab81bf49afa62b75f36daee46d (patch)
tree97ef78f2ea8e07aab50014176d061fe3c1d49134 /js/tests/unit/tooltip.spec.js
parent018ee6a3b50b958ddb49657086cd9168abf5a485 (diff)
parent7ea6578773cb1b7f5cfb8fb41321b3fa10349daf (diff)
downloadbootstrap-jo-docs-thanks-page.tar.xz
bootstrap-jo-docs-thanks-page.zip
Merge branch 'main' into jo-docs-thanks-pagejo-docs-thanks-page
Diffstat (limited to 'js/tests/unit/tooltip.spec.js')
-rw-r--r--js/tests/unit/tooltip.spec.js221
1 files changed, 194 insertions, 27 deletions
diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js
index 9ea9096de..399f1f22a 100644
--- a/js/tests/unit/tooltip.spec.js
+++ b/js/tests/unit/tooltip.spec.js
@@ -63,6 +63,17 @@ 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">'
@@ -107,6 +118,41 @@ describe('Tooltip', () => {
tooltipInContainerEl.click()
})
+ it('should create offset modifier when offset is passed as a function', done => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Offset from function">'
+
+ const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ offset: getOffset,
+ popperConfig: {
+ onFirstUpdate: state => {
+ expect(getOffset).toHaveBeenCalledWith({
+ popper: state.rects.popper,
+ reference: state.rects.reference,
+ placement: state.placement
+ }, tooltipEl)
+ done()
+ }
+ }
+ })
+
+ const offset = tooltip._getOffset()
+
+ expect(typeof offset).toEqual('function')
+
+ tooltip.show()
+ })
+
+ it('should create offset modifier when offset option is passed in data attribute', () => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-offset="10,20" title="Another tooltip">'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ expect(tooltip._getOffset()).toEqual([10, 20])
+ })
+
it('should allow to pass config to Popper with `popperConfig`', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
@@ -121,6 +167,21 @@ describe('Tooltip', () => {
expect(popperConfig.placement).toEqual('left')
})
+
+ it('should allow to pass config to Popper with `popperConfig` as a function', () => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const getPopperConfig = jasmine.createSpy('getPopperConfig').and.returnValue({ placement: 'left' })
+ const tooltip = new Tooltip(tooltipEl, {
+ popperConfig: getPopperConfig
+ })
+
+ const popperConfig = tooltip._getPopperConfig('top')
+
+ expect(getPopperConfig).toHaveBeenCalled()
+ expect(popperConfig.placement).toEqual('left')
+ })
})
describe('enable', () => {
@@ -277,13 +338,45 @@ describe('Tooltip', () => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
const tooltipEl = fixtureEl.querySelector('a')
+ const addEventSpy = spyOn(tooltipEl, 'addEventListener').and.callThrough()
+ const removeEventSpy = spyOn(tooltipEl, 'removeEventListener').and.callThrough()
+
const tooltip = new Tooltip(tooltipEl)
expect(Tooltip.getInstance(tooltipEl)).toEqual(tooltip)
+ const expectedArgs = [
+ ['mouseover', jasmine.any(Function), jasmine.any(Boolean)],
+ ['mouseout', jasmine.any(Function), jasmine.any(Boolean)],
+ ['focusin', jasmine.any(Function), jasmine.any(Boolean)],
+ ['focusout', jasmine.any(Function), jasmine.any(Boolean)]
+ ]
+
+ expect(addEventSpy.calls.allArgs()).toEqual(expectedArgs)
+
tooltip.dispose()
expect(Tooltip.getInstance(tooltipEl)).toEqual(null)
+ expect(removeEventSpy.calls.allArgs()).toEqual(expectedArgs)
+ })
+
+ it('should destroy a tooltip after it is shown and hidden', done => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ tooltip.hide()
+ })
+ tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+ tooltip.dispose()
+ expect(tooltip.tip).toEqual(null)
+ expect(Tooltip.getInstance(tooltipEl)).toEqual(null)
+ done()
+ })
+
+ tooltip.show()
})
it('should destroy a tooltip and remove it from the dom', done => {
@@ -357,7 +450,7 @@ describe('Tooltip', () => {
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()
})
@@ -483,24 +576,6 @@ describe('Tooltip', () => {
tooltip.show()
})
- it('should show a tooltip with offset as a function', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
-
- const spy = jasmine.createSpy('offset').and.returnValue({})
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- offset: spy
- })
-
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeDefined()
- expect(spy).toHaveBeenCalled()
- done()
- })
-
- tooltip.show()
- })
-
it('should show a tooltip without the animation', done => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
@@ -633,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">'
@@ -720,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()
})
@@ -789,18 +958,18 @@ describe('Tooltip', () => {
})
describe('update', () => {
- it('should call popper schedule update', done => {
+ it('should call popper update', done => {
fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
tooltipEl.addEventListener('shown.bs.tooltip', () => {
- spyOn(tooltip._popper, 'scheduleUpdate')
+ spyOn(tooltip._popper, 'update')
tooltip.update()
- expect(tooltip._popper.scheduleUpdate).toHaveBeenCalled()
+ expect(tooltip._popper.update).toHaveBeenCalled()
done()
})
@@ -1206,11 +1375,9 @@ describe('Tooltip', () => {
jQueryMock.fn.tooltip = Tooltip.jQueryInterface
jQueryMock.elements = [div]
- try {
+ expect(() => {
jQueryMock.fn.tooltip.call(jQueryMock, action)
- } catch (error) {
- expect(error.message).toEqual(`No method named "${action}"`)
- }
+ }).toThrowError(TypeError, `No method named "${action}"`)
})
})
})