import Tooltip from '../../src/index.js';
import '@popperjs/test-utils';
import then from '@popperjs/test-utils/utils/then.js';
import '@popperjs/test-utils/utils/customEventPolyfill.js';
const jasmineWrapper = document.getElementById('jasmineWrapper');
const isIE10 = navigator.appVersion.indexOf('MSIE 10') !== -1;
let reference;
let instance;
function createReference() {
reference = document.createElement('div');
reference.style.width = '100px';
reference.style.height = '100px';
reference.style.margin = '100px';
reference.textContent = 'reference';
jasmineWrapper.appendChild(reference);
}
describe('[tooltip.js]', () => {
describe('manual', () => {
beforeEach(() => {
createReference();
});
it('should show tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
});
instance.show();
then(() => {
expect(document.querySelector('.tooltip')).not.toBeNull();
done();
});
});
it('should hide tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
});
instance.show();
then(() => instance.hide());
then(() => {
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
done();
});
});
it('should toggle (show) tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
});
instance.toggle();
then(() => {
expect(document.querySelector('.tooltip')).not.toBeNull();
done();
});
});
it('should toggle (hide) tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
});
instance.show();
then(() => instance.toggle());
then(() => {
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
done();
});
});
it('should show, hide and show again a tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
});
instance.show();
then(() => instance.hide());
then(() => instance.show());
then(() => {
expect(document.querySelector('.tooltip')).not.toBeNull();
done();
});
});
it('should dispose tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
});
instance.show();
then(() => instance.dispose());
then(() => {
expect(document.querySelector('.tooltip')).toBeNull();
});
then(() => {
reference.click();
});
then(() => {
expect(document.querySelector('.tooltip')).toBeNull();
done();
});
});
it('should dispose tooltip despite never being shown', done => {
instance = new Tooltip(reference, {
title: 'foobar',
});
instance.dispose();
then(() => {
expect(document.querySelector('.tooltip')).toBeNull();
});
then(() => {
reference.dispatchEvent(new CustomEvent('mouseenter'));
});
then(() => {
expect(document.querySelector('.tooltip')).toBeNull();
done();
});
});
});
describe('container', () => {
beforeEach(() => {
createReference();
});
it('should show tooltip as child of body', done => {
instance = new Tooltip(reference, {
title: 'foobar',
container: 'body',
});
instance.show();
then(() => {
expect(document.querySelector('.tooltip').parentNode).toBe(
document.body
);
instance.dispose();
done();
});
});
});
describe('content', () => {
beforeEach(() => {
createReference();
});
it('should show text tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').textContent
).toBe('foobar');
done();
});
});
it('should show HTML tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
html: true,
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').innerHTML
).toBe('foobar');
done();
});
});
it('should show stripped out HTML tooltip', done => {
instance = new Tooltip(reference, {
title: 'foobar',
html: true,
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').textContent
).toBe('foobar');
done();
});
});
it('should use a DOM node as tooltip content', done => {
const content = document.createElement('div');
content.textContent = 'foobar';
instance = new Tooltip(reference, {
title: content,
html: true,
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').innerHTML
).toBe('
foobar
');
done();
});
});
it('should use a document fragment as tooltip content', done => {
const content = document.createDocumentFragment();
const inner = document.createElement('div');
inner.textContent = 'test';
content.appendChild(inner);
instance = new Tooltip(reference, {
title: content,
html: true,
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').innerHTML
).toBe('test
');
done();
});
});
it('should use a string returned by a function as tooltip content', done => {
instance = new Tooltip(reference, {
title: () => 'foobar',
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').textContent
).toBe('foobar');
done();
});
});
it('should use a DOM node returned by a function as tooltip content', done => {
const content = document.createElement('div');
content.textContent = 'foobar';
instance = new Tooltip(reference, {
title: () => content,
html: true,
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').innerHTML
).toBe('foobar
');
done();
});
});
it('should use a document fragment returned by a function as tooltip content', done => {
const content = document.createDocumentFragment();
const inner = document.createElement('div');
inner.textContent = 'test';
content.appendChild(inner);
instance = new Tooltip(reference, {
title: () => content,
html: true,
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').innerHTML
).toBe('test
');
done();
});
});
it('should not use dom node returned by function as tooltip content if html option disabled', done => {
const content = document.createElement('div');
content.textContent = 'test';
instance = new Tooltip(reference, {
title: () => content,
html: false,
});
instance.show();
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').innerHTML
).toBe('');
done();
});
});
it('should use a custom template with empty leading&trailing spaces', done => {
instance = new Tooltip(reference, {
title: 'foobar',
template: `
`,
});
instance.show();
then(() => {
expect(document.querySelector('.tooltip .foobar').textContent).toBe(
'foobar'
);
done();
});
});
it('should update title content with String', done => {
const updatedContent = 'Updated string';
instance = new Tooltip(reference, {
title: 'Constructor message',
});
instance.show();
instance.updateTitleContent(updatedContent);
then(() => {
expect(
document.querySelector('.tooltip .tooltip-inner').textContent
).toBe(updatedContent);
done();
});
});
it('should update title content with HTMLElement', done => {
const updatedContent = 'Updated with div element';
const el = document.createElement('div');
el.textContent = updatedContent;
instance = new Tooltip(reference, {
title: 'Constructor message',
html: true,
});
instance.show();
instance.updateTitleContent(el);
then(() => {
expect(document.querySelector('.tooltip-inner').innerHTML).toBe(
el.outerHTML
);
done();
});
});
it('should update the tooltip position when changing the title', done => {
// Unreliable on IE...
if (isIE10) {
pending();
}
const updatedContent = 'Updated string with a different length';
instance = new Tooltip(reference, {
title: 'Constructor message',
});
instance.show();
const oldPosition = document
.querySelector('.tooltip .tooltip-inner')
.getBoundingClientRect().left;
instance.updateTitleContent(updatedContent);
then(() => {
expect(
document
.querySelector('.tooltip .tooltip-inner')
.getBoundingClientRect().left
).not.toBe(oldPosition);
done();
}, 500);
});
});
describe('events', () => {
beforeEach(() => {
createReference();
});
afterEach(() => {
instance.dispose();
jasmineWrapper.innerHTML = '';
});
it('should show a tooltip when hovered', done => {
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'hover',
});
expect(document.querySelector('.tooltip')).toBeNull();
reference.dispatchEvent(new CustomEvent('mouseenter'));
then(() => {
expect(document.querySelector('.tooltip')).not.toBeNull();
done();
});
});
it('should hide a tooltip on reference mouseleave', done => {
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'hover',
});
expect(document.querySelector('.tooltip')).toBeNull();
reference.dispatchEvent(new CustomEvent('mouseenter'));
then(() => reference.dispatchEvent(new CustomEvent('mouseleave')), 200);
then(() => {
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
done();
}, 200);
});
it('should hide a tooltip on tooltip mouseleave', done => {
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'hover',
});
expect(document.querySelector('.tooltip')).toBeNull();
reference.dispatchEvent(new CustomEvent('mouseenter'));
then(() => reference.dispatchEvent(new CustomEvent('mouseleave')));
then(() =>
document
.querySelector('.tooltip')
.dispatchEvent(new CustomEvent('mouseenter'))
);
then(() =>
document
.querySelector('.tooltip')
.dispatchEvent(new CustomEvent('mouseleave'))
);
then(() => {
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
done();
}, 200);
});
it('should not hide a tooltip if user mouseenter tooltip and then mouseenter reference element again', done => {
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'hover',
});
expect(document.querySelector('.tooltip')).toBeNull();
reference.dispatchEvent(new CustomEvent('mouseenter'));
then(() => reference.dispatchEvent(new CustomEvent('mouseleave')));
then(() =>
document
.querySelector('.tooltip')
.dispatchEvent(new CustomEvent('mouseenter'))
);
then(() =>
document
.querySelector('.tooltip')
.dispatchEvent(new CustomEvent('mouseleave'))
);
then(() => reference.dispatchEvent(new CustomEvent('mouseenter')));
then(() => {
expect(document.querySelector('.tooltip').style.visibility).toBe('visible');
done();
}, 200);
});
it('should show a tooltip on click', done => {
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'click',
});
expect(document.querySelector('.tooltip')).toBeNull();
reference.dispatchEvent(new CustomEvent('click'));
then(() => {
expect(document.querySelector('.tooltip')).not.toBeNull();
done();
});
});
it('should not show tooltip if mouse leaves reference before tooltip is shown', done => {
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'hover',
delay: { show: 1000, hide: 0 },
});
expect(document.querySelector('.tooltip')).toBeNull();
reference.dispatchEvent(new CustomEvent('mouseenter'));
reference.dispatchEvent(new CustomEvent('mouseleave'));
then(() => {
expect(document.querySelector('.tooltip')).toBeNull();
done();
});
});
it('should not flash the tooltip if mouse leaves reference before tooltip is shown', done => {
const delay = 500;
const hoverTime = 200;
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'hover',
delay,
});
spyOn(instance, '_show');
expect(document.querySelector('.tooltip')).toBeNull();
then(() => reference.dispatchEvent(new CustomEvent('mouseenter')), hoverTime);
then(() => reference.dispatchEvent(new CustomEvent('mouseleave')), delay * 2);
then(() => {
expect(instance._show).not.toHaveBeenCalled();
done()
});
});
it('should hide a tooltip on click while open', done => {
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'click',
});
expect(document.querySelector('.tooltip')).toBeNull();
reference.dispatchEvent(new CustomEvent('click'));
then(() => reference.dispatchEvent(new CustomEvent('click')));
then(() => {
expect(document.querySelector('.tooltip').style.visibility).toBe('hidden');
done();
});
});
});
describe('options', () => {
beforeEach(() => {
createReference();
});
it('should proxy the `options.offset` value to the Popper.js instance', done => {
instance = new Tooltip(reference, {
title: 'test',
offset: 10,
}).show();
expect(instance._popperOptions.modifiers.offset.offset).toBe(10);
done();
});
it('should proxy a `offset` modifier to the Popper.js instance', done => {
instance = new Tooltip(reference, {
title: 'test',
popperOptions: {
modifiers: { offset: { offset: 10 } },
},
}).show();
expect(instance._popperOptions.modifiers.offset.offset).toBe(10);
done();
});
it('should hide on click outside with `options.closeOnClickOutside`', done => {
instance = new Tooltip(reference, {
title: 'foobar',
trigger: 'click',
closeOnClickOutside: true,
});
reference.dispatchEvent(new CustomEvent('click'));
then(() => expect(document.querySelector('.tooltip')).not.toBeNull());
then(() => document.body.dispatchEvent(new CustomEvent('mousedown')));
then(() => expect(document.querySelector('.tooltip').style.visibility).toBe('hidden'));
then(done);
});
it('should proxy the arrow modifier to the Popper.js instance', done => {
instance = new Tooltip(reference, {
title: 'test',
popperOptions: {
modifiers: {
arrow: {
enabled: false,
},
},
},
}).show();
expect(instance._popperOptions.modifiers.arrow.enabled).toBe(false);
done();
});
it('should proxy the offset modifier to the Popper.js instance', done => {
instance = new Tooltip(reference, {
title: 'test',
popperOptions: {
modifiers: {
offset: {
enabled: false,
},
},
},
}).show();
expect(instance._popperOptions.modifiers.offset.enabled).toBe(false);
done();
});
});
});