Commit 7572aaf3 authored by Jacob Schatz's avatar Jacob Schatz

Merge branch 'ide' of gitlab.com:gitlab-org/gitlab-ce into ide

parents 7435c5d4 b3b24458
...@@ -3,7 +3,6 @@ import RepoSidebar from './repo_sidebar.vue'; ...@@ -3,7 +3,6 @@ import RepoSidebar from './repo_sidebar.vue';
import RepoCommitSection from './repo_commit_section.vue'; import RepoCommitSection from './repo_commit_section.vue';
import RepoTabs from './repo_tabs.vue'; import RepoTabs from './repo_tabs.vue';
import RepoFileButtons from './repo_file_buttons.vue'; import RepoFileButtons from './repo_file_buttons.vue';
import RepoBinaryViewer from './repo_binary_viewer.vue';
import RepoPreview from './repo_preview.vue'; import RepoPreview from './repo_preview.vue';
import RepoMixin from '../mixins/repo_mixin'; import RepoMixin from '../mixins/repo_mixin';
import PopupDialog from '../../vue_shared/components/popup_dialog.vue'; import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
...@@ -18,7 +17,6 @@ export default { ...@@ -18,7 +17,6 @@ export default {
'repo-sidebar': RepoSidebar, 'repo-sidebar': RepoSidebar,
'repo-tabs': RepoTabs, 'repo-tabs': RepoTabs,
'repo-file-buttons': RepoFileButtons, 'repo-file-buttons': RepoFileButtons,
'repo-binary-viewer': RepoBinaryViewer,
'repo-editor': MonacoLoaderHelper.repoEditorLoader, 'repo-editor': MonacoLoaderHelper.repoEditorLoader,
'repo-commit-section': RepoCommitSection, 'repo-commit-section': RepoCommitSection,
'popup-dialog': PopupDialog, 'popup-dialog': PopupDialog,
...@@ -50,7 +48,6 @@ export default { ...@@ -50,7 +48,6 @@ export default {
<repo-tabs/> <repo-tabs/>
<component :is="currentBlobView" class="blob-viewer-container"></component> <component :is="currentBlobView" class="blob-viewer-container"></component>
<repo-file-buttons/> <repo-file-buttons/>
<!-- <repo-binary-viewer/> soon™ -->
</div> </div>
<repo-commit-section/> <repo-commit-section/>
<popup-dialog <popup-dialog
......
<script>
import Store from '../stores/repo_store';
import Helper from '../helpers/repo_helper';
const RepoBinaryViewer = {
data: () => Store,
computed: {
pngBlobWithDataURI() {
if (this.binaryTypes.png) {
return `data:image/png;base64,${this.blobRaw}`;
}
return '';
},
svgBlobWithDataURI() {
if (this.binaryTypes.svg) {
return `data:image/svg+xml;utf8,${this.blobRaw}`;
}
return '';
},
},
methods: {
errored() {
Store.binaryLoaded = false;
},
loaded() {
Store.binaryLoaded = true;
},
getBinaryType() {
if (Object.hasOwnProperty.call(this.binaryTypes, this.activeFile.extension)) {
return this.activeFile.extension;
}
return 'unknown';
},
},
watch: {
blobRaw() {
Store.resetBinaryTypes();
if (Helper.isKindaBinary()) {
this.activeFile.raw = false;
// counts as binaryish so we use the binary viewer in this case.
this.binary = true;
}
if (!this.binary) return;
this.binaryTypes[this.getBinaryType()] = true;
},
},
};
export default RepoBinaryViewer;
</script>
<template>
<div id="binary-viewer" v-if="binary && !activeFile.raw">
<img v-show="binaryTypes.png && binaryLoaded" @error="errored" @load="loaded" :src="pngBlobWithDataURI" :alt="activeFile.name"/>
<img v-show="binaryTypes.svg" @error="errored" @load="loaded" :src="svgBlobWithDataURI" :alt="activeFile.name"/>
<div v-if="binaryTypes.md" v-html="activeFile.html"></div>
<div class="binary-unknown" v-if="binaryTypes.unknown">
<span>Binary file. No preview available.</span>
</div>
</div>
</template>
...@@ -41,14 +41,16 @@ const RepoCommitSection = { ...@@ -41,14 +41,16 @@ const RepoCommitSection = {
actions, actions,
}; };
Store.submitCommitsLoading = true; Store.submitCommitsLoading = true;
Service.commitFiles(payload, () => { Service.commitFiles(payload, this.resetCommitState);
Store.submitCommitsLoading = false; },
resetCommitState() {
this.submitCommitsLoading = false;
this.changedFiles = []; this.changedFiles = [];
this.openedFiles = []; this.openedFiles = [];
this.commitMessage = ''; this.commitMessage = '';
this.editMode = false; this.editMode = false;
$('html, body').animate({ scrollTop: 0 }, 'fast'); $('html, body').animate({ scrollTop: 0 }, 'fast');
});
}, },
}, },
}; };
......
...@@ -5,12 +5,12 @@ import monacoLoader from '../monaco_loader'; ...@@ -5,12 +5,12 @@ import monacoLoader from '../monaco_loader';
function repoEditorLoader() { function repoEditorLoader() {
Store.monacoLoading = true; Store.monacoLoading = true;
return new Promise((resolve) => { return new Promise((resolve, reject) => {
monacoLoader(['vs/editor/editor.main'], () => { monacoLoader(['vs/editor/editor.main'], () => {
Store.monaco = monaco; Store.monaco = monaco;
Store.monacoLoading = false; Store.monacoLoading = false;
resolve(RepoEditor); resolve(RepoEditor);
}); }, reject);
}); });
} }
......
/* global Flash */
import axios from 'axios'; import axios from 'axios';
import Store from '../stores/repo_store'; import Store from '../stores/repo_store';
import Api from '../../api'; import Api from '../../api';
......
...@@ -126,12 +126,12 @@ ...@@ -126,12 +126,12 @@
} }
a { a {
@include str-truncated(100px);
color: $black; color: $black;
display: inline-block; display: inline-block;
width: 100px; width: 100px;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
@include str-truncated(100px);
&.close { &.close {
width: auto; width: auto;
......
import $ from 'jquery';
import ScrollHelper from '~/helpers/scroll_helper';
describe('ScrollHelper', () => {
const width = 10;
describe('getScrollWidth', () => {
const parent = jasmine.createSpyObj('parent', ['css', 'appendTo', 'remove']);
const child = jasmine.createSpyObj('child', ['css', 'appendTo', 'get']);
let scrollWidth;
beforeEach(() => {
spyOn($.fn, 'init').and.returnValues(parent, child);
spyOn(jasmine.Fixtures.prototype, 'cleanUp'); // disable jasmine-jquery cleanup, we dont want it but its imported in test_bundle :(
parent.css.and.returnValue(parent);
child.css.and.returnValue(child);
child.get.and.returnValue({
offsetWidth: width,
});
scrollWidth = ScrollHelper.getScrollWidth();
});
it('inserts 2 nested hidden scrollable divs, calls parents outerWidth, removes parent and returns the width', () => {
const initArgs = $.fn.init.calls.allArgs();
expect(initArgs[0][0]).toEqual('<div>');
expect(initArgs[1][0]).toEqual('<div>');
expect(parent.css).toHaveBeenCalledWith({
visibility: 'hidden',
width: 100,
overflow: 'scroll',
});
expect(child.css).toHaveBeenCalledWith({
width: 100,
});
expect(child.appendTo).toHaveBeenCalledWith(parent);
expect(parent.appendTo).toHaveBeenCalledWith('body');
expect(child.get).toHaveBeenCalledWith(0);
expect(parent.remove).toHaveBeenCalled();
expect(scrollWidth).toEqual(100 - width);
});
});
describe('setScrollWidth', () => {
it('calls getScrollWidth and sets data-scroll-width', () => {
spyOn($.fn, 'find').and.callThrough();
spyOn($.fn, 'attr');
spyOn(ScrollHelper, 'getScrollWidth').and.returnValue(width);
ScrollHelper.setScrollWidth();
expect($.fn.find).toHaveBeenCalledWith('body');
expect($.fn.attr).toHaveBeenCalledWith('data-scroll-width', width);
expect(ScrollHelper.getScrollWidth).toHaveBeenCalled();
});
});
});
import Vue from 'vue';
import Store from '~/repo/stores/repo_store';
import repoBinaryViewer from '~/repo/components/repo_binary_viewer.vue';
describe('RepoBinaryViewer', () => {
function createComponent() {
const RepoBinaryViewer = Vue.extend(repoBinaryViewer);
return new RepoBinaryViewer().$mount();
}
function createActiveFile(type, activeFile = {}) {
const file = activeFile;
switch (type) {
case 'svg':
case 'png':
file.name = 'name';
break;
case 'md':
file.html = 'html';
break;
default:
break;
}
return file;
}
function setActiveBinary(type) {
const binaryTypes = {};
binaryTypes[type] = true;
const activeFile = createActiveFile(type);
const uri = 'uri';
Store.binary = true;
Store.binaryTypes = binaryTypes;
Store.activeFile = activeFile;
Store.pngBlobWithDataURI = uri;
return {
activeFile,
uri,
};
}
function assertBinaryImg(img, activeFile, uri) {
expect(img.src).toMatch(`/${uri}`);
expect(img.alt).toEqual(activeFile.name);
}
it('renders an img if its png', () => {
const { activeFile, uri } = setActiveBinary('png');
const vm = createComponent();
const img = vm.$el.querySelector(':scope > img');
assertBinaryImg(img, activeFile, uri);
});
it('renders an img if its svg', () => {
const { activeFile, uri } = setActiveBinary('svg');
const vm = createComponent();
const img = vm.$el.querySelector(':scope > img');
assertBinaryImg(img, activeFile, uri);
});
it('renders an div with content if its markdown', () => {
const { activeFile } = setActiveBinary('md');
const vm = createComponent();
expect(vm.$el.querySelector(':scope > div').innerHTML).toEqual(activeFile.html);
});
it('renders no preview message if its unknown', () => {
setActiveBinary('unknown');
const vm = createComponent();
expect(vm.$el.querySelector('.binary-unknown').textContent).toMatch('Binary file. No preview available.');
});
it('does not render if no binary', () => {
Store.binary = false;
const vm = createComponent();
expect(vm.$el.innerHTML).toBeFalsy();
});
});
...@@ -6,21 +6,25 @@ import Api from '~/api'; ...@@ -6,21 +6,25 @@ import Api from '~/api';
describe('RepoCommitSection', () => { describe('RepoCommitSection', () => {
const branch = 'master'; const branch = 'master';
const projectUrl = 'projectUrl';
const openedFiles = [{ const openedFiles = [{
id: 0, id: 0,
changed: true, changed: true,
url: `${branch}/url0`, url: `/namespace/${projectUrl}/blob/${branch}/dir/file0.ext`,
newContent: 'a', newContent: 'a',
}, { }, {
id: 1, id: 1,
changed: true, changed: true,
url: `${branch}/url1`, url: `/namespace/${projectUrl}/blob/${branch}/dir/file1.ext`,
newContent: 'b', newContent: 'b',
}, { }, {
id: 2, id: 2,
url: `/namespace/${projectUrl}/blob/${branch}/dir/file2.ext`,
changed: false, changed: false,
}]; }];
RepoStore.projectUrl = projectUrl;
function createComponent() { function createComponent() {
const RepoCommitSection = Vue.extend(repoCommitSection); const RepoCommitSection = Vue.extend(repoCommitSection);
...@@ -129,4 +133,26 @@ describe('RepoCommitSection', () => { ...@@ -129,4 +133,26 @@ describe('RepoCommitSection', () => {
}); });
}); });
}); });
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);
});
});
});
}); });
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;
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', () => {
});
});
});
});
...@@ -30,7 +30,6 @@ describe('RepoFileButtons', () => { ...@@ -30,7 +30,6 @@ describe('RepoFileButtons', () => {
const history = vm.$el.querySelector('.history'); const history = vm.$el.querySelector('.history');
expect(vm.$el.id).toEqual('repo-file-buttons'); expect(vm.$el.id).toEqual('repo-file-buttons');
expect(vm.$el.style.borderBottom).toEqual('1px solid rgb(31, 120, 209)');
expect(raw.href).toMatch(`/${activeFile.raw_path}`); expect(raw.href).toMatch(`/${activeFile.raw_path}`);
expect(raw.textContent).toEqual('Raw'); expect(raw.textContent).toEqual('Raw');
expect(blame.href).toMatch(`/${activeFile.blame_path}`); expect(blame.href).toMatch(`/${activeFile.blame_path}`);
...@@ -41,20 +40,6 @@ describe('RepoFileButtons', () => { ...@@ -41,20 +40,6 @@ describe('RepoFileButtons', () => {
expect(vm.$el.querySelector('.preview').textContent).toEqual(activeFileLabel); expect(vm.$el.querySelector('.preview').textContent).toEqual(activeFileLabel);
}); });
it('renders a white border if not editMode', () => {
const activeFile = {
extension: 'md',
url: 'url',
};
RepoStore.openedFiles = new Array(1);
RepoStore.activeFile = activeFile;
RepoStore.editMode = false;
const vm = createComponent();
expect(vm.$el.style.borderBottom).toEqual('1px solid rgb(240, 240, 240)');
});
it('triggers rawPreviewToggle on preview click', () => { it('triggers rawPreviewToggle on preview click', () => {
const activeFile = { const activeFile = {
extension: 'md', extension: 'md',
......
...@@ -119,4 +119,18 @@ describe('RepoFile', () => { ...@@ -119,4 +119,18 @@ describe('RepoFile', () => {
expect(vm.linkClicked).toHaveBeenCalledWith(file); 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);
});
});
});
}); });
...@@ -26,4 +26,18 @@ describe('RepoPrevDirectory', () => { ...@@ -26,4 +26,18 @@ describe('RepoPrevDirectory', () => {
expect(vm.linkClicked).toHaveBeenCalledWith(prevUrl); 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);
});
});
});
}); });
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).toEqual(activeFile.html);
});
});
...@@ -2,7 +2,7 @@ import Vue from 'vue'; ...@@ -2,7 +2,7 @@ import Vue from 'vue';
import RepoStore from '~/repo/stores/repo_store'; import RepoStore from '~/repo/stores/repo_store';
import repoSidebar from '~/repo/components/repo_sidebar.vue'; import repoSidebar from '~/repo/components/repo_sidebar.vue';
describe('RepoSidebar', () => { fdescribe('RepoSidebar', () => {
function createComponent() { function createComponent() {
const RepoSidebar = Vue.extend(repoSidebar); const RepoSidebar = Vue.extend(repoSidebar);
......
...@@ -64,4 +64,25 @@ describe('RepoTab', () => { ...@@ -64,4 +64,25 @@ describe('RepoTab', () => {
expect(vm.$el.querySelector('.close .fa-circle')).toBeTruthy(); 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);
});
});
});
}); });
...@@ -47,4 +47,18 @@ describe('RepoTabs', () => { ...@@ -47,4 +47,18 @@ describe('RepoTabs', () => {
expect(vm.$el.classList.contains('overflown')).toBeFalsy(); 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);
});
});
});
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment