diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-20 12:08:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-20 12:08:11 +0300 |
commit | 1f5a2543e4daf21dd98d8ff0514781c403445c81 (patch) | |
tree | 22af0594a5de457ffb346c2259f9d30c3fd5479f /spec/javascripts | |
parent | 9bded6fb2268204757c35540fadef8e1b6351249 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/javascripts')
-rw-r--r-- | spec/javascripts/ide/components/commit_sidebar/message_field_spec.js | 170 | ||||
-rw-r--r-- | spec/javascripts/ide/components/jobs/detail_spec.js | 184 | ||||
-rw-r--r-- | spec/javascripts/ide/components/repo_editor_spec.js | 512 | ||||
-rw-r--r-- | spec/javascripts/ide/helpers.js | 1 | ||||
-rw-r--r-- | spec/javascripts/ide/mock_data.js | 1 | ||||
-rw-r--r-- | spec/javascripts/ide/stores/actions/merge_request_spec.js | 510 | ||||
-rw-r--r-- | spec/javascripts/ide/stores/actions/project_spec.js | 404 | ||||
-rw-r--r-- | spec/javascripts/ide/stores/actions/tree_spec.js | 216 | ||||
-rw-r--r-- | spec/javascripts/ide/stores/actions_spec.js | 1116 | ||||
-rw-r--r-- | spec/javascripts/ide/stores/modules/commit/actions_spec.js | 603 | ||||
-rw-r--r-- | spec/javascripts/vue_shared/components/file_finder/index_spec.js | 2 |
11 files changed, 1 insertions, 3718 deletions
diff --git a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js b/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js deleted file mode 100644 index 53508f52b2f..00000000000 --- a/spec/javascripts/ide/components/commit_sidebar/message_field_spec.js +++ /dev/null @@ -1,170 +0,0 @@ -import Vue from 'vue'; -import createComponent from 'spec/helpers/vue_mount_component_helper'; -import CommitMessageField from '~/ide/components/commit_sidebar/message_field.vue'; - -describe('IDE commit message field', () => { - const Component = Vue.extend(CommitMessageField); - let vm; - - beforeEach(() => { - setFixtures('<div id="app"></div>'); - - vm = createComponent( - Component, - { - text: '', - placeholder: 'testing', - }, - '#app', - ); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('adds is-focused class on focus', done => { - vm.$el.querySelector('textarea').focus(); - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.is-focused')).not.toBeNull(); - - done(); - }); - }); - - it('removed is-focused class on blur', done => { - vm.$el.querySelector('textarea').focus(); - - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelector('.is-focused')).not.toBeNull(); - - vm.$el.querySelector('textarea').blur(); - - return vm.$nextTick(); - }) - .then(() => { - expect(vm.$el.querySelector('.is-focused')).toBeNull(); - - done(); - }) - .then(done) - .catch(done.fail); - }); - - it('emits input event on input', () => { - spyOn(vm, '$emit'); - - const textarea = vm.$el.querySelector('textarea'); - textarea.value = 'testing'; - - textarea.dispatchEvent(new Event('input')); - - expect(vm.$emit).toHaveBeenCalledWith('input', 'testing'); - }); - - describe('highlights', () => { - describe('subject line', () => { - it('does not highlight less than 50 characters', done => { - vm.text = 'text less than 50 chars'; - - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelector('.highlights span').textContent).toContain( - 'text less than 50 chars', - ); - - expect(vm.$el.querySelector('mark').style.display).toBe('none'); - }) - .then(done) - .catch(done.fail); - }); - - it('highlights characters over 50 length', done => { - vm.text = - 'text less than 50 chars that should not highlighted. text more than 50 should be highlighted'; - - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelector('.highlights span').textContent).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', - ); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('body text', () => { - it('does not highlight body text less tan 72 characters', done => { - vm.text = 'subject line\nbody content'; - - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2); - expect(vm.$el.querySelectorAll('mark')[1].style.display).toBe('none'); - }) - .then(done) - .catch(done.fail); - }); - - it('highlights body text more than 72 characters', done => { - vm.text = - 'subject line\nbody content that will be highlighted when it is more than 72 characters in length'; - - vm.$nextTick() - .then(() => { - 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'); - }) - .then(done) - .catch(done.fail); - }); - - it('highlights body text & subject line', done => { - 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'; - - vm.$nextTick() - .then(() => { - expect(vm.$el.querySelectorAll('.highlights span').length).toBe(2); - expect(vm.$el.querySelectorAll('mark').length).toBe(2); - - expect(vm.$el.querySelectorAll('mark')[0].textContent).toContain('d'); - expect(vm.$el.querySelectorAll('mark')[1].textContent).toBe(' in length'); - }) - .then(done) - .catch(done.fail); - }); - }); - }); - - describe('scrolling textarea', () => { - it('updates transform of highlights', done => { - vm.text = 'subject line\n\n\n\n\n\n\n\n\n\n\nbody content'; - - vm.$nextTick() - .then(() => { - vm.$el.querySelector('textarea').scrollTo(0, 50); - - vm.handleScroll(); - }) - .then(vm.$nextTick) - .then(() => { - expect(vm.scrollTop).toBe(50); - expect(vm.$el.querySelector('.highlights').style.transform).toBe( - 'translate3d(0px, -50px, 0px)', - ); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/ide/components/jobs/detail_spec.js b/spec/javascripts/ide/components/jobs/detail_spec.js deleted file mode 100644 index a4e6b81acba..00000000000 --- a/spec/javascripts/ide/components/jobs/detail_spec.js +++ /dev/null @@ -1,184 +0,0 @@ -import Vue from 'vue'; -import JobDetail from '~/ide/components/jobs/detail.vue'; -import { createStore } from '~/ide/stores'; -import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper'; -import { jobs } from '../../mock_data'; - -describe('IDE jobs detail view', () => { - const Component = Vue.extend(JobDetail); - let vm; - - beforeEach(() => { - const store = createStore(); - - store.state.pipelines.detailJob = { - ...jobs[0], - isLoading: true, - output: 'testing', - rawPath: `${gl.TEST_HOST}/raw`, - }; - - vm = createComponentWithStore(Component, store); - - spyOn(vm, 'fetchJobTrace').and.returnValue(Promise.resolve()); - - vm = vm.$mount(); - - spyOn(vm.$refs.buildTrace, 'scrollTo'); - }); - - afterEach(() => { - vm.$destroy(); - }); - - it('calls fetchJobTrace on mount', () => { - expect(vm.fetchJobTrace).toHaveBeenCalled(); - }); - - it('scrolls to bottom on mount', done => { - setTimeout(() => { - expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalled(); - - done(); - }); - }); - - it('renders job output', () => { - expect(vm.$el.querySelector('.bash').textContent).toContain('testing'); - }); - - it('renders empty message output', done => { - vm.$store.state.pipelines.detailJob.output = ''; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.bash').textContent).toContain('No messages were logged'); - - done(); - }); - }); - - it('renders loading icon', () => { - expect(vm.$el.querySelector('.build-loader-animation')).not.toBe(null); - expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe(''); - }); - - it('hides output when loading', () => { - expect(vm.$el.querySelector('.bash')).not.toBe(null); - expect(vm.$el.querySelector('.bash').style.display).toBe('none'); - }); - - it('hide loading icon when isLoading is false', done => { - vm.$store.state.pipelines.detailJob.isLoading = false; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe('none'); - - done(); - }); - }); - - it('resets detailJob when clicking header button', () => { - spyOn(vm, 'setDetailJob'); - - vm.$el.querySelector('.btn').click(); - - expect(vm.setDetailJob).toHaveBeenCalledWith(null); - }); - - it('renders raw path link', () => { - expect(vm.$el.querySelector('.controllers-buttons').getAttribute('href')).toBe( - `${gl.TEST_HOST}/raw`, - ); - }); - - describe('scroll buttons', () => { - it('triggers scrollDown when clicking down button', done => { - spyOn(vm, 'scrollDown'); - - vm.$el.querySelectorAll('.btn-scroll')[1].click(); - - vm.$nextTick(() => { - expect(vm.scrollDown).toHaveBeenCalled(); - - done(); - }); - }); - - it('triggers scrollUp when clicking up button', done => { - spyOn(vm, 'scrollUp'); - - vm.scrollPos = 1; - - vm.$nextTick() - .then(() => vm.$el.querySelector('.btn-scroll').click()) - .then(() => vm.$nextTick()) - .then(() => { - expect(vm.scrollUp).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('scrollDown', () => { - it('scrolls build trace to bottom', () => { - spyOnProperty(vm.$refs.buildTrace, 'scrollHeight').and.returnValue(1000); - - vm.scrollDown(); - - expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalledWith(0, 1000); - }); - }); - - describe('scrollUp', () => { - it('scrolls build trace to top', () => { - vm.scrollUp(); - - expect(vm.$refs.buildTrace.scrollTo).toHaveBeenCalledWith(0, 0); - }); - }); - - describe('scrollBuildLog', () => { - beforeEach(() => { - spyOnProperty(vm.$refs.buildTrace, 'offsetHeight').and.returnValue(100); - spyOnProperty(vm.$refs.buildTrace, 'scrollHeight').and.returnValue(200); - }); - - it('sets scrollPos to bottom when at the bottom', done => { - spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(100); - - vm.scrollBuildLog(); - - setTimeout(() => { - expect(vm.scrollPos).toBe(1); - - done(); - }); - }); - - it('sets scrollPos to top when at the top', done => { - spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(0); - vm.scrollPos = 1; - - vm.scrollBuildLog(); - - setTimeout(() => { - expect(vm.scrollPos).toBe(0); - - done(); - }); - }); - - it('resets scrollPos when not at top or bottom', done => { - spyOnProperty(vm.$refs.buildTrace, 'scrollTop').and.returnValue(10); - - vm.scrollBuildLog(); - - setTimeout(() => { - expect(vm.scrollPos).toBe(''); - - done(); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js deleted file mode 100644 index 8db29011da7..00000000000 --- a/spec/javascripts/ide/components/repo_editor_spec.js +++ /dev/null @@ -1,512 +0,0 @@ -import Vue from 'vue'; -import MockAdapter from 'axios-mock-adapter'; -import '~/behaviors/markdown/render_gfm'; -import axios from '~/lib/utils/axios_utils'; -import store from '~/ide/stores'; -import repoEditor from '~/ide/components/repo_editor.vue'; -import Editor from '~/ide/lib/editor'; -import { leftSidebarViews, FILE_VIEW_MODE_EDITOR, FILE_VIEW_MODE_PREVIEW } from '~/ide/constants'; -import { createComponentWithStore } from '../../helpers/vue_mount_component_helper'; -import setTimeoutPromise from '../../helpers/set_timeout_promise_helper'; -import { file, resetStore } from '../helpers'; - -describe('RepoEditor', () => { - let vm; - - beforeEach(done => { - const f = { - ...file(), - viewMode: FILE_VIEW_MODE_EDITOR, - }; - const RepoEditor = Vue.extend(repoEditor); - - vm = createComponentWithStore(RepoEditor, store, { - file: f, - }); - - f.active = true; - f.tempFile = true; - - vm.$store.state.openFiles.push(f); - vm.$store.state.projects = { - 'gitlab-org/gitlab': { - branches: { - master: { - name: 'master', - commit: { - id: 'abcdefgh', - }, - }, - }, - }, - }; - vm.$store.state.currentProjectId = 'gitlab-org/gitlab'; - vm.$store.state.currentBranchId = 'master'; - - Vue.set(vm.$store.state.entries, f.path, f); - - spyOn(vm, 'getFileData').and.returnValue(Promise.resolve()); - spyOn(vm, 'getRawFileData').and.returnValue(Promise.resolve()); - - vm.$mount(); - - Vue.nextTick(() => setTimeout(done)); - }); - - afterEach(() => { - vm.$destroy(); - - resetStore(vm.$store); - - Editor.editorInstance.dispose(); - }); - - const findEditor = () => vm.$el.querySelector('.multi-file-editor-holder'); - - it('sets renderWhitespace to `all`', () => { - vm.$store.state.renderWhitespaceInCode = true; - - expect(vm.editorOptions.renderWhitespace).toEqual('all'); - }); - - it('sets renderWhitespace to `none`', () => { - vm.$store.state.renderWhitespaceInCode = false; - - expect(vm.editorOptions.renderWhitespace).toEqual('none'); - }); - - it('renders an ide container', () => { - expect(vm.shouldHideEditor).toBeFalsy(); - expect(vm.showEditor).toBe(true); - expect(findEditor()).not.toHaveCss({ display: 'none' }); - }); - - it('renders only an edit tab', done => { - Vue.nextTick(() => { - const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li'); - - expect(tabs.length).toBe(1); - expect(tabs[0].textContent.trim()).toBe('Edit'); - - done(); - }); - }); - - describe('when file is markdown', () => { - beforeEach(done => { - vm.file.previewMode = { - id: 'markdown', - previewTitle: 'Preview Markdown', - }; - - vm.$nextTick(done); - }); - - it('renders an Edit and a Preview Tab', done => { - Vue.nextTick(() => { - const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li'); - - expect(tabs.length).toBe(2); - expect(tabs[0].textContent.trim()).toBe('Edit'); - expect(tabs[1].textContent.trim()).toBe('Preview Markdown'); - - done(); - }); - }); - }); - - describe('when file is markdown and viewer mode is review', () => { - let mock; - - beforeEach(done => { - mock = new MockAdapter(axios); - - vm.file.projectId = 'namespace/project'; - vm.file.previewMode = { - id: 'markdown', - previewTitle: 'Preview Markdown', - }; - vm.file.content = 'testing 123'; - vm.$store.state.viewer = 'diff'; - - mock.onPost(/(.*)\/preview_markdown/).reply(200, { - body: '<p>testing 123</p>', - }); - - vm.$nextTick(done); - }); - - afterEach(() => { - mock.restore(); - }); - - it('renders an Edit and a Preview Tab', done => { - Vue.nextTick(() => { - const tabs = vm.$el.querySelectorAll('.ide-mode-tabs .nav-links li'); - - expect(tabs.length).toBe(2); - expect(tabs[0].textContent.trim()).toBe('Review'); - expect(tabs[1].textContent.trim()).toBe('Preview Markdown'); - - done(); - }); - }); - - it('renders markdown for tempFile', done => { - vm.file.tempFile = true; - vm.file.path = `${vm.file.path}.md`; - vm.$store.state.entries[vm.file.path] = vm.file; - - vm.$nextTick() - .then(() => { - vm.$el.querySelectorAll('.ide-mode-tabs .nav-links a')[1].click(); - }) - .then(setTimeoutPromise) - .then(() => { - expect(vm.$el.querySelector('.preview-container').innerHTML).toContain( - '<p>testing 123</p>', - ); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('when open file is binary and not raw', () => { - beforeEach(done => { - vm.file.binary = true; - - vm.$nextTick(done); - }); - - it('does not render the IDE', () => { - expect(vm.shouldHideEditor).toBeTruthy(); - }); - }); - - describe('createEditorInstance', () => { - it('calls createInstance when viewer is editor', done => { - spyOn(vm.editor, 'createInstance'); - - vm.createEditorInstance(); - - vm.$nextTick(() => { - expect(vm.editor.createInstance).toHaveBeenCalled(); - - done(); - }); - }); - - it('calls createDiffInstance when viewer is diff', done => { - vm.$store.state.viewer = 'diff'; - - spyOn(vm.editor, 'createDiffInstance'); - - vm.createEditorInstance(); - - vm.$nextTick(() => { - expect(vm.editor.createDiffInstance).toHaveBeenCalled(); - - done(); - }); - }); - - it('calls createDiffInstance when viewer is a merge request diff', done => { - vm.$store.state.viewer = 'mrdiff'; - - spyOn(vm.editor, 'createDiffInstance'); - - vm.createEditorInstance(); - - vm.$nextTick(() => { - expect(vm.editor.createDiffInstance).toHaveBeenCalled(); - - done(); - }); - }); - }); - - describe('setupEditor', () => { - it('creates new model', () => { - spyOn(vm.editor, 'createModel').and.callThrough(); - - Editor.editorInstance.modelManager.dispose(); - - vm.setupEditor(); - - expect(vm.editor.createModel).toHaveBeenCalledWith(vm.file, null); - expect(vm.model).not.toBeNull(); - }); - - it('attaches model to editor', () => { - spyOn(vm.editor, 'attachModel').and.callThrough(); - - Editor.editorInstance.modelManager.dispose(); - - vm.setupEditor(); - - expect(vm.editor.attachModel).toHaveBeenCalledWith(vm.model); - }); - - it('attaches model to merge request editor', () => { - vm.$store.state.viewer = 'mrdiff'; - vm.file.mrChange = true; - spyOn(vm.editor, 'attachMergeRequestModel'); - - Editor.editorInstance.modelManager.dispose(); - - vm.setupEditor(); - - expect(vm.editor.attachMergeRequestModel).toHaveBeenCalledWith(vm.model); - }); - - it('does not attach model to merge request editor when not a MR change', () => { - vm.$store.state.viewer = 'mrdiff'; - vm.file.mrChange = false; - spyOn(vm.editor, 'attachMergeRequestModel'); - - Editor.editorInstance.modelManager.dispose(); - - vm.setupEditor(); - - expect(vm.editor.attachMergeRequestModel).not.toHaveBeenCalledWith(vm.model); - }); - - it('adds callback methods', () => { - spyOn(vm.editor, 'onPositionChange').and.callThrough(); - - Editor.editorInstance.modelManager.dispose(); - - vm.setupEditor(); - - expect(vm.editor.onPositionChange).toHaveBeenCalled(); - expect(vm.model.events.size).toBe(2); - }); - - it('updates state when model content changed', done => { - vm.model.setValue('testing 123\n'); - - setTimeout(() => { - expect(vm.file.content).toBe('testing 123\n'); - - done(); - }); - }); - - it('sets head model as staged file', () => { - spyOn(vm.editor, 'createModel').and.callThrough(); - - Editor.editorInstance.modelManager.dispose(); - - vm.$store.state.stagedFiles.push({ ...vm.file, key: 'staged' }); - vm.file.staged = true; - vm.file.key = `unstaged-${vm.file.key}`; - - vm.setupEditor(); - - expect(vm.editor.createModel).toHaveBeenCalledWith(vm.file, vm.$store.state.stagedFiles[0]); - }); - }); - - describe('editor updateDimensions', () => { - beforeEach(() => { - spyOn(vm.editor, 'updateDimensions').and.callThrough(); - spyOn(vm.editor, 'updateDiffView'); - }); - - it('calls updateDimensions when panelResizing is false', done => { - vm.$store.state.panelResizing = true; - - vm.$nextTick() - .then(() => { - vm.$store.state.panelResizing = false; - }) - .then(vm.$nextTick) - .then(() => { - expect(vm.editor.updateDimensions).toHaveBeenCalled(); - expect(vm.editor.updateDiffView).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - - it('does not call updateDimensions when panelResizing is true', done => { - vm.$store.state.panelResizing = true; - - vm.$nextTick(() => { - expect(vm.editor.updateDimensions).not.toHaveBeenCalled(); - expect(vm.editor.updateDiffView).not.toHaveBeenCalled(); - - done(); - }); - }); - - it('calls updateDimensions when rightPane is opened', done => { - vm.$store.state.rightPane.isOpen = true; - - vm.$nextTick(() => { - expect(vm.editor.updateDimensions).toHaveBeenCalled(); - expect(vm.editor.updateDiffView).toHaveBeenCalled(); - - done(); - }); - }); - }); - - describe('show tabs', () => { - it('shows tabs in edit mode', () => { - expect(vm.$el.querySelector('.nav-links')).not.toBe(null); - }); - - it('hides tabs in review mode', done => { - vm.$store.state.currentActivityView = leftSidebarViews.review.name; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.nav-links')).toBe(null); - - done(); - }); - }); - - it('hides tabs in commit mode', done => { - vm.$store.state.currentActivityView = leftSidebarViews.commit.name; - - vm.$nextTick(() => { - expect(vm.$el.querySelector('.nav-links')).toBe(null); - - done(); - }); - }); - }); - - describe('when files view mode is preview', () => { - beforeEach(done => { - spyOn(vm.editor, 'updateDimensions'); - vm.file.viewMode = FILE_VIEW_MODE_PREVIEW; - vm.$nextTick(done); - }); - - it('should hide editor', () => { - expect(vm.showEditor).toBe(false); - expect(findEditor()).toHaveCss({ display: 'none' }); - }); - - describe('when file view mode changes to editor', () => { - beforeEach(done => { - vm.file.viewMode = FILE_VIEW_MODE_EDITOR; - - // one tick to trigger watch - vm.$nextTick() - // another tick needed until we can update dimensions - .then(() => vm.$nextTick()) - .then(done) - .catch(done.fail); - }); - - it('should update dimensions', () => { - expect(vm.editor.updateDimensions).toHaveBeenCalled(); - }); - }); - }); - - describe('initEditor', () => { - beforeEach(() => { - vm.file.tempFile = false; - spyOn(vm.editor, 'createInstance'); - spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true); - }); - - it('does not fetch file information for temp entries', done => { - vm.file.tempFile = true; - - vm.initEditor(); - vm.$nextTick() - .then(() => { - expect(vm.getFileData).not.toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - - it('is being initialised for files without content even if shouldHideEditor is `true`', done => { - vm.file.content = ''; - vm.file.raw = ''; - - vm.initEditor(); - vm.$nextTick() - .then(() => { - expect(vm.getFileData).toHaveBeenCalled(); - expect(vm.getRawFileData).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - - it('does not initialize editor for files already with content', done => { - vm.file.content = 'foo'; - - vm.initEditor(); - vm.$nextTick() - .then(() => { - expect(vm.getFileData).not.toHaveBeenCalled(); - expect(vm.getRawFileData).not.toHaveBeenCalled(); - expect(vm.editor.createInstance).not.toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('updates on file changes', () => { - beforeEach(() => { - spyOn(vm, 'initEditor'); - }); - - it('calls removePendingTab when old file is pending', done => { - spyOnProperty(vm, 'shouldHideEditor').and.returnValue(true); - spyOn(vm, 'removePendingTab'); - - vm.file.pending = true; - - vm.$nextTick() - .then(() => { - vm.file = file('testing'); - vm.file.content = 'foo'; // need to prevent full cycle of initEditor - - return vm.$nextTick(); - }) - .then(() => { - expect(vm.removePendingTab).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - - it('does not call initEditor if the file did not change', done => { - Vue.set(vm, 'file', vm.file); - - vm.$nextTick() - .then(() => { - expect(vm.initEditor).not.toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - - it('calls initEditor when file key is changed', done => { - expect(vm.initEditor).not.toHaveBeenCalled(); - - Vue.set(vm, 'file', { - ...vm.file, - key: 'new', - }); - - vm.$nextTick() - .then(() => { - expect(vm.initEditor).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/ide/helpers.js b/spec/javascripts/ide/helpers.js deleted file mode 100644 index 2c52780f316..00000000000 --- a/spec/javascripts/ide/helpers.js +++ /dev/null @@ -1 +0,0 @@ -export * from '../../frontend/ide/helpers'; diff --git a/spec/javascripts/ide/mock_data.js b/spec/javascripts/ide/mock_data.js deleted file mode 100644 index 27f0ad01f54..00000000000 --- a/spec/javascripts/ide/mock_data.js +++ /dev/null @@ -1 +0,0 @@ -export * from '../../frontend/ide/mock_data'; diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js deleted file mode 100644 index ce09cf51ac5..00000000000 --- a/spec/javascripts/ide/stores/actions/merge_request_spec.js +++ /dev/null @@ -1,510 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import store from '~/ide/stores'; -import actions, { - getMergeRequestData, - getMergeRequestChanges, - getMergeRequestVersions, - openMergeRequest, -} from '~/ide/stores/actions/merge_request'; -import service from '~/ide/services'; -import { leftSidebarViews, PERMISSION_READ_MR } from '~/ide/constants'; -import { resetStore } from '../../helpers'; - -const TEST_PROJECT = 'abcproject'; -const TEST_PROJECT_ID = 17; - -describe('IDE store merge request actions', () => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - - store.state.projects[TEST_PROJECT] = { - id: TEST_PROJECT_ID, - mergeRequests: {}, - userPermissions: { - [PERMISSION_READ_MR]: true, - }, - }; - }); - - afterEach(() => { - mock.restore(); - resetStore(store); - }); - - describe('getMergeRequestsForBranch', () => { - describe('success', () => { - const mrData = { iid: 2, source_branch: 'bar' }; - const mockData = [mrData]; - - describe('base case', () => { - beforeEach(() => { - spyOn(service, 'getProjectMergeRequests').and.callThrough(); - mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).reply(200, mockData); - }); - - it('calls getProjectMergeRequests service method', done => { - store - .dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' }) - .then(() => { - expect(service.getProjectMergeRequests).toHaveBeenCalledWith(TEST_PROJECT, { - source_branch: 'bar', - source_project_id: TEST_PROJECT_ID, - order_by: 'created_at', - per_page: 1, - }); - - done(); - }) - .catch(done.fail); - }); - - it('sets the "Merge Request" Object', done => { - store - .dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' }) - .then(() => { - expect(store.state.projects.abcproject.mergeRequests).toEqual({ - '2': jasmine.objectContaining(mrData), - }); - done(); - }) - .catch(done.fail); - }); - - it('sets "Current Merge Request" object to the most recent MR', done => { - store - .dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' }) - .then(() => { - expect(store.state.currentMergeRequestId).toEqual('2'); - done(); - }) - .catch(done.fail); - }); - - it('does nothing if user cannot read MRs', done => { - store.state.projects[TEST_PROJECT].userPermissions[PERMISSION_READ_MR] = false; - - store - .dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' }) - .then(() => { - expect(service.getProjectMergeRequests).not.toHaveBeenCalled(); - expect(store.state.currentMergeRequestId).toBe(''); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('no merge requests for branch available case', () => { - beforeEach(() => { - spyOn(service, 'getProjectMergeRequests').and.callThrough(); - mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).reply(200, []); - }); - - it('does not fail if there are no merge requests for current branch', done => { - store - .dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'foo' }) - .then(() => { - expect(store.state.projects[TEST_PROJECT].mergeRequests).toEqual({}); - expect(store.state.currentMergeRequestId).toEqual(''); - done(); - }) - .catch(done.fail); - }); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).networkError(); - }); - - it('flashes message, if error', done => { - const flashSpy = spyOnDependency(actions, 'flash'); - - store - .dispatch('getMergeRequestsForBranch', { projectId: TEST_PROJECT, branchId: 'bar' }) - .then(() => { - fail('Expected getMergeRequestsForBranch to throw an error'); - }) - .catch(() => { - expect(flashSpy).toHaveBeenCalled(); - expect(flashSpy.calls.argsFor(0)[0]).toEqual('Error fetching merge requests for bar'); - }) - .then(done) - .catch(done.fail); - }); - }); - }); - - describe('getMergeRequestData', () => { - describe('success', () => { - beforeEach(() => { - spyOn(service, 'getProjectMergeRequestData').and.callThrough(); - - mock - .onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1/) - .reply(200, { title: 'mergerequest' }); - }); - - it('calls getProjectMergeRequestData service method', done => { - store - .dispatch('getMergeRequestData', { projectId: TEST_PROJECT, mergeRequestId: 1 }) - .then(() => { - expect(service.getProjectMergeRequestData).toHaveBeenCalledWith(TEST_PROJECT, 1); - - done(); - }) - .catch(done.fail); - }); - - it('sets the Merge Request Object', done => { - store - .dispatch('getMergeRequestData', { projectId: TEST_PROJECT, mergeRequestId: 1 }) - .then(() => { - expect(store.state.currentMergeRequestId).toBe(1); - expect(store.state.projects[TEST_PROJECT].mergeRequests['1'].title).toBe( - 'mergerequest', - ); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1/).networkError(); - }); - - it('dispatches error action', done => { - const dispatch = jasmine.createSpy('dispatch'); - - getMergeRequestData( - { - commit() {}, - dispatch, - state: store.state, - }, - { projectId: TEST_PROJECT, mergeRequestId: 1 }, - ) - .then(done.fail) - .catch(() => { - expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { - text: 'An error occurred while loading the merge request.', - action: jasmine.any(Function), - actionText: 'Please try again', - actionPayload: { - projectId: TEST_PROJECT, - mergeRequestId: 1, - force: false, - }, - }); - - done(); - }); - }); - }); - }); - - describe('getMergeRequestChanges', () => { - beforeEach(() => { - store.state.projects[TEST_PROJECT].mergeRequests['1'] = { changes: [] }; - }); - - describe('success', () => { - beforeEach(() => { - spyOn(service, 'getProjectMergeRequestChanges').and.callThrough(); - - mock - .onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/changes/) - .reply(200, { title: 'mergerequest' }); - }); - - it('calls getProjectMergeRequestChanges service method', done => { - store - .dispatch('getMergeRequestChanges', { projectId: TEST_PROJECT, mergeRequestId: 1 }) - .then(() => { - expect(service.getProjectMergeRequestChanges).toHaveBeenCalledWith(TEST_PROJECT, 1); - - done(); - }) - .catch(done.fail); - }); - - it('sets the Merge Request Changes Object', done => { - store - .dispatch('getMergeRequestChanges', { projectId: TEST_PROJECT, mergeRequestId: 1 }) - .then(() => { - expect(store.state.projects[TEST_PROJECT].mergeRequests['1'].changes.title).toBe( - 'mergerequest', - ); - done(); - }) - .catch(done.fail); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/changes/).networkError(); - }); - - it('dispatches error action', done => { - const dispatch = jasmine.createSpy('dispatch'); - - getMergeRequestChanges( - { - commit() {}, - dispatch, - state: store.state, - }, - { projectId: TEST_PROJECT, mergeRequestId: 1 }, - ) - .then(done.fail) - .catch(() => { - expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { - text: 'An error occurred while loading the merge request changes.', - action: jasmine.any(Function), - actionText: 'Please try again', - actionPayload: { - projectId: TEST_PROJECT, - mergeRequestId: 1, - force: false, - }, - }); - - done(); - }); - }); - }); - }); - - describe('getMergeRequestVersions', () => { - beforeEach(() => { - store.state.projects[TEST_PROJECT].mergeRequests['1'] = { versions: [] }; - }); - - describe('success', () => { - beforeEach(() => { - mock - .onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/versions/) - .reply(200, [{ id: 789 }]); - spyOn(service, 'getProjectMergeRequestVersions').and.callThrough(); - }); - - it('calls getProjectMergeRequestVersions service method', done => { - store - .dispatch('getMergeRequestVersions', { projectId: TEST_PROJECT, mergeRequestId: 1 }) - .then(() => { - expect(service.getProjectMergeRequestVersions).toHaveBeenCalledWith(TEST_PROJECT, 1); - - done(); - }) - .catch(done.fail); - }); - - it('sets the Merge Request Versions Object', done => { - store - .dispatch('getMergeRequestVersions', { projectId: TEST_PROJECT, mergeRequestId: 1 }) - .then(() => { - expect(store.state.projects[TEST_PROJECT].mergeRequests['1'].versions.length).toBe(1); - done(); - }) - .catch(done.fail); - }); - }); - - describe('error', () => { - beforeEach(() => { - mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests\/1\/versions/).networkError(); - }); - - it('dispatches error action', done => { - const dispatch = jasmine.createSpy('dispatch'); - - getMergeRequestVersions( - { - commit() {}, - dispatch, - state: store.state, - }, - { projectId: TEST_PROJECT, mergeRequestId: 1 }, - ) - .then(done.fail) - .catch(() => { - expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { - text: 'An error occurred while loading the merge request version data.', - action: jasmine.any(Function), - actionText: 'Please try again', - actionPayload: { - projectId: TEST_PROJECT, - mergeRequestId: 1, - force: false, - }, - }); - - done(); - }); - }); - }); - }); - - describe('openMergeRequest', () => { - const mr = { - projectId: TEST_PROJECT, - targetProjectId: 'defproject', - mergeRequestId: 2, - }; - let testMergeRequest; - let testMergeRequestChanges; - - const mockGetters = { findBranch: () => ({ commit: { id: 'abcd2322' } }) }; - - beforeEach(() => { - testMergeRequest = { - source_branch: 'abcbranch', - }; - testMergeRequestChanges = { - changes: [], - }; - store.state.entries = { - foo: { - type: 'blob', - }, - bar: { - type: 'blob', - }, - }; - - store.state.currentProjectId = 'test/test'; - store.state.currentBranchId = 'master'; - - store.state.projects['test/test'] = { - branches: { - master: { - commit: { - id: '7297abc', - }, - }, - abcbranch: { - commit: { - id: '29020fc', - }, - }, - }, - }; - - const originalDispatch = store.dispatch; - - spyOn(store, 'dispatch').and.callFake((type, payload) => { - switch (type) { - case 'getMergeRequestData': - return Promise.resolve(testMergeRequest); - case 'getMergeRequestChanges': - return Promise.resolve(testMergeRequestChanges); - case 'getFiles': - case 'getMergeRequestVersions': - case 'getBranchData': - case 'setFileMrChange': - return Promise.resolve(); - default: - return originalDispatch(type, payload); - } - }); - spyOn(service, 'getFileData').and.callFake(() => - Promise.resolve({ - headers: {}, - }), - ); - }); - - it('dispatches actions for merge request data', done => { - openMergeRequest({ state: store.state, dispatch: store.dispatch, getters: mockGetters }, mr) - .then(() => { - expect(store.dispatch.calls.allArgs()).toEqual([ - ['getMergeRequestData', mr], - ['setCurrentBranchId', testMergeRequest.source_branch], - [ - 'getBranchData', - { - projectId: mr.projectId, - branchId: testMergeRequest.source_branch, - }, - ], - [ - 'getFiles', - { - projectId: mr.projectId, - branchId: testMergeRequest.source_branch, - ref: 'abcd2322', - }, - ], - ['getMergeRequestVersions', mr], - ['getMergeRequestChanges', mr], - ]); - }) - .then(done) - .catch(done.fail); - }); - - it('updates activity bar view and gets file data, if changes are found', done => { - store.state.entries.foo = { - url: 'test', - type: 'blob', - }; - store.state.entries.bar = { - url: 'test', - type: 'blob', - }; - - testMergeRequestChanges.changes = [ - { new_path: 'foo', path: 'foo' }, - { new_path: 'bar', path: 'bar' }, - ]; - - openMergeRequest({ state: store.state, dispatch: store.dispatch, getters: mockGetters }, mr) - .then(() => { - expect(store.dispatch).toHaveBeenCalledWith( - 'updateActivityBarView', - leftSidebarViews.review.name, - ); - - testMergeRequestChanges.changes.forEach((change, i) => { - expect(store.dispatch).toHaveBeenCalledWith('setFileMrChange', { - file: store.state.entries[change.new_path], - mrChange: change, - }); - - expect(store.dispatch).toHaveBeenCalledWith('getFileData', { - path: change.new_path, - makeFileActive: i === 0, - openFile: true, - }); - }); - - expect(store.state.openFiles.length).toBe(testMergeRequestChanges.changes.length); - }) - .then(done) - .catch(done.fail); - }); - - it('flashes message, if error', done => { - const flashSpy = spyOnDependency(actions, 'flash'); - store.dispatch.and.returnValue(Promise.reject()); - - openMergeRequest(store, mr) - .then(() => { - fail('Expected openMergeRequest to throw an error'); - }) - .catch(() => { - expect(flashSpy).toHaveBeenCalledWith(jasmine.any(String)); - }) - .then(done) - .catch(done.fail); - }); - }); -}); diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js deleted file mode 100644 index e962224d1ad..00000000000 --- a/spec/javascripts/ide/stores/actions/project_spec.js +++ /dev/null @@ -1,404 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import axios from '~/lib/utils/axios_utils'; -import { - refreshLastCommitData, - showBranchNotFoundError, - createNewBranchFromDefault, - loadEmptyBranch, - openBranch, - loadFile, - loadBranch, -} from '~/ide/stores/actions'; -import { createStore } from '~/ide/stores'; -import service from '~/ide/services'; -import api from '~/api'; -import router from '~/ide/ide_router'; -import { resetStore } from '../../helpers'; -import testAction from '../../../helpers/vuex_action_helper'; - -const TEST_PROJECT_ID = 'abc/def'; - -describe('IDE store project actions', () => { - let mock; - let store; - - beforeEach(() => { - store = createStore(); - mock = new MockAdapter(axios); - - store.state.projects[TEST_PROJECT_ID] = { - branches: {}, - }; - }); - - afterEach(() => { - mock.restore(); - - resetStore(store); - }); - - describe('refreshLastCommitData', () => { - beforeEach(() => { - store.state.currentProjectId = 'abc/def'; - store.state.currentBranchId = 'master'; - store.state.projects['abc/def'] = { - id: 4, - branches: { - master: { - commit: null, - }, - }, - }; - spyOn(service, 'getBranchData').and.returnValue( - Promise.resolve({ - data: { - commit: { id: '123' }, - }, - }), - ); - }); - - it('calls the service', done => { - store - .dispatch('refreshLastCommitData', { - projectId: store.state.currentProjectId, - branchId: store.state.currentBranchId, - }) - .then(() => { - expect(service.getBranchData).toHaveBeenCalledWith('abc/def', 'master'); - - done(); - }) - .catch(done.fail); - }); - - it('commits getBranchData', done => { - testAction( - refreshLastCommitData, - { - projectId: store.state.currentProjectId, - branchId: store.state.currentBranchId, - }, - store.state, - // mutations - [ - { - type: 'SET_BRANCH_COMMIT', - payload: { - projectId: TEST_PROJECT_ID, - branchId: 'master', - commit: { id: '123' }, - }, - }, - ], - // action - [], - done, - ); - }); - }); - - describe('showBranchNotFoundError', () => { - it('dispatches setErrorMessage', done => { - testAction( - showBranchNotFoundError, - 'master', - null, - [], - [ - { - type: 'setErrorMessage', - payload: { - text: "Branch <strong>master</strong> was not found in this project's repository.", - action: jasmine.any(Function), - actionText: 'Create branch', - actionPayload: 'master', - }, - }, - ], - done, - ); - }); - }); - - describe('createNewBranchFromDefault', () => { - it('calls API', done => { - spyOn(api, 'createBranch').and.returnValue(Promise.resolve()); - spyOn(router, 'push'); - - createNewBranchFromDefault( - { - state: { - currentProjectId: 'project-path', - }, - getters: { - currentProject: { - default_branch: 'master', - }, - }, - dispatch() {}, - }, - 'new-branch-name', - ) - .then(() => { - expect(api.createBranch).toHaveBeenCalledWith('project-path', { - ref: 'master', - branch: 'new-branch-name', - }); - }) - .then(done) - .catch(done.fail); - }); - - it('clears error message', done => { - const dispatchSpy = jasmine.createSpy('dispatch'); - spyOn(api, 'createBranch').and.returnValue(Promise.resolve()); - spyOn(router, 'push'); - - createNewBranchFromDefault( - { - state: { - currentProjectId: 'project-path', - }, - getters: { - currentProject: { - default_branch: 'master', - }, - }, - dispatch: dispatchSpy, - }, - 'new-branch-name', - ) - .then(() => { - expect(dispatchSpy).toHaveBeenCalledWith('setErrorMessage', null); - }) - .then(done) - .catch(done.fail); - }); - - it('reloads window', done => { - spyOn(api, 'createBranch').and.returnValue(Promise.resolve()); - spyOn(router, 'push'); - - createNewBranchFromDefault( - { - state: { - currentProjectId: 'project-path', - }, - getters: { - currentProject: { - default_branch: 'master', - }, - }, - dispatch() {}, - }, - 'new-branch-name', - ) - .then(() => { - expect(router.push).toHaveBeenCalled(); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('loadEmptyBranch', () => { - it('creates a blank tree and sets loading state to false', done => { - testAction( - loadEmptyBranch, - { projectId: TEST_PROJECT_ID, branchId: 'master' }, - store.state, - [ - { type: 'CREATE_TREE', payload: { treePath: `${TEST_PROJECT_ID}/master` } }, - { - type: 'TOGGLE_LOADING', - payload: { entry: store.state.trees[`${TEST_PROJECT_ID}/master`], forceValue: false }, - }, - ], - jasmine.any(Object), - done, - ); - }); - - it('does nothing, if tree already exists', done => { - const trees = { [`${TEST_PROJECT_ID}/master`]: [] }; - - testAction( - loadEmptyBranch, - { projectId: TEST_PROJECT_ID, branchId: 'master' }, - { trees }, - [], - [], - done, - ); - }); - }); - - describe('loadFile', () => { - beforeEach(() => { - Object.assign(store.state, { - entries: { - foo: { pending: false }, - 'foo/bar-pending': { pending: true }, - 'foo/bar': { pending: false }, - }, - }); - spyOn(store, 'dispatch'); - }); - - it('does nothing, if basePath is not given', () => { - loadFile(store, { basePath: undefined }); - - expect(store.dispatch).not.toHaveBeenCalled(); - }); - - it('handles tree entry action, if basePath is given and the entry is not pending', () => { - loadFile(store, { basePath: 'foo/bar/' }); - - expect(store.dispatch).toHaveBeenCalledWith( - 'handleTreeEntryAction', - store.state.entries['foo/bar'], - ); - }); - - it('does not handle tree entry action, if entry is pending', () => { - loadFile(store, { basePath: 'foo/bar-pending/' }); - - expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything()); - }); - - it('creates a new temp file supplied via URL if the file does not exist yet', () => { - loadFile(store, { basePath: 'not-existent.md' }); - - expect(store.dispatch.calls.count()).toBe(1); - - expect(store.dispatch).not.toHaveBeenCalledWith('handleTreeEntryAction', jasmine.anything()); - - expect(store.dispatch).toHaveBeenCalledWith('createTempEntry', { - name: 'not-existent.md', - type: 'blob', - }); - }); - }); - - describe('loadBranch', () => { - const projectId = TEST_PROJECT_ID; - const branchId = '123-lorem'; - const ref = 'abcd2322'; - - it('when empty repo, loads empty branch', done => { - const mockGetters = { emptyRepo: true }; - - testAction( - loadBranch, - { projectId, branchId }, - { ...store.state, ...mockGetters }, - [], - [{ type: 'loadEmptyBranch', payload: { projectId, branchId } }], - done, - ); - }); - - it('when branch already exists, does nothing', done => { - store.state.projects[projectId].branches[branchId] = {}; - - testAction(loadBranch, { projectId, branchId }, store.state, [], [], done); - }); - - it('fetches branch data', done => { - const mockGetters = { findBranch: () => ({ commit: { id: ref } }) }; - spyOn(store, 'dispatch').and.returnValue(Promise.resolve()); - - loadBranch( - { getters: mockGetters, state: store.state, dispatch: store.dispatch }, - { projectId, branchId }, - ) - .then(() => { - expect(store.dispatch.calls.allArgs()).toEqual([ - ['getBranchData', { projectId, branchId }], - ['getMergeRequestsForBranch', { projectId, branchId }], - ['getFiles', { projectId, branchId, ref }], - ]); - }) - .then(done) - .catch(done.fail); - }); - - it('shows an error if branch can not be fetched', done => { - spyOn(store, 'dispatch').and.returnValue(Promise.reject()); - - loadBranch(store, { projectId, branchId }) - .then(done.fail) - .catch(() => { - expect(store.dispatch.calls.allArgs()).toEqual([ - ['getBranchData', { projectId, branchId }], - ['showBranchNotFoundError', branchId], - ]); - done(); - }); - }); - }); - - describe('openBranch', () => { - const projectId = TEST_PROJECT_ID; - const branchId = '123-lorem'; - - const branch = { - projectId, - branchId, - }; - - beforeEach(() => { - Object.assign(store.state, { - entries: { - foo: { pending: false }, - 'foo/bar-pending': { pending: true }, - 'foo/bar': { pending: false }, - }, - }); - }); - - describe('existing branch', () => { - beforeEach(() => { - spyOn(store, 'dispatch').and.returnValue(Promise.resolve()); - }); - - it('dispatches branch actions', done => { - openBranch(store, branch) - .then(() => { - expect(store.dispatch.calls.allArgs()).toEqual([ - ['setCurrentBranchId', branchId], - ['loadBranch', { projectId, branchId }], - ['loadFile', { basePath: undefined }], - ]); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('non-existent branch', () => { - beforeEach(() => { - spyOn(store, 'dispatch').and.returnValue(Promise.reject()); - }); - - it('dispatches correct branch actions', done => { - openBranch(store, branch) - .then(val => { - expect(store.dispatch.calls.allArgs()).toEqual([ - ['setCurrentBranchId', branchId], - ['loadBranch', { projectId, branchId }], - ]); - - expect(val).toEqual( - new Error( - `An error occurred while getting files for - <strong>${projectId}/${branchId}</strong>`, - ), - ); - }) - .then(done) - .catch(done.fail); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js deleted file mode 100644 index 2201a3b4b57..00000000000 --- a/spec/javascripts/ide/stores/actions/tree_spec.js +++ /dev/null @@ -1,216 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import testAction from 'spec/helpers/vuex_action_helper'; -import { showTreeEntry, getFiles, setDirectoryData } from '~/ide/stores/actions/tree'; -import * as types from '~/ide/stores/mutation_types'; -import axios from '~/lib/utils/axios_utils'; -import store from '~/ide/stores'; -import service from '~/ide/services'; -import router from '~/ide/ide_router'; -import { file, resetStore, createEntriesFromPaths } from '../../helpers'; - -describe('Multi-file store tree actions', () => { - let projectTree; - let mock; - - const basicCallParameters = { - endpoint: 'rootEndpoint', - projectId: 'abcproject', - branch: 'master', - branchId: 'master', - ref: '12345678', - }; - - beforeEach(() => { - jasmine.clock().install(); - spyOn(router, 'push'); - - mock = new MockAdapter(axios); - - store.state.currentProjectId = 'abcproject'; - store.state.currentBranchId = 'master'; - store.state.projects.abcproject = { - web_url: '', - path_with_namespace: 'foo/abcproject', - }; - }); - - afterEach(() => { - jasmine.clock().uninstall(); - mock.restore(); - resetStore(store); - }); - - describe('getFiles', () => { - describe('success', () => { - beforeEach(() => { - spyOn(service, 'getFiles').and.callThrough(); - - mock - .onGet(/(.*)/) - .replyOnce(200, [ - 'file.txt', - 'folder/fileinfolder.js', - 'folder/subfolder/fileinsubfolder.js', - ]); - }); - - it('calls service getFiles', done => { - store - .dispatch('getFiles', basicCallParameters) - .then(() => { - expect(service.getFiles).toHaveBeenCalledWith('foo/abcproject', '12345678'); - - done(); - }) - .catch(done.fail); - }); - - it('adds data into tree', done => { - store - .dispatch('getFiles', basicCallParameters) - .then(() => { - // The populating of the tree is deferred for performance reasons. - // See this merge request for details: https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/25700 - jasmine.clock().tick(1); - }) - .then(() => { - projectTree = store.state.trees['abcproject/master']; - - expect(projectTree.tree.length).toBe(2); - expect(projectTree.tree[0].type).toBe('tree'); - expect(projectTree.tree[0].tree[1].name).toBe('fileinfolder.js'); - expect(projectTree.tree[1].type).toBe('blob'); - expect(projectTree.tree[0].tree[0].tree[0].type).toBe('blob'); - expect(projectTree.tree[0].tree[0].tree[0].name).toBe('fileinsubfolder.js'); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('error', () => { - it('dispatches error action', done => { - const dispatch = jasmine.createSpy('dispatchSpy'); - - store.state.projects = { - 'abc/def': { - web_url: `${gl.TEST_HOST}/files`, - branches: { - 'master-testing': { - commit: { - id: '12345', - }, - }, - }, - }, - }; - const getters = { - findBranch: () => store.state.projects['abc/def'].branches['master-testing'], - }; - - mock.onGet(/(.*)/).replyOnce(500); - - getFiles( - { - commit() {}, - dispatch, - state: store.state, - getters, - }, - { - projectId: 'abc/def', - branchId: 'master-testing', - }, - ) - .then(done.fail) - .catch(() => { - expect(dispatch).toHaveBeenCalledWith('setErrorMessage', { - text: 'An error occurred while loading all the files.', - action: jasmine.any(Function), - actionText: 'Please try again', - actionPayload: { projectId: 'abc/def', branchId: 'master-testing' }, - }); - done(); - }); - }); - }); - }); - - describe('toggleTreeOpen', () => { - let tree; - - beforeEach(() => { - tree = file('testing', '1', 'tree'); - store.state.entries[tree.path] = tree; - }); - - it('toggles the tree open', done => { - store - .dispatch('toggleTreeOpen', tree.path) - .then(() => { - expect(tree.opened).toBeTruthy(); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('showTreeEntry', () => { - beforeEach(() => { - const paths = [ - 'grandparent', - 'ancestor', - 'grandparent/parent', - 'grandparent/aunt', - 'grandparent/parent/child.txt', - 'grandparent/aunt/cousing.txt', - ]; - - Object.assign(store.state.entries, createEntriesFromPaths(paths)); - }); - - it('opens the parents', done => { - testAction( - showTreeEntry, - 'grandparent/parent/child.txt', - store.state, - [{ type: types.SET_TREE_OPEN, payload: 'grandparent/parent' }], - [{ type: 'showTreeEntry', payload: 'grandparent/parent' }], - done, - ); - }); - }); - - describe('setDirectoryData', () => { - it('sets tree correctly if there are no opened files yet', done => { - const treeFile = file({ name: 'README.md' }); - store.state.trees['abcproject/master'] = {}; - - testAction( - setDirectoryData, - { projectId: 'abcproject', branchId: 'master', treeList: [treeFile] }, - store.state, - [ - { - type: types.SET_DIRECTORY_DATA, - payload: { - treePath: 'abcproject/master', - data: [treeFile], - }, - }, - { - type: types.TOGGLE_LOADING, - payload: { - entry: {}, - forceValue: false, - }, - }, - ], - [], - done, - ); - }); - }); -}); diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js deleted file mode 100644 index 364c8421b6b..00000000000 --- a/spec/javascripts/ide/stores/actions_spec.js +++ /dev/null @@ -1,1116 +0,0 @@ -import MockAdapter from 'axios-mock-adapter'; -import actions, { - stageAllChanges, - unstageAllChanges, - toggleFileFinder, - setCurrentBranchId, - setEmptyStateSvgs, - updateActivityBarView, - updateTempFlagForEntry, - setErrorMessage, - deleteEntry, - renameEntry, - getBranchData, - createTempEntry, - discardAllChanges, -} from '~/ide/stores/actions'; -import axios from '~/lib/utils/axios_utils'; -import { createStore } from '~/ide/stores'; -import * as types from '~/ide/stores/mutation_types'; -import router from '~/ide/ide_router'; -import { file } from '../helpers'; -import testAction from '../../helpers/vuex_action_helper'; -import eventHub from '~/ide/eventhub'; - -describe('Multi-file store actions', () => { - let store; - - beforeEach(() => { - store = createStore(); - - spyOn(store, 'commit').and.callThrough(); - spyOn(store, 'dispatch').and.callThrough(); - spyOn(router, 'push'); - }); - - describe('redirectToUrl', () => { - it('calls visitUrl', done => { - const visitUrl = spyOnDependency(actions, 'visitUrl'); - - store - .dispatch('redirectToUrl', 'test') - .then(() => { - expect(visitUrl).toHaveBeenCalledWith('test'); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('setInitialData', () => { - it('commits initial data', done => { - store - .dispatch('setInitialData', { canCommit: true }) - .then(() => { - expect(store.state.canCommit).toBeTruthy(); - done(); - }) - .catch(done.fail); - }); - }); - - describe('discardAllChanges', () => { - const paths = ['to_discard', 'another_one_to_discard']; - - beforeEach(() => { - paths.forEach(path => { - const f = file(path); - f.changed = true; - - store.state.openFiles.push(f); - store.state.changedFiles.push(f); - store.state.entries[f.path] = f; - }); - }); - - it('discards all changes in file', () => { - const expectedCalls = paths.map(path => ['restoreOriginalFile', path]); - - discardAllChanges(store); - - expect(store.dispatch.calls.allArgs()).toEqual(jasmine.arrayContaining(expectedCalls)); - }); - - it('removes all files from changedFiles state', done => { - store - .dispatch('discardAllChanges') - .then(() => { - expect(store.state.changedFiles.length).toBe(0); - expect(store.state.openFiles.length).toBe(2); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('closeAllFiles', () => { - beforeEach(() => { - const f = file('closeAll'); - store.state.openFiles.push(f); - store.state.openFiles[0].opened = true; - store.state.entries[f.path] = f; - }); - - it('closes all open files', done => { - store - .dispatch('closeAllFiles') - .then(() => { - expect(store.state.openFiles.length).toBe(0); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('createTempEntry', () => { - beforeEach(() => { - document.body.innerHTML += '<div class="flash-container"></div>'; - - store.state.currentProjectId = 'abcproject'; - store.state.currentBranchId = 'mybranch'; - - store.state.trees['abcproject/mybranch'] = { - tree: [], - }; - store.state.projects.abcproject = { - web_url: '', - }; - }); - - afterEach(() => { - document.querySelector('.flash-container').remove(); - }); - - describe('tree', () => { - it('creates temp tree', done => { - store - .dispatch('createTempEntry', { - branchId: store.state.currentBranchId, - name: 'test', - type: 'tree', - }) - .then(() => { - const entry = store.state.entries.test; - - expect(entry).not.toBeNull(); - expect(entry.type).toBe('tree'); - - done(); - }) - .catch(done.fail); - }); - - it('creates new folder inside another tree', done => { - const tree = { - type: 'tree', - name: 'testing', - path: 'testing', - tree: [], - }; - - store.state.entries[tree.path] = tree; - - store - .dispatch('createTempEntry', { - branchId: store.state.currentBranchId, - name: 'testing/test', - type: 'tree', - }) - .then(() => { - expect(tree.tree[0].tempFile).toBeTruthy(); - expect(tree.tree[0].name).toBe('test'); - expect(tree.tree[0].type).toBe('tree'); - - done(); - }) - .catch(done.fail); - }); - - it('does not create new tree if already exists', done => { - const tree = { - type: 'tree', - path: 'testing', - tempFile: false, - tree: [], - }; - - store.state.entries[tree.path] = tree; - - store - .dispatch('createTempEntry', { - branchId: store.state.currentBranchId, - name: 'testing', - type: 'tree', - }) - .then(() => { - expect(store.state.entries[tree.path].tempFile).toEqual(false); - expect(document.querySelector('.flash-alert')).not.toBeNull(); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('blob', () => { - it('creates temp file', done => { - const name = 'test'; - - store - .dispatch('createTempEntry', { - name, - branchId: 'mybranch', - type: 'blob', - }) - .then(() => { - const f = store.state.entries[name]; - - expect(f.tempFile).toBeTruthy(); - expect(store.state.trees['abcproject/mybranch'].tree.length).toBe(1); - - done(); - }) - .catch(done.fail); - }); - - it('adds tmp file to open files', done => { - const name = 'test'; - - store - .dispatch('createTempEntry', { - name, - branchId: 'mybranch', - type: 'blob', - }) - .then(() => { - const f = store.state.entries[name]; - - expect(store.state.openFiles.length).toBe(1); - expect(store.state.openFiles[0].name).toBe(f.name); - - done(); - }) - .catch(done.fail); - }); - - it('adds tmp file to staged files', done => { - const name = 'test'; - - store - .dispatch('createTempEntry', { - name, - branchId: 'mybranch', - type: 'blob', - }) - .then(() => { - expect(store.state.stagedFiles).toEqual([jasmine.objectContaining({ name })]); - - done(); - }) - .catch(done.fail); - }); - - it('sets tmp file as active', () => { - createTempEntry(store, { name: 'test', branchId: 'mybranch', type: 'blob' }); - - expect(store.dispatch).toHaveBeenCalledWith('setFileActive', 'test'); - }); - - it('creates flash message if file already exists', done => { - const f = file('test', '1', 'blob'); - store.state.trees['abcproject/mybranch'].tree = [f]; - store.state.entries[f.path] = f; - - store - .dispatch('createTempEntry', { - name: 'test', - branchId: 'mybranch', - type: 'blob', - }) - .then(() => { - expect(document.querySelector('.flash-alert')?.textContent.trim()).toEqual( - `The name "${f.name}" is already taken in this directory.`, - ); - - done(); - }) - .catch(done.fail); - }); - - it('bursts unused seal', done => { - store - .dispatch('createTempEntry', { - name: 'test', - branchId: 'mybranch', - type: 'blob', - }) - .then(() => { - expect(store.state.unusedSeal).toBe(false); - - done(); - }) - .catch(done.fail); - }); - }); - }); - - describe('scrollToTab', () => { - it('focuses the current active element', done => { - document.body.innerHTML += - '<div id="tabs"><div class="active"><div class="repo-tab"></div></div></div>'; - const el = document.querySelector('.repo-tab'); - spyOn(el, 'focus'); - - store - .dispatch('scrollToTab') - .then(() => { - setTimeout(() => { - expect(el.focus).toHaveBeenCalled(); - - document.getElementById('tabs').remove(); - - done(); - }); - }) - .catch(done.fail); - }); - }); - - describe('stage/unstageAllChanges', () => { - let file1; - let file2; - - beforeEach(() => { - file1 = { ...file('test'), content: 'changed test', raw: 'test' }; - file2 = { ...file('test2'), content: 'changed test2', raw: 'test2' }; - - store.state.openFiles = [file1]; - store.state.changedFiles = [file1]; - store.state.stagedFiles = [{ ...file2, content: 'staged test' }]; - - store.state.entries = { - [file1.path]: { ...file1 }, - [file2.path]: { ...file2 }, - }; - }); - - describe('stageAllChanges', () => { - it('adds all files from changedFiles to stagedFiles', () => { - stageAllChanges(store); - - expect(store.commit.calls.allArgs()).toEqual([ - [types.SET_LAST_COMMIT_MSG, ''], - [types.STAGE_CHANGE, jasmine.objectContaining({ path: file1.path })], - ]); - }); - - it('opens pending tab if a change exists in that file', () => { - stageAllChanges(store); - - expect(store.dispatch.calls.allArgs()).toEqual([ - [ - 'openPendingTab', - { file: { ...file1, staged: true, changed: true }, keyPrefix: 'staged' }, - ], - ]); - }); - - it('does not open pending tab if no change exists in that file', () => { - store.state.entries[file1.path].content = 'test'; - store.state.stagedFiles = [file1]; - store.state.changedFiles = [store.state.entries[file1.path]]; - - stageAllChanges(store); - - expect(store.dispatch).not.toHaveBeenCalled(); - }); - }); - - describe('unstageAllChanges', () => { - it('removes all files from stagedFiles after unstaging', () => { - unstageAllChanges(store); - - expect(store.commit.calls.allArgs()).toEqual([ - [types.UNSTAGE_CHANGE, jasmine.objectContaining({ path: file2.path })], - ]); - }); - - it('opens pending tab if a change exists in that file', () => { - unstageAllChanges(store); - - expect(store.dispatch.calls.allArgs()).toEqual([ - ['openPendingTab', { file: file1, keyPrefix: 'unstaged' }], - ]); - }); - - it('does not open pending tab if no change exists in that file', () => { - store.state.entries[file1.path].content = 'test'; - store.state.stagedFiles = [file1]; - store.state.changedFiles = [store.state.entries[file1.path]]; - - unstageAllChanges(store); - - expect(store.dispatch).not.toHaveBeenCalled(); - }); - }); - }); - - describe('updateViewer', () => { - it('updates viewer state', done => { - store - .dispatch('updateViewer', 'diff') - .then(() => { - expect(store.state.viewer).toBe('diff'); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('updateActivityBarView', () => { - it('commits UPDATE_ACTIVITY_BAR_VIEW', done => { - testAction( - updateActivityBarView, - 'test', - {}, - [{ type: 'UPDATE_ACTIVITY_BAR_VIEW', payload: 'test' }], - [], - done, - ); - }); - }); - - describe('setEmptyStateSvgs', () => { - it('commits setEmptyStateSvgs', done => { - testAction( - setEmptyStateSvgs, - 'svg', - {}, - [{ type: 'SET_EMPTY_STATE_SVGS', payload: 'svg' }], - [], - done, - ); - }); - }); - - describe('updateTempFlagForEntry', () => { - it('commits UPDATE_TEMP_FLAG', done => { - const f = { - ...file(), - path: 'test', - tempFile: true, - }; - store.state.entries[f.path] = f; - - testAction( - updateTempFlagForEntry, - { file: f, tempFile: false }, - store.state, - [{ type: 'UPDATE_TEMP_FLAG', payload: { path: f.path, tempFile: false } }], - [], - done, - ); - }); - - it('commits UPDATE_TEMP_FLAG and dispatches for parent', done => { - const parent = { - ...file(), - path: 'testing', - }; - const f = { - ...file(), - path: 'test', - parentPath: 'testing', - }; - store.state.entries[parent.path] = parent; - store.state.entries[f.path] = f; - - testAction( - updateTempFlagForEntry, - { file: f, tempFile: false }, - store.state, - [{ type: 'UPDATE_TEMP_FLAG', payload: { path: f.path, tempFile: false } }], - [{ type: 'updateTempFlagForEntry', payload: { file: parent, tempFile: false } }], - done, - ); - }); - - it('does not dispatch for parent, if parent does not exist', done => { - const f = { - ...file(), - path: 'test', - parentPath: 'testing', - }; - store.state.entries[f.path] = f; - - testAction( - updateTempFlagForEntry, - { file: f, tempFile: false }, - store.state, - [{ type: 'UPDATE_TEMP_FLAG', payload: { path: f.path, tempFile: false } }], - [], - done, - ); - }); - }); - - describe('setCurrentBranchId', () => { - it('commits setCurrentBranchId', done => { - testAction( - setCurrentBranchId, - 'branchId', - {}, - [{ type: 'SET_CURRENT_BRANCH', payload: 'branchId' }], - [], - done, - ); - }); - }); - - describe('toggleFileFinder', () => { - it('commits TOGGLE_FILE_FINDER', done => { - testAction( - toggleFileFinder, - true, - null, - [{ type: 'TOGGLE_FILE_FINDER', payload: true }], - [], - done, - ); - }); - }); - - describe('setErrorMessage', () => { - it('commis error messsage', done => { - testAction( - setErrorMessage, - 'error', - null, - [{ type: types.SET_ERROR_MESSAGE, payload: 'error' }], - [], - done, - ); - }); - }); - - describe('deleteEntry', () => { - it('commits entry deletion', done => { - store.state.entries.path = 'testing'; - - testAction( - deleteEntry, - 'path', - store.state, - [{ type: types.DELETE_ENTRY, payload: 'path' }], - [{ type: 'stageChange', payload: 'path' }, { type: 'triggerFilesChange' }], - done, - ); - }); - - it('does not delete a folder after it is emptied', done => { - const testFolder = { - type: 'tree', - tree: [], - }; - const testEntry = { - path: 'testFolder/entry-to-delete', - parentPath: 'testFolder', - opened: false, - tree: [], - }; - testFolder.tree.push(testEntry); - store.state.entries = { - testFolder, - 'testFolder/entry-to-delete': testEntry, - }; - - testAction( - deleteEntry, - 'testFolder/entry-to-delete', - store.state, - [{ type: types.DELETE_ENTRY, payload: 'testFolder/entry-to-delete' }], - [ - { type: 'stageChange', payload: 'testFolder/entry-to-delete' }, - { type: 'triggerFilesChange' }, - ], - done, - ); - }); - - describe('when renamed', () => { - let testEntry; - - beforeEach(() => { - testEntry = { - path: 'test', - name: 'test', - prevPath: 'test_old', - prevName: 'test_old', - prevParentPath: '', - }; - - store.state.entries = { test: testEntry }; - }); - - describe('and previous does not exist', () => { - it('reverts the rename before deleting', done => { - testAction( - deleteEntry, - testEntry.path, - store.state, - [], - [ - { - type: 'renameEntry', - payload: { - path: testEntry.path, - name: testEntry.prevName, - parentPath: testEntry.prevParentPath, - }, - }, - { - type: 'deleteEntry', - payload: testEntry.prevPath, - }, - ], - done, - ); - }); - }); - - describe('and previous exists', () => { - beforeEach(() => { - const oldEntry = { - path: testEntry.prevPath, - name: testEntry.prevName, - }; - - store.state.entries[oldEntry.path] = oldEntry; - }); - - it('does not revert rename before deleting', done => { - testAction( - deleteEntry, - testEntry.path, - store.state, - [{ type: types.DELETE_ENTRY, payload: testEntry.path }], - [{ type: 'stageChange', payload: testEntry.path }, { type: 'triggerFilesChange' }], - done, - ); - }); - - it('when previous is deleted, it reverts rename before deleting', done => { - store.state.entries[testEntry.prevPath].deleted = true; - - testAction( - deleteEntry, - testEntry.path, - store.state, - [], - [ - { - type: 'renameEntry', - payload: { - path: testEntry.path, - name: testEntry.prevName, - parentPath: testEntry.prevParentPath, - }, - }, - { - type: 'deleteEntry', - payload: testEntry.prevPath, - }, - ], - done, - ); - }); - }); - }); - - it('bursts unused seal', done => { - store.state.entries.test = file('test'); - - store - .dispatch('deleteEntry', 'test') - .then(() => { - expect(store.state.unusedSeal).toBe(false); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('renameEntry', () => { - describe('purging of file model cache', () => { - beforeEach(() => { - spyOn(eventHub, '$emit'); - }); - - it('does not purge model cache for temporary entries that got renamed', done => { - Object.assign(store.state.entries, { - test: { - ...file('test'), - key: 'foo-key', - type: 'blob', - tempFile: true, - }, - }); - - store - .dispatch('renameEntry', { - path: 'test', - name: 'new', - }) - .then(() => { - expect(eventHub.$emit.calls.allArgs()).not.toContain( - 'editor.update.model.dispose.foo-bar', - ); - }) - .then(done) - .catch(done.fail); - }); - - it('purges model cache for renamed entry', done => { - Object.assign(store.state.entries, { - test: { - ...file('test'), - key: 'foo-key', - type: 'blob', - tempFile: false, - }, - }); - - store - .dispatch('renameEntry', { - path: 'test', - name: 'new', - }) - .then(() => { - expect(eventHub.$emit).toHaveBeenCalled(); - expect(eventHub.$emit).toHaveBeenCalledWith(`editor.update.model.dispose.foo-key`); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('single entry', () => { - let origEntry; - let renamedEntry; - - beforeEach(() => { - // Need to insert both because `testAction` doesn't actually call the mutation - origEntry = file('orig', 'orig', 'blob'); - renamedEntry = { - ...file('renamed', 'renamed', 'blob'), - prevKey: origEntry.key, - prevName: origEntry.name, - prevPath: origEntry.path, - }; - - Object.assign(store.state.entries, { - orig: origEntry, - renamed: renamedEntry, - }); - }); - - it('by default renames an entry and stages it', () => { - const dispatch = jasmine.createSpy(); - const commit = jasmine.createSpy(); - - renameEntry( - { dispatch, commit, state: store.state, getters: store.getters }, - { path: 'orig', name: 'renamed' }, - ); - - expect(commit.calls.allArgs()).toEqual([ - [types.RENAME_ENTRY, { path: 'orig', name: 'renamed', parentPath: undefined }], - [types.STAGE_CHANGE, jasmine.objectContaining({ path: 'renamed' })], - ]); - }); - - it('if not changed, completely unstages and discards entry if renamed to original', done => { - testAction( - renameEntry, - { path: 'renamed', name: 'orig' }, - store.state, - [ - { - type: types.RENAME_ENTRY, - payload: { - path: 'renamed', - name: 'orig', - parentPath: undefined, - }, - }, - { - type: types.REMOVE_FILE_FROM_STAGED_AND_CHANGED, - payload: origEntry, - }, - ], - [{ type: 'triggerFilesChange' }], - done, - ); - }); - - it('if already in changed, does not add to change', done => { - store.state.changedFiles.push(renamedEntry); - - testAction( - renameEntry, - { path: 'orig', name: 'renamed' }, - store.state, - [jasmine.objectContaining({ type: types.RENAME_ENTRY })], - [{ type: 'triggerFilesChange' }], - done, - ); - }); - - it('routes to the renamed file if the original file has been opened', done => { - Object.assign(store.state.entries.orig, { - opened: true, - url: '/foo-bar.md', - }); - - store - .dispatch('renameEntry', { - path: 'orig', - name: 'renamed', - }) - .then(() => { - expect(router.push.calls.count()).toBe(1); - expect(router.push).toHaveBeenCalledWith(`/project/foo-bar.md`); - }) - .then(done) - .catch(done.fail); - }); - - it('bursts unused seal', done => { - store - .dispatch('renameEntry', { - path: 'orig', - name: 'renamed', - }) - .then(() => { - expect(store.state.unusedSeal).toBe(false); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('folder', () => { - let folder; - let file1; - let file2; - - beforeEach(() => { - folder = file('folder', 'folder', 'tree'); - file1 = file('file-1', 'file-1', 'blob', folder); - file2 = file('file-2', 'file-2', 'blob', folder); - - folder.tree = [file1, file2]; - - Object.assign(store.state.entries, { - [folder.path]: folder, - [file1.path]: file1, - [file2.path]: file2, - }); - }); - - it('updates entries in a folder correctly, when folder is renamed', done => { - store - .dispatch('renameEntry', { - path: 'folder', - name: 'new-folder', - }) - .then(() => { - const keys = Object.keys(store.state.entries); - - expect(keys.length).toBe(3); - expect(keys.indexOf('new-folder')).toBe(0); - expect(keys.indexOf('new-folder/file-1')).toBe(1); - expect(keys.indexOf('new-folder/file-2')).toBe(2); - }) - .then(done) - .catch(done.fail); - }); - - it('discards renaming of an entry if the root folder is renamed back to a previous name', done => { - const rootFolder = file('old-folder', 'old-folder', 'tree'); - const testEntry = file('test', 'test', 'blob', rootFolder); - - Object.assign(store.state, { - entries: { - 'old-folder': { - ...rootFolder, - tree: [testEntry], - }, - 'old-folder/test': testEntry, - }, - }); - - store - .dispatch('renameEntry', { - path: 'old-folder', - name: 'new-folder', - }) - .then(() => { - const { entries } = store.state; - - expect(Object.keys(entries).length).toBe(2); - expect(entries['old-folder']).toBeUndefined(); - expect(entries['old-folder/test']).toBeUndefined(); - - expect(entries['new-folder']).toBeDefined(); - expect(entries['new-folder/test']).toEqual( - jasmine.objectContaining({ - path: 'new-folder/test', - name: 'test', - prevPath: 'old-folder/test', - prevName: 'test', - }), - ); - }) - .then(() => - store.dispatch('renameEntry', { - path: 'new-folder', - name: 'old-folder', - }), - ) - .then(() => { - const { entries } = store.state; - - expect(Object.keys(entries).length).toBe(2); - expect(entries['new-folder']).toBeUndefined(); - expect(entries['new-folder/test']).toBeUndefined(); - - expect(entries['old-folder']).toBeDefined(); - expect(entries['old-folder/test']).toEqual( - jasmine.objectContaining({ - path: 'old-folder/test', - name: 'test', - prevPath: undefined, - prevName: undefined, - }), - ); - }) - .then(done) - .catch(done.fail); - }); - - describe('with file in directory', () => { - const parentPath = 'original-dir'; - const newParentPath = 'new-dir'; - const fileName = 'test.md'; - const filePath = `${parentPath}/${fileName}`; - - let rootDir; - - beforeEach(() => { - const parentEntry = file(parentPath, parentPath, 'tree'); - const fileEntry = file(filePath, filePath, 'blob', parentEntry); - rootDir = { - tree: [], - }; - - Object.assign(store.state, { - entries: { - [parentPath]: { - ...parentEntry, - tree: [fileEntry], - }, - [filePath]: fileEntry, - }, - trees: { - '/': rootDir, - }, - }); - }); - - it('creates new directory', done => { - expect(store.state.entries[newParentPath]).toBeUndefined(); - - store - .dispatch('renameEntry', { path: filePath, name: fileName, parentPath: newParentPath }) - .then(() => { - expect(store.state.entries[newParentPath]).toEqual( - jasmine.objectContaining({ - path: newParentPath, - type: 'tree', - tree: jasmine.arrayContaining([ - store.state.entries[`${newParentPath}/${fileName}`], - ]), - }), - ); - }) - .then(done) - .catch(done.fail); - }); - - describe('when new directory exists', () => { - let newDir; - - beforeEach(() => { - newDir = file(newParentPath, newParentPath, 'tree'); - - store.state.entries[newDir.path] = newDir; - rootDir.tree.push(newDir); - }); - - it('inserts in new directory', done => { - expect(newDir.tree).toEqual([]); - - store - .dispatch('renameEntry', { - path: filePath, - name: fileName, - parentPath: newParentPath, - }) - .then(() => { - expect(newDir.tree).toEqual([store.state.entries[`${newParentPath}/${fileName}`]]); - }) - .then(done) - .catch(done.fail); - }); - - it('when new directory is deleted, it undeletes it', done => { - store.dispatch('deleteEntry', newParentPath); - - expect(store.state.entries[newParentPath].deleted).toBe(true); - expect(rootDir.tree.some(x => x.path === newParentPath)).toBe(false); - - store - .dispatch('renameEntry', { - path: filePath, - name: fileName, - parentPath: newParentPath, - }) - .then(() => { - expect(store.state.entries[newParentPath].deleted).toBe(false); - expect(rootDir.tree.some(x => x.path === newParentPath)).toBe(true); - }) - .then(done) - .catch(done.fail); - }); - }); - }); - }); - }); - - describe('getBranchData', () => { - let mock; - - beforeEach(() => { - mock = new MockAdapter(axios); - }); - - afterEach(() => { - mock.restore(); - }); - - describe('error', () => { - let dispatch; - let callParams; - - beforeEach(() => { - callParams = [ - { - commit() {}, - state: store.state, - }, - { - projectId: 'abc/def', - branchId: 'master-testing', - }, - ]; - dispatch = jasmine.createSpy('dispatchSpy'); - document.body.innerHTML += '<div class="flash-container"></div>'; - }); - - afterEach(() => { - document.querySelector('.flash-container').remove(); - }); - - it('passes the error further unchanged without dispatching any action when response is 404', done => { - mock.onGet(/(.*)/).replyOnce(404); - - getBranchData(...callParams) - .then(done.fail) - .catch(e => { - expect(dispatch.calls.count()).toEqual(0); - expect(e.response.status).toEqual(404); - expect(document.querySelector('.flash-alert')).toBeNull(); - done(); - }); - }); - - it('does not pass the error further and flashes an alert if error is not 404', done => { - mock.onGet(/(.*)/).replyOnce(418); - - getBranchData(...callParams) - .then(done.fail) - .catch(e => { - expect(dispatch.calls.count()).toEqual(0); - expect(e.response).toBeUndefined(); - expect(document.querySelector('.flash-alert')).not.toBeNull(); - done(); - }); - }); - }); - }); -}); diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js deleted file mode 100644 index fb8cb300209..00000000000 --- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js +++ /dev/null @@ -1,603 +0,0 @@ -import { resetStore, file } from 'spec/ide/helpers'; -import rootActions from '~/ide/stores/actions'; -import { createStore } from '~/ide/stores'; -import service from '~/ide/services'; -import router from '~/ide/ide_router'; -import eventHub from '~/ide/eventhub'; -import consts from '~/ide/stores/modules/commit/constants'; -import * as mutationTypes from '~/ide/stores/modules/commit/mutation_types'; -import * as actions from '~/ide/stores/modules/commit/actions'; -import { commitActionTypes, PERMISSION_CREATE_MR } from '~/ide/constants'; -import testAction from '../../../../helpers/vuex_action_helper'; - -const TEST_COMMIT_SHA = '123456789'; -const store = createStore(); - -describe('IDE commit module actions', () => { - beforeEach(() => { - spyOn(router, 'push'); - }); - - afterEach(() => { - resetStore(store); - }); - - describe('updateCommitMessage', () => { - it('updates store with new commit message', done => { - store - .dispatch('commit/updateCommitMessage', 'testing') - .then(() => { - expect(store.state.commit.commitMessage).toBe('testing'); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('discardDraft', () => { - it('resets commit message to blank', done => { - store.state.commit.commitMessage = 'testing'; - - store - .dispatch('commit/discardDraft') - .then(() => { - expect(store.state.commit.commitMessage).not.toBe('testing'); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('updateCommitAction', () => { - it('updates store with new commit action', done => { - store - .dispatch('commit/updateCommitAction', '1') - .then(() => { - expect(store.state.commit.commitAction).toBe('1'); - }) - .then(done) - .catch(done.fail); - }); - - it('sets shouldCreateMR to true if "Create new MR" option is visible', done => { - Object.assign(store.state, { - shouldHideNewMrOption: false, - }); - - testAction( - actions.updateCommitAction, - {}, - store.state, - [ - { - type: mutationTypes.UPDATE_COMMIT_ACTION, - payload: { commitAction: jasmine.anything() }, - }, - { type: mutationTypes.TOGGLE_SHOULD_CREATE_MR, payload: true }, - ], - [], - done, - ); - }); - - it('sets shouldCreateMR to false if "Create new MR" option is hidden', done => { - Object.assign(store.state, { - shouldHideNewMrOption: true, - }); - - testAction( - actions.updateCommitAction, - {}, - store.state, - [ - { - type: mutationTypes.UPDATE_COMMIT_ACTION, - payload: { commitAction: jasmine.anything() }, - }, - { type: mutationTypes.TOGGLE_SHOULD_CREATE_MR, payload: false }, - ], - [], - done, - ); - }); - }); - - describe('updateBranchName', () => { - it('updates store with new branch name', done => { - store - .dispatch('commit/updateBranchName', 'branch-name') - .then(() => { - expect(store.state.commit.newBranchName).toBe('branch-name'); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('setLastCommitMessage', () => { - beforeEach(() => { - Object.assign(store.state, { - currentProjectId: 'abcproject', - projects: { - abcproject: { - web_url: 'http://testing', - }, - }, - }); - }); - - it('updates commit message with short_id', done => { - store - .dispatch('commit/setLastCommitMessage', { short_id: '123' }) - .then(() => { - expect(store.state.lastCommitMsg).toContain( - 'Your changes have been committed. Commit <a href="http://testing/-/commit/123" class="commit-sha">123</a>', - ); - }) - .then(done) - .catch(done.fail); - }); - - it('updates commit message with stats', done => { - store - .dispatch('commit/setLastCommitMessage', { - short_id: '123', - stats: { - additions: '1', - deletions: '2', - }, - }) - .then(() => { - expect(store.state.lastCommitMsg).toBe( - 'Your changes have been committed. Commit <a href="http://testing/-/commit/123" class="commit-sha">123</a> with 1 additions, 2 deletions.', - ); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('updateFilesAfterCommit', () => { - const data = { - id: '123', - message: 'testing commit message', - committed_date: '123', - committer_name: 'root', - }; - const branch = 'master'; - let f; - - beforeEach(() => { - spyOn(eventHub, '$emit'); - - f = file('changedFile'); - Object.assign(f, { - active: true, - changed: true, - content: 'file content', - }); - - Object.assign(store.state, { - currentProjectId: 'abcproject', - currentBranchId: 'master', - projects: { - abcproject: { - web_url: 'web_url', - branches: { - master: { - workingReference: '', - commit: { - short_id: TEST_COMMIT_SHA, - }, - }, - }, - }, - }, - stagedFiles: [ - f, - { - ...file('changedFile2'), - changed: true, - }, - ], - openFiles: store.state.stagedFiles, - }); - - store.state.stagedFiles.forEach(stagedFile => { - store.state.entries[stagedFile.path] = stagedFile; - }); - }); - - it('updates stores working reference', done => { - store - .dispatch('commit/updateFilesAfterCommit', { - data, - branch, - }) - .then(() => { - expect(store.state.projects.abcproject.branches.master.workingReference).toBe(data.id); - }) - .then(done) - .catch(done.fail); - }); - - it('resets all files changed status', done => { - store - .dispatch('commit/updateFilesAfterCommit', { - data, - branch, - }) - .then(() => { - store.state.openFiles.forEach(entry => { - expect(entry.changed).toBeFalsy(); - }); - }) - .then(done) - .catch(done.fail); - }); - - it('sets files commit data', done => { - store - .dispatch('commit/updateFilesAfterCommit', { - data, - branch, - }) - .then(() => { - expect(f.lastCommitSha).toBe(data.id); - }) - .then(done) - .catch(done.fail); - }); - - it('updates raw content for changed file', done => { - store - .dispatch('commit/updateFilesAfterCommit', { - data, - branch, - }) - .then(() => { - expect(f.raw).toBe(f.content); - }) - .then(done) - .catch(done.fail); - }); - - it('emits changed event for file', done => { - store - .dispatch('commit/updateFilesAfterCommit', { - data, - branch, - }) - .then(() => { - expect(eventHub.$emit).toHaveBeenCalledWith(`editor.update.model.content.${f.key}`, { - content: f.content, - changed: false, - }); - }) - .then(done) - .catch(done.fail); - }); - }); - - describe('commitChanges', () => { - let visitUrl; - - beforeEach(() => { - visitUrl = spyOnDependency(rootActions, 'visitUrl'); - - document.body.innerHTML += '<div class="flash-container"></div>'; - - const f = { - ...file('changed'), - type: 'blob', - active: true, - lastCommitSha: TEST_COMMIT_SHA, - content: '\n', - raw: '\n', - }; - - Object.assign(store.state, { - stagedFiles: [f], - changedFiles: [f], - openFiles: [f], - currentProjectId: 'abcproject', - currentBranchId: 'master', - projects: { - abcproject: { - web_url: 'webUrl', - branches: { - master: { - workingReference: '1', - commit: { - id: TEST_COMMIT_SHA, - }, - }, - }, - userPermissions: { - [PERMISSION_CREATE_MR]: true, - }, - }, - }, - }); - - store.state.commit.commitAction = '2'; - store.state.commit.commitMessage = 'testing 123'; - - store.state.openFiles.forEach(localF => { - store.state.entries[localF.path] = localF; - }); - }); - - afterEach(() => { - document.querySelector('.flash-container').remove(); - }); - - describe('success', () => { - const COMMIT_RESPONSE = { - id: '123456', - short_id: '123', - message: 'test message', - committed_date: 'date', - parent_ids: '321', - stats: { - additions: '1', - deletions: '2', - }, - }; - - beforeEach(() => { - spyOn(service, 'commit').and.returnValue( - Promise.resolve({ - data: COMMIT_RESPONSE, - }), - ); - }); - - it('calls service', done => { - store - .dispatch('commit/commitChanges') - .then(() => { - expect(service.commit).toHaveBeenCalledWith('abcproject', { - branch: jasmine.anything(), - commit_message: 'testing 123', - actions: [ - { - action: commitActionTypes.update, - file_path: jasmine.anything(), - content: '\n', - encoding: jasmine.anything(), - last_commit_id: undefined, - previous_path: undefined, - }, - ], - start_sha: TEST_COMMIT_SHA, - }); - - done(); - }) - .catch(done.fail); - }); - - it('sends lastCommit ID when not creating new branch', done => { - store.state.commit.commitAction = '1'; - - store - .dispatch('commit/commitChanges') - .then(() => { - expect(service.commit).toHaveBeenCalledWith('abcproject', { - branch: jasmine.anything(), - commit_message: 'testing 123', - actions: [ - { - action: commitActionTypes.update, - file_path: jasmine.anything(), - content: '\n', - encoding: jasmine.anything(), - last_commit_id: TEST_COMMIT_SHA, - previous_path: undefined, - }, - ], - start_sha: undefined, - }); - - done(); - }) - .catch(done.fail); - }); - - it('sets last Commit Msg', done => { - store - .dispatch('commit/commitChanges') - .then(() => { - expect(store.state.lastCommitMsg).toBe( - 'Your changes have been committed. Commit <a href="webUrl/-/commit/123" class="commit-sha">123</a> with 1 additions, 2 deletions.', - ); - - done(); - }) - .catch(done.fail); - }); - - it('adds commit data to files', done => { - store - .dispatch('commit/commitChanges') - .then(() => { - expect(store.state.entries[store.state.openFiles[0].path].lastCommitSha).toBe( - COMMIT_RESPONSE.id, - ); - - done(); - }) - .catch(done.fail); - }); - - it('resets stores commit actions', done => { - store.state.commit.commitAction = consts.COMMIT_TO_NEW_BRANCH; - - store - .dispatch('commit/commitChanges') - .then(() => { - expect(store.state.commit.commitAction).not.toBe(consts.COMMIT_TO_NEW_BRANCH); - }) - .then(done) - .catch(done.fail); - }); - - it('removes all staged files', done => { - store - .dispatch('commit/commitChanges') - .then(() => { - expect(store.state.stagedFiles.length).toBe(0); - }) - .then(done) - .catch(done.fail); - }); - - describe('merge request', () => { - it('redirects to new merge request page', done => { - spyOn(eventHub, '$on'); - - store.state.commit.commitAction = consts.COMMIT_TO_NEW_BRANCH; - store.state.commit.shouldCreateMR = true; - - store - .dispatch('commit/commitChanges') - .then(() => { - expect(visitUrl).toHaveBeenCalledWith( - `webUrl/-/merge_requests/new?merge_request[source_branch]=${ - store.getters['commit/placeholderBranchName'] - }&merge_request[target_branch]=master&nav_source=webide`, - ); - - done(); - }) - .catch(done.fail); - }); - - it('does not redirect to new merge request page when shouldCreateMR is not checked', done => { - spyOn(eventHub, '$on'); - - store.state.commit.commitAction = consts.COMMIT_TO_NEW_BRANCH; - store.state.commit.shouldCreateMR = false; - - store - .dispatch('commit/commitChanges') - .then(() => { - expect(visitUrl).not.toHaveBeenCalled(); - done(); - }) - .catch(done.fail); - }); - - it('resets changed files before redirecting', done => { - visitUrl = visitUrl.and.callFake(() => { - expect(store.state.stagedFiles.length).toBe(0); - done(); - }); - - spyOn(eventHub, '$on'); - - store.state.commit.commitAction = '3'; - - store.dispatch('commit/commitChanges').catch(done.fail); - }); - }); - }); - - describe('failed', () => { - beforeEach(() => { - spyOn(service, 'commit').and.returnValue( - Promise.resolve({ - data: { - message: 'failed message', - }, - }), - ); - }); - - it('shows failed message', done => { - store - .dispatch('commit/commitChanges') - .then(() => { - const alert = document.querySelector('.flash-container'); - - expect(alert.textContent.trim()).toBe('failed message'); - - done(); - }) - .catch(done.fail); - }); - }); - - describe('first commit of a branch', () => { - const COMMIT_RESPONSE = { - id: '123456', - short_id: '123', - message: 'test message', - committed_date: 'date', - parent_ids: [], - stats: { - additions: '1', - deletions: '2', - }, - }; - - it('commits TOGGLE_EMPTY_STATE mutation on empty repo', done => { - spyOn(service, 'commit').and.returnValue( - Promise.resolve({ - data: COMMIT_RESPONSE, - }), - ); - - spyOn(store, 'commit').and.callThrough(); - - store - .dispatch('commit/commitChanges') - .then(() => { - expect(store.commit.calls.allArgs()).toEqual( - jasmine.arrayContaining([ - ['TOGGLE_EMPTY_STATE', jasmine.any(Object), jasmine.any(Object)], - ]), - ); - done(); - }) - .catch(done.fail); - }); - - it('does not commmit TOGGLE_EMPTY_STATE mutation on existing project', done => { - COMMIT_RESPONSE.parent_ids.push('1234'); - spyOn(service, 'commit').and.returnValue( - Promise.resolve({ - data: COMMIT_RESPONSE, - }), - ); - spyOn(store, 'commit').and.callThrough(); - - store - .dispatch('commit/commitChanges') - .then(() => { - expect(store.commit.calls.allArgs()).not.toEqual( - jasmine.arrayContaining([ - ['TOGGLE_EMPTY_STATE', jasmine.any(Object), jasmine.any(Object)], - ]), - ); - done(); - }) - .catch(done.fail); - }); - }); - }); - - describe('toggleShouldCreateMR', () => { - it('commits both toggle and interacting with MR checkbox actions', done => { - testAction( - actions.toggleShouldCreateMR, - {}, - store.state, - [{ type: mutationTypes.TOGGLE_SHOULD_CREATE_MR }], - [], - done, - ); - }); - }); -}); diff --git a/spec/javascripts/vue_shared/components/file_finder/index_spec.js b/spec/javascripts/vue_shared/components/file_finder/index_spec.js index 7ded228d3ea..bb927b7d7f2 100644 --- a/spec/javascripts/vue_shared/components/file_finder/index_spec.js +++ b/spec/javascripts/vue_shared/components/file_finder/index_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import Mousetrap from 'mousetrap'; -import { file } from 'spec/ide/helpers'; +import { file } from 'jest/ide/helpers'; import timeoutPromise from 'spec/helpers/set_timeout_promise_helper'; import FindFileComponent from '~/vue_shared/components/file_finder/index.vue'; import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes'; |