diff options
| author | XhmikosR <[email protected]> | 2021-08-18 07:29:56 +0300 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-08-18 07:29:56 +0300 |
| commit | 433a148c9e61aa942801fd8101dfa5c4045fdaed (patch) | |
| tree | f41db59fd06019169df5ea0338213ec0e298f226 /js/tests/unit/tooltip.spec.js | |
| parent | b97cfa163b5098db70e03b27c91fca5dde9c267e (diff) | |
| parent | 18b3e1ac71f73d006756684a285c5a818e2d1454 (diff) | |
| download | bootstrap-global-focus-vars.tar.xz bootstrap-global-focus-vars.zip | |
Merge branch 'main' into global-focus-varsglobal-focus-vars
Diffstat (limited to 'js/tests/unit/tooltip.spec.js')
| -rw-r--r-- | js/tests/unit/tooltip.spec.js | 230 |
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>' |
