Commit ed7ea3bf authored by Himanshu Kapoor's avatar Himanshu Kapoor

Improve test coverage of content_viewer module

Add more test cases + use Vue test utils in content_viewer
Vue shared components
parent 48623138
...@@ -26,8 +26,8 @@ const fileExtensionViewers = { ...@@ -26,8 +26,8 @@ const fileExtensionViewers = {
export function viewerInformationForPath(path) { export function viewerInformationForPath(path) {
if (!path) return null; if (!path) return null;
const name = path.split('/').pop(); const name = path.split('/').pop();
const viewerName = const extension = name.includes('.') && name.split('.').pop();
fileNameViewers[name] || fileExtensionViewers[name ? name.split('.').pop() : ''] || ''; const viewerName = fileNameViewers[name] || fileExtensionViewers[extension];
return viewers[viewerName]; return viewers[viewerName];
} }
......
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import { GlSkeletonLoading } from '@gitlab/ui'; import { GlSkeletonLoading } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale'; import { __ } from '~/locale';
...@@ -78,7 +80,7 @@ export default { ...@@ -78,7 +80,7 @@ export default {
this.isLoading = false; this.isLoading = false;
this.$nextTick(() => { this.$nextTick(() => {
$(this.$refs['markdown-preview']).renderGFM(); $(this.$refs.markdownPreview).renderGFM();
}); });
}) })
.catch(() => { .catch(() => {
...@@ -92,7 +94,7 @@ export default { ...@@ -92,7 +94,7 @@ export default {
</script> </script>
<template> <template>
<div ref="markdown-preview" class="md-previewer"> <div ref="markdownPreview" class="md-previewer">
<gl-skeleton-loading v-if="isLoading" /> <gl-skeleton-loading v-if="isLoading" />
<div v-else class="md" v-html="previewContent"></div> <div v-else class="md" v-html="previewContent"></div>
</div> </div>
......
import Vue from 'vue'; import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import mountComponent from 'helpers/vue_mount_component_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants'; import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils'; import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import '~/behaviors/markdown/render_gfm'; import '~/behaviors/markdown/render_gfm';
describe('ContentViewer', () => { describe('ContentViewer', () => {
let vm; let wrapper;
let mock;
it.each`
function createComponent(props) { path | type | selector | viewer
const ContentViewer = Vue.extend(contentViewer); ${GREEN_BOX_IMAGE_URL} | ${'image'} | ${'img'} | ${'<image-viewer>'}
vm = mountComponent(ContentViewer, props); ${'myfile.md'} | ${'markdown'} | ${'.md-previewer'} | ${'<markdown-viewer>'}
} ${'myfile.abc'} | ${undefined} | ${'[download]'} | ${'<download-viewer>'}
`('renders $viewer when file type="$type"', ({ path, type, selector }) => {
afterEach(() => { wrapper = mount(ContentViewer, {
vm.$destroy(); propsData: { path, fileSize: 1024, type },
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`).replyOnce(200, {
body: '<b>testing</b>',
});
createComponent({
path: 'test.md',
content: '* Test',
projectPath: 'testproject',
type: 'markdown',
});
waitForPromises()
.then(() => {
expect(vm.$el.querySelector('.md-previewer').textContent).toContain('testing');
})
.then(done)
.catch(done.fail);
});
it('renders image preview', done => {
createComponent({
path: GREEN_BOX_IMAGE_URL,
fileSize: 1024,
type: 'image',
});
vm.$nextTick()
.then(() => {
expect(vm.$el.querySelector('img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
})
.then(done)
.catch(done.fail);
});
it('renders fallback download control', done => {
createComponent({
path: 'somepath/test.abc',
fileSize: 1024,
});
vm.$nextTick()
.then(() => {
expect(
vm.$el
.querySelector('.file-info')
.textContent.trim()
.replace(/\s+/, ' '),
).toEqual('test.abc (1.00 KiB)');
expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toEqual('Download');
})
.then(done)
.catch(done.fail);
});
it('renders fallback download control for file with a data URL path properly', done => {
createComponent({
path: 'data:application/octet-stream;base64,U0VMRUNUICfEhHNnc2cnIGZyb20gVGFibGVuYW1lOwoK',
filePath: 'somepath/test.abc',
});
vm.$nextTick()
.then(() => {
expect(vm.$el.querySelector('.file-info').textContent.trim()).toEqual('test.abc');
expect(vm.$el.querySelector('.btn.btn-default')).toHaveAttr('download', 'test.abc');
expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toEqual('Download');
})
.then(done)
.catch(done.fail);
});
it('markdown preview receives the file path as a parameter', done => {
mock = new MockAdapter(axios);
jest.spyOn(axios, 'post');
mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).reply(200, {
body: '<b>testing</b>',
});
createComponent({
path: 'test.md',
content: '* Test',
projectPath: 'testproject',
type: 'markdown',
filePath: 'foo/test.md',
}); });
vm.$nextTick() expect(wrapper.find(selector).element).toExist();
.then(() => {
expect(axios.post).toHaveBeenCalledWith(
`${gon.relative_url_root}/testproject/preview_markdown`,
{ path: 'foo/test.md', text: '* Test' },
expect.any(Object),
);
})
.then(done)
.catch(done.fail);
}); });
}); });
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
describe('viewerInformationForPath', () => {
it.each`
path | type
${'p/somefile.jpg'} | ${'image'}
${'p/somefile.jpeg'} | ${'image'}
${'p/somefile.bmp'} | ${'image'}
${'p/somefile.ico'} | ${'image'}
${'p/somefile.png'} | ${'image'}
${'p/somefile.gif'} | ${'image'}
${'p/somefile.md'} | ${'markdown'}
${'p/md'} | ${undefined}
${'p/png'} | ${undefined}
${'p/md.png/a'} | ${undefined}
${'p/some-file.php'} | ${undefined}
`('when path=$path, type=$type', ({ path, type }) => {
expect(viewerInformationForPath(path)?.id).toBe(type);
});
});
import { mount } from '@vue/test-utils';
import DownloadViewer from '~/vue_shared/components/content_viewer/viewers/download_viewer.vue';
describe('DownloadViewer', () => {
let wrapper;
it.each`
path | filePath | fileSize | renderedName | renderedSize
${'somepath/test.abc'} | ${undefined} | ${1024} | ${'test.abc'} | ${'1.00 KiB'}
${'somepath/test.abc'} | ${undefined} | ${null} | ${'test.abc'} | ${''}
${'data:application/unknown;base64,U0VMRUNU'} | ${'somepath/test.abc'} | ${2048} | ${'test.abc'} | ${'2.00 KiB'}
`(
'renders the file name as "$renderedName" and shows size as "$renderedSize"',
({ path, filePath, fileSize, renderedName, renderedSize }) => {
wrapper = mount(DownloadViewer, {
propsData: { path, filePath, fileSize },
});
const renderedFileInfo = wrapper.find('.file-info').text();
expect(renderedFileInfo).toContain(renderedName);
expect(renderedFileInfo).toContain(renderedSize);
expect(wrapper.find('.btn.btn-default').text()).toContain('Download');
expect(wrapper.find('.btn.btn-default').element).toHaveAttr('download', 'test.abc');
},
);
});
import { shallowMount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants'; import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
import ImageViewer from '~/vue_shared/components/content_viewer/viewers/image_viewer.vue'; import ImageViewer from '~/vue_shared/components/content_viewer/viewers/image_viewer.vue';
describe('Image Viewer', () => { describe('Image Viewer', () => {
const requiredProps = {
path: GREEN_BOX_IMAGE_URL,
renderInfo: true,
};
let wrapper; let wrapper;
let imageInfo;
function createElement({ props, includeRequired = true } = {}) {
const data = includeRequired ? { ...requiredProps, ...props } : { ...props };
wrapper = shallowMount(ImageViewer, { it('renders image preview', () => {
propsData: data, wrapper = mount(ImageViewer, {
propsData: { path: GREEN_BOX_IMAGE_URL, fileSize: 1024 },
}); });
imageInfo = wrapper.find('.image-info');
}
describe('file sizes', () => {
it('should show the humanized file size when `renderInfo` is true and there is size info', () => {
createElement({ props: { fileSize: 1024 } });
expect(imageInfo.text()).toContain('1.00 KiB');
});
it('should not show the humanized file size when `renderInfo` is true and there is no size', () => {
const FILESIZE_RE = /\d+(\.\d+)?\s*([KMGTP]i)*B/;
createElement({ props: { fileSize: 0 } }); expect(wrapper.find('img').element).toHaveAttr('src', GREEN_BOX_IMAGE_URL);
});
// It shouldn't show any filesize info
expect(imageInfo.text()).not.toMatch(FILESIZE_RE);
});
it('should not show any image information when `renderInfo` is false', () => {
createElement({ props: { renderInfo: false } });
expect(imageInfo.exists()).toBe(false); describe('file sizes', () => {
}); it.each`
fileSize | renderInfo | elementExists | humanizedFileSize
${1024} | ${true} | ${true} | ${'1.00 KiB'}
${0} | ${true} | ${true} | ${''}
${1024} | ${false} | ${false} | ${undefined}
`(
'shows file size as "$humanizedFileSize", if fileSize=$fileSize and renderInfo=$renderInfo',
({ fileSize, renderInfo, elementExists, humanizedFileSize }) => {
wrapper = mount(ImageViewer, {
propsData: { path: GREEN_BOX_IMAGE_URL, fileSize, renderInfo },
});
const imageInfo = wrapper.find('.image-info');
expect(imageInfo.exists()).toBe(elementExists);
expect(imageInfo.element?.textContent.trim()).toBe(humanizedFileSize);
},
);
}); });
}); });
import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import MockAdapter from 'axios-mock-adapter';
import { mount } from '@vue/test-utils';
import waitForPromises from 'helpers/wait_for_promises';
import MarkdownViewer from '~/vue_shared/components/content_viewer/viewers/markdown_viewer.vue';
describe('MarkdownViewer', () => {
let wrapper;
let mock;
const createComponent = props => {
wrapper = mount(MarkdownViewer, {
propsData: {
...props,
path: 'test.md',
content: '* Test',
projectPath: 'testproject',
type: 'markdown',
},
});
};
beforeEach(() => {
mock = new MockAdapter(axios);
jest.spyOn(axios, 'post');
jest.spyOn($.fn, 'renderGFM');
});
afterEach(() => {
mock.restore();
});
describe('success', () => {
beforeEach(() => {
mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).replyOnce(200, {
body: '<b>testing</b>',
});
});
it('renders an animation container while the markdown is loading', () => {
createComponent();
expect(wrapper.find('.animation-container')).toExist();
});
it('renders markdown preview preview renders and loads rendered markdown from server', () => {
createComponent();
return waitForPromises().then(() => {
expect(wrapper.find('.md-previewer').text()).toContain('testing');
});
});
it('receives the filePath as a parameter and passes it on to the server', () => {
createComponent({ filePath: 'foo/test.md' });
expect(axios.post).toHaveBeenCalledWith(
`${gon.relative_url_root}/testproject/preview_markdown`,
{ path: 'foo/test.md', text: '* Test' },
expect.any(Object),
);
});
});
describe('error', () => {
beforeEach(() => {
mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).replyOnce(500, {
body: 'Internal Server Error',
});
});
it('renders an error message if loading the markdown preview fails', () => {
createComponent();
return waitForPromises().then(() => {
expect(wrapper.find('.md-previewer').text()).toContain('error');
});
});
});
});
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