diff options
Diffstat (limited to 'spec/javascripts/repo')
14 files changed, 982 insertions, 0 deletions
diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js new file mode 100644 index 00000000000..db2b7d51626 --- /dev/null +++ b/spec/javascripts/repo/components/repo_commit_section_spec.js @@ -0,0 +1,158 @@ +import Vue from 'vue'; +import repoCommitSection from '~/repo/components/repo_commit_section.vue'; +import RepoStore from '~/repo/stores/repo_store'; +import RepoHelper from '~/repo/helpers/repo_helper'; +import Api from '~/api'; + +describe('RepoCommitSection', () => { + const branch = 'master'; + const projectUrl = 'projectUrl'; + const openedFiles = [{ + id: 0, + changed: true, + url: `/namespace/${projectUrl}/blob/${branch}/dir/file0.ext`, + newContent: 'a', + }, { + id: 1, + changed: true, + url: `/namespace/${projectUrl}/blob/${branch}/dir/file1.ext`, + newContent: 'b', + }, { + id: 2, + url: `/namespace/${projectUrl}/blob/${branch}/dir/file2.ext`, + changed: false, + }]; + + RepoStore.projectUrl = projectUrl; + + function createComponent() { + const RepoCommitSection = Vue.extend(repoCommitSection); + + return new RepoCommitSection().$mount(); + } + + it('renders a commit section', () => { + RepoStore.isCommitable = true; + RepoStore.targetBranch = branch; + RepoStore.openedFiles = openedFiles; + + spyOn(RepoHelper, 'getBranch').and.returnValue(branch); + + const vm = createComponent(); + const changedFiles = [...vm.$el.querySelectorAll('.changed-files > li')]; + const commitMessage = vm.$el.querySelector('#commit-message'); + const submitCommit = vm.$el.querySelector('.submit-commit'); + const targetBranch = vm.$el.querySelector('.target-branch'); + + expect(vm.$el.querySelector(':scope > form')).toBeTruthy(); + expect(vm.$el.querySelector('.staged-files').textContent).toEqual('Staged files (2)'); + expect(changedFiles.length).toEqual(2); + + changedFiles.forEach((changedFile, i) => { + const filePath = RepoHelper.getFilePathFromFullPath(openedFiles[i].url, branch); + + expect(changedFile.textContent).toEqual(filePath); + }); + + expect(commitMessage.tagName).toEqual('TEXTAREA'); + expect(commitMessage.name).toEqual('commit-message'); + expect(submitCommit.type).toEqual('submit'); + expect(submitCommit.disabled).toBeTruthy(); + expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeFalsy(); + expect(vm.$el.querySelector('.commit-summary').textContent).toEqual('Commit 2 files'); + expect(targetBranch.querySelector(':scope > label').textContent).toEqual('Target branch'); + expect(targetBranch.querySelector('.help-block').textContent).toEqual(branch); + }); + + it('does not render if not isCommitable', () => { + RepoStore.isCommitable = false; + RepoStore.openedFiles = [{ + id: 0, + changed: true, + }]; + + const vm = createComponent(); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); + + it('does not render if no changedFiles', () => { + RepoStore.isCommitable = true; + RepoStore.openedFiles = []; + + const vm = createComponent(); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); + + it('shows commit submit and summary if commitMessage and spinner if submitCommitsLoading', (done) => { + const projectId = 'projectId'; + const commitMessage = 'commitMessage'; + RepoStore.isCommitable = true; + RepoStore.openedFiles = openedFiles; + RepoStore.projectId = projectId; + + spyOn(RepoHelper, 'getBranch').and.returnValue(branch); + + const vm = createComponent(); + const commitMessageEl = vm.$el.querySelector('#commit-message'); + const submitCommit = vm.$el.querySelector('.submit-commit'); + + vm.commitMessage = commitMessage; + + Vue.nextTick(() => { + expect(commitMessageEl.value).toBe(commitMessage); + expect(submitCommit.disabled).toBeFalsy(); + + spyOn(vm, 'makeCommit').and.callThrough(); + spyOn(Api, 'commitMultiple'); + + submitCommit.click(); + + Vue.nextTick(() => { + expect(vm.makeCommit).toHaveBeenCalled(); + expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeTruthy(); + + const args = Api.commitMultiple.calls.allArgs()[0]; + const { commit_message, actions, branch: payloadBranch } = args[1]; + + expect(args[0]).toBe(projectId); + expect(commit_message).toBe(commitMessage); + expect(actions.length).toEqual(2); + expect(payloadBranch).toEqual(branch); + expect(actions[0].action).toEqual('update'); + expect(actions[1].action).toEqual('update'); + expect(actions[0].content).toEqual(openedFiles[0].newContent); + expect(actions[1].content).toEqual(openedFiles[1].newContent); + expect(actions[0].file_path) + .toEqual(RepoHelper.getFilePathFromFullPath(openedFiles[0].url, branch)); + expect(actions[1].file_path) + .toEqual(RepoHelper.getFilePathFromFullPath(openedFiles[1].url, branch)); + + done(); + }); + }); + }); + + describe('methods', () => { + describe('resetCommitState', () => { + it('should reset store vars and scroll to top', () => { + const vm = { + submitCommitsLoading: true, + changedFiles: new Array(10), + openedFiles: new Array(10), + commitMessage: 'commitMessage', + editMode: true, + }; + + repoCommitSection.methods.resetCommitState.call(vm); + + expect(vm.submitCommitsLoading).toEqual(false); + expect(vm.changedFiles).toEqual([]); + expect(vm.openedFiles).toEqual([]); + expect(vm.commitMessage).toEqual(''); + expect(vm.editMode).toEqual(false); + }); + }); + }); +}); diff --git a/spec/javascripts/repo/components/repo_edit_button_spec.js b/spec/javascripts/repo/components/repo_edit_button_spec.js new file mode 100644 index 00000000000..df2f9697acc --- /dev/null +++ b/spec/javascripts/repo/components/repo_edit_button_spec.js @@ -0,0 +1,51 @@ +import Vue from 'vue'; +import repoEditButton from '~/repo/components/repo_edit_button.vue'; +import RepoStore from '~/repo/stores/repo_store'; + +describe('RepoEditButton', () => { + function createComponent() { + const RepoEditButton = Vue.extend(repoEditButton); + + return new RepoEditButton().$mount(); + } + + it('renders an edit button that toggles the view state', (done) => { + RepoStore.isCommitable = true; + RepoStore.changedFiles = []; + + const vm = createComponent(); + + expect(vm.$el.tagName).toEqual('BUTTON'); + expect(vm.$el.textContent).toMatch('Edit'); + + spyOn(vm, 'editClicked').and.callThrough(); + + vm.$el.click(); + + Vue.nextTick(() => { + expect(vm.editClicked).toHaveBeenCalled(); + expect(vm.$el.textContent).toMatch('Cancel edit'); + done(); + }); + }); + + it('does not render if not isCommitable', () => { + RepoStore.isCommitable = false; + + const vm = createComponent(); + + expect(vm.$el.innerHTML).toBeUndefined(); + }); + + describe('methods', () => { + describe('editClicked', () => { + it('sets dialog to open when there are changedFiles', () => { + + }); + + it('toggles editMode and calls toggleBlobView', () => { + + }); + }); + }); +}); diff --git a/spec/javascripts/repo/components/repo_editor_spec.js b/spec/javascripts/repo/components/repo_editor_spec.js new file mode 100644 index 00000000000..35e0c995163 --- /dev/null +++ b/spec/javascripts/repo/components/repo_editor_spec.js @@ -0,0 +1,26 @@ +import Vue from 'vue'; +import repoEditor from '~/repo/components/repo_editor.vue'; +import RepoStore from '~/repo/stores/repo_store'; + +describe('RepoEditor', () => { + function createComponent() { + const RepoEditor = Vue.extend(repoEditor); + + return new RepoEditor().$mount(); + } + + it('renders an ide container', () => { + const monacoInstance = jasmine.createSpyObj('monacoInstance', ['onMouseUp', 'onKeyUp', 'setModel', 'updateOptions']); + const monaco = { + editor: jasmine.createSpyObj('editor', ['create']), + }; + RepoStore.monaco = monaco; + + monaco.editor.create.and.returnValue(monacoInstance); + spyOn(repoEditor.watch, 'blobRaw'); + + const vm = createComponent(); + + expect(vm.$el.id).toEqual('ide'); + }); +}); diff --git a/spec/javascripts/repo/components/repo_file_buttons_spec.js b/spec/javascripts/repo/components/repo_file_buttons_spec.js new file mode 100644 index 00000000000..e1f25e4485f --- /dev/null +++ b/spec/javascripts/repo/components/repo_file_buttons_spec.js @@ -0,0 +1,82 @@ +import Vue from 'vue'; +import repoFileButtons from '~/repo/components/repo_file_buttons.vue'; +import RepoStore from '~/repo/stores/repo_store'; + +describe('RepoFileButtons', () => { + function createComponent() { + const RepoFileButtons = Vue.extend(repoFileButtons); + + return new RepoFileButtons().$mount(); + } + + it('renders Raw, Blame, History, Permalink and Preview toggle', () => { + const activeFile = { + extension: 'md', + url: 'url', + raw_path: 'raw_path', + blame_path: 'blame_path', + commits_path: 'commits_path', + permalink: 'permalink', + }; + const activeFileLabel = 'activeFileLabel'; + RepoStore.openedFiles = new Array(1); + RepoStore.activeFile = activeFile; + RepoStore.activeFileLabel = activeFileLabel; + RepoStore.editMode = true; + + const vm = createComponent(); + const raw = vm.$el.querySelector('.raw'); + const blame = vm.$el.querySelector('.blame'); + const history = vm.$el.querySelector('.history'); + + expect(vm.$el.id).toEqual('repo-file-buttons'); + expect(raw.href).toMatch(`/${activeFile.raw_path}`); + expect(raw.textContent).toEqual('Raw'); + expect(blame.href).toMatch(`/${activeFile.blame_path}`); + expect(blame.textContent).toEqual('Blame'); + expect(history.href).toMatch(`/${activeFile.commits_path}`); + expect(history.textContent).toEqual('History'); + expect(vm.$el.querySelector('.permalink').textContent).toEqual('Permalink'); + expect(vm.$el.querySelector('.preview').textContent).toEqual(activeFileLabel); + }); + + it('triggers rawPreviewToggle on preview click', () => { + const activeFile = { + extension: 'md', + url: 'url', + }; + RepoStore.openedFiles = new Array(1); + RepoStore.activeFile = activeFile; + RepoStore.editMode = true; + + const vm = createComponent(); + const preview = vm.$el.querySelector('.preview'); + + spyOn(vm, 'rawPreviewToggle'); + + preview.click(); + + expect(vm.rawPreviewToggle).toHaveBeenCalled(); + }); + + it('does not render preview toggle if not canPreview', () => { + const activeFile = { + extension: 'abcd', + url: 'url', + }; + RepoStore.openedFiles = new Array(1); + RepoStore.activeFile = activeFile; + + const vm = createComponent(); + + expect(vm.$el.querySelector('.preview')).toBeFalsy(); + }); + + it('does not render if not isMini', () => { + RepoStore.openedFiles = []; + + const vm = createComponent(); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); +}); diff --git a/spec/javascripts/repo/components/repo_file_options_spec.js b/spec/javascripts/repo/components/repo_file_options_spec.js new file mode 100644 index 00000000000..9759b4bf12d --- /dev/null +++ b/spec/javascripts/repo/components/repo_file_options_spec.js @@ -0,0 +1,33 @@ +import Vue from 'vue'; +import repoFileOptions from '~/repo/components/repo_file_options.vue'; + +describe('RepoFileOptions', () => { + const projectName = 'projectName'; + + function createComponent(propsData) { + const RepoFileOptions = Vue.extend(repoFileOptions); + + return new RepoFileOptions({ + propsData, + }).$mount(); + } + + it('renders the title and new file/folder buttons if isMini is true', () => { + const vm = createComponent({ + isMini: true, + projectName, + }); + + expect(vm.$el.classList.contains('repo-file-options')).toBeTruthy(); + expect(vm.$el.querySelector('.title').textContent).toEqual(projectName); + }); + + it('does not render if isMini is false', () => { + const vm = createComponent({ + isMini: false, + projectName, + }); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); +}); diff --git a/spec/javascripts/repo/components/repo_file_spec.js b/spec/javascripts/repo/components/repo_file_spec.js new file mode 100644 index 00000000000..90616ae13ca --- /dev/null +++ b/spec/javascripts/repo/components/repo_file_spec.js @@ -0,0 +1,136 @@ +import Vue from 'vue'; +import repoFile from '~/repo/components/repo_file.vue'; + +describe('RepoFile', () => { + const updated = 'updated'; + const file = { + icon: 'icon', + url: 'url', + name: 'name', + lastCommitMessage: 'message', + lastCommitUpdate: Date.now(), + level: 10, + }; + const activeFile = { + url: 'url', + }; + + function createComponent(propsData) { + const RepoFile = Vue.extend(repoFile); + + return new RepoFile({ + propsData, + }).$mount(); + } + + beforeEach(() => { + spyOn(repoFile.mixins[0].methods, 'timeFormated').and.returnValue(updated); + }); + + it('renders link, icon, name and last commit details', () => { + const vm = createComponent({ + file, + activeFile, + }); + const name = vm.$el.querySelector('.repo-file-name'); + const fileIcon = vm.$el.querySelector('.file-icon'); + + expect(vm.$el.classList.contains('active')).toBeTruthy(); + expect(vm.$el.querySelector(`.${file.icon}`).style.marginLeft).toEqual('100px'); + expect(name.title).toEqual(file.url); + expect(name.href).toMatch(`/${file.url}`); + expect(name.textContent).toEqual(file.name); + expect(vm.$el.querySelector('.commit-message').textContent).toBe(file.lastCommitMessage); + expect(vm.$el.querySelector('.commit-update').textContent).toBe(updated); + expect(fileIcon.classList.contains(file.icon)).toBeTruthy(); + expect(fileIcon.style.marginLeft).toEqual(`${file.level * 10}px`); + }); + + it('does render if hasFiles is true and is loading tree', () => { + const vm = createComponent({ + file, + activeFile, + loading: { + tree: true, + }, + hasFiles: true, + }); + + expect(vm.$el.innerHTML).toBeTruthy(); + expect(vm.$el.querySelector('.fa-spin.fa-spinner')).toBeFalsy(); + }); + + it('renders a spinner if the file is loading', () => { + file.loading = true; + const vm = createComponent({ + file, + activeFile, + loading: { + tree: true, + }, + hasFiles: true, + }); + + expect(vm.$el.innerHTML).toBeTruthy(); + expect(vm.$el.querySelector('.fa-spin.fa-spinner').style.marginLeft).toEqual(`${file.level * 10}px`); + }); + + it('does not render if loading tree', () => { + const vm = createComponent({ + file, + activeFile, + loading: { + tree: true, + }, + }); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); + + it('does not render commit message and datetime if mini', () => { + const vm = createComponent({ + file, + activeFile, + isMini: true, + }); + + expect(vm.$el.querySelector('.commit-message')).toBeFalsy(); + expect(vm.$el.querySelector('.commit-update')).toBeFalsy(); + }); + + it('does not set active class if file is active file', () => { + const vm = createComponent({ + file, + activeFile: {}, + }); + + expect(vm.$el.classList.contains('active')).toBeFalsy(); + }); + + it('fires linkClicked when the link is clicked', () => { + const vm = createComponent({ + file, + activeFile, + }); + + spyOn(vm, 'linkClicked'); + + vm.$el.querySelector('.repo-file-name').click(); + + expect(vm.linkClicked).toHaveBeenCalledWith(file); + }); + + describe('methods', () => { + describe('linkClicked', () => { + const vm = jasmine.createSpyObj('vm', ['$emit']); + + it('$emits linkclicked with file obj', () => { + const theFile = {}; + + repoFile.methods.linkClicked.call(vm, theFile); + + expect(vm.$emit).toHaveBeenCalledWith('linkclicked', theFile); + }); + }); + }); +}); diff --git a/spec/javascripts/repo/components/repo_loading_file_spec.js b/spec/javascripts/repo/components/repo_loading_file_spec.js new file mode 100644 index 00000000000..d84f4c5609e --- /dev/null +++ b/spec/javascripts/repo/components/repo_loading_file_spec.js @@ -0,0 +1,79 @@ +import Vue from 'vue'; +import repoLoadingFile from '~/repo/components/repo_loading_file.vue'; + +describe('RepoLoadingFile', () => { + function createComponent(propsData) { + const RepoLoadingFile = Vue.extend(repoLoadingFile); + + return new RepoLoadingFile({ + propsData, + }).$mount(); + } + + function assertLines(lines) { + lines.forEach((line, n) => { + const index = n + 1; + expect(line.classList.contains(`line-of-code-${index}`)).toBeTruthy(); + }); + } + + function assertColumns(columns) { + columns.forEach((column) => { + const container = column.querySelector('.animation-container'); + const lines = [...container.querySelectorAll(':scope > div')]; + + expect(container).toBeTruthy(); + expect(lines.length).toEqual(6); + assertLines(lines); + }); + } + + it('renders 3 columns of animated LoC', () => { + const vm = createComponent({ + loading: { + tree: true, + }, + hasFiles: false, + }); + const columns = [...vm.$el.querySelectorAll('td')]; + + expect(columns.length).toEqual(3); + assertColumns(columns); + }); + + it('renders 1 column of animated LoC if isMini', () => { + const vm = createComponent({ + loading: { + tree: true, + }, + hasFiles: false, + isMini: true, + }); + const columns = [...vm.$el.querySelectorAll('td')]; + + expect(columns.length).toEqual(1); + assertColumns(columns); + }); + + it('does not render if tree is not loading', () => { + const vm = createComponent({ + loading: { + tree: false, + }, + hasFiles: false, + }); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); + + it('does not render if hasFiles is true', () => { + const vm = createComponent({ + loading: { + tree: true, + }, + hasFiles: true, + }); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); +}); diff --git a/spec/javascripts/repo/components/repo_prev_directory_spec.js b/spec/javascripts/repo/components/repo_prev_directory_spec.js new file mode 100644 index 00000000000..34dde545e6a --- /dev/null +++ b/spec/javascripts/repo/components/repo_prev_directory_spec.js @@ -0,0 +1,43 @@ +import Vue from 'vue'; +import repoPrevDirectory from '~/repo/components/repo_prev_directory.vue'; + +describe('RepoPrevDirectory', () => { + function createComponent(propsData) { + const RepoPrevDirectory = Vue.extend(repoPrevDirectory); + + return new RepoPrevDirectory({ + propsData, + }).$mount(); + } + + it('renders a prev dir link', () => { + const prevUrl = 'prevUrl'; + const vm = createComponent({ + prevUrl, + }); + const link = vm.$el.querySelector('a'); + + spyOn(vm, 'linkClicked'); + + expect(link.href).toMatch(`/${prevUrl}`); + expect(link.textContent).toEqual('..'); + + link.click(); + + expect(vm.linkClicked).toHaveBeenCalledWith(prevUrl); + }); + + describe('methods', () => { + describe('linkClicked', () => { + const vm = jasmine.createSpyObj('vm', ['$emit']); + + it('$emits linkclicked with file obj', () => { + const file = {}; + + repoPrevDirectory.methods.linkClicked.call(vm, file); + + expect(vm.$emit).toHaveBeenCalledWith('linkclicked', file); + }); + }); + }); +}); diff --git a/spec/javascripts/repo/components/repo_preview_spec.js b/spec/javascripts/repo/components/repo_preview_spec.js new file mode 100644 index 00000000000..4920cf02083 --- /dev/null +++ b/spec/javascripts/repo/components/repo_preview_spec.js @@ -0,0 +1,23 @@ +import Vue from 'vue'; +import repoPreview from '~/repo/components/repo_preview.vue'; +import RepoStore from '~/repo/stores/repo_store'; + +describe('RepoPreview', () => { + function createComponent() { + const RepoPreview = Vue.extend(repoPreview); + + return new RepoPreview().$mount(); + } + + it('renders a div with the activeFile html', () => { + const activeFile = { + html: '<p class="file-content">html</p>', + }; + RepoStore.activeFile = activeFile; + + const vm = createComponent(); + + expect(vm.$el.tagName).toEqual('DIV'); + expect(vm.$el.innerHTML).toContain(activeFile.html); + }); +}); diff --git a/spec/javascripts/repo/components/repo_sidebar_spec.js b/spec/javascripts/repo/components/repo_sidebar_spec.js new file mode 100644 index 00000000000..0d216c9c026 --- /dev/null +++ b/spec/javascripts/repo/components/repo_sidebar_spec.js @@ -0,0 +1,61 @@ +import Vue from 'vue'; +import RepoStore from '~/repo/stores/repo_store'; +import repoSidebar from '~/repo/components/repo_sidebar.vue'; + +describe('RepoSidebar', () => { + function createComponent() { + const RepoSidebar = Vue.extend(repoSidebar); + + return new RepoSidebar().$mount(); + } + + it('renders a sidebar', () => { + RepoStore.files = [{ + id: 0, + }]; + const vm = createComponent(); + const thead = vm.$el.querySelector('thead'); + const tbody = vm.$el.querySelector('tbody'); + + expect(vm.$el.id).toEqual('sidebar'); + expect(vm.$el.classList.contains('sidebar-mini')).toBeFalsy(); + expect(thead.querySelector('.name').textContent).toEqual('Name'); + expect(thead.querySelector('.last-commit').textContent).toEqual('Last Commit'); + expect(thead.querySelector('.last-update').textContent).toEqual('Last Update'); + expect(tbody.querySelector('.repo-file-options')).toBeFalsy(); + expect(tbody.querySelector('.prev-directory')).toBeFalsy(); + expect(tbody.querySelector('.loading-file')).toBeFalsy(); + expect(tbody.querySelector('.file')).toBeTruthy(); + }); + + it('does not render a thead, renders repo-file-options and sets sidebar-mini class if isMini', () => { + RepoStore.openedFiles = [{ + id: 0, + }]; + const vm = createComponent(); + + expect(vm.$el.classList.contains('sidebar-mini')).toBeTruthy(); + expect(vm.$el.querySelector('thead')).toBeFalsy(); + expect(vm.$el.querySelector('tbody .repo-file-options')).toBeTruthy(); + }); + + it('renders 5 loading files if tree is loading and not hasFiles', () => { + RepoStore.loading = { + tree: true, + }; + RepoStore.files = []; + const vm = createComponent(); + + expect(vm.$el.querySelectorAll('tbody .loading-file').length).toEqual(5); + }); + + it('renders a prev directory if isRoot', () => { + RepoStore.files = [{ + id: 0, + }]; + RepoStore.isRoot = true; + const vm = createComponent(); + + expect(vm.$el.querySelector('tbody .prev-directory')).toBeTruthy(); + }); +}); diff --git a/spec/javascripts/repo/components/repo_tab_spec.js b/spec/javascripts/repo/components/repo_tab_spec.js new file mode 100644 index 00000000000..f3572804b4a --- /dev/null +++ b/spec/javascripts/repo/components/repo_tab_spec.js @@ -0,0 +1,88 @@ +import Vue from 'vue'; +import repoTab from '~/repo/components/repo_tab.vue'; + +describe('RepoTab', () => { + function createComponent(propsData) { + const RepoTab = Vue.extend(repoTab); + + return new RepoTab({ + propsData, + }).$mount(); + } + + it('renders a close link and a name link', () => { + const tab = { + loading: false, + url: 'url', + name: 'name', + }; + const vm = createComponent({ + tab, + }); + const close = vm.$el.querySelector('.close'); + const name = vm.$el.querySelector(`a[title="${tab.url}"]`); + + spyOn(vm, 'xClicked'); + spyOn(vm, 'tabClicked'); + + expect(close.querySelector('.fa-times')).toBeTruthy(); + expect(name.textContent).toEqual(tab.name); + + close.click(); + name.click(); + + expect(vm.xClicked).toHaveBeenCalledWith(tab); + expect(vm.tabClicked).toHaveBeenCalledWith(tab); + }); + + it('renders a spinner if tab is loading', () => { + const tab = { + loading: true, + url: 'url', + }; + const vm = createComponent({ + tab, + }); + const close = vm.$el.querySelector('.close'); + const name = vm.$el.querySelector(`a[title="${tab.url}"]`); + + expect(close).toBeFalsy(); + expect(name).toBeFalsy(); + expect(vm.$el.querySelector('.fa.fa-spinner.fa-spin')).toBeTruthy(); + }); + + it('renders an fa-circle icon if tab is changed', () => { + const tab = { + loading: false, + url: 'url', + name: 'name', + changed: true, + }; + const vm = createComponent({ + tab, + }); + + expect(vm.$el.querySelector('.close .fa-circle')).toBeTruthy(); + }); + + describe('methods', () => { + describe('xClicked', () => { + const vm = jasmine.createSpyObj('vm', ['$emit']); + + it('returns undefined and does not $emit if file is changed', () => { + const file = { changed: true }; + const returnVal = repoTab.methods.xClicked.call(vm, file); + + expect(returnVal).toBeUndefined(); + expect(vm.$emit).not.toHaveBeenCalled(); + }); + + it('$emits xclicked event with file obj', () => { + const file = { changed: false }; + repoTab.methods.xClicked.call(vm, file); + + expect(vm.$emit).toHaveBeenCalledWith('xclicked', file); + }); + }); + }); +}); diff --git a/spec/javascripts/repo/components/repo_tabs_spec.js b/spec/javascripts/repo/components/repo_tabs_spec.js new file mode 100644 index 00000000000..fdb12cfc00f --- /dev/null +++ b/spec/javascripts/repo/components/repo_tabs_spec.js @@ -0,0 +1,64 @@ +import Vue from 'vue'; +import RepoStore from '~/repo/stores/repo_store'; +import repoTabs from '~/repo/components/repo_tabs.vue'; + +describe('RepoTabs', () => { + const openedFiles = [{ + id: 0, + active: true, + }, { + id: 1, + }]; + + function createComponent() { + const RepoTabs = Vue.extend(repoTabs); + + return new RepoTabs().$mount(); + } + + it('renders a list of tabs', () => { + RepoStore.openedFiles = openedFiles; + RepoStore.tabsOverflow = true; + + const vm = createComponent(); + const tabs = [...vm.$el.querySelectorAll(':scope > li')]; + + expect(vm.$el.id).toEqual('tabs'); + expect(vm.$el.classList.contains('overflown')).toBeTruthy(); + expect(tabs.length).toEqual(3); + expect(tabs[0].classList.contains('active')).toBeTruthy(); + expect(tabs[1].classList.contains('active')).toBeFalsy(); + expect(tabs[2].classList.contains('tabs-divider')).toBeTruthy(); + }); + + it('does not render a tabs list if not isMini', () => { + RepoStore.openedFiles = []; + + const vm = createComponent(); + + expect(vm.$el.innerHTML).toBeFalsy(); + }); + + it('does not apply overflown class if not tabsOverflow', () => { + RepoStore.openedFiles = openedFiles; + RepoStore.tabsOverflow = false; + + const vm = createComponent(); + + expect(vm.$el.classList.contains('overflown')).toBeFalsy(); + }); + + describe('methods', () => { + describe('xClicked', () => { + it('calls removeFromOpenedFiles with file obj', () => { + const file = {}; + + spyOn(RepoStore, 'removeFromOpenedFiles'); + + repoTabs.methods.xClicked(file); + + expect(RepoStore.removeFromOpenedFiles).toHaveBeenCalledWith(file); + }); + }); + }); +}); diff --git a/spec/javascripts/repo/monaco_loader_spec.js b/spec/javascripts/repo/monaco_loader_spec.js new file mode 100644 index 00000000000..be6e779c50f --- /dev/null +++ b/spec/javascripts/repo/monaco_loader_spec.js @@ -0,0 +1,17 @@ +/* global __webpack_public_path__ */ +import monacoContext from 'monaco-editor/dev/vs/loader'; + +describe('MonacoLoader', () => { + it('calls require.config and exports require', () => { + spyOn(monacoContext.require, 'config'); + + const monacoLoader = require('~/repo/monaco_loader'); // eslint-disable-line global-require + + expect(monacoContext.require.config).toHaveBeenCalledWith({ + paths: { + vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase + }, + }); + expect(monacoLoader.default).toBe(monacoContext.require); + }); +}); diff --git a/spec/javascripts/repo/services/repo_service_spec.js b/spec/javascripts/repo/services/repo_service_spec.js new file mode 100644 index 00000000000..d74e6a67b1e --- /dev/null +++ b/spec/javascripts/repo/services/repo_service_spec.js @@ -0,0 +1,121 @@ +import axios from 'axios'; +import RepoService from '~/repo/services/repo_service'; + +describe('RepoService', () => { + it('has default json format param', () => { + expect(RepoService.options.params.format).toBe('json'); + }); + + describe('buildParams', () => { + let newParams; + const url = 'url'; + + beforeEach(() => { + newParams = {}; + + spyOn(Object, 'assign').and.returnValue(newParams); + }); + + it('clones params', () => { + const params = RepoService.buildParams(url); + + expect(Object.assign).toHaveBeenCalledWith({}, RepoService.options.params); + + expect(params).toBe(newParams); + }); + + it('sets and returns viewer params to richif urlIsRichBlob is true', () => { + spyOn(RepoService, 'urlIsRichBlob').and.returnValue(true); + + const params = RepoService.buildParams(url); + + expect(params.viewer).toEqual('rich'); + }); + + it('returns params urlIsRichBlob is false', () => { + spyOn(RepoService, 'urlIsRichBlob').and.returnValue(false); + + const params = RepoService.buildParams(url); + + expect(params.viewer).toBeUndefined(); + }); + + it('calls urlIsRichBlob with the objects url prop if no url arg is provided', () => { + spyOn(RepoService, 'urlIsRichBlob'); + RepoService.url = url; + + RepoService.buildParams(); + + expect(RepoService.urlIsRichBlob).toHaveBeenCalledWith(url); + }); + }); + + describe('urlIsRichBlob', () => { + it('returns true for md extension', () => { + const isRichBlob = RepoService.urlIsRichBlob('url.md'); + + expect(isRichBlob).toBeTruthy(); + }); + + it('returns false for js extension', () => { + const isRichBlob = RepoService.urlIsRichBlob('url.js'); + + expect(isRichBlob).toBeFalsy(); + }); + }); + + describe('getContent', () => { + const params = {}; + const url = 'url'; + const requestPromise = Promise.resolve(); + + beforeEach(() => { + spyOn(RepoService, 'buildParams').and.returnValue(params); + spyOn(axios, 'get').and.returnValue(requestPromise); + }); + + it('calls buildParams and axios.get', () => { + const request = RepoService.getContent(url); + + expect(RepoService.buildParams).toHaveBeenCalledWith(url); + expect(axios.get).toHaveBeenCalledWith(url, { + params, + }); + expect(request).toBe(requestPromise); + }); + + it('uses object url prop if no url arg is provided', () => { + RepoService.url = url; + + RepoService.getContent(); + + expect(axios.get).toHaveBeenCalledWith(url, { + params, + }); + }); + }); + + describe('getBase64Content', () => { + const url = 'url'; + const response = { data: 'data' }; + + beforeEach(() => { + spyOn(RepoService, 'bufferToBase64'); + spyOn(axios, 'get').and.returnValue(Promise.resolve(response)); + }); + + it('calls axios.get and bufferToBase64 on completion', (done) => { + const request = RepoService.getBase64Content(url); + + expect(axios.get).toHaveBeenCalledWith(url, { + responseType: 'arraybuffer', + }); + expect(request).toEqual(jasmine.any(Promise)); + + request.then(() => { + expect(RepoService.bufferToBase64).toHaveBeenCalledWith(response.data); + done(); + }).catch(done.fail); + }); + }); +}); |