diff options
Diffstat (limited to 'spec/frontend/vue_shared/components/content_viewer')
5 files changed, 208 insertions, 34 deletions
diff --git a/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js new file mode 100644 index 00000000000..16e7e4dd5cc --- /dev/null +++ b/spec/frontend/vue_shared/components/content_viewer/content_viewer_spec.js @@ -0,0 +1,21 @@ +import { mount } from '@vue/test-utils'; +import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants'; +import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue'; +import '~/behaviors/markdown/render_gfm'; + +describe('ContentViewer', () => { + let wrapper; + + it.each` + path | type | selector | viewer + ${GREEN_BOX_IMAGE_URL} | ${'image'} | ${'img'} | ${'<image-viewer>'} + ${'myfile.md'} | ${'markdown'} | ${'.md-previewer'} | ${'<markdown-viewer>'} + ${'myfile.abc'} | ${undefined} | ${'[download]'} | ${'<download-viewer>'} + `('renders $viewer when file type="$type"', ({ path, type, selector }) => { + wrapper = mount(ContentViewer, { + propsData: { path, fileSize: 1024, type }, + }); + + expect(wrapper.find(selector).element).toExist(); + }); +}); diff --git a/spec/frontend/vue_shared/components/content_viewer/lib/viewer_utils_spec.js b/spec/frontend/vue_shared/components/content_viewer/lib/viewer_utils_spec.js new file mode 100644 index 00000000000..facdaa86f84 --- /dev/null +++ b/spec/frontend/vue_shared/components/content_viewer/lib/viewer_utils_spec.js @@ -0,0 +1,20 @@ +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); + }); +}); diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/download_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/download_viewer_spec.js new file mode 100644 index 00000000000..b83602e7bfc --- /dev/null +++ b/spec/frontend/vue_shared/components/content_viewer/viewers/download_viewer_spec.js @@ -0,0 +1,28 @@ +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'); + }, + ); +}); diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js index ef785b9f0f5..31e843297fa 100644 --- a/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js +++ b/spec/frontend/vue_shared/components/content_viewer/viewers/image_viewer_spec.js @@ -1,45 +1,36 @@ -import { shallowMount } from '@vue/test-utils'; - +import { mount } from '@vue/test-utils'; import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants'; import ImageViewer from '~/vue_shared/components/content_viewer/viewers/image_viewer.vue'; describe('Image Viewer', () => { - const requiredProps = { - path: GREEN_BOX_IMAGE_URL, - renderInfo: true, - }; let wrapper; - let imageInfo; - - function createElement({ props, includeRequired = true } = {}) { - const data = includeRequired ? { ...requiredProps, ...props } : { ...props }; - wrapper = shallowMount(ImageViewer, { - propsData: data, + it('renders image preview', () => { + 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 } }); - - // 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(wrapper.find('img').element).toHaveAttr('src', GREEN_BOX_IMAGE_URL); + }); - 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); + }, + ); }); }); diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js new file mode 100644 index 00000000000..8d3fcdd48d2 --- /dev/null +++ b/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js @@ -0,0 +1,114 @@ +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> {{gl_md_img_1}}', + }); + }); + + 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 and commitSha as a parameters and passes them on to the server', () => { + createComponent({ filePath: 'foo/test.md', commitSha: 'abcdef' }); + + expect(axios.post).toHaveBeenCalledWith( + `${gon.relative_url_root}/testproject/preview_markdown`, + { path: 'foo/test.md', text: '* Test', ref: 'abcdef' }, + expect.any(Object), + ); + }); + + it.each` + imgSrc | imgAlt + ${'data:image/jpeg;base64,AAAAAA+/'} | ${'my image title'} + ${'data:image/jpeg;base64,AAAAAA+/'} | ${'"somebody\'s image" &'} + ${'hack" onclick=alert(0)'} | ${'hack" onclick=alert(0)'} + ${'hack\\" onclick=alert(0)'} | ${'hack\\" onclick=alert(0)'} + ${"hack' onclick=alert(0)"} | ${"hack' onclick=alert(0)"} + ${"hack'><script>alert(0)</script>"} | ${"hack'><script>alert(0)</script>"} + `( + 'transforms template tags with base64 encoded images available locally', + ({ imgSrc, imgAlt }) => { + createComponent({ + images: { + '{{gl_md_img_1}}': { + src: imgSrc, + alt: imgAlt, + title: imgAlt, + }, + }, + }); + + return waitForPromises().then(() => { + const img = wrapper.find('.md-previewer img').element; + + // if the values are the same as the input, it means + // they were escaped correctly + expect(img).toHaveAttr('src', imgSrc); + expect(img).toHaveAttr('alt', imgAlt); + expect(img).toHaveAttr('title', imgAlt); + }); + }, + ); + }); + + 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'); + }); + }); + }); +}); |