diff options
Diffstat (limited to 'spec/frontend/tooltips/components/tooltips_spec.js')
-rw-r--r-- | spec/frontend/tooltips/components/tooltips_spec.js | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/spec/frontend/tooltips/components/tooltips_spec.js b/spec/frontend/tooltips/components/tooltips_spec.js new file mode 100644 index 00000000000..0edc5248629 --- /dev/null +++ b/spec/frontend/tooltips/components/tooltips_spec.js @@ -0,0 +1,202 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlTooltip } from '@gitlab/ui'; +import { useMockMutationObserver } from 'helpers/mock_dom_observer'; +import Tooltips from '~/tooltips/components/tooltips.vue'; + +describe('tooltips/components/tooltips.vue', () => { + const { trigger: triggerMutate, observersCount } = useMockMutationObserver(); + let wrapper; + + const buildWrapper = () => { + wrapper = shallowMount(Tooltips); + }; + + const createTooltipTarget = (attributes = {}) => { + const target = document.createElement('button'); + const defaults = { + title: 'default title', + ...attributes, + }; + + Object.keys(defaults).forEach(name => { + target.setAttribute(name, defaults[name]); + }); + + document.body.appendChild(target); + + return target; + }; + + const allTooltips = () => wrapper.findAll(GlTooltip); + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('addTooltips', () => { + let target; + + beforeEach(() => { + buildWrapper(); + + target = createTooltipTarget(); + }); + + it('attaches tooltips to the targets specified', async () => { + wrapper.vm.addTooltips([target]); + + await wrapper.vm.$nextTick(); + + expect(wrapper.find(GlTooltip).props('target')).toBe(target); + }); + + it('does not attach a tooltip twice to the same element', async () => { + wrapper.vm.addTooltips([target]); + wrapper.vm.addTooltips([target]); + + await wrapper.vm.$nextTick(); + + expect(wrapper.findAll(GlTooltip)).toHaveLength(1); + }); + + it('sets tooltip content from title attribute', async () => { + wrapper.vm.addTooltips([target]); + + await wrapper.vm.$nextTick(); + + expect(wrapper.find(GlTooltip).text()).toBe(target.getAttribute('title')); + }); + + it('supports HTML content', async () => { + target = createTooltipTarget({ + title: 'content with <b>HTML</b>', + 'data-html': true, + }); + wrapper.vm.addTooltips([target]); + + await wrapper.vm.$nextTick(); + + expect(wrapper.find(GlTooltip).html()).toContain(target.getAttribute('title')); + }); + + it.each` + attribute | value | prop + ${'data-placement'} | ${'bottom'} | ${'placement'} + ${'data-container'} | ${'custom-container'} | ${'container'} + ${'data-boundary'} | ${'viewport'} | ${'boundary'} + ${'data-triggers'} | ${'manual'} | ${'triggers'} + `( + 'sets $prop to $value when $attribute is set in target', + async ({ attribute, value, prop }) => { + target = createTooltipTarget({ [attribute]: value }); + wrapper.vm.addTooltips([target]); + + await wrapper.vm.$nextTick(); + + expect(wrapper.find(GlTooltip).props(prop)).toBe(value); + }, + ); + }); + + describe('dispose', () => { + beforeEach(() => { + buildWrapper(); + }); + + it('removes all tooltips when elements is nil', async () => { + wrapper.vm.addTooltips([createTooltipTarget(), createTooltipTarget()]); + await wrapper.vm.$nextTick(); + + wrapper.vm.dispose(); + await wrapper.vm.$nextTick(); + + expect(allTooltips()).toHaveLength(0); + }); + + it('removes the tooltips that target the elements specified', async () => { + const target = createTooltipTarget(); + + wrapper.vm.addTooltips([target, createTooltipTarget()]); + await wrapper.vm.$nextTick(); + + wrapper.vm.dispose(target); + await wrapper.vm.$nextTick(); + + expect(allTooltips()).toHaveLength(1); + }); + }); + + describe('observe', () => { + beforeEach(() => { + buildWrapper(); + }); + + it('removes tooltip when target is removed from the document', async () => { + const target = createTooltipTarget(); + + wrapper.vm.addTooltips([target, createTooltipTarget()]); + await wrapper.vm.$nextTick(); + + triggerMutate(document.body, { + entry: { removedNodes: [target] }, + options: { childList: true }, + }); + await wrapper.vm.$nextTick(); + + expect(allTooltips()).toHaveLength(1); + }); + }); + + describe('triggerEvent', () => { + it('triggers a bootstrap-vue tooltip global event for the tooltip specified', async () => { + const target = createTooltipTarget(); + const event = 'hide'; + + buildWrapper(); + + wrapper.vm.addTooltips([target]); + + await wrapper.vm.$nextTick(); + + wrapper.vm.triggerEvent(target, event); + + expect(wrapper.find(GlTooltip).emitted(event)).toHaveLength(1); + }); + }); + + describe('fixTitle', () => { + it('updates tooltip content with the latest value the target title property', async () => { + const target = createTooltipTarget(); + const currentTitle = 'title'; + const newTitle = 'new title'; + + target.setAttribute('title', currentTitle); + + buildWrapper(); + + wrapper.vm.addTooltips([target]); + + await wrapper.vm.$nextTick(); + + expect(wrapper.find(GlTooltip).text()).toBe(currentTitle); + + target.setAttribute('title', newTitle); + wrapper.vm.fixTitle(target); + + await wrapper.vm.$nextTick(); + + expect(wrapper.find(GlTooltip).text()).toBe(newTitle); + }); + }); + + it('disconnects mutation observer on beforeDestroy', () => { + buildWrapper(); + wrapper.vm.addTooltips([createTooltipTarget()]); + + expect(observersCount()).toBe(1); + + wrapper.destroy(); + expect(observersCount()).toBe(0); + }); +}); |