diff options
Diffstat (limited to 'spec/frontend/ide/components/commit_sidebar')
4 files changed, 212 insertions, 241 deletions
diff --git a/spec/frontend/ide/components/commit_sidebar/actions_spec.js b/spec/frontend/ide/components/commit_sidebar/actions_spec.js index c9425f6c9cd..dc103fec5d0 100644 --- a/spec/frontend/ide/components/commit_sidebar/actions_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/actions_spec.js @@ -1,7 +1,7 @@ import Vue, { nextTick } from 'vue'; -import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { mount } from '@vue/test-utils'; import { projectData, branches } from 'jest/ide/mock_data'; -import commitActions from '~/ide/components/commit_sidebar/actions.vue'; +import CommitActions from '~/ide/components/commit_sidebar/actions.vue'; import { createStore } from '~/ide/stores'; import { COMMIT_TO_NEW_BRANCH, @@ -18,32 +18,27 @@ const BRANCH_REGULAR_NO_ACCESS = 'regular/no-access'; describe('IDE commit sidebar actions', () => { let store; - let vm; + let wrapper; const createComponent = ({ hasMR = false, currentBranchId = 'main', emptyRepo = false } = {}) => { - const Component = Vue.extend(commitActions); - - vm = createComponentWithStore(Component, store); - - vm.$store.state.currentBranchId = currentBranchId; - vm.$store.state.currentProjectId = 'abcproject'; + store.state.currentBranchId = currentBranchId; + store.state.currentProjectId = 'abcproject'; const proj = { ...projectData }; proj.branches[currentBranchId] = branches.find((branch) => branch.name === currentBranchId); proj.empty_repo = emptyRepo; - Vue.set(vm.$store.state.projects, 'abcproject', proj); + Vue.set(store.state.projects, 'abcproject', proj); if (hasMR) { - vm.$store.state.currentMergeRequestId = '1'; - vm.$store.state.projects[store.state.currentProjectId].mergeRequests[ + store.state.currentMergeRequestId = '1'; + store.state.projects[store.state.currentProjectId].mergeRequests[ store.state.currentMergeRequestId ] = { foo: 'bar' }; } - vm.$mount(); - - return vm; + wrapper = mount(CommitActions, { store }); + return wrapper; }; beforeEach(() => { @@ -52,17 +47,16 @@ describe('IDE commit sidebar actions', () => { }); afterEach(() => { - vm.$destroy(); - vm = null; + wrapper.destroy(); }); - const findText = () => vm.$el.textContent; - const findRadios = () => Array.from(vm.$el.querySelectorAll('input[type="radio"]')); + const findText = () => wrapper.text(); + const findRadios = () => wrapper.findAll('input[type="radio"]'); it('renders 2 groups', () => { createComponent(); - expect(findRadios().length).toBe(2); + expect(findRadios()).toHaveLength(2); }); it('renders current branch text', () => { @@ -79,41 +73,38 @@ describe('IDE commit sidebar actions', () => { expect(findText()).not.toContain('Create a new branch and merge request'); }); - describe('currentBranchText', () => { - it('escapes current branch', () => { - const injectedSrc = '<img src="x" />'; - createComponent({ currentBranchId: injectedSrc }); + it('escapes current branch name', () => { + const injectedSrc = '<img src="x" />'; + const escapedSrc = '<img src="x" />'; + createComponent({ currentBranchId: injectedSrc }); - expect(vm.currentBranchText).not.toContain(injectedSrc); - }); + expect(wrapper.text()).not.toContain(injectedSrc); + expect(wrapper.text).not.toContain(escapedSrc); }); describe('updateSelectedCommitAction', () => { it('does not return anything if currentBranch does not exist', () => { createComponent({ currentBranchId: null }); - expect(vm.$store.dispatch).not.toHaveBeenCalled(); + expect(store.dispatch).not.toHaveBeenCalled(); }); it('is not called on mount if there is already a selected commitAction', () => { store.state.commitAction = '1'; createComponent({ currentBranchId: null }); - expect(vm.$store.dispatch).not.toHaveBeenCalled(); + expect(store.dispatch).not.toHaveBeenCalled(); }); it('calls again after staged changes', async () => { createComponent({ currentBranchId: null }); - vm.$store.state.currentBranchId = 'main'; - vm.$store.state.changedFiles.push({}); - vm.$store.state.stagedFiles.push({}); + store.state.currentBranchId = 'main'; + store.state.changedFiles.push({}); + store.state.stagedFiles.push({}); await nextTick(); - expect(vm.$store.dispatch).toHaveBeenCalledWith( - ACTION_UPDATE_COMMIT_ACTION, - expect.anything(), - ); + expect(store.dispatch).toHaveBeenCalledWith(ACTION_UPDATE_COMMIT_ACTION, expect.anything()); }); it.each` @@ -133,9 +124,7 @@ describe('IDE commit sidebar actions', () => { ({ input, expectedOption }) => { createComponent(input); - expect(vm.$store.dispatch.mock.calls).toEqual([ - [ACTION_UPDATE_COMMIT_ACTION, expectedOption], - ]); + expect(store.dispatch.mock.calls).toEqual([[ACTION_UPDATE_COMMIT_ACTION, expectedOption]]); }, ); }); diff --git a/spec/frontend/ide/components/commit_sidebar/list_item_spec.js b/spec/frontend/ide/components/commit_sidebar/list_item_spec.js index dea920ecb5e..c9571d39acb 100644 --- a/spec/frontend/ide/components/commit_sidebar/list_item_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/list_item_spec.js @@ -1,133 +1,136 @@ +import { mount } from '@vue/test-utils'; +import { GlIcon } from '@gitlab/ui'; import Vue, { nextTick } from 'vue'; import { trimText } from 'helpers/text_helper'; import waitForPromises from 'helpers/wait_for_promises'; -import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; -import listItem from '~/ide/components/commit_sidebar/list_item.vue'; +import ListItem from '~/ide/components/commit_sidebar/list_item.vue'; import { createRouter } from '~/ide/ide_router'; import { createStore } from '~/ide/stores'; import { file } from '../../helpers'; describe('Multi-file editor commit sidebar list item', () => { - let vm; - let f; + let wrapper; + let testFile; let findPathEl; let store; let router; beforeEach(() => { store = createStore(); - router = createRouter(store); + jest.spyOn(store, 'dispatch'); - const Component = Vue.extend(listItem); + router = createRouter(store); - f = file('test-file'); + testFile = file('test-file'); - store.state.entries[f.path] = f; + store.state.entries[testFile.path] = testFile; - vm = createComponentWithStore(Component, store, { - file: f, - activeFileKey: `staged-${f.key}`, - }).$mount(); + wrapper = mount(ListItem, { + store, + propsData: { + file: testFile, + activeFileKey: `staged-${testFile.key}`, + }, + }); - findPathEl = vm.$el.querySelector('.multi-file-commit-list-path'); + findPathEl = wrapper.find('.multi-file-commit-list-path'); }); afterEach(() => { - vm.$destroy(); + wrapper.destroy(); }); - const findPathText = () => trimText(findPathEl.textContent); + const findPathText = () => trimText(findPathEl.text()); it('renders file path', () => { - expect(findPathText()).toContain(f.path); + expect(findPathText()).toContain(testFile.path); }); it('correctly renders renamed entries', async () => { - Vue.set(vm.file, 'prevName', 'Old name'); - + Vue.set(testFile, 'prevName', 'Old name'); await nextTick(); - expect(findPathText()).toEqual(`Old name → ${f.name}`); + + expect(findPathText()).toEqual(`Old name → ${testFile.name}`); }); it('correctly renders entry, the name of which did not change after rename (as within a folder)', async () => { - Vue.set(vm.file, 'prevName', f.name); - + Vue.set(testFile, 'prevName', testFile.name); await nextTick(); - expect(findPathText()).toEqual(f.name); + + expect(findPathText()).toEqual(testFile.name); }); it('opens a closed file in the editor when clicking the file path', async () => { - jest.spyOn(vm, 'openPendingTab'); jest.spyOn(router, 'push').mockImplementation(() => {}); - findPathEl.click(); - - await nextTick(); + await findPathEl.trigger('click'); - expect(vm.openPendingTab).toHaveBeenCalled(); + expect(store.dispatch).toHaveBeenCalledWith('openPendingTab', expect.anything()); expect(router.push).toHaveBeenCalled(); }); it('calls updateViewer with diff when clicking file', async () => { - jest.spyOn(vm, 'openFileInEditor'); - jest.spyOn(vm, 'updateViewer'); jest.spyOn(router, 'push').mockImplementation(() => {}); - findPathEl.click(); - + await findPathEl.trigger('click'); await waitForPromises(); - expect(vm.updateViewer).toHaveBeenCalledWith('diff'); + expect(store.dispatch).toHaveBeenCalledWith('updateViewer', 'diff'); }); - describe('computed', () => { - describe('iconName', () => { - it('returns modified when not a tempFile', () => { - expect(vm.iconName).toBe('file-modified'); - }); + describe('icon name', () => { + const getIconName = () => wrapper.findComponent(GlIcon).props('name'); + + it('is modified when not a tempFile', () => { + expect(getIconName()).toBe('file-modified'); + }); - it('returns addition when not a tempFile', () => { - f.tempFile = true; + it('is addition when is a tempFile', async () => { + testFile.tempFile = true; + await nextTick(); - expect(vm.iconName).toBe('file-addition'); - }); + expect(getIconName()).toBe('file-addition'); + }); - it('returns deletion', () => { - f.deleted = true; + it('is deletion when is deleted', async () => { + testFile.deleted = true; + await nextTick(); - expect(vm.iconName).toBe('file-deletion'); - }); + expect(getIconName()).toBe('file-deletion'); }); + }); - describe('iconClass', () => { - it('returns modified when not a tempFile', () => { - expect(vm.iconClass).toContain('ide-file-modified'); - }); + describe('icon class', () => { + const getIconClass = () => wrapper.findComponent(GlIcon).classes(); - it('returns addition when not a tempFile', () => { - f.tempFile = true; + it('is modified when not a tempFile', () => { + expect(getIconClass()).toContain('ide-file-modified'); + }); - expect(vm.iconClass).toContain('ide-file-addition'); - }); + it('is addition when is a tempFile', async () => { + testFile.tempFile = true; + await nextTick(); - it('returns deletion', () => { - f.deleted = true; + expect(getIconClass()).toContain('ide-file-addition'); + }); - expect(vm.iconClass).toContain('ide-file-deletion'); - }); + it('returns deletion when is deleted', async () => { + testFile.deleted = true; + await nextTick(); + + expect(getIconClass()).toContain('ide-file-deletion'); }); }); describe('is active', () => { it('does not add active class when dont keys match', () => { - expect(vm.$el.querySelector('.is-active')).toBe(null); + expect(wrapper.find('.is-active').exists()).toBe(false); }); it('adds active class when keys match', async () => { - vm.keyPrefix = 'staged'; + await wrapper.setProps({ keyPrefix: 'staged' }); - await nextTick(); - expect(vm.$el.querySelector('.is-active')).not.toBe(null); + expect(wrapper.find('.is-active').exists()).toBe(true); }); }); }); diff --git a/spec/frontend/ide/components/commit_sidebar/message_field_spec.js b/spec/frontend/ide/components/commit_sidebar/message_field_spec.js index ace266aec5e..c2ef29c1059 100644 --- a/spec/frontend/ide/components/commit_sidebar/message_field_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/message_field_spec.js @@ -1,135 +1,121 @@ -import Vue, { nextTick } from 'vue'; -import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; -import createComponent from 'helpers/vue_mount_component_helper'; +import { nextTick } from 'vue'; +import { mount } from '@vue/test-utils'; import CommitMessageField from '~/ide/components/commit_sidebar/message_field.vue'; describe('IDE commit message field', () => { - const Component = Vue.extend(CommitMessageField); - let vm; + let wrapper; beforeEach(() => { - setHTMLFixture('<div id="app"></div>'); - - vm = createComponent( - Component, - { + wrapper = mount(CommitMessageField, { + propsData: { text: '', placeholder: 'testing', }, - '#app', - ); + attachTo: document.body, + }); }); afterEach(() => { - vm.$destroy(); - - resetHTMLFixture(); + wrapper.destroy(); }); + const findMessage = () => wrapper.find('textarea'); + const findHighlights = () => wrapper.findAll('.highlights span'); + const findMarks = () => wrapper.findAll('mark'); + it('adds is-focused class on focus', async () => { - vm.$el.querySelector('textarea').focus(); + await findMessage().trigger('focus'); - await nextTick(); - expect(vm.$el.querySelector('.is-focused')).not.toBeNull(); + expect(wrapper.find('.is-focused').exists()).toBe(true); }); it('removed is-focused class on blur', async () => { - vm.$el.querySelector('textarea').focus(); + await findMessage().trigger('focus'); - await nextTick(); - expect(vm.$el.querySelector('.is-focused')).not.toBeNull(); + expect(wrapper.find('.is-focused').exists()).toBe(true); - vm.$el.querySelector('textarea').blur(); + await findMessage().trigger('blur'); - await nextTick(); - expect(vm.$el.querySelector('.is-focused')).toBeNull(); + expect(wrapper.find('.is-focused').exists()).toBe(false); }); - it('emits input event on input', () => { - jest.spyOn(vm, '$emit').mockImplementation(); - - const textarea = vm.$el.querySelector('textarea'); - textarea.value = 'testing'; - - textarea.dispatchEvent(new Event('input')); + it('emits input event on input', async () => { + await findMessage().setValue('testing'); - expect(vm.$emit).toHaveBeenCalledWith('input', 'testing'); + expect(wrapper.emitted('input')[0]).toStrictEqual(['testing']); }); describe('highlights', () => { describe('subject line', () => { it('does not highlight less than 50 characters', async () => { - vm.text = 'text less than 50 chars'; + await wrapper.setProps({ text: 'text less than 50 chars' }); - await nextTick(); - expect(vm.$el.querySelector('.highlights span').textContent).toContain( - 'text less than 50 chars', - ); + expect(findHighlights()).toHaveLength(1); + expect(findHighlights().at(0).text()).toContain('text less than 50 chars'); - expect(vm.$el.querySelector('mark').style.display).toBe('none'); + expect(findMarks()).toHaveLength(1); + expect(findMarks().at(0).isVisible()).toBe(false); }); it('highlights characters over 50 length', async () => { - vm.text = - 'text less than 50 chars that should not highlighted. text more than 50 should be highlighted'; + await wrapper.setProps({ + text: + 'text less than 50 chars that should not highlighted. text more than 50 should be highlighted', + }); - await nextTick(); - expect(vm.$el.querySelector('.highlights span').textContent).toContain( + expect(findHighlights()).toHaveLength(1); + expect(findHighlights().at(0).text()).toContain( 'text less than 50 chars that should not highlighte', ); - expect(vm.$el.querySelector('mark').style.display).not.toBe('none'); - expect(vm.$el.querySelector('mark').textContent).toBe( - 'd. text more than 50 should be highlighted', - ); + expect(findMarks()).toHaveLength(1); + expect(findMarks().at(0).isVisible()).toBe(true); + expect(findMarks().at(0).text()).toBe('d. text more than 50 should be highlighted'); }); }); describe('body text', () => { it('does not highlight body text less tan 72 characters', async () => { - vm.text = 'subject line\nbody content'; + await wrapper.setProps({ text: 'subject line\nbody content' }); - await nextTick(); - expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2); - expect(vm.$el.querySelectorAll('mark')[1].style.display).toBe('none'); + expect(findHighlights()).toHaveLength(2); + expect(findMarks().at(1).isVisible()).toBe(false); }); it('highlights body text more than 72 characters', async () => { - vm.text = - 'subject line\nbody content that will be highlighted when it is more than 72 characters in length'; - - await nextTick(); - expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2); - expect(vm.$el.querySelectorAll('mark')[1].style.display).not.toBe('none'); - expect(vm.$el.querySelectorAll('mark')[1].textContent).toBe(' in length'); + await wrapper.setProps({ + text: + 'subject line\nbody content that will be highlighted when it is more than 72 characters in length', + }); + + expect(findHighlights()).toHaveLength(2); + expect(findMarks().at(1).isVisible()).toBe(true); + expect(findMarks().at(1).text()).toBe('in length'); }); it('highlights body text & subject line', async () => { - vm.text = - 'text less than 50 chars that should not highlighted\nbody content that will be highlighted when it is more than 72 characters in length'; + await wrapper.setProps({ + text: + 'text less than 50 chars that should not highlighted\nbody content that will be highlighted when it is more than 72 characters in length', + }); - await nextTick(); - expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2); - expect(vm.$el.querySelectorAll('mark').length).toBe(2); + expect(findHighlights()).toHaveLength(2); + expect(findMarks()).toHaveLength(2); - expect(vm.$el.querySelectorAll('mark')[0].textContent).toContain('d'); - expect(vm.$el.querySelectorAll('mark')[1].textContent).toBe(' in length'); + expect(findMarks().at(0).text()).toContain('d'); + expect(findMarks().at(1).text()).toBe('in length'); }); }); }); describe('scrolling textarea', () => { it('updates transform of highlights', async () => { - vm.text = 'subject line\n\n\n\n\n\n\n\n\n\n\nbody content'; + await wrapper.setProps({ text: 'subject line\n\n\n\n\n\n\n\n\n\n\nbody content' }); + findMessage().element.scrollTo(0, 50); await nextTick(); - vm.$el.querySelector('textarea').scrollTo(0, 50); - vm.handleScroll(); - - await nextTick(); - expect(vm.scrollTop).toBe(50); - expect(vm.$el.querySelector('.highlights').style.transform).toBe('translate3d(0, -50px, 0)'); + expect(wrapper.find('.highlights').element.style.transform).toBe('translate3d(0, -50px, 0)'); }); }); }); diff --git a/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js b/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js index ee6ed694285..a3fa03a4aa5 100644 --- a/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js +++ b/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js @@ -1,123 +1,116 @@ -import Vue, { nextTick } from 'vue'; -import { createComponentWithStore } from 'helpers/vue_mount_component_helper'; +import { GlFormRadioGroup } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; import RadioGroup from '~/ide/components/commit_sidebar/radio_group.vue'; import { createStore } from '~/ide/stores'; +import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; describe('IDE commit sidebar radio group', () => { - let vm; + let wrapper; let store; - beforeEach(async () => { + const createComponent = (config = {}) => { store = createStore(); - const Component = Vue.extend(RadioGroup); - store.state.commit.commitAction = '2'; + store.state.commit.newBranchName = 'test-123'; - vm = createComponentWithStore(Component, store, { - value: '1', - label: 'test', - checked: true, + wrapper = mount(RadioGroup, { + store, + propsData: config.props, + slots: config.slots, + directives: { + GlTooltip: createMockDirective(), + }, }); - - vm.$mount(); - - await nextTick(); - }); + }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); }); - it('uses label if present', () => { - expect(vm.$el.textContent).toContain('test'); - }); + describe('without input', () => { + const props = { + value: '1', + label: 'test', + checked: true, + }; - it('uses slot if label is not present', async () => { - vm.$destroy(); + it('uses label if present', () => { + createComponent({ props }); - vm = new Vue({ - components: { - RadioGroup, - }, - store, - render: (createElement) => - createElement('radio-group', { props: { value: '1' } }, 'Testing slot'), + expect(wrapper.text()).toContain('test'); }); - vm.$mount(); + it('uses slot if label is not present', () => { + createComponent({ props: { value: '1', checked: true }, slots: { default: 'Testing slot' } }); - await nextTick(); - expect(vm.$el.textContent).toContain('Testing slot'); - }); + expect(wrapper.text()).toContain('Testing slot'); + }); - it('updates store when changing radio button', async () => { - vm.$el.querySelector('input').dispatchEvent(new Event('change')); + it('updates store when changing radio button', async () => { + createComponent({ props }); - await nextTick(); - expect(store.state.commit.commitAction).toBe('1'); + await wrapper.find('input').trigger('change'); + + expect(store.state.commit.commitAction).toBe('1'); + }); }); describe('with input', () => { - beforeEach(async () => { - vm.$destroy(); - - const Component = Vue.extend(RadioGroup); - - store.state.commit.commitAction = '1'; - store.state.commit.newBranchName = 'test-123'; - - vm = createComponentWithStore(Component, store, { - value: '1', - label: 'test', - checked: true, - showInput: true, - }); - - vm.$mount(); - - await nextTick(); - }); + const props = { + value: '2', + label: 'test', + checked: true, + showInput: true, + }; it('renders input box when commitAction matches value', () => { - expect(vm.$el.querySelector('.form-control')).not.toBeNull(); + createComponent({ props: { ...props, value: '2' } }); + + expect(wrapper.find('.form-control').exists()).toBe(true); }); - it('hides input when commitAction doesnt match value', async () => { - store.state.commit.commitAction = '2'; + it('hides input when commitAction doesnt match value', () => { + createComponent({ props: { ...props, value: '1' } }); - await nextTick(); - expect(vm.$el.querySelector('.form-control')).toBeNull(); + expect(wrapper.find('.form-control').exists()).toBe(false); }); it('updates branch name in store on input', async () => { - const input = vm.$el.querySelector('.form-control'); - input.value = 'testing-123'; - input.dispatchEvent(new Event('input')); + createComponent({ props }); + + await wrapper.find('.form-control').setValue('testing-123'); - await nextTick(); expect(store.state.commit.newBranchName).toBe('testing-123'); }); it('renders newBranchName if present', () => { - const input = vm.$el.querySelector('.form-control'); + createComponent({ props }); - expect(input.value).toBe('test-123'); + const input = wrapper.find('.form-control'); + + expect(input.element.value).toBe('test-123'); }); }); describe('tooltipTitle', () => { it('returns title when disabled', () => { - vm.title = 'test title'; - vm.disabled = true; + createComponent({ + props: { value: '1', label: 'test', disabled: true, title: 'test title' }, + }); - expect(vm.tooltipTitle).toBe('test title'); + const tooltip = getBinding(wrapper.findComponent(GlFormRadioGroup).element, 'gl-tooltip'); + expect(tooltip.value).toBe('test title'); }); it('returns blank when not disabled', () => { - vm.title = 'test title'; + createComponent({ + props: { value: '1', label: 'test', title: 'test title' }, + }); + + const tooltip = getBinding(wrapper.findComponent(GlFormRadioGroup).element, 'gl-tooltip'); - expect(vm.tooltipTitle).not.toBe('test title'); + expect(tooltip.value).toBe(''); }); }); }); |