From a09983ae35713f5a2bbb100981116d31ce99826e Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 20 Jul 2020 12:26:25 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-2-stable-ee --- spec/javascripts/boards/components/board_spec.js | 251 ---------------- spec/javascripts/boards/mock_data.js | 1 - spec/javascripts/fly_out_nav_browser_spec.js | 333 +++++++++++++++++++++ spec/javascripts/fly_out_nav_spec.js | 329 -------------------- spec/javascripts/helpers/class_spec_helper.js | 9 - .../helpers/filtered_search_spec_helper.js | 1 - spec/javascripts/helpers/index.js | 3 - .../javascripts/helpers/init_vue_mr_page_helper.js | 46 --- spec/javascripts/helpers/locale_helper.js | 11 - .../helpers/set_timeout_promise_helper.js | 4 - spec/javascripts/helpers/text_helper.js | 18 -- spec/javascripts/helpers/tracking_helper.js | 5 - spec/javascripts/helpers/user_mock_data_helper.js | 14 - .../helpers/vue_mount_component_helper.js | 2 - spec/javascripts/helpers/vue_test_utils_helper.js | 5 - spec/javascripts/helpers/vuex_action_helper.js | 102 ------- spec/javascripts/helpers/wait_for_promises.js | 1 - spec/javascripts/jobs/mock_data.js | 2 - spec/javascripts/matchers.js | 55 ---- .../components/dashboard_resize_browser_spec.js | 6 +- .../components/tooltip_on_truncate_browser_spec.js | 240 +++++++++++++++ .../components/tooltip_on_truncate_spec.js | 236 --------------- 22 files changed, 576 insertions(+), 1098 deletions(-) delete mode 100644 spec/javascripts/boards/components/board_spec.js delete mode 100644 spec/javascripts/boards/mock_data.js create mode 100644 spec/javascripts/fly_out_nav_browser_spec.js delete mode 100644 spec/javascripts/fly_out_nav_spec.js delete mode 100644 spec/javascripts/helpers/class_spec_helper.js delete mode 100644 spec/javascripts/helpers/filtered_search_spec_helper.js delete mode 100644 spec/javascripts/helpers/index.js delete mode 100644 spec/javascripts/helpers/init_vue_mr_page_helper.js delete mode 100644 spec/javascripts/helpers/locale_helper.js delete mode 100644 spec/javascripts/helpers/set_timeout_promise_helper.js delete mode 100644 spec/javascripts/helpers/text_helper.js delete mode 100644 spec/javascripts/helpers/tracking_helper.js delete mode 100644 spec/javascripts/helpers/user_mock_data_helper.js delete mode 100644 spec/javascripts/helpers/vue_mount_component_helper.js delete mode 100644 spec/javascripts/helpers/vue_test_utils_helper.js delete mode 100644 spec/javascripts/helpers/vuex_action_helper.js delete mode 100644 spec/javascripts/helpers/wait_for_promises.js delete mode 100644 spec/javascripts/jobs/mock_data.js create mode 100644 spec/javascripts/vue_shared/components/tooltip_on_truncate_browser_spec.js delete mode 100644 spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js (limited to 'spec/javascripts') diff --git a/spec/javascripts/boards/components/board_spec.js b/spec/javascripts/boards/components/board_spec.js deleted file mode 100644 index 86a2a10b7a0..00000000000 --- a/spec/javascripts/boards/components/board_spec.js +++ /dev/null @@ -1,251 +0,0 @@ -import Vue from 'vue'; -import Board from '~/boards/components/board'; -import List from '~/boards/models/list'; - -describe('Board component', () => { - let vm; - - const createComponent = ({ gon = {}, collapsed = false, listType = 'backlog' } = {}) => { - if (Object.prototype.hasOwnProperty.call(gon, 'current_user_id')) { - window.gon = gon; - } else { - window.gon = {}; - } - const el = document.createElement('div'); - document.body.appendChild(el); - - vm = new Board({ - propsData: { - boardId: '1', - disabled: false, - issueLinkBase: '/', - rootPath: '/', - list: new List({ - id: 1, - position: 0, - title: 'test', - list_type: listType, - collapsed, - }), - }, - }).$mount(el); - }; - - const setUpTests = (done, opts = {}) => { - loadFixtures('boards/show.html'); - - createComponent(opts); - - Vue.nextTick(done); - }; - - const cleanUpTests = spy => { - if (spy) { - spy.calls.reset(); - } - - vm.$destroy(); - - // remove the component from the DOM - document.querySelector('.board').remove(); - - localStorage.removeItem(`${vm.uniqueKey}.expanded`); - }; - - describe('List', () => { - it('board is expandable when list type is closed', () => { - expect(new List({ id: 1, list_type: 'closed' }).isExpandable).toBe(true); - }); - - it('board is expandable when list type is label', () => { - expect(new List({ id: 1, list_type: 'closed' }).isExpandable).toBe(true); - }); - - it('board is not expandable when list type is blank', () => { - expect(new List({ id: 1, list_type: 'blank' }).isExpandable).toBe(false); - }); - }); - - describe('when clicking the header', () => { - beforeEach(done => { - setUpTests(done); - }); - - afterEach(() => { - cleanUpTests(); - }); - - it('does not collapse', done => { - vm.list.isExpanded = true; - vm.$el.querySelector('.board-header').click(); - - Vue.nextTick() - .then(() => { - expect(vm.$el.classList.contains('is-collapsed')).toBe(false); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('when clicking the collapse icon', () => { - beforeEach(done => { - setUpTests(done); - }); - - afterEach(() => { - cleanUpTests(); - }); - - it('collapses', done => { - Vue.nextTick() - .then(() => { - vm.$el.querySelector('.board-title-caret').click(); - }) - .then(() => { - expect(vm.$el.classList.contains('is-collapsed')).toBe(true); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('when clicking the expand icon', () => { - beforeEach(done => { - setUpTests(done); - }); - - afterEach(() => { - cleanUpTests(); - }); - - it('expands', done => { - vm.list.isExpanded = false; - - Vue.nextTick() - .then(() => { - vm.$el.querySelector('.board-title-caret').click(); - }) - .then(() => { - expect(vm.$el.classList.contains('is-collapsed')).toBe(false); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('when collapsed is false', () => { - beforeEach(done => { - setUpTests(done); - }); - - afterEach(() => { - cleanUpTests(); - }); - - it('is expanded when collapsed is false', () => { - expect(vm.list.isExpanded).toBe(true); - expect(vm.$el.classList.contains('is-collapsed')).toBe(false); - }); - }); - - describe('when list type is blank', () => { - beforeEach(done => { - setUpTests(done, { listType: 'blank' }); - }); - - afterEach(() => { - cleanUpTests(); - }); - - it('does not render add issue button when list type is blank', done => { - Vue.nextTick(() => { - expect(vm.$el.querySelector('.issue-count-badge-add-button')).toBeNull(); - - done(); - }); - }); - }); - - describe('when list type is backlog', () => { - beforeEach(done => { - setUpTests(done); - }); - - afterEach(() => { - cleanUpTests(); - }); - - it('board is expandable', () => { - expect(vm.$el.classList.contains('is-expandable')).toBe(true); - }); - }); - - describe('when logged in', () => { - let spy; - - beforeEach(done => { - spy = spyOn(List.prototype, 'update'); - setUpTests(done, { gon: { current_user_id: 1 } }); - }); - - afterEach(() => { - cleanUpTests(spy); - }); - - it('calls list update', done => { - Vue.nextTick() - .then(() => { - vm.$el.querySelector('.board-title-caret').click(); - }) - .then(() => { - expect(vm.list.update).toHaveBeenCalledTimes(1); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('when logged out', () => { - let spy; - beforeEach(done => { - spy = spyOn(List.prototype, 'update'); - setUpTests(done, { collapsed: false }); - }); - - afterEach(() => { - cleanUpTests(spy); - }); - - // can only be one or the other cant toggle window.gon.current_user_id states. - it('clicking on the caret does not call list update', done => { - Vue.nextTick() - .then(() => { - vm.$el.querySelector('.board-title-caret').click(); - }) - .then(() => { - expect(vm.list.update).toHaveBeenCalledTimes(0); - }) - .then(done) - .catch(done.fail); - }); - - it('sets expanded to be the opposite of its value when toggleExpanded is called', done => { - const expanded = true; - vm.list.isExpanded = expanded; - vm.toggleExpanded(); - - Vue.nextTick() - .then(() => { - expect(vm.list.isExpanded).toBe(!expanded); - expect(localStorage.getItem(`${vm.uniqueKey}.expanded`)).toBe(String(!expanded)); - }) - .then(done) - .catch(done.fail); - }); - - it('does render add issue button', () => { - expect(vm.$el.querySelector('.issue-count-badge-add-button')).not.toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/boards/mock_data.js b/spec/javascripts/boards/mock_data.js deleted file mode 100644 index 8b39ad1abb4..00000000000 --- a/spec/javascripts/boards/mock_data.js +++ /dev/null @@ -1 +0,0 @@ -export * from '../../frontend/boards/mock_data'; diff --git a/spec/javascripts/fly_out_nav_browser_spec.js b/spec/javascripts/fly_out_nav_browser_spec.js new file mode 100644 index 00000000000..f84cee72042 --- /dev/null +++ b/spec/javascripts/fly_out_nav_browser_spec.js @@ -0,0 +1,333 @@ +// this file can't be migrated to jest because it relies on the browser to perform integration tests: +// (specifically getClientBoundingRect and mouse movements) +// see: https://gitlab.com/groups/gitlab-org/-/epics/895#what-if-theres-a-karma-spec-which-is-simply-unmovable-to-jest-ie-it-is-dependent-on-a-running-browser-environment + +import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; +import { + calculateTop, + showSubLevelItems, + canShowSubItems, + canShowActiveSubItems, + mouseEnterTopItems, + mouseLeaveTopItem, + getOpenMenu, + setOpenMenu, + mousePos, + getHideSubItemsInterval, + documentMouseMove, + getHeaderHeight, + setSidebar, + subItemsMouseLeave, +} from '~/fly_out_nav'; +import { SIDEBAR_COLLAPSED_CLASS } from '~/contextual_sidebar'; + +describe('Fly out sidebar navigation', () => { + let el; + let breakpointSize = 'lg'; + + beforeEach(() => { + el = document.createElement('div'); + el.style.position = 'relative'; + document.body.appendChild(el); + + spyOn(GlBreakpointInstance, 'getBreakpointSize').and.callFake(() => breakpointSize); + + setOpenMenu(null); + }); + + afterEach(() => { + document.body.innerHTML = ''; + breakpointSize = 'lg'; + mousePos.length = 0; + + setSidebar(null); + }); + + describe('calculateTop', () => { + it('returns boundingRect top', () => { + const boundingRect = { + top: 100, + height: 100, + }; + + expect(calculateTop(boundingRect, 100)).toBe(100); + }); + + it('returns boundingRect - bottomOverflow', () => { + const boundingRect = { + top: window.innerHeight - 50, + height: 100, + }; + + expect(calculateTop(boundingRect, 100)).toBe(window.innerHeight - 50); + }); + }); + + describe('getHideSubItemsInterval', () => { + beforeEach(() => { + el.innerHTML = + ''; + }); + + it('returns 0 if currentOpenMenu is nil', () => { + expect(getHideSubItemsInterval()).toBe(0); + }); + + it('returns 0 if mousePos is empty', () => { + expect(getHideSubItemsInterval()).toBe(0); + }); + + it('returns 0 when mouse above sub-items', () => { + showSubLevelItems(el); + documentMouseMove({ + clientX: el.getBoundingClientRect().left, + clientY: el.getBoundingClientRect().top, + }); + documentMouseMove({ + clientX: el.getBoundingClientRect().left, + clientY: el.getBoundingClientRect().top - 50, + }); + + expect(getHideSubItemsInterval()).toBe(0); + }); + + it('returns 0 when mouse is below sub-items', () => { + const subItems = el.querySelector('.sidebar-sub-level-items'); + + showSubLevelItems(el); + documentMouseMove({ + clientX: el.getBoundingClientRect().left, + clientY: el.getBoundingClientRect().top, + }); + documentMouseMove({ + clientX: el.getBoundingClientRect().left, + clientY: el.getBoundingClientRect().top - subItems.getBoundingClientRect().height + 50, + }); + + expect(getHideSubItemsInterval()).toBe(0); + }); + + it('returns 300 when mouse is moved towards sub-items', () => { + documentMouseMove({ + clientX: el.getBoundingClientRect().left, + clientY: el.getBoundingClientRect().top, + }); + showSubLevelItems(el); + documentMouseMove({ + clientX: el.getBoundingClientRect().left + 20, + clientY: el.getBoundingClientRect().top + 10, + }); + + expect(getHideSubItemsInterval()).toBe(300); + }); + }); + + describe('mouseLeaveTopItem', () => { + beforeEach(() => { + spyOn(el.classList, 'remove'); + }); + + it('removes is-over class if currentOpenMenu is null', () => { + mouseLeaveTopItem(el); + + expect(el.classList.remove).toHaveBeenCalledWith('is-over'); + }); + + it('removes is-over class if currentOpenMenu is null & there are sub-items', () => { + el.innerHTML = ''; + + mouseLeaveTopItem(el); + + expect(el.classList.remove).toHaveBeenCalledWith('is-over'); + }); + + it('does not remove is-over class if currentOpenMenu is the passed in sub-items', () => { + el.innerHTML = ''; + + setOpenMenu(el.querySelector('.sidebar-sub-level-items')); + mouseLeaveTopItem(el); + + expect(el.classList.remove).not.toHaveBeenCalled(); + }); + }); + + describe('mouseEnterTopItems', () => { + beforeEach(() => { + el.innerHTML = + ''; + }); + + it('shows sub-items after 0ms if no menu is open', done => { + mouseEnterTopItems(el); + + expect(getHideSubItemsInterval()).toBe(0); + + setTimeout(() => { + expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); + + done(); + }); + }); + + it('shows sub-items after 300ms if a menu is currently open', done => { + documentMouseMove({ + clientX: el.getBoundingClientRect().left, + clientY: el.getBoundingClientRect().top, + }); + + setOpenMenu(el.querySelector('.sidebar-sub-level-items')); + + documentMouseMove({ + clientX: el.getBoundingClientRect().left + 20, + clientY: el.getBoundingClientRect().top + 10, + }); + + mouseEnterTopItems(el, 0); + + expect(getHideSubItemsInterval()).toBe(300); + + setTimeout(() => { + expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); + + done(); + }); + }); + }); + + describe('showSubLevelItems', () => { + beforeEach(() => { + el.innerHTML = ''; + }); + + it('adds is-over class to el', () => { + spyOn(el.classList, 'add'); + + showSubLevelItems(el); + + expect(el.classList.add).toHaveBeenCalledWith('is-over'); + }); + + it('does not show sub-items on mobile', () => { + breakpointSize = 'xs'; + + showSubLevelItems(el); + + expect(el.querySelector('.sidebar-sub-level-items').style.display).not.toBe('block'); + }); + + it('shows sub-items', () => { + showSubLevelItems(el); + + expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); + }); + + it('shows collapsed only sub-items if icon only sidebar', () => { + const subItems = el.querySelector('.sidebar-sub-level-items'); + const sidebar = document.createElement('div'); + sidebar.classList.add(SIDEBAR_COLLAPSED_CLASS); + subItems.classList.add('is-fly-out-only'); + + setSidebar(sidebar); + + showSubLevelItems(el); + + expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); + }); + + it('does not show collapsed only sub-items if icon only sidebar', () => { + const subItems = el.querySelector('.sidebar-sub-level-items'); + subItems.classList.add('is-fly-out-only'); + + showSubLevelItems(el); + + expect(subItems.style.display).not.toBe('block'); + }); + + it('sets transform of sub-items', () => { + const sidebar = document.createElement('div'); + const subItems = el.querySelector('.sidebar-sub-level-items'); + + sidebar.style.width = '200px'; + + document.body.appendChild(sidebar); + + setSidebar(sidebar); + showSubLevelItems(el); + + expect(subItems.style.transform).toBe( + `translate3d(200px, ${Math.floor(el.getBoundingClientRect().top) - + getHeaderHeight()}px, 0px)`, + ); + }); + + it('sets is-above when element is above', () => { + const subItems = el.querySelector('.sidebar-sub-level-items'); + subItems.style.height = `${window.innerHeight + el.offsetHeight}px`; + el.style.top = `${window.innerHeight - el.offsetHeight}px`; + + spyOn(subItems.classList, 'add'); + + showSubLevelItems(el); + + expect(subItems.classList.add).toHaveBeenCalledWith('is-above'); + }); + }); + + describe('canShowSubItems', () => { + it('returns true if on desktop size', () => { + expect(canShowSubItems()).toBeTruthy(); + }); + + it('returns false if on mobile size', () => { + breakpointSize = 'xs'; + + expect(canShowSubItems()).toBeFalsy(); + }); + }); + + describe('canShowActiveSubItems', () => { + it('returns true by default', () => { + expect(canShowActiveSubItems(el)).toBeTruthy(); + }); + + it('returns false when active & expanded sidebar', () => { + const sidebar = document.createElement('div'); + el.classList.add('active'); + + setSidebar(sidebar); + + expect(canShowActiveSubItems(el)).toBeFalsy(); + }); + + it('returns true when active & collapsed sidebar', () => { + const sidebar = document.createElement('div'); + sidebar.classList.add(SIDEBAR_COLLAPSED_CLASS); + el.classList.add('active'); + + setSidebar(sidebar); + + expect(canShowActiveSubItems(el)).toBeTruthy(); + }); + }); + + describe('subItemsMouseLeave', () => { + beforeEach(() => { + el.innerHTML = ''; + + setOpenMenu(el.querySelector('.sidebar-sub-level-items')); + }); + + it('hides subMenu if element is not hovered', () => { + subItemsMouseLeave(el); + + expect(getOpenMenu()).toBeNull(); + }); + + it('does not hide subMenu if element is hovered', () => { + el.classList.add('is-over'); + subItemsMouseLeave(el); + + expect(getOpenMenu()).not.toBeNull(); + }); + }); +}); diff --git a/spec/javascripts/fly_out_nav_spec.js b/spec/javascripts/fly_out_nav_spec.js deleted file mode 100644 index afcf132bea3..00000000000 --- a/spec/javascripts/fly_out_nav_spec.js +++ /dev/null @@ -1,329 +0,0 @@ -import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; -import { - calculateTop, - showSubLevelItems, - canShowSubItems, - canShowActiveSubItems, - mouseEnterTopItems, - mouseLeaveTopItem, - getOpenMenu, - setOpenMenu, - mousePos, - getHideSubItemsInterval, - documentMouseMove, - getHeaderHeight, - setSidebar, - subItemsMouseLeave, -} from '~/fly_out_nav'; -import { SIDEBAR_COLLAPSED_CLASS } from '~/contextual_sidebar'; - -describe('Fly out sidebar navigation', () => { - let el; - let breakpointSize = 'lg'; - - beforeEach(() => { - el = document.createElement('div'); - el.style.position = 'relative'; - document.body.appendChild(el); - - spyOn(GlBreakpointInstance, 'getBreakpointSize').and.callFake(() => breakpointSize); - - setOpenMenu(null); - }); - - afterEach(() => { - document.body.innerHTML = ''; - breakpointSize = 'lg'; - mousePos.length = 0; - - setSidebar(null); - }); - - describe('calculateTop', () => { - it('returns boundingRect top', () => { - const boundingRect = { - top: 100, - height: 100, - }; - - expect(calculateTop(boundingRect, 100)).toBe(100); - }); - - it('returns boundingRect - bottomOverflow', () => { - const boundingRect = { - top: window.innerHeight - 50, - height: 100, - }; - - expect(calculateTop(boundingRect, 100)).toBe(window.innerHeight - 50); - }); - }); - - describe('getHideSubItemsInterval', () => { - beforeEach(() => { - el.innerHTML = - ''; - }); - - it('returns 0 if currentOpenMenu is nil', () => { - expect(getHideSubItemsInterval()).toBe(0); - }); - - it('returns 0 if mousePos is empty', () => { - expect(getHideSubItemsInterval()).toBe(0); - }); - - it('returns 0 when mouse above sub-items', () => { - showSubLevelItems(el); - documentMouseMove({ - clientX: el.getBoundingClientRect().left, - clientY: el.getBoundingClientRect().top, - }); - documentMouseMove({ - clientX: el.getBoundingClientRect().left, - clientY: el.getBoundingClientRect().top - 50, - }); - - expect(getHideSubItemsInterval()).toBe(0); - }); - - it('returns 0 when mouse is below sub-items', () => { - const subItems = el.querySelector('.sidebar-sub-level-items'); - - showSubLevelItems(el); - documentMouseMove({ - clientX: el.getBoundingClientRect().left, - clientY: el.getBoundingClientRect().top, - }); - documentMouseMove({ - clientX: el.getBoundingClientRect().left, - clientY: el.getBoundingClientRect().top - subItems.getBoundingClientRect().height + 50, - }); - - expect(getHideSubItemsInterval()).toBe(0); - }); - - it('returns 300 when mouse is moved towards sub-items', () => { - documentMouseMove({ - clientX: el.getBoundingClientRect().left, - clientY: el.getBoundingClientRect().top, - }); - showSubLevelItems(el); - documentMouseMove({ - clientX: el.getBoundingClientRect().left + 20, - clientY: el.getBoundingClientRect().top + 10, - }); - - expect(getHideSubItemsInterval()).toBe(300); - }); - }); - - describe('mouseLeaveTopItem', () => { - beforeEach(() => { - spyOn(el.classList, 'remove'); - }); - - it('removes is-over class if currentOpenMenu is null', () => { - mouseLeaveTopItem(el); - - expect(el.classList.remove).toHaveBeenCalledWith('is-over'); - }); - - it('removes is-over class if currentOpenMenu is null & there are sub-items', () => { - el.innerHTML = ''; - - mouseLeaveTopItem(el); - - expect(el.classList.remove).toHaveBeenCalledWith('is-over'); - }); - - it('does not remove is-over class if currentOpenMenu is the passed in sub-items', () => { - el.innerHTML = ''; - - setOpenMenu(el.querySelector('.sidebar-sub-level-items')); - mouseLeaveTopItem(el); - - expect(el.classList.remove).not.toHaveBeenCalled(); - }); - }); - - describe('mouseEnterTopItems', () => { - beforeEach(() => { - el.innerHTML = - ''; - }); - - it('shows sub-items after 0ms if no menu is open', done => { - mouseEnterTopItems(el); - - expect(getHideSubItemsInterval()).toBe(0); - - setTimeout(() => { - expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); - - done(); - }); - }); - - it('shows sub-items after 300ms if a menu is currently open', done => { - documentMouseMove({ - clientX: el.getBoundingClientRect().left, - clientY: el.getBoundingClientRect().top, - }); - - setOpenMenu(el.querySelector('.sidebar-sub-level-items')); - - documentMouseMove({ - clientX: el.getBoundingClientRect().left + 20, - clientY: el.getBoundingClientRect().top + 10, - }); - - mouseEnterTopItems(el, 0); - - expect(getHideSubItemsInterval()).toBe(300); - - setTimeout(() => { - expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); - - done(); - }); - }); - }); - - describe('showSubLevelItems', () => { - beforeEach(() => { - el.innerHTML = ''; - }); - - it('adds is-over class to el', () => { - spyOn(el.classList, 'add'); - - showSubLevelItems(el); - - expect(el.classList.add).toHaveBeenCalledWith('is-over'); - }); - - it('does not show sub-items on mobile', () => { - breakpointSize = 'xs'; - - showSubLevelItems(el); - - expect(el.querySelector('.sidebar-sub-level-items').style.display).not.toBe('block'); - }); - - it('shows sub-items', () => { - showSubLevelItems(el); - - expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); - }); - - it('shows collapsed only sub-items if icon only sidebar', () => { - const subItems = el.querySelector('.sidebar-sub-level-items'); - const sidebar = document.createElement('div'); - sidebar.classList.add(SIDEBAR_COLLAPSED_CLASS); - subItems.classList.add('is-fly-out-only'); - - setSidebar(sidebar); - - showSubLevelItems(el); - - expect(el.querySelector('.sidebar-sub-level-items').style.display).toBe('block'); - }); - - it('does not show collapsed only sub-items if icon only sidebar', () => { - const subItems = el.querySelector('.sidebar-sub-level-items'); - subItems.classList.add('is-fly-out-only'); - - showSubLevelItems(el); - - expect(subItems.style.display).not.toBe('block'); - }); - - it('sets transform of sub-items', () => { - const sidebar = document.createElement('div'); - const subItems = el.querySelector('.sidebar-sub-level-items'); - - sidebar.style.width = '200px'; - - document.body.appendChild(sidebar); - - setSidebar(sidebar); - showSubLevelItems(el); - - expect(subItems.style.transform).toBe( - `translate3d(200px, ${Math.floor(el.getBoundingClientRect().top) - - getHeaderHeight()}px, 0px)`, - ); - }); - - it('sets is-above when element is above', () => { - const subItems = el.querySelector('.sidebar-sub-level-items'); - subItems.style.height = `${window.innerHeight + el.offsetHeight}px`; - el.style.top = `${window.innerHeight - el.offsetHeight}px`; - - spyOn(subItems.classList, 'add'); - - showSubLevelItems(el); - - expect(subItems.classList.add).toHaveBeenCalledWith('is-above'); - }); - }); - - describe('canShowSubItems', () => { - it('returns true if on desktop size', () => { - expect(canShowSubItems()).toBeTruthy(); - }); - - it('returns false if on mobile size', () => { - breakpointSize = 'xs'; - - expect(canShowSubItems()).toBeFalsy(); - }); - }); - - describe('canShowActiveSubItems', () => { - it('returns true by default', () => { - expect(canShowActiveSubItems(el)).toBeTruthy(); - }); - - it('returns false when active & expanded sidebar', () => { - const sidebar = document.createElement('div'); - el.classList.add('active'); - - setSidebar(sidebar); - - expect(canShowActiveSubItems(el)).toBeFalsy(); - }); - - it('returns true when active & collapsed sidebar', () => { - const sidebar = document.createElement('div'); - sidebar.classList.add(SIDEBAR_COLLAPSED_CLASS); - el.classList.add('active'); - - setSidebar(sidebar); - - expect(canShowActiveSubItems(el)).toBeTruthy(); - }); - }); - - describe('subItemsMouseLeave', () => { - beforeEach(() => { - el.innerHTML = ''; - - setOpenMenu(el.querySelector('.sidebar-sub-level-items')); - }); - - it('hides subMenu if element is not hovered', () => { - subItemsMouseLeave(el); - - expect(getOpenMenu()).toBeNull(); - }); - - it('does not hide subMenu if element is hovered', () => { - el.classList.add('is-over'); - subItemsMouseLeave(el); - - expect(getOpenMenu()).not.toBeNull(); - }); - }); -}); diff --git a/spec/javascripts/helpers/class_spec_helper.js b/spec/javascripts/helpers/class_spec_helper.js deleted file mode 100644 index 7a60d33b471..00000000000 --- a/spec/javascripts/helpers/class_spec_helper.js +++ /dev/null @@ -1,9 +0,0 @@ -export default class ClassSpecHelper { - static itShouldBeAStaticMethod(base, method) { - return it('should be a static method', () => { - expect(Object.prototype.hasOwnProperty.call(base, method)).toBeTruthy(); - }); - } -} - -window.ClassSpecHelper = ClassSpecHelper; diff --git a/spec/javascripts/helpers/filtered_search_spec_helper.js b/spec/javascripts/helpers/filtered_search_spec_helper.js deleted file mode 100644 index de17518ea51..00000000000 --- a/spec/javascripts/helpers/filtered_search_spec_helper.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from '../../frontend/helpers/filtered_search_spec_helper'; diff --git a/spec/javascripts/helpers/index.js b/spec/javascripts/helpers/index.js deleted file mode 100644 index d2c5caf0bdb..00000000000 --- a/spec/javascripts/helpers/index.js +++ /dev/null @@ -1,3 +0,0 @@ -import mountComponent, { mountComponentWithStore } from './vue_mount_component_helper'; - -export { mountComponent, mountComponentWithStore }; diff --git a/spec/javascripts/helpers/init_vue_mr_page_helper.js b/spec/javascripts/helpers/init_vue_mr_page_helper.js deleted file mode 100644 index 1ba08199764..00000000000 --- a/spec/javascripts/helpers/init_vue_mr_page_helper.js +++ /dev/null @@ -1,46 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import initMRPage from '~/mr_notes/index'; -import axios from '~/lib/utils/axios_utils'; -import { userDataMock, notesDataMock, noteableDataMock } from '../../frontend/notes/mock_data'; -import diffFileMockData from '../../frontend/diffs/mock_data/diff_file'; - -export default function initVueMRPage() { - const mrTestEl = document.createElement('div'); - mrTestEl.className = 'js-merge-request-test'; - document.body.appendChild(mrTestEl); - - const diffsAppEndpoint = '/diffs/app/endpoint'; - const diffsAppProjectPath = 'testproject'; - const mrEl = document.createElement('div'); - mrEl.className = 'merge-request fixture-mr'; - mrEl.setAttribute('data-mr-action', 'diffs'); - mrTestEl.appendChild(mrEl); - - const mrDiscussionsEl = document.createElement('div'); - mrDiscussionsEl.id = 'js-vue-mr-discussions'; - mrDiscussionsEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); - mrDiscussionsEl.setAttribute('data-noteable-data', JSON.stringify(noteableDataMock)); - mrDiscussionsEl.setAttribute('data-notes-data', JSON.stringify(notesDataMock)); - mrDiscussionsEl.setAttribute('data-noteable-type', 'merge-request'); - mrTestEl.appendChild(mrDiscussionsEl); - - const discussionCounterEl = document.createElement('div'); - discussionCounterEl.id = 'js-vue-discussion-counter'; - mrTestEl.appendChild(discussionCounterEl); - - const diffsAppEl = document.createElement('div'); - diffsAppEl.id = 'js-diffs-app'; - diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint); - diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath); - diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock)); - mrTestEl.appendChild(diffsAppEl); - - const mock = new MockAdapter(axios); - mock.onGet(diffsAppEndpoint).reply(200, { - branch_name: 'foo', - diff_files: [diffFileMockData], - }); - - initMRPage(); - return mock; -} diff --git a/spec/javascripts/helpers/locale_helper.js b/spec/javascripts/helpers/locale_helper.js deleted file mode 100644 index 80047b06003..00000000000 --- a/spec/javascripts/helpers/locale_helper.js +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable import/prefer-default-export */ - -export const setLanguage = languageCode => { - const htmlElement = document.querySelector('html'); - - if (languageCode) { - htmlElement.setAttribute('lang', languageCode); - } else { - htmlElement.removeAttribute('lang'); - } -}; diff --git a/spec/javascripts/helpers/set_timeout_promise_helper.js b/spec/javascripts/helpers/set_timeout_promise_helper.js deleted file mode 100644 index 47087619187..00000000000 --- a/spec/javascripts/helpers/set_timeout_promise_helper.js +++ /dev/null @@ -1,4 +0,0 @@ -export default (time = 0) => - new Promise(resolve => { - setTimeout(resolve, time); - }); diff --git a/spec/javascripts/helpers/text_helper.js b/spec/javascripts/helpers/text_helper.js deleted file mode 100644 index e0fe18e5560..00000000000 --- a/spec/javascripts/helpers/text_helper.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Replaces line break with an empty space - * @param {*} data - */ -export const removeBreakLine = data => data.replace(/\r?\n|\r/g, ' '); - -/** - * Removes line breaks, spaces and trims the given text - * @param {String} str - * @returns {String} - */ -export const trimText = str => - str - .replace(/\r?\n|\r/g, '') - .replace(/\s\s+/g, ' ') - .trim(); - -export const removeWhitespace = str => str.replace(/\s\s+/g, ' '); diff --git a/spec/javascripts/helpers/tracking_helper.js b/spec/javascripts/helpers/tracking_helper.js deleted file mode 100644 index ea322de46f4..00000000000 --- a/spec/javascripts/helpers/tracking_helper.js +++ /dev/null @@ -1,5 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -export * from '../../frontend/helpers/tracking_helper'; diff --git a/spec/javascripts/helpers/user_mock_data_helper.js b/spec/javascripts/helpers/user_mock_data_helper.js deleted file mode 100644 index 6999fa1f8a1..00000000000 --- a/spec/javascripts/helpers/user_mock_data_helper.js +++ /dev/null @@ -1,14 +0,0 @@ -export default { - createNumberRandomUsers(numberUsers) { - const users = []; - for (let i = 0; i < numberUsers; i += 1) { - users.push({ - avatar: 'https://gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon', - id: i + 1, - name: `GitLab User ${i}`, - username: `gitlab${i}`, - }); - } - return users; - }, -}; diff --git a/spec/javascripts/helpers/vue_mount_component_helper.js b/spec/javascripts/helpers/vue_mount_component_helper.js deleted file mode 100644 index c1857115b61..00000000000 --- a/spec/javascripts/helpers/vue_mount_component_helper.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from '../../frontend/helpers/vue_mount_component_helper'; -export * from '../../frontend/helpers/vue_mount_component_helper'; diff --git a/spec/javascripts/helpers/vue_test_utils_helper.js b/spec/javascripts/helpers/vue_test_utils_helper.js deleted file mode 100644 index 1f5d8716dd3..00000000000 --- a/spec/javascripts/helpers/vue_test_utils_helper.js +++ /dev/null @@ -1,5 +0,0 @@ -// No new code should be added to this file. Instead, modify the -// file this one re-exports from. For more detail about why, see: -// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/31349 - -export * from '../../frontend/helpers/vue_test_utils_helper'; diff --git a/spec/javascripts/helpers/vuex_action_helper.js b/spec/javascripts/helpers/vuex_action_helper.js deleted file mode 100644 index c5de31a4138..00000000000 --- a/spec/javascripts/helpers/vuex_action_helper.js +++ /dev/null @@ -1,102 +0,0 @@ -const noop = () => {}; - -/** - * Helper for testing action with expected mutations inspired in - * https://vuex.vuejs.org/en/testing.html - * - * @param {Function} action to be tested - * @param {Object} payload will be provided to the action - * @param {Object} state will be provided to the action - * @param {Array} [expectedMutations=[]] mutations expected to be committed - * @param {Array} [expectedActions=[]] actions expected to be dispatched - * @param {Function} [done=noop] to be executed after the tests - * @return {Promise} - * - * @example - * testAction( - * actions.actionName, // action - * { }, // mocked payload - * state, //state - * // expected mutations - * [ - * { type: types.MUTATION} - * { type: types.MUTATION_1, payload: jasmine.any(Number)} - * ], - * // expected actions - * [ - * { type: 'actionName', payload: {param: 'foobar'}}, - * { type: 'actionName1'} - * ] - * done, - * ); - * - * @example - * testAction( - * actions.actionName, // action - * { }, // mocked payload - * state, //state - * [ { type: types.MUTATION} ], // expected mutations - * [], // expected actions - * ).then(done) - * .catch(done.fail); - */ -export default ( - action, - payload, - state, - expectedMutations = [], - expectedActions = [], - done = noop, -) => { - const mutations = []; - const actions = []; - - // mock commit - const commit = (type, mutationPayload) => { - const mutation = { type }; - - if (typeof mutationPayload !== 'undefined') { - mutation.payload = mutationPayload; - } - - mutations.push(mutation); - }; - - // mock dispatch - const dispatch = (type, actionPayload) => { - const dispatchedAction = { type }; - - if (typeof actionPayload !== 'undefined') { - dispatchedAction.payload = actionPayload; - } - - actions.push(dispatchedAction); - }; - - const validateResults = () => { - expect({ - mutations, - actions, - }).toEqual({ - mutations: expectedMutations, - actions: expectedActions, - }); - done(); - }; - - const result = action( - { commit, state, dispatch, rootState: state, rootGetters: state, getters: state }, - payload, - ); - - return new Promise(setImmediate) - .then(() => result) - .catch(error => { - validateResults(); - throw error; - }) - .then(data => { - validateResults(); - return data; - }); -}; diff --git a/spec/javascripts/helpers/wait_for_promises.js b/spec/javascripts/helpers/wait_for_promises.js deleted file mode 100644 index 1d2b53fc770..00000000000 --- a/spec/javascripts/helpers/wait_for_promises.js +++ /dev/null @@ -1 +0,0 @@ -export default () => new Promise(resolve => requestAnimationFrame(resolve)); diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js deleted file mode 100644 index f0ba46c058a..00000000000 --- a/spec/javascripts/jobs/mock_data.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from '../../frontend/jobs/mock_data'; -export * from '../../frontend/jobs/mock_data'; diff --git a/spec/javascripts/matchers.js b/spec/javascripts/matchers.js index 7d1921cabcf..5b7b7dc78b1 100644 --- a/spec/javascripts/matchers.js +++ b/spec/javascripts/matchers.js @@ -1,61 +1,6 @@ import pixelmatch from 'pixelmatch'; export default { - toContainText: () => ({ - compare(vm, text) { - if (!(vm.$el instanceof HTMLElement)) { - throw new Error('vm.$el is not a DOM element!'); - } - - const result = { - pass: vm.$el.innerText.includes(text), - }; - return result; - }, - }), - toHaveSpriteIcon: () => ({ - compare(element, iconName) { - if (!iconName) { - throw new Error('toHaveSpriteIcon is missing iconName argument!'); - } - - if (!(element instanceof HTMLElement)) { - throw new Error(`${element} is not a DOM element!`); - } - - const iconReferences = [].slice.apply(element.querySelectorAll('svg use')); - const matchingIcon = iconReferences.find(reference => - reference.getAttribute('xlink:href').endsWith(`#${iconName}`), - ); - const result = { - pass: Boolean(matchingIcon), - }; - - if (result.pass) { - result.message = `${element.outerHTML} contains the sprite icon "${iconName}"!`; - } else { - result.message = `${element.outerHTML} does not contain the sprite icon "${iconName}"!`; - - const existingIcons = iconReferences.map(reference => { - const iconUrl = reference.getAttribute('xlink:href'); - return `"${iconUrl.replace(/^.+#/, '')}"`; - }); - if (existingIcons.length > 0) { - result.message += ` (only found ${existingIcons.join(',')})`; - } - } - - return result; - }, - }), - toRender: () => ({ - compare(vm) { - const result = { - pass: vm.$el.nodeType !== Node.COMMENT_NODE, - }; - return result; - }, - }), toImageDiffEqual: () => { const getImageData = img => { const canvas = document.createElement('canvas'); diff --git a/spec/javascripts/monitoring/components/dashboard_resize_browser_spec.js b/spec/javascripts/monitoring/components/dashboard_resize_browser_spec.js index 4416dbd014a..bbcdc0b879f 100644 --- a/spec/javascripts/monitoring/components/dashboard_resize_browser_spec.js +++ b/spec/javascripts/monitoring/components/dashboard_resize_browser_spec.js @@ -11,8 +11,8 @@ import MockAdapter from 'axios-mock-adapter'; import Dashboard from '~/monitoring/components/dashboard.vue'; import { createStore } from '~/monitoring/stores'; import axios from '~/lib/utils/axios_utils'; -import { mockApiEndpoint, propsData } from '../mock_data'; -import { metricsDashboardPayload } from '../fixture_data'; +import { mockApiEndpoint } from '../mock_data'; +import { metricsDashboardPayload, dashboardProps } from '../fixture_data'; import { setupStoreWithData } from '../store_utils'; const localVue = createLocalVue(); @@ -56,7 +56,7 @@ describe('Dashboard', () => { component = new DashboardComponent({ el: document.querySelector('.prometheus-graphs'), propsData: { - ...propsData, + ...dashboardProps, hasMetrics: true, showPanels: true, }, diff --git a/spec/javascripts/vue_shared/components/tooltip_on_truncate_browser_spec.js b/spec/javascripts/vue_shared/components/tooltip_on_truncate_browser_spec.js new file mode 100644 index 00000000000..da964a2d5b9 --- /dev/null +++ b/spec/javascripts/vue_shared/components/tooltip_on_truncate_browser_spec.js @@ -0,0 +1,240 @@ +// this file can't be migrated to jest because it relies on the browser to perform integration tests: +// (specifically testing around css properties `overflow` and `white-space`) +// see: https://gitlab.com/groups/gitlab-org/-/epics/895#what-if-theres-a-karma-spec-which-is-simply-unmovable-to-jest-ie-it-is-dependent-on-a-running-browser-environment + +import { mount, shallowMount } from '@vue/test-utils'; +import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; + +const TEXT_SHORT = 'lorem'; +const TEXT_LONG = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do'; + +const TEXT_TRUNCATE = 'white-space: nowrap; overflow:hidden;'; +const STYLE_NORMAL = `${TEXT_TRUNCATE} display: inline-block; max-width: 1000px;`; // does not overflows +const STYLE_OVERFLOWED = `${TEXT_TRUNCATE} display: inline-block; max-width: 50px;`; // overflowed when text is long + +const createElementWithStyle = (style, content) => `${content}`; + +describe('TooltipOnTruncate component', () => { + let wrapper; + let parent; + + const createComponent = ({ propsData, ...options } = {}) => { + wrapper = shallowMount(TooltipOnTruncate, { + attachToDocument: true, + propsData: { + ...propsData, + }, + attrs: { + style: STYLE_OVERFLOWED, + }, + ...options, + }); + }; + + const createWrappedComponent = ({ propsData, ...options }) => { + // set a parent around the tested component + parent = mount( + { + props: { + title: { default: '' }, + }, + template: ` + +
{{title}}
+
+ `, + components: { + TooltipOnTruncate, + }, + }, + { + propsData: { ...propsData }, + attachToDocument: true, + ...options, + }, + ); + + wrapper = parent.find(TooltipOnTruncate); + }; + + const hasTooltip = () => wrapper.classes('js-show-tooltip'); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('with default target', () => { + it('renders tooltip if truncated', () => { + createComponent({ + propsData: { + title: TEXT_LONG, + }, + slots: { + default: [TEXT_LONG], + }, + }); + + return wrapper.vm.$nextTick().then(() => { + expect(hasTooltip()).toBe(true); + expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG); + expect(wrapper.attributes('data-placement')).toEqual('top'); + }); + }); + + it('does not render tooltip if normal', () => { + createComponent({ + propsData: { + title: TEXT_SHORT, + }, + slots: { + default: [TEXT_SHORT], + }, + }); + + return wrapper.vm.$nextTick().then(() => { + expect(hasTooltip()).toBe(false); + }); + }); + }); + + describe('with child target', () => { + it('renders tooltip if truncated', () => { + createComponent({ + attrs: { + style: STYLE_NORMAL, + }, + propsData: { + title: TEXT_LONG, + truncateTarget: 'child', + }, + slots: { + default: createElementWithStyle(STYLE_OVERFLOWED, TEXT_LONG), + }, + }); + + return wrapper.vm.$nextTick().then(() => { + expect(hasTooltip()).toBe(true); + }); + }); + + it('does not render tooltip if normal', () => { + createComponent({ + propsData: { + truncateTarget: 'child', + }, + slots: { + default: createElementWithStyle(STYLE_NORMAL, TEXT_LONG), + }, + }); + + return wrapper.vm.$nextTick().then(() => { + expect(hasTooltip()).toBe(false); + }); + }); + }); + + describe('with fn target', () => { + it('renders tooltip if truncated', () => { + createComponent({ + attrs: { + style: STYLE_NORMAL, + }, + propsData: { + title: TEXT_LONG, + truncateTarget: el => el.childNodes[1], + }, + slots: { + default: [ + createElementWithStyle('', TEXT_LONG), + createElementWithStyle(STYLE_OVERFLOWED, TEXT_LONG), + ], + }, + }); + + return wrapper.vm.$nextTick().then(() => { + expect(hasTooltip()).toBe(true); + }); + }); + }); + + describe('placement', () => { + it('sets data-placement when tooltip is rendered', () => { + const placement = 'bottom'; + + createComponent({ + propsData: { + placement, + }, + attrs: { + style: STYLE_OVERFLOWED, + }, + slots: { + default: TEXT_LONG, + }, + }); + + return wrapper.vm.$nextTick().then(() => { + expect(hasTooltip()).toBe(true); + expect(wrapper.attributes('data-placement')).toEqual(placement); + }); + }); + }); + + describe('updates when title and slot content changes', () => { + describe('is initialized with a long text', () => { + beforeEach(() => { + createWrappedComponent({ + propsData: { title: TEXT_LONG }, + }); + return parent.vm.$nextTick(); + }); + + it('renders tooltip', () => { + expect(hasTooltip()).toBe(true); + expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG); + expect(wrapper.attributes('data-placement')).toEqual('top'); + }); + + it('does not render tooltip after updated to a short text', () => { + parent.setProps({ + title: TEXT_SHORT, + }); + + return wrapper.vm + .$nextTick() + .then(() => wrapper.vm.$nextTick()) // wait 2 times to get an updated slot + .then(() => { + expect(hasTooltip()).toBe(false); + }); + }); + }); + + describe('is initialized with a short text', () => { + beforeEach(() => { + createWrappedComponent({ + propsData: { title: TEXT_SHORT }, + }); + return wrapper.vm.$nextTick(); + }); + + it('does not render tooltip', () => { + expect(hasTooltip()).toBe(false); + }); + + it('renders tooltip after updated to a long text', () => { + parent.setProps({ + title: TEXT_LONG, + }); + + return wrapper.vm + .$nextTick() + .then(() => wrapper.vm.$nextTick()) // wait 2 times to get an updated slot + .then(() => { + expect(hasTooltip()).toBe(true); + expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG); + expect(wrapper.attributes('data-placement')).toEqual('top'); + }); + }); + }); + }); +}); diff --git a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js deleted file mode 100644 index 5f432f2a1b5..00000000000 --- a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js +++ /dev/null @@ -1,236 +0,0 @@ -import { mount, shallowMount } from '@vue/test-utils'; -import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; - -const TEXT_SHORT = 'lorem'; -const TEXT_LONG = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do'; - -const TEXT_TRUNCATE = 'white-space: nowrap; overflow:hidden;'; -const STYLE_NORMAL = `${TEXT_TRUNCATE} display: inline-block; max-width: 1000px;`; // does not overflows -const STYLE_OVERFLOWED = `${TEXT_TRUNCATE} display: inline-block; max-width: 50px;`; // overflowed when text is long - -const createElementWithStyle = (style, content) => `${content}`; - -describe('TooltipOnTruncate component', () => { - let wrapper; - let parent; - - const createComponent = ({ propsData, ...options } = {}) => { - wrapper = shallowMount(TooltipOnTruncate, { - attachToDocument: true, - propsData: { - ...propsData, - }, - attrs: { - style: STYLE_OVERFLOWED, - }, - ...options, - }); - }; - - const createWrappedComponent = ({ propsData, ...options }) => { - // set a parent around the tested component - parent = mount( - { - props: { - title: { default: '' }, - }, - template: ` - -
{{title}}
-
- `, - components: { - TooltipOnTruncate, - }, - }, - { - propsData: { ...propsData }, - attachToDocument: true, - ...options, - }, - ); - - wrapper = parent.find(TooltipOnTruncate); - }; - - const hasTooltip = () => wrapper.classes('js-show-tooltip'); - - afterEach(() => { - wrapper.destroy(); - }); - - describe('with default target', () => { - it('renders tooltip if truncated', () => { - createComponent({ - propsData: { - title: TEXT_LONG, - }, - slots: { - default: [TEXT_LONG], - }, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(hasTooltip()).toBe(true); - expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG); - expect(wrapper.attributes('data-placement')).toEqual('top'); - }); - }); - - it('does not render tooltip if normal', () => { - createComponent({ - propsData: { - title: TEXT_SHORT, - }, - slots: { - default: [TEXT_SHORT], - }, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(hasTooltip()).toBe(false); - }); - }); - }); - - describe('with child target', () => { - it('renders tooltip if truncated', () => { - createComponent({ - attrs: { - style: STYLE_NORMAL, - }, - propsData: { - title: TEXT_LONG, - truncateTarget: 'child', - }, - slots: { - default: createElementWithStyle(STYLE_OVERFLOWED, TEXT_LONG), - }, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(hasTooltip()).toBe(true); - }); - }); - - it('does not render tooltip if normal', () => { - createComponent({ - propsData: { - truncateTarget: 'child', - }, - slots: { - default: createElementWithStyle(STYLE_NORMAL, TEXT_LONG), - }, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(hasTooltip()).toBe(false); - }); - }); - }); - - describe('with fn target', () => { - it('renders tooltip if truncated', () => { - createComponent({ - attrs: { - style: STYLE_NORMAL, - }, - propsData: { - title: TEXT_LONG, - truncateTarget: el => el.childNodes[1], - }, - slots: { - default: [ - createElementWithStyle('', TEXT_LONG), - createElementWithStyle(STYLE_OVERFLOWED, TEXT_LONG), - ], - }, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(hasTooltip()).toBe(true); - }); - }); - }); - - describe('placement', () => { - it('sets data-placement when tooltip is rendered', () => { - const placement = 'bottom'; - - createComponent({ - propsData: { - placement, - }, - attrs: { - style: STYLE_OVERFLOWED, - }, - slots: { - default: TEXT_LONG, - }, - }); - - return wrapper.vm.$nextTick().then(() => { - expect(hasTooltip()).toBe(true); - expect(wrapper.attributes('data-placement')).toEqual(placement); - }); - }); - }); - - describe('updates when title and slot content changes', () => { - describe('is initialized with a long text', () => { - beforeEach(() => { - createWrappedComponent({ - propsData: { title: TEXT_LONG }, - }); - return parent.vm.$nextTick(); - }); - - it('renders tooltip', () => { - expect(hasTooltip()).toBe(true); - expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG); - expect(wrapper.attributes('data-placement')).toEqual('top'); - }); - - it('does not render tooltip after updated to a short text', () => { - parent.setProps({ - title: TEXT_SHORT, - }); - - return wrapper.vm - .$nextTick() - .then(() => wrapper.vm.$nextTick()) // wait 2 times to get an updated slot - .then(() => { - expect(hasTooltip()).toBe(false); - }); - }); - }); - - describe('is initialized with a short text', () => { - beforeEach(() => { - createWrappedComponent({ - propsData: { title: TEXT_SHORT }, - }); - return wrapper.vm.$nextTick(); - }); - - it('does not render tooltip', () => { - expect(hasTooltip()).toBe(false); - }); - - it('renders tooltip after updated to a long text', () => { - parent.setProps({ - title: TEXT_LONG, - }); - - return wrapper.vm - .$nextTick() - .then(() => wrapper.vm.$nextTick()) // wait 2 times to get an updated slot - .then(() => { - expect(hasTooltip()).toBe(true); - expect(wrapper.attributes('data-original-title')).toEqual(TEXT_LONG); - expect(wrapper.attributes('data-placement')).toEqual('top'); - }); - }); - }); - }); -}); -- cgit v1.2.3