Web IDE markdown preview
This commit is contained in:
parent
21488c7422
commit
c88cc0c0ec
17 changed files with 466 additions and 104 deletions
|
@ -3,7 +3,6 @@ import { mapState, mapGetters } from 'vuex';
|
|||
import ideSidebar from './ide_side_bar.vue';
|
||||
import ideContextbar from './ide_context_bar.vue';
|
||||
import repoTabs from './repo_tabs.vue';
|
||||
import repoFileButtons from './repo_file_buttons.vue';
|
||||
import ideStatusBar from './ide_status_bar.vue';
|
||||
import repoEditor from './repo_editor.vue';
|
||||
|
||||
|
@ -12,7 +11,6 @@ export default {
|
|||
ideSidebar,
|
||||
ideContextbar,
|
||||
repoTabs,
|
||||
repoFileButtons,
|
||||
ideStatusBar,
|
||||
repoEditor,
|
||||
},
|
||||
|
@ -70,9 +68,6 @@ export default {
|
|||
class="multi-file-edit-pane-content"
|
||||
:file="activeFile"
|
||||
/>
|
||||
<repo-file-buttons
|
||||
:file="activeFile"
|
||||
/>
|
||||
<ide-status-bar
|
||||
:file="activeFile"
|
||||
/>
|
||||
|
|
83
app/assets/javascripts/ide/components/ide_file_buttons.vue
Normal file
83
app/assets/javascripts/ide/components/ide_file_buttons.vue
Normal file
|
@ -0,0 +1,83 @@
|
|||
<script>
|
||||
import { __ } from '~/locale';
|
||||
import tooltip from '~/vue_shared/directives/tooltip';
|
||||
import Icon from '~/vue_shared/components/icon.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Icon,
|
||||
},
|
||||
directives: {
|
||||
tooltip,
|
||||
},
|
||||
props: {
|
||||
file: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showButtons() {
|
||||
return (
|
||||
this.file.rawPath || this.file.blamePath || this.file.commitsPath || this.file.permalink
|
||||
);
|
||||
},
|
||||
rawDownloadButtonLabel() {
|
||||
return this.file.binary ? __('Download') : __('Raw');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="showButtons"
|
||||
class="pull-right ide-btn-group"
|
||||
>
|
||||
<a
|
||||
v-tooltip
|
||||
:href="file.blamePath"
|
||||
:title="__('Blame')"
|
||||
class="btn btn-xs btn-transparent blame"
|
||||
>
|
||||
<icon
|
||||
name="blame"
|
||||
:size="16"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
v-tooltip
|
||||
:href="file.commitsPath"
|
||||
:title="__('History')"
|
||||
class="btn btn-xs btn-transparent history"
|
||||
>
|
||||
<icon
|
||||
name="history"
|
||||
:size="16"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
v-tooltip
|
||||
:href="file.permalink"
|
||||
:title="__('Permalink')"
|
||||
class="btn btn-xs btn-transparent permalink"
|
||||
>
|
||||
<icon
|
||||
name="link"
|
||||
:size="16"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
v-tooltip
|
||||
:href="file.rawPath"
|
||||
target="_blank"
|
||||
class="btn btn-xs btn-transparent prepend-left-10 raw"
|
||||
rel="noopener noreferrer"
|
||||
:title="rawDownloadButtonLabel">
|
||||
<icon
|
||||
name="download"
|
||||
:size="16"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
|
@ -2,10 +2,16 @@
|
|||
/* global monaco */
|
||||
import { mapState, mapGetters, mapActions } from 'vuex';
|
||||
import flash from '~/flash';
|
||||
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
|
||||
import monacoLoader from '../monaco_loader';
|
||||
import Editor from '../lib/editor';
|
||||
import IdeFileButtons from './ide_file_buttons.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContentViewer,
|
||||
IdeFileButtons,
|
||||
},
|
||||
props: {
|
||||
file: {
|
||||
type: Object,
|
||||
|
@ -18,6 +24,16 @@ export default {
|
|||
shouldHideEditor() {
|
||||
return this.file && this.file.binary && !this.file.raw;
|
||||
},
|
||||
editTabCSS() {
|
||||
return {
|
||||
active: this.file.viewMode === 'edit',
|
||||
};
|
||||
},
|
||||
previewTabCSS() {
|
||||
return {
|
||||
active: this.file.viewMode === 'preview',
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
file(oldVal, newVal) {
|
||||
|
@ -56,6 +72,7 @@ export default {
|
|||
'changeFileContent',
|
||||
'setFileLanguage',
|
||||
'setEditorPosition',
|
||||
'setFileViewMode',
|
||||
'setFileEOL',
|
||||
'updateViewer',
|
||||
'updateDelayViewerUpdated',
|
||||
|
@ -153,15 +170,47 @@ export default {
|
|||
class="blob-viewer-container blob-editor-container"
|
||||
>
|
||||
<div
|
||||
v-if="shouldHideEditor"
|
||||
v-html="file.html"
|
||||
>
|
||||
class="ide-mode-tabs clearfix"
|
||||
v-if="!shouldHideEditor">
|
||||
<ul class="nav-links pull-left">
|
||||
<li :class="editTabCSS">
|
||||
<a
|
||||
href="javascript:void(0);"
|
||||
role="button"
|
||||
@click.prevent="setFileViewMode({ file, viewMode: 'edit' })">
|
||||
<template v-if="viewer === 'editor'">
|
||||
{{ __('Edit') }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ __('Review') }}
|
||||
</template>
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
v-if="file.previewMode"
|
||||
:class="previewTabCSS">
|
||||
<a
|
||||
href="javascript:void(0);"
|
||||
role="button"
|
||||
@click.prevent="setFileViewMode({ file, viewMode:'preview' })">
|
||||
{{ file.previewMode.previewTitle }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ide-file-buttons
|
||||
:file="file"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-show="!shouldHideEditor"
|
||||
v-show="!shouldHideEditor && file.viewMode === 'edit'"
|
||||
ref="editor"
|
||||
class="multi-file-editor-holder"
|
||||
>
|
||||
</div>
|
||||
<content-viewer
|
||||
v-if="!shouldHideEditor && file.viewMode === 'preview'"
|
||||
:content="file.content || file.raw"
|
||||
:path="file.path"
|
||||
:project-path="file.projectId"/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
<script>
|
||||
export default {
|
||||
props: {
|
||||
file: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showButtons() {
|
||||
return this.file.rawPath ||
|
||||
this.file.blamePath ||
|
||||
this.file.commitsPath ||
|
||||
this.file.permalink;
|
||||
},
|
||||
rawDownloadButtonLabel() {
|
||||
return this.file.binary ? 'Download' : 'Raw';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="showButtons"
|
||||
class="multi-file-editor-btn-group"
|
||||
>
|
||||
<a
|
||||
:href="file.rawPath"
|
||||
target="_blank"
|
||||
class="btn btn-default btn-sm raw"
|
||||
rel="noopener noreferrer">
|
||||
{{ rawDownloadButtonLabel }}
|
||||
</a>
|
||||
|
||||
<div
|
||||
class="btn-group"
|
||||
role="group"
|
||||
aria-label="File actions"
|
||||
>
|
||||
<a
|
||||
:href="file.blamePath"
|
||||
class="btn btn-default btn-sm blame"
|
||||
>
|
||||
Blame
|
||||
</a>
|
||||
<a
|
||||
:href="file.commitsPath"
|
||||
class="btn btn-default btn-sm history"
|
||||
>
|
||||
History
|
||||
</a>
|
||||
<a
|
||||
:href="file.permalink"
|
||||
class="btn btn-default btn-sm permalink"
|
||||
>
|
||||
Permalink
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -149,6 +149,10 @@ export const setEditorPosition = ({ getters, commit }, { editorRow, editorColumn
|
|||
}
|
||||
};
|
||||
|
||||
export const setFileViewMode = ({ state, commit }, { file, viewMode }) => {
|
||||
commit(types.SET_FILE_VIEWMODE, { file, viewMode });
|
||||
};
|
||||
|
||||
export const discardFileChanges = ({ state, commit }, path) => {
|
||||
const file = state.entries[path];
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ export const SET_FILE_BASE_RAW_DATA = 'SET_FILE_BASE_RAW_DATA';
|
|||
export const UPDATE_FILE_CONTENT = 'UPDATE_FILE_CONTENT';
|
||||
export const SET_FILE_LANGUAGE = 'SET_FILE_LANGUAGE';
|
||||
export const SET_FILE_POSITION = 'SET_FILE_POSITION';
|
||||
export const SET_FILE_VIEWMODE = 'SET_FILE_VIEWMODE';
|
||||
export const SET_FILE_EOL = 'SET_FILE_EOL';
|
||||
export const DISCARD_FILE_CHANGES = 'DISCARD_FILE_CHANGES';
|
||||
export const ADD_FILE_TO_CHANGED = 'ADD_FILE_TO_CHANGED';
|
||||
|
|
|
@ -42,6 +42,7 @@ export default {
|
|||
renderError: data.render_error,
|
||||
raw: null,
|
||||
baseRaw: null,
|
||||
html: data.html,
|
||||
});
|
||||
},
|
||||
[types.SET_FILE_RAW_DATA](state, { file, raw }) {
|
||||
|
@ -83,6 +84,11 @@ export default {
|
|||
mrChange,
|
||||
});
|
||||
},
|
||||
[types.SET_FILE_VIEWMODE](state, { file, viewMode }) {
|
||||
Object.assign(state.entries[file.path], {
|
||||
viewMode,
|
||||
});
|
||||
},
|
||||
[types.DISCARD_FILE_CHANGES](state, path) {
|
||||
Object.assign(state.entries[path], {
|
||||
content: state.entries[path].raw,
|
||||
|
|
|
@ -38,6 +38,8 @@ export const dataStructure = () => ({
|
|||
editorColumn: 1,
|
||||
fileLanguage: '',
|
||||
eol: '',
|
||||
viewMode: 'edit',
|
||||
previewMode: null,
|
||||
});
|
||||
|
||||
export const decorateData = entity => {
|
||||
|
@ -57,8 +59,9 @@ export const decorateData = entity => {
|
|||
changed = false,
|
||||
parentTreeUrl = '',
|
||||
base64 = false,
|
||||
|
||||
previewMode,
|
||||
file_lock,
|
||||
html,
|
||||
} = entity;
|
||||
|
||||
return {
|
||||
|
@ -79,8 +82,9 @@ export const decorateData = entity => {
|
|||
renderError,
|
||||
content,
|
||||
base64,
|
||||
|
||||
previewMode,
|
||||
file_lock,
|
||||
html,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
|
||||
import { decorateData, sortTree } from '../utils';
|
||||
|
||||
self.addEventListener('message', e => {
|
||||
const {
|
||||
data,
|
||||
projectId,
|
||||
branchId,
|
||||
tempFile = false,
|
||||
content = '',
|
||||
base64 = false,
|
||||
} = e.data;
|
||||
const { data, projectId, branchId, tempFile = false, content = '', base64 = false } = e.data;
|
||||
|
||||
const treeList = [];
|
||||
let file;
|
||||
|
@ -19,9 +13,7 @@ self.addEventListener('message', e => {
|
|||
if (pathSplit.length > 0) {
|
||||
pathSplit.reduce((pathAcc, folderName) => {
|
||||
const parentFolder = acc[pathAcc[pathAcc.length - 1]];
|
||||
const folderPath = `${
|
||||
parentFolder ? `${parentFolder.path}/` : ''
|
||||
}${folderName}`;
|
||||
const folderPath = `${parentFolder ? `${parentFolder.path}/` : ''}${folderName}`;
|
||||
const foundEntry = acc[folderPath];
|
||||
|
||||
if (!foundEntry) {
|
||||
|
@ -33,9 +25,7 @@ self.addEventListener('message', e => {
|
|||
path: folderPath,
|
||||
url: `/${projectId}/tree/${branchId}/${folderPath}/`,
|
||||
type: 'tree',
|
||||
parentTreeUrl: parentFolder
|
||||
? parentFolder.url
|
||||
: `/${projectId}/tree/${branchId}/`,
|
||||
parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`,
|
||||
tempFile,
|
||||
changed: tempFile,
|
||||
opened: tempFile,
|
||||
|
@ -70,13 +60,12 @@ self.addEventListener('message', e => {
|
|||
path,
|
||||
url: `/${projectId}/blob/${branchId}/${path}`,
|
||||
type: 'blob',
|
||||
parentTreeUrl: fileFolder
|
||||
? fileFolder.url
|
||||
: `/${projectId}/blob/${branchId}`,
|
||||
parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`,
|
||||
tempFile,
|
||||
changed: tempFile,
|
||||
content,
|
||||
base64,
|
||||
previewMode: viewerInformationForPath(blobName),
|
||||
});
|
||||
|
||||
Object.assign(acc, {
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<script>
|
||||
import { viewerInformationForPath } from './lib/viewer_utils';
|
||||
import MarkdownViewer from './viewers/markdown_viewer.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
viewer() {
|
||||
const previewInfo = viewerInformationForPath(this.path);
|
||||
switch (previewInfo.id) {
|
||||
case 'markdown':
|
||||
return MarkdownViewer;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="preview-container">
|
||||
<component
|
||||
:is="viewer"
|
||||
:project-path="projectPath"
|
||||
:content="content"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1,23 @@
|
|||
const viewers = {
|
||||
markdown: {
|
||||
id: 'markdown',
|
||||
previewTitle: 'Preview Markdown',
|
||||
},
|
||||
};
|
||||
|
||||
const fileNameViewers = {};
|
||||
const fileExtensionViewers = {
|
||||
md: 'markdown',
|
||||
markdown: 'markdown',
|
||||
};
|
||||
|
||||
export function viewerInformationForPath(path) {
|
||||
if (!path) return null;
|
||||
const name = path.split('/').pop();
|
||||
const viewerName =
|
||||
fileNameViewers[name] || fileExtensionViewers[name ? name.split('.').pop() : ''] || '';
|
||||
|
||||
return viewers[viewerName];
|
||||
}
|
||||
|
||||
export default viewers;
|
|
@ -0,0 +1,90 @@
|
|||
<script>
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { __ } from '~/locale';
|
||||
import $ from 'jquery';
|
||||
import SkeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
|
||||
|
||||
const CancelToken = axios.CancelToken;
|
||||
let axiosSource;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SkeletonLoadingContainer,
|
||||
},
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
previewContent: null,
|
||||
isLoading: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
content() {
|
||||
this.previewContent = null;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
axiosSource = CancelToken.source();
|
||||
this.fetchMarkdownPreview();
|
||||
},
|
||||
updated() {
|
||||
this.fetchMarkdownPreview();
|
||||
},
|
||||
destroyed() {
|
||||
if (this.isLoading) axiosSource.cancel('Cancelling Preview');
|
||||
},
|
||||
methods: {
|
||||
fetchMarkdownPreview() {
|
||||
if (this.content && this.previewContent === null) {
|
||||
this.isLoading = true;
|
||||
const postBody = {
|
||||
text: this.content,
|
||||
};
|
||||
const postOptions = {
|
||||
cancelToken: axiosSource.token,
|
||||
};
|
||||
|
||||
axios
|
||||
.post(
|
||||
`${gon.relative_url_root}/${this.projectPath}/preview_markdown`,
|
||||
postBody,
|
||||
postOptions,
|
||||
)
|
||||
.then(({ data }) => {
|
||||
this.previewContent = data.body;
|
||||
this.isLoading = false;
|
||||
|
||||
this.$nextTick(() => {
|
||||
$(this.$refs['markdown-preview']).renderGFM();
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
this.previewContent = __('An error occurred while fetching markdown preview');
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
ref="markdown-preview"
|
||||
class="md md-previewer">
|
||||
<skeleton-loading-container v-if="isLoading" />
|
||||
<div
|
||||
v-else
|
||||
v-html="previewContent">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -308,14 +308,34 @@
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.multi-file-editor-btn-group {
|
||||
padding: $gl-bar-padding $gl-padding;
|
||||
border-top: 1px solid $white-dark;
|
||||
.preview-container {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.md-previewer {
|
||||
padding: $gl-padding;
|
||||
}
|
||||
}
|
||||
|
||||
.ide-mode-tabs {
|
||||
border-bottom: 1px solid $white-dark;
|
||||
background: $white-light;
|
||||
|
||||
.nav-links {
|
||||
border-bottom: 0;
|
||||
|
||||
li a {
|
||||
padding: $gl-padding-8 $gl-padding;
|
||||
line-height: $gl-btn-line-height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ide-btn-group {
|
||||
padding: $gl-padding-4 $gl-vert-padding;
|
||||
}
|
||||
|
||||
.ide-status-bar {
|
||||
border-top: 1px solid $white-dark;
|
||||
padding: $gl-bar-padding $gl-padding;
|
||||
background: $white-light;
|
||||
display: flex;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import repoFileButtons from '~/ide/components/repo_file_buttons.vue';
|
||||
import repoFileButtons from '~/ide/components/ide_file_buttons.vue';
|
||||
import createVueComponent from '../../helpers/vue_mount_component_helper';
|
||||
import { file } from '../helpers';
|
||||
|
||||
|
@ -23,7 +23,7 @@ describe('RepoFileButtons', () => {
|
|||
vm.$destroy();
|
||||
});
|
||||
|
||||
it('renders Raw, Blame, History, Permalink and Preview toggle', done => {
|
||||
it('renders Raw, Blame, History and Permalink', done => {
|
||||
vm = createComponent();
|
||||
|
||||
vm.$nextTick(() => {
|
||||
|
@ -32,16 +32,30 @@ describe('RepoFileButtons', () => {
|
|||
const history = vm.$el.querySelector('.history');
|
||||
|
||||
expect(raw.href).toMatch(`/${activeFile.rawPath}`);
|
||||
expect(raw.textContent.trim()).toEqual('Raw');
|
||||
expect(raw.getAttribute('data-original-title')).toEqual('Raw');
|
||||
expect(blame.href).toMatch(`/${activeFile.blamePath}`);
|
||||
expect(blame.textContent.trim()).toEqual('Blame');
|
||||
expect(blame.getAttribute('data-original-title')).toEqual('Blame');
|
||||
expect(history.href).toMatch(`/${activeFile.commitsPath}`);
|
||||
expect(history.textContent.trim()).toEqual('History');
|
||||
expect(vm.$el.querySelector('.permalink').textContent.trim()).toEqual(
|
||||
expect(history.getAttribute('data-original-title')).toEqual('History');
|
||||
expect(vm.$el.querySelector('.permalink').getAttribute('data-original-title')).toEqual(
|
||||
'Permalink',
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders Download', done => {
|
||||
activeFile.binary = true;
|
||||
vm = createComponent();
|
||||
|
||||
vm.$nextTick(() => {
|
||||
const raw = vm.$el.querySelector('.raw');
|
||||
|
||||
expect(raw.href).toMatch(`/${activeFile.rawPath}`);
|
||||
expect(raw.getAttribute('data-original-title')).toEqual('Download');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -19,7 +19,6 @@ describe('RepoEditor', () => {
|
|||
|
||||
f.active = true;
|
||||
f.tempFile = true;
|
||||
f.html = 'testing';
|
||||
vm.$store.state.openFiles.push(f);
|
||||
vm.$store.state.entries[f.path] = f;
|
||||
vm.monaco = true;
|
||||
|
@ -47,6 +46,61 @@ describe('RepoEditor', () => {
|
|||
});
|
||||
});
|
||||
|
||||
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', () => {
|
||||
beforeEach(done => {
|
||||
vm.file.previewMode = {
|
||||
id: 'markdown',
|
||||
previewTitle: 'Preview Markdown',
|
||||
};
|
||||
vm.$store.state.viewer = 'diff';
|
||||
|
||||
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('Review');
|
||||
expect(tabs[1].textContent.trim()).toBe('Preview Markdown');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when open file is binary and not raw', () => {
|
||||
beforeEach(done => {
|
||||
vm.file.binary = true;
|
||||
|
@ -57,10 +111,6 @@ describe('RepoEditor', () => {
|
|||
it('does not render the IDE', () => {
|
||||
expect(vm.shouldHideEditor).toBeTruthy();
|
||||
});
|
||||
|
||||
it('shows activeFile html', () => {
|
||||
expect(vm.$el.textContent).toContain('testing');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createEditorInstance', () => {
|
||||
|
|
|
@ -194,6 +194,17 @@ describe('IDE store file mutations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('SET_FILE_VIEWMODE', () => {
|
||||
it('updates file view mode', () => {
|
||||
mutations.SET_FILE_VIEWMODE(localState, {
|
||||
file: localFile,
|
||||
viewMode: 'preview',
|
||||
});
|
||||
|
||||
expect(localFile.viewMode).toBe('preview');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ADD_PENDING_TAB', () => {
|
||||
beforeEach(() => {
|
||||
const f = {
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import Vue from 'vue';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
|
||||
import mountComponent from 'spec/helpers/vue_mount_component_helper';
|
||||
|
||||
describe('ContentViewer', () => {
|
||||
let vm;
|
||||
let mock;
|
||||
|
||||
function createComponent(props) {
|
||||
const ContentViewer = Vue.extend(contentViewer);
|
||||
vm = mountComponent(ContentViewer, props);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
if (mock) mock.restore();
|
||||
});
|
||||
|
||||
it('markdown preview renders + loads rendered markdown from server', done => {
|
||||
mock = new MockAdapter(axios);
|
||||
mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).reply(200, {
|
||||
body: '<b>testing</b>',
|
||||
});
|
||||
|
||||
createComponent({
|
||||
path: 'test.md',
|
||||
content: '* Test',
|
||||
projectPath: 'testproject',
|
||||
});
|
||||
|
||||
const previewContainer = vm.$el.querySelector('.md-previewer');
|
||||
|
||||
setTimeout(() => {
|
||||
expect(previewContainer.textContent).toContain('testing');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue