diff options
| author | Patrick H. Lauke <[email protected]> | 2021-05-04 12:46:06 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-05-04 12:46:06 +0100 |
| commit | 8865a8ab1c7157ab81bf49afa62b75f36daee46d (patch) | |
| tree | 97ef78f2ea8e07aab50014176d061fe3c1d49134 /js/tests/unit/tooltip.spec.js | |
| parent | 018ee6a3b50b958ddb49657086cd9168abf5a485 (diff) | |
| parent | 7ea6578773cb1b7f5cfb8fb41321b3fa10349daf (diff) | |
| download | bootstrap-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.js | 221 |
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}"`) }) }) }) |
