diff options
Diffstat (limited to 'spec/javascripts/vue_shared')
9 files changed, 154 insertions, 261 deletions
diff --git a/spec/javascripts/vue_shared/components/changed_file_icon_spec.js b/spec/javascripts/vue_shared/components/changed_file_icon_spec.js deleted file mode 100644 index 634ba8403d5..00000000000 --- a/spec/javascripts/vue_shared/components/changed_file_icon_spec.js +++ /dev/null @@ -1,63 +0,0 @@ -import Vue from 'vue'; -import changedFileIcon from '~/vue_shared/components/changed_file_icon.vue'; -import createComponent from 'spec/helpers/vue_mount_component_helper'; - -describe('Changed file icon', () => { - let vm; - - function factory(props = {}) { - const component = Vue.extend(changedFileIcon); - - vm = createComponent(component, { - ...props, - file: { - tempFile: false, - changed: true, - }, - }); - } - - afterEach(() => { - vm.$destroy(); - }); - - it('centers icon', () => { - factory({ - isCentered: true, - }); - - expect(vm.$el.classList).toContain('ml-auto'); - }); - - describe('changedIcon', () => { - it('equals file-modified when not a temp file and has changes', () => { - factory(); - - expect(vm.changedIcon).toBe('file-modified'); - }); - - it('equals file-addition when a temp file', () => { - factory(); - - vm.file.tempFile = true; - - expect(vm.changedIcon).toBe('file-addition'); - }); - }); - - describe('changedIconClass', () => { - it('includes file-modified when not a temp file', () => { - factory(); - - expect(vm.changedIconClass).toContain('file-modified'); - }); - - it('includes file-addition when a temp file', () => { - factory(); - - vm.file.tempFile = true; - - expect(vm.changedIconClass).toContain('file-addition'); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js index 5bea8c43da3..1f61e19fa84 100644 --- a/spec/javascripts/vue_shared/components/file_icon_spec.js +++ b/spec/javascripts/vue_shared/components/file_icon_spec.js @@ -72,7 +72,7 @@ describe('File Icon component', () => { const { classList } = vm.$el.querySelector('.loading-container span'); - expect(classList.contains('spinner')).toEqual(true); + expect(classList.contains('gl-spinner')).toEqual(true); }); it('should add a special class and a size class', () => { diff --git a/spec/javascripts/vue_shared/components/file_row_spec.js b/spec/javascripts/vue_shared/components/file_row_spec.js index 7da69e3fa84..6abcac5c0ff 100644 --- a/spec/javascripts/vue_shared/components/file_row_spec.js +++ b/spec/javascripts/vue_shared/components/file_row_spec.js @@ -90,6 +90,19 @@ describe('File row component', () => { expect(vm.$el.querySelector('.js-file-row-header')).not.toBe(null); }); + it('is not rendered for `moved` entries in subfolders', () => { + createComponent({ + file: { + path: 't5', + moved: true, + tree: [], + }, + level: 2, + }); + + expect(vm.$el.nodeType).not.toEqual(1); + }); + describe('new dropdown', () => { beforeEach(() => { createComponent({ diff --git a/spec/javascripts/vue_shared/components/header_ci_component_spec.js b/spec/javascripts/vue_shared/components/header_ci_component_spec.js index a9c1a67b39b..2b059e5e9f4 100644 --- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js +++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js @@ -88,7 +88,7 @@ describe('Header CI Component', () => { vm.actions[0].isLoading = true; Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn .spinner').getAttribute('style')).toBeFalsy(); + expect(vm.$el.querySelector('.btn .gl-spinner').getAttribute('style')).toBeFalsy(); done(); }); }); diff --git a/spec/javascripts/vue_shared/components/markdown/header_spec.js b/spec/javascripts/vue_shared/components/markdown/header_spec.js deleted file mode 100644 index d4be2451f0b..00000000000 --- a/spec/javascripts/vue_shared/components/markdown/header_spec.js +++ /dev/null @@ -1,111 +0,0 @@ -import Vue from 'vue'; -import $ from 'jquery'; -import headerComponent from '~/vue_shared/components/markdown/header.vue'; - -describe('Markdown field header component', () => { - let vm; - - beforeEach(done => { - const Component = Vue.extend(headerComponent); - - vm = new Component({ - propsData: { - previewMarkdown: false, - }, - }).$mount(); - - Vue.nextTick(done); - }); - - it('renders markdown header buttons', () => { - const buttons = [ - 'Add bold text', - 'Add italic text', - 'Insert a quote', - 'Insert code', - 'Add a link', - 'Add a bullet list', - 'Add a numbered list', - 'Add a task list', - 'Add a table', - 'Insert suggestion', - 'Go full screen', - ]; - const elements = vm.$el.querySelectorAll('.toolbar-btn'); - - elements.forEach((buttonEl, index) => { - expect(buttonEl.getAttribute('data-original-title')).toBe(buttons[index]); - }); - }); - - it('renders `write` link as active when previewMarkdown is false', () => { - expect(vm.$el.querySelector('li:nth-child(1)').classList.contains('active')).toBeTruthy(); - }); - - it('renders `preview` link as active when previewMarkdown is true', done => { - vm.previewMarkdown = true; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('li:nth-child(2)').classList.contains('active')).toBeTruthy(); - - done(); - }); - }); - - it('emits toggle markdown event when clicking preview', () => { - spyOn(vm, '$emit'); - - vm.$el.querySelector('.js-preview-link').click(); - - expect(vm.$emit).toHaveBeenCalledWith('preview-markdown'); - - vm.$el.querySelector('.js-write-link').click(); - - expect(vm.$emit).toHaveBeenCalledWith('write-markdown'); - }); - - it('does not emit toggle markdown event when triggered from another form', () => { - spyOn(vm, '$emit'); - - $(document).triggerHandler('markdown-preview:show', [ - $( - '<form><div class="js-vue-markdown-field"><textarea class="markdown-area"></textarea></div></form>', - ), - ]); - - expect(vm.$emit).not.toHaveBeenCalled(); - }); - - it('blurs preview link after click', done => { - const link = vm.$el.querySelector('li:nth-child(2) button'); - spyOn(HTMLElement.prototype, 'blur'); - - link.click(); - - setTimeout(() => { - expect(link.blur).toHaveBeenCalled(); - - done(); - }); - }); - - it('renders markdown table template', () => { - expect(vm.mdTable).toEqual( - '| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |', - ); - }); - - it('renders suggestion template', () => { - vm.lineContent = 'Some content'; - - expect(vm.mdSuggestion).toEqual('```suggestion:-0+0\n{text}\n```'); - }); - - it('does not render suggestion button if `canSuggest` is set to false', () => { - vm.canSuggest = false; - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.qa-suggestion-btn')).toBe(null); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js index ea74cb9eb21..dc929e83eb7 100644 --- a/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js +++ b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js @@ -60,7 +60,7 @@ describe('Suggestion Diff component', () => { describe('init', () => { it('renders a suggestion header', () => { - expect(vm.$el.querySelector('.qa-suggestion-diff-header')).not.toBeNull(); + expect(vm.$el.querySelector('.js-suggestion-diff-header')).not.toBeNull(); }); it('renders a diff table with syntax highlighting', () => { diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js index 42abb4d83f0..258530f32f7 100644 --- a/spec/javascripts/vue_shared/components/table_pagination_spec.js +++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js @@ -217,7 +217,7 @@ describe('Pagination component', () => { change: spy, }); - expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next'); + expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next ›'); component.$el.querySelector('.js-next-button .page-link').click(); @@ -237,7 +237,7 @@ describe('Pagination component', () => { change: spy, }); - expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next'); + expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next ›'); component.$el.querySelector('.js-next-button .page-link').click(); diff --git a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js index 997d84dcc42..ad8d5a53291 100644 --- a/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js +++ b/spec/javascripts/vue_shared/components/tooltip_on_truncate_spec.js @@ -1,68 +1,72 @@ -import { mountComponentWithRender } from 'spec/helpers/vue_mount_component_helper'; +import { shallowMount, createLocalVue } from '@vue/test-utils'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; const TEST_TITLE = 'lorem-ipsum-dolar-sit-amit-consectur-adipiscing-elit-sed-do'; -const CLASS_SHOW_TOOLTIP = 'js-show-tooltip'; -const STYLE_TRUNCATED = { - display: 'inline-block', - 'max-width': '20px', -}; -const STYLE_NORMAL = { - display: 'inline-block', - 'max-width': '1000px', -}; - -function mountTooltipOnTruncate(options, createChildren) { - return mountComponentWithRender(h => h(TooltipOnTruncate, options, createChildren(h)), '#app'); -} +const STYLE_TRUNCATED = 'display: inline-block; max-width: 20px;'; +const STYLE_NORMAL = 'display: inline-block; max-width: 1000px;'; -describe('TooltipOnTruncate component', () => { - let vm; +const localVue = createLocalVue(); - beforeEach(() => { - const el = document.createElement('div'); - el.id = 'app'; - document.body.appendChild(el); - }); +const createElementWithStyle = (style, content) => `<a href="#" style="${style}">${content}</a>`; + +describe('TooltipOnTruncate component', () => { + let wrapper; + + const createComponent = ({ propsData, ...options } = {}) => { + wrapper = shallowMount(localVue.extend(TooltipOnTruncate), { + localVue, + sync: false, + attachToDocument: true, + propsData: { + title: TEST_TITLE, + ...propsData, + }, + ...options, + }); + }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); }); + const hasTooltip = () => wrapper.classes('js-show-tooltip'); + describe('with default target', () => { it('renders tooltip if truncated', done => { - const options = { - style: STYLE_TRUNCATED, - props: { - title: TEST_TITLE, + createComponent({ + attrs: { + style: STYLE_TRUNCATED, }, - }; - - vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]); + slots: { + default: [TEST_TITLE], + }, + }); - vm.$nextTick() + wrapper.vm + .$nextTick() .then(() => { - expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP); - expect(vm.$el).toHaveData('original-title', TEST_TITLE); - expect(vm.$el).toHaveData('placement', 'top'); + expect(hasTooltip()).toBe(true); + expect(wrapper.attributes('data-original-title')).toEqual(TEST_TITLE); + expect(wrapper.attributes('data-placement')).toEqual('top'); }) .then(done) .catch(done.fail); }); it('does not render tooltip if normal', done => { - const options = { - style: STYLE_NORMAL, - props: { - title: TEST_TITLE, + createComponent({ + attrs: { + style: STYLE_NORMAL, }, - }; - - vm = mountTooltipOnTruncate(options, () => [TEST_TITLE]); + slots: { + default: [TEST_TITLE], + }, + }); - vm.$nextTick() + wrapper.vm + .$nextTick() .then(() => { - expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP); + expect(hasTooltip()).toBe(false); }) .then(done) .catch(done.fail); @@ -71,37 +75,41 @@ describe('TooltipOnTruncate component', () => { describe('with child target', () => { it('renders tooltip if truncated', done => { - const options = { - style: STYLE_NORMAL, - props: { - title: TEST_TITLE, + createComponent({ + attrs: { + style: STYLE_NORMAL, + }, + propsData: { truncateTarget: 'child', }, - }; - - vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]); + slots: { + default: createElementWithStyle(STYLE_TRUNCATED, TEST_TITLE), + }, + }); - vm.$nextTick() + wrapper.vm + .$nextTick() .then(() => { - expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP); + expect(hasTooltip()).toBe(true); }) .then(done) .catch(done.fail); }); it('does not render tooltip if normal', done => { - const options = { - props: { - title: TEST_TITLE, + createComponent({ + propsData: { truncateTarget: 'child', }, - }; - - vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_NORMAL }, TEST_TITLE)]); + slots: { + default: createElementWithStyle(STYLE_NORMAL, TEST_TITLE), + }, + }); - vm.$nextTick() + wrapper.vm + .$nextTick() .then(() => { - expect(vm.$el).not.toHaveClass(CLASS_SHOW_TOOLTIP); + expect(hasTooltip()).toBe(false); }) .then(done) .catch(done.fail); @@ -110,22 +118,25 @@ describe('TooltipOnTruncate component', () => { describe('with fn target', () => { it('renders tooltip if truncated', done => { - const options = { - style: STYLE_NORMAL, - props: { - title: TEST_TITLE, + createComponent({ + attrs: { + style: STYLE_NORMAL, + }, + propsData: { truncateTarget: el => el.childNodes[1], }, - }; - - vm = mountTooltipOnTruncate(options, h => [ - h('a', { style: STYLE_NORMAL }, TEST_TITLE), - h('span', { style: STYLE_TRUNCATED }, TEST_TITLE), - ]); + slots: { + default: [ + createElementWithStyle('', TEST_TITLE), + createElementWithStyle(STYLE_TRUNCATED, TEST_TITLE), + ], + }, + }); - vm.$nextTick() + wrapper.vm + .$nextTick() .then(() => { - expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP); + expect(hasTooltip()).toBe(true); }) .then(done) .catch(done.fail); @@ -134,20 +145,25 @@ describe('TooltipOnTruncate component', () => { describe('placement', () => { it('sets data-placement when tooltip is rendered', done => { - const options = { - props: { - title: TEST_TITLE, - truncateTarget: 'child', - placement: 'bottom', - }, - }; + const placement = 'bottom'; - vm = mountTooltipOnTruncate(options, h => [h('a', { style: STYLE_TRUNCATED }, TEST_TITLE)]); + createComponent({ + propsData: { + placement, + }, + attrs: { + style: STYLE_TRUNCATED, + }, + slots: { + default: TEST_TITLE, + }, + }); - vm.$nextTick() + wrapper.vm + .$nextTick() .then(() => { - expect(vm.$el).toHaveClass(CLASS_SHOW_TOOLTIP); - expect(vm.$el).toHaveData('placement', options.props.placement); + expect(hasTooltip()).toBe(true); + expect(wrapper.attributes('data-placement')).toEqual(placement); }) .then(done) .catch(done.fail); diff --git a/spec/javascripts/vue_shared/directives/autofocusonshow_spec.js b/spec/javascripts/vue_shared/directives/autofocusonshow_spec.js new file mode 100644 index 00000000000..f1ca5f61496 --- /dev/null +++ b/spec/javascripts/vue_shared/directives/autofocusonshow_spec.js @@ -0,0 +1,38 @@ +import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; + +/** + * We're testing this directive's hooks as pure functions + * since behaviour of this directive is highly-dependent + * on underlying DOM methods. + */ +describe('AutofocusOnShow directive', () => { + describe('with input invisible on component render', () => { + let el; + + beforeAll(() => { + setFixtures('<div id="container" style="display: none;"><input id="inputel"/></div>'); + el = document.querySelector('#inputel'); + }); + + it('should bind IntersectionObserver on input element', () => { + spyOn(el, 'focus'); + + autofocusonshow.inserted(el); + + expect(el.visibilityObserver).toBeDefined(); + expect(el.focus).not.toHaveBeenCalled(); + }); + + it('should stop IntersectionObserver on input element on unbind hook', () => { + el.visibilityObserver = { + disconnect: () => {}, + }; + spyOn(el.visibilityObserver, 'disconnect'); + + autofocusonshow.unbind(el); + + expect(el.visibilityObserver).toBeDefined(); + expect(el.visibilityObserver.disconnect).toHaveBeenCalled(); + }); + }); +}); |
