summaryrefslogtreecommitdiff
path: root/spec/javascripts/repo
diff options
context:
space:
mode:
Diffstat (limited to 'spec/javascripts/repo')
-rw-r--r--spec/javascripts/repo/components/repo_commit_section_spec.js158
-rw-r--r--spec/javascripts/repo/components/repo_edit_button_spec.js51
-rw-r--r--spec/javascripts/repo/components/repo_editor_spec.js26
-rw-r--r--spec/javascripts/repo/components/repo_file_buttons_spec.js82
-rw-r--r--spec/javascripts/repo/components/repo_file_options_spec.js33
-rw-r--r--spec/javascripts/repo/components/repo_file_spec.js136
-rw-r--r--spec/javascripts/repo/components/repo_loading_file_spec.js79
-rw-r--r--spec/javascripts/repo/components/repo_prev_directory_spec.js43
-rw-r--r--spec/javascripts/repo/components/repo_preview_spec.js23
-rw-r--r--spec/javascripts/repo/components/repo_sidebar_spec.js61
-rw-r--r--spec/javascripts/repo/components/repo_tab_spec.js88
-rw-r--r--spec/javascripts/repo/components/repo_tabs_spec.js64
-rw-r--r--spec/javascripts/repo/monaco_loader_spec.js17
-rw-r--r--spec/javascripts/repo/services/repo_service_spec.js121
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);
+ });
+ });
+});