diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-14 18:08:45 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-14 18:08:45 +0000 |
commit | 26a50872e9da9509c52c70f74dc21698fec906db (patch) | |
tree | b1bd36bd72e701e346ef880fc7a905f6186525e7 /spec | |
parent | b3a736ed88a1db0391cd9881e70b987bab7d89d1 (diff) | |
download | gitlab-ce-26a50872e9da9509c52c70f74dc21698fec906db.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec')
11 files changed, 453 insertions, 34 deletions
diff --git a/spec/controllers/concerns/metrics_dashboard_spec.rb b/spec/controllers/concerns/metrics_dashboard_spec.rb index 466021d6ecd..4e42171e3d3 100644 --- a/spec/controllers/concerns/metrics_dashboard_spec.rb +++ b/spec/controllers/concerns/metrics_dashboard_spec.rb @@ -45,6 +45,7 @@ describe MetricsDashboard do it 'returns the specified dashboard' do expect(json_response['dashboard']['dashboard']).to eq('Environment metrics') expect(json_response).not_to have_key('all_dashboards') + expect(json_response).not_to have_key('metrics_data') end context 'when the params are in an alternate format' do @@ -53,6 +54,25 @@ describe MetricsDashboard do it 'returns the specified dashboard' do expect(json_response['dashboard']['dashboard']).to eq('Environment metrics') expect(json_response).not_to have_key('all_dashboards') + expect(json_response).not_to have_key('metrics_data') + end + end + + context 'when environment for dashboard is available' do + let(:params) { { environment: environment } } + + before do + allow(controller).to receive(:project).and_return(project) + allow(controller).to receive(:environment).and_return(environment) + allow(controller) + .to receive(:metrics_dashboard_params) + .and_return(params) + end + + it 'returns the specified dashboard' do + expect(json_response['dashboard']['dashboard']).to eq('Environment metrics') + expect(json_response).not_to have_key('all_dashboards') + expect(json_response).to have_key('metrics_data') end end diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 7f0f8e36564..6c63b220322 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -489,7 +489,7 @@ describe Projects::EnvironmentsController do end shared_examples_for '200 response' do - let(:expected_keys) { %w(dashboard status) } + let(:expected_keys) { %w(dashboard status metrics_data) } it_behaves_like 'correctly formatted response', :ok end diff --git a/spec/frontend/blob/components/blob_content_error_spec.js b/spec/frontend/blob/components/blob_content_error_spec.js new file mode 100644 index 00000000000..58a9ee761df --- /dev/null +++ b/spec/frontend/blob/components/blob_content_error_spec.js @@ -0,0 +1,27 @@ +import { shallowMount } from '@vue/test-utils'; +import BlobContentError from '~/blob/components/blob_content_error.vue'; + +describe('Blob Content Error component', () => { + let wrapper; + const viewerError = '<h1 id="error">Foo Error</h1>'; + + function createComponent() { + wrapper = shallowMount(BlobContentError, { + propsData: { + viewerError, + }, + }); + } + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders the passed error without transformations', () => { + expect(wrapper.html()).toContain(viewerError); + }); +}); diff --git a/spec/frontend/blob/components/blob_content_spec.js b/spec/frontend/blob/components/blob_content_spec.js new file mode 100644 index 00000000000..6a130c9c43d --- /dev/null +++ b/spec/frontend/blob/components/blob_content_spec.js @@ -0,0 +1,70 @@ +import { shallowMount } from '@vue/test-utils'; +import BlobContent from '~/blob/components/blob_content.vue'; +import BlobContentError from '~/blob/components/blob_content_error.vue'; +import { + RichViewerMock, + SimpleViewerMock, + RichBlobContentMock, + SimpleBlobContentMock, +} from './mock_data'; +import { GlLoadingIcon } from '@gitlab/ui'; +import { RichViewer, SimpleViewer } from '~/vue_shared/components/blob_viewers'; + +describe('Blob Content component', () => { + let wrapper; + + function createComponent(propsData = {}, activeViewer = SimpleViewerMock) { + wrapper = shallowMount(BlobContent, { + propsData: { + loading: false, + activeViewer, + ...propsData, + }, + }); + } + + afterEach(() => { + wrapper.destroy(); + }); + + describe('rendering', () => { + it('renders loader if `loading: true`', () => { + createComponent({ loading: true }); + expect(wrapper.contains(GlLoadingIcon)).toBe(true); + expect(wrapper.contains(BlobContentError)).toBe(false); + expect(wrapper.contains(RichViewer)).toBe(false); + expect(wrapper.contains(SimpleViewer)).toBe(false); + }); + + it('renders error if there is any in the viewer', () => { + const renderError = 'Oops'; + const viewer = Object.assign({}, SimpleViewerMock, { renderError }); + createComponent({}, viewer); + expect(wrapper.contains(GlLoadingIcon)).toBe(false); + expect(wrapper.contains(BlobContentError)).toBe(true); + expect(wrapper.contains(RichViewer)).toBe(false); + expect(wrapper.contains(SimpleViewer)).toBe(false); + }); + + it.each` + type | mock | viewer + ${'simple'} | ${SimpleViewerMock} | ${SimpleViewer} + ${'rich'} | ${RichViewerMock} | ${RichViewer} + `( + 'renders $type viewer when activeViewer is $type and no loading or error detected', + ({ mock, viewer }) => { + createComponent({}, mock); + expect(wrapper.contains(viewer)).toBe(true); + }, + ); + + it.each` + content | mock | viewer + ${SimpleBlobContentMock.plainData} | ${SimpleViewerMock} | ${SimpleViewer} + ${RichBlobContentMock.richData} | ${RichViewerMock} | ${RichViewer} + `('renders correct content that is passed to the component', ({ content, mock, viewer }) => { + createComponent({ content }, mock); + expect(wrapper.find(viewer).html()).toContain(content); + }); + }); +}); diff --git a/spec/frontend/blob/components/blob_header_default_actions_spec.js b/spec/frontend/blob/components/blob_header_default_actions_spec.js index 5da0d40ab14..39d627e71c5 100644 --- a/spec/frontend/blob/components/blob_header_default_actions_spec.js +++ b/spec/frontend/blob/components/blob_header_default_actions_spec.js @@ -67,13 +67,4 @@ describe('Blob Header Default Actions', () => { expect(buttons.at(0).attributes('disabled')).toBeTruthy(); }); }); - - describe('functionally', () => { - it('emits an event when a Copy Contents button is clicked', () => { - jest.spyOn(wrapper.vm, '$emit'); - buttons.at(0).vm.$emit('click'); - - expect(wrapper.vm.$emit).toHaveBeenCalledWith('copy'); - }); - }); }); diff --git a/spec/frontend/blob/components/mock_data.js b/spec/frontend/blob/components/mock_data.js index 4f7b297aba0..bfcca14324f 100644 --- a/spec/frontend/blob/components/mock_data.js +++ b/spec/frontend/blob/components/mock_data.js @@ -1,29 +1,43 @@ +import { SIMPLE_BLOB_VIEWER, RICH_BLOB_VIEWER } from '~/blob/components/constants'; + +export const SimpleViewerMock = { + collapsed: false, + loadingPartialName: 'loading', + renderError: null, + tooLarge: false, + type: SIMPLE_BLOB_VIEWER, + fileType: 'text', +}; + +export const RichViewerMock = { + collapsed: false, + loadingPartialName: 'loading', + renderError: null, + tooLarge: false, + type: RICH_BLOB_VIEWER, + fileType: 'markdown', +}; + export const Blob = { binary: false, - highlightedData: - '<h1 data-sourcepos="1:1-1:19" dir="auto">\n<a id="user-content-this-one-is-dummy" class="anchor" href="#this-one-is-dummy" aria-hidden="true"></a>This one is dummy</h1>\n<h2 data-sourcepos="3:1-3:21" dir="auto">\n<a id="user-content-and-has-sub-header" class="anchor" href="#and-has-sub-header" aria-hidden="true"></a>And has sub-header</h2>\n<p data-sourcepos="5:1-5:27" dir="auto">Even some stupid text here</p>', name: 'dummy.md', path: 'dummy.md', rawPath: '/flightjs/flight/snippets/51/raw', size: 75, simpleViewer: { - collapsed: false, - fileType: 'text', - loadAsync: true, - loadingPartialName: 'loading', - renderError: null, - tooLarge: false, - type: 'simple', + ...SimpleViewerMock, }, richViewer: { - collapsed: false, - fileType: 'markup', - loadAsync: true, - loadingPartialName: 'loading', - renderError: null, - tooLarge: false, - type: 'rich', + ...RichViewerMock, }, }; +export const RichBlobContentMock = { + richData: '<h1>Rich</h1>', +}; + +export const SimpleBlobContentMock = { + plainData: 'Plain', +}; + export default {}; diff --git a/spec/frontend/snippets/components/snippet_blob_view_spec.js b/spec/frontend/snippets/components/snippet_blob_view_spec.js index efc1c6dcef9..c4f1dd0ca35 100644 --- a/spec/frontend/snippets/components/snippet_blob_view_spec.js +++ b/spec/frontend/snippets/components/snippet_blob_view_spec.js @@ -1,14 +1,18 @@ -import { shallowMount } from '@vue/test-utils'; +import { mount } from '@vue/test-utils'; import { GlLoadingIcon } from '@gitlab/ui'; import SnippetBlobView from '~/snippets/components/snippet_blob_view.vue'; import BlobHeader from '~/blob/components/blob_header.vue'; import BlobEmbeddable from '~/blob/components/blob_embeddable.vue'; +import BlobContent from '~/blob/components/blob_content.vue'; +import { RichViewer, SimpleViewer } from '~/vue_shared/components/blob_viewers'; import { SNIPPET_VISIBILITY_PRIVATE, SNIPPET_VISIBILITY_INTERNAL, SNIPPET_VISIBILITY_PUBLIC, } from '~/snippets/constants'; +import { Blob as BlobMock, SimpleViewerMock, RichViewerMock } from 'jest/blob/components/mock_data'; + describe('Blob Embeddable', () => { let wrapper; const snippet = { @@ -16,27 +20,42 @@ describe('Blob Embeddable', () => { webUrl: 'https://foo.bar', visibilityLevel: SNIPPET_VISIBILITY_PUBLIC, }; + const dataMock = { + blob: BlobMock, + activeViewerType: SimpleViewerMock.type, + }; - function createComponent(props = {}, loading = false) { + function createComponent( + props = {}, + data = dataMock, + blobLoading = false, + contentLoading = false, + ) { const $apollo = { queries: { blob: { - loading, + loading: blobLoading, + }, + blobContent: { + loading: contentLoading, }, }, }; - wrapper = shallowMount(SnippetBlobView, { + wrapper = mount(SnippetBlobView, { propsData: { snippet: { ...snippet, ...props, }, }, + data() { + return { + ...data, + }; + }, mocks: { $apollo }, }); - - wrapper.vm.$apollo.queries.blob.loading = false; } afterEach(() => { @@ -48,6 +67,7 @@ describe('Blob Embeddable', () => { createComponent(); expect(wrapper.find(BlobEmbeddable).exists()).toBe(true); expect(wrapper.find(BlobHeader).exists()).toBe(true); + expect(wrapper.find(BlobContent).exists()).toBe(true); }); it.each([SNIPPET_VISIBILITY_INTERNAL, SNIPPET_VISIBILITY_PRIVATE, 'foo'])( @@ -68,9 +88,92 @@ describe('Blob Embeddable', () => { }); it('shows loading icon while blob data is in flight', () => { - createComponent({}, true); + createComponent({}, dataMock, true); expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); expect(wrapper.find('.snippet-file-content').exists()).toBe(false); }); + + it('sets simple viewer correctly', () => { + createComponent(); + expect(wrapper.find(SimpleViewer).exists()).toBe(true); + }); + + it('sets rich viewer correctly', () => { + const data = Object.assign({}, dataMock, { + activeViewerType: RichViewerMock.type, + }); + createComponent({}, data); + expect(wrapper.find(RichViewer).exists()).toBe(true); + }); + + it('correctly switches viewer type', () => { + createComponent(); + expect(wrapper.find(SimpleViewer).exists()).toBe(true); + + wrapper.vm.switchViewer(RichViewerMock.type); + + return wrapper.vm + .$nextTick() + .then(() => { + expect(wrapper.find(RichViewer).exists()).toBe(true); + wrapper.vm.switchViewer(SimpleViewerMock.type); + }) + .then(() => { + expect(wrapper.find(SimpleViewer).exists()).toBe(true); + }); + }); + + describe('URLS with hash', () => { + beforeEach(() => { + window.location.hash = '#LC2'; + }); + + afterEach(() => { + window.location.hash = ''; + }); + + it('renders simple viewer by default if URL contains hash', () => { + createComponent(); + + expect(wrapper.vm.activeViewerType).toBe(SimpleViewerMock.type); + expect(wrapper.find(SimpleViewer).exists()).toBe(true); + }); + + describe('switchViewer()', () => { + it('by default switches to the passed viewer', () => { + createComponent(); + + wrapper.vm.switchViewer(RichViewerMock.type); + return wrapper.vm + .$nextTick() + .then(() => { + expect(wrapper.vm.activeViewerType).toBe(RichViewerMock.type); + expect(wrapper.find(RichViewer).exists()).toBe(true); + + wrapper.vm.switchViewer(SimpleViewerMock.type); + }) + .then(() => { + expect(wrapper.vm.activeViewerType).toBe(SimpleViewerMock.type); + expect(wrapper.find(SimpleViewer).exists()).toBe(true); + }); + }); + + it('respects hash over richViewer in the blob when corresponding parameter is passed', () => { + createComponent( + {}, + { + blob: BlobMock, + }, + ); + expect(wrapper.vm.blob.richViewer).toEqual(expect.any(Object)); + + wrapper.vm.switchViewer(RichViewerMock.type, true); + return wrapper.vm.$nextTick().then(() => { + expect(wrapper.vm.activeViewerType).toBe(SimpleViewerMock.type); + expect(wrapper.find(SimpleViewer).exists()).toBe(true); + }); + }); + }); + }); }); }); diff --git a/spec/frontend/vue_shared/components/blob_viewers/__snapshots__/simple_viewer_spec.js.snap b/spec/frontend/vue_shared/components/blob_viewers/__snapshots__/simple_viewer_spec.js.snap new file mode 100644 index 00000000000..87f2a8f9eff --- /dev/null +++ b/spec/frontend/vue_shared/components/blob_viewers/__snapshots__/simple_viewer_spec.js.snap @@ -0,0 +1,86 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Blob Simple Viewer component rendering matches the snapshot 1`] = ` +<div + class="file-content code js-syntax-highlight qa-file-content" +> + <div + class="line-numbers" + > + <a + class="diff-line-num js-line-number" + data-line-number="1" + href="#LC1" + id="L1" + > + <gl-icon-stub + name="link" + size="12" + /> + + 1 + + </a> + <a + class="diff-line-num js-line-number" + data-line-number="2" + href="#LC2" + id="L2" + > + <gl-icon-stub + name="link" + size="12" + /> + + 2 + + </a> + <a + class="diff-line-num js-line-number" + data-line-number="3" + href="#LC3" + id="L3" + > + <gl-icon-stub + name="link" + size="12" + /> + + 3 + + </a> + </div> + + <div + class="blob-content" + > + <pre + class="code highlight" + > + <code + id="blob-code-content" + > + <span + id="LC1" + > + First + </span> + + + <span + id="LC2" + > + Second + </span> + + + <span + id="LC3" + > + Third + </span> + </code> + </pre> + </div> +</div> +`; diff --git a/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js b/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js new file mode 100644 index 00000000000..17ea78b5826 --- /dev/null +++ b/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js @@ -0,0 +1,27 @@ +import { shallowMount } from '@vue/test-utils'; +import RichViewer from '~/vue_shared/components/blob_viewers/rich_viewer.vue'; + +describe('Blob Rich Viewer component', () => { + let wrapper; + const content = '<h1 id="markdown">Foo Bar</h1>'; + + function createComponent() { + wrapper = shallowMount(RichViewer, { + propsData: { + content, + }, + }); + } + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders the passed content without transformations', () => { + expect(wrapper.html()).toContain(content); + }); +}); diff --git a/spec/frontend/vue_shared/components/blob_viewers/simple_viewer_spec.js b/spec/frontend/vue_shared/components/blob_viewers/simple_viewer_spec.js new file mode 100644 index 00000000000..d12bfc5c686 --- /dev/null +++ b/spec/frontend/vue_shared/components/blob_viewers/simple_viewer_spec.js @@ -0,0 +1,81 @@ +import { shallowMount } from '@vue/test-utils'; +import SimpleViewer from '~/vue_shared/components/blob_viewers/simple_viewer.vue'; +import { HIGHLIGHT_CLASS_NAME } from '~/vue_shared/components/blob_viewers/constants'; + +describe('Blob Simple Viewer component', () => { + let wrapper; + const contentMock = `<span id="LC1">First</span>\n<span id="LC2">Second</span>\n<span id="LC3">Third</span>`; + + function createComponent(content = contentMock) { + wrapper = shallowMount(SimpleViewer, { + propsData: { + content, + }, + }); + } + + afterEach(() => { + wrapper.destroy(); + }); + + it('does not fail if content is empty', () => { + const spy = jest.spyOn(window.console, 'error'); + createComponent(''); + expect(spy).not.toHaveBeenCalled(); + }); + + describe('rendering', () => { + beforeEach(() => { + createComponent(); + }); + + it('matches the snapshot', () => { + expect(wrapper.element).toMatchSnapshot(); + }); + + it('renders exactly three lines', () => { + expect(wrapper.findAll('.js-line-number')).toHaveLength(3); + }); + + it('renders the content without transformations', () => { + expect(wrapper.html()).toContain(contentMock); + }); + }); + + describe('functionality', () => { + const scrollIntoViewMock = jest.fn(); + HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; + + beforeEach(() => { + window.location.hash = '#LC2'; + createComponent(); + }); + + afterEach(() => { + window.location.hash = ''; + }); + + it('scrolls to requested line when rendered', () => { + const linetoBeHighlighted = wrapper.find('#LC2'); + expect(scrollIntoViewMock).toHaveBeenCalled(); + expect(wrapper.vm.highlightedLine).toBe(linetoBeHighlighted.element); + expect(linetoBeHighlighted.classes()).toContain(HIGHLIGHT_CLASS_NAME); + }); + + it('switches highlighting when another line is selected', () => { + const currentlyHighlighted = wrapper.find('#LC2'); + const hash = '#LC3'; + const linetoBeHighlighted = wrapper.find(hash); + + expect(wrapper.vm.highlightedLine).toBe(currentlyHighlighted.element); + + wrapper.vm.scrollToLine(hash); + + return wrapper.vm.$nextTick(() => { + expect(wrapper.vm.highlightedLine).toBe(linetoBeHighlighted.element); + expect(currentlyHighlighted.classes()).not.toContain(HIGHLIGHT_CLASS_NAME); + expect(linetoBeHighlighted.classes()).toContain(HIGHLIGHT_CLASS_NAME); + }); + }); + }); +}); diff --git a/spec/helpers/environments_helper_spec.rb b/spec/helpers/environments_helper_spec.rb index ca0360b363e..b72fbc9fd3c 100644 --- a/spec/helpers/environments_helper_spec.rb +++ b/spec/helpers/environments_helper_spec.rb @@ -20,7 +20,7 @@ describe EnvironmentsHelper do expect(metrics_data).to include( 'settings-path' => edit_project_service_path(project, 'prometheus'), 'clusters-path' => project_clusters_path(project), - 'current-environment-name': environment.name, + 'current-environment-name' => environment.name, 'documentation-path' => help_page_path('administration/monitoring/prometheus/index.md'), 'empty-getting-started-svg-path' => match_asset_path('/assets/illustrations/monitoring/getting_started.svg'), 'empty-loading-svg-path' => match_asset_path('/assets/illustrations/monitoring/loading.svg'), |