Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 15:26:25 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-20 15:26:25 +0300
commita09983ae35713f5a2bbb100981116d31ce99826e (patch)
tree2ee2af7bd104d57086db360a7e6d8c9d5d43667a /spec/frontend/snippets
parent18c5ab32b738c0b6ecb4d0df3994000482f34bd8 (diff)
Add latest changes from gitlab-org/gitlab@13-2-stable-ee
Diffstat (limited to 'spec/frontend/snippets')
-rw-r--r--spec/frontend/snippets/components/edit_spec.js179
-rw-r--r--spec/frontend/snippets/components/show_spec.js35
-rw-r--r--spec/frontend/snippets/components/snippet_blob_edit_spec.js137
-rw-r--r--spec/frontend/snippets/components/snippet_blob_view_spec.js38
-rw-r--r--spec/frontend/snippets/components/snippet_header_spec.js19
5 files changed, 265 insertions, 143 deletions
diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js
index 83f46dd347f..d2265dfd506 100644
--- a/spec/frontend/snippets/components/edit_spec.js
+++ b/spec/frontend/snippets/components/edit_spec.js
@@ -1,9 +1,8 @@
import { shallowMount } from '@vue/test-utils';
-import axios from '~/lib/utils/axios_utils';
import Flash from '~/flash';
import { GlLoadingIcon } from '@gitlab/ui';
-import { joinPaths, redirectTo } from '~/lib/utils/url_utility';
+import { redirectTo } from '~/lib/utils/url_utility';
import SnippetEditApp from '~/snippets/components/edit.vue';
import SnippetDescriptionEdit from '~/snippets/components/snippet_description_edit.vue';
@@ -16,25 +15,17 @@ import { SNIPPET_CREATE_MUTATION_ERROR, SNIPPET_UPDATE_MUTATION_ERROR } from '~/
import UpdateSnippetMutation from '~/snippets/mutations/updateSnippet.mutation.graphql';
import CreateSnippetMutation from '~/snippets/mutations/createSnippet.mutation.graphql';
-import AxiosMockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import { ApolloMutation } from 'vue-apollo';
jest.mock('~/lib/utils/url_utility', () => ({
- getBaseURL: jest.fn().mockReturnValue('foo/'),
redirectTo: jest.fn().mockName('redirectTo'),
- joinPaths: jest
- .fn()
- .mockName('joinPaths')
- .mockReturnValue('contentApiURL'),
}));
jest.mock('~/flash');
let flashSpy;
-const contentMock = 'Foo Bar';
-const rawPathMock = '/foo/bar';
const rawProjectPathMock = '/project/path';
const newlyEditedSnippetUrl = 'http://foo.bar';
const apiError = { message: 'Ufff' };
@@ -43,15 +34,27 @@ const mutationError = 'Bummer';
const attachedFilePath1 = 'foo/bar';
const attachedFilePath2 = 'alpha/beta';
+const actionWithContent = {
+ content: 'Foo Bar',
+};
+const actionWithoutContent = {
+ content: '',
+};
+
const defaultProps = {
snippetGid: 'gid://gitlab/PersonalSnippet/42',
markdownPreviewPath: 'http://preview.foo.bar',
markdownDocsPath: 'http://docs.foo.bar',
};
+const defaultData = {
+ blobsActions: {
+ ...actionWithContent,
+ action: '',
+ },
+};
describe('Snippet Edit app', () => {
let wrapper;
- let axiosMock;
const resolveMutate = jest.fn().mockResolvedValue({
data: {
@@ -156,18 +159,21 @@ describe('Snippet Edit app', () => {
});
it.each`
- title | content | expectation
- ${''} | ${''} | ${true}
- ${'foo'} | ${''} | ${true}
- ${''} | ${'foo'} | ${true}
- ${'foo'} | ${'bar'} | ${false}
+ title | blobsActions | expectation
+ ${''} | ${{}} | ${true}
+ ${''} | ${{ actionWithContent }} | ${true}
+ ${''} | ${{ actionWithoutContent }} | ${true}
+ ${'foo'} | ${{}} | ${true}
+ ${'foo'} | ${{ actionWithoutContent }} | ${true}
+ ${'foo'} | ${{ actionWithoutContent, actionWithContent }} | ${true}
+ ${'foo'} | ${{ actionWithContent }} | ${false}
`(
- 'disables submit button unless both title and content are present',
- ({ title, content, expectation }) => {
+ 'disables submit button unless both title and content for all blobs are present',
+ ({ title, blobsActions, expectation }) => {
createComponent({
data: {
snippet: { title },
- content,
+ blobsActions,
},
});
const isBtnDisabled = Boolean(findSubmitButton().attributes('disabled'));
@@ -192,83 +198,31 @@ describe('Snippet Edit app', () => {
});
describe('functionality', () => {
- describe('handling of the data from GraphQL response', () => {
- const snippet = {
- blob: {
- rawPath: rawPathMock,
- },
- };
- const getResSchema = newSnippet => {
- return {
- data: {
- snippets: {
- edges: newSnippet ? [] : [snippet],
- },
- },
+ describe('form submission handling', () => {
+ it('does not submit unchanged blobs', () => {
+ const foo = {
+ action: '',
+ };
+ const bar = {
+ action: 'update',
};
- };
-
- const bootstrapForExistingSnippet = resp => {
createComponent({
data: {
- snippet,
+ blobsActions: {
+ foo,
+ bar,
+ },
},
});
-
- if (resp === 500) {
- axiosMock.onGet('contentApiURL').reply(500);
- } else {
- axiosMock.onGet('contentApiURL').reply(200, contentMock);
- }
- wrapper.vm.onSnippetFetch(getResSchema());
- };
-
- const bootstrapForNewSnippet = () => {
- createComponent();
- wrapper.vm.onSnippetFetch(getResSchema(true));
- };
-
- beforeEach(() => {
- axiosMock = new AxiosMockAdapter(axios);
- });
-
- afterEach(() => {
- axiosMock.restore();
- });
-
- it('fetches blob content with the additional query', () => {
- bootstrapForExistingSnippet();
-
- return waitForPromises().then(() => {
- expect(joinPaths).toHaveBeenCalledWith('foo/', rawPathMock);
- expect(wrapper.vm.newSnippet).toBe(false);
- expect(wrapper.vm.content).toBe(contentMock);
- });
- });
-
- it('flashes the error message if fetching content fails', () => {
- bootstrapForExistingSnippet(500);
-
- return waitForPromises().then(() => {
- expect(flashSpy).toHaveBeenCalled();
- expect(wrapper.vm.content).toBe('');
- });
- });
-
- it('does not fetch content for new snippet', () => {
- bootstrapForNewSnippet();
+ clickSubmitBtn();
return waitForPromises().then(() => {
- // we keep using waitForPromises to make sure we do not run failed test
- expect(wrapper.vm.newSnippet).toBe(true);
- expect(wrapper.vm.content).toBe('');
- expect(joinPaths).not.toHaveBeenCalled();
- expect(wrapper.vm.snippet).toEqual(wrapper.vm.$options.newSnippetSchema);
+ expect(resolveMutate).toHaveBeenCalledWith(
+ expect.objectContaining({ variables: { input: { files: [bar] } } }),
+ );
});
});
- });
- describe('form submission handling', () => {
it.each`
newSnippet | projectPath | mutation | mutationName
${true} | ${rawProjectPathMock} | ${CreateSnippetMutation} | ${'CreateSnippetMutation with projectPath'}
@@ -279,6 +233,7 @@ describe('Snippet Edit app', () => {
createComponent({
data: {
newSnippet,
+ ...defaultData,
},
props: {
...defaultProps,
@@ -419,5 +374,57 @@ describe('Snippet Edit app', () => {
expect(resolveMutate).toHaveBeenCalledWith(updateMutationPayload());
});
});
+
+ describe('on before unload', () => {
+ let event;
+ let returnValueSetter;
+
+ const bootstrap = data => {
+ createComponent({
+ data,
+ });
+
+ event = new Event('beforeunload');
+ returnValueSetter = jest.spyOn(event, 'returnValue', 'set');
+ };
+
+ it('does not prevent page navigation if there are no blobs', () => {
+ bootstrap();
+ window.dispatchEvent(event);
+
+ expect(returnValueSetter).not.toHaveBeenCalled();
+ });
+
+ it('does not prevent page navigation if there are no changes to the blobs content', () => {
+ bootstrap({
+ blobsActions: {
+ foo: {
+ ...actionWithContent,
+ action: '',
+ },
+ },
+ });
+ window.dispatchEvent(event);
+
+ expect(returnValueSetter).not.toHaveBeenCalled();
+ });
+
+ it('prevents page navigation if there are some changes in the snippet content', () => {
+ bootstrap({
+ blobsActions: {
+ foo: {
+ ...actionWithContent,
+ action: 'update',
+ },
+ },
+ });
+
+ window.dispatchEvent(event);
+
+ expect(returnValueSetter).toHaveBeenCalledWith(
+ 'Are you sure you want to lose unsaved changes?',
+ );
+ });
+ });
});
});
diff --git a/spec/frontend/snippets/components/show_spec.js b/spec/frontend/snippets/components/show_spec.js
index 33608df8cf2..b5446e70028 100644
--- a/spec/frontend/snippets/components/show_spec.js
+++ b/spec/frontend/snippets/components/show_spec.js
@@ -1,10 +1,13 @@
import SnippetApp from '~/snippets/components/show.vue';
+import BlobEmbeddable from '~/blob/components/blob_embeddable.vue';
import SnippetHeader from '~/snippets/components/snippet_header.vue';
import SnippetTitle from '~/snippets/components/snippet_title.vue';
import SnippetBlob from '~/snippets/components/snippet_blob_view.vue';
import { GlLoadingIcon } from '@gitlab/ui';
+import { Blob, BinaryBlob } from 'jest/blob/components/mock_data';
import { shallowMount } from '@vue/test-utils';
+import { SNIPPET_VISIBILITY_PUBLIC } from '~/snippets/constants';
describe('Snippet view app', () => {
let wrapper;
@@ -12,7 +15,7 @@ describe('Snippet view app', () => {
snippetGid: 'gid://gitlab/PersonalSnippet/42',
};
- function createComponent({ props = defaultProps, loading = false } = {}) {
+ function createComponent({ props = defaultProps, data = {}, loading = false } = {}) {
const $apollo = {
queries: {
snippet: {
@@ -26,6 +29,9 @@ describe('Snippet view app', () => {
propsData: {
...props,
},
+ data() {
+ return data;
+ },
});
}
afterEach(() => {
@@ -37,10 +43,33 @@ describe('Snippet view app', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
});
- it('renders all components after the query is finished', () => {
+ it('renders all simple components after the query is finished', () => {
createComponent();
expect(wrapper.find(SnippetHeader).exists()).toBe(true);
expect(wrapper.find(SnippetTitle).exists()).toBe(true);
- expect(wrapper.find(SnippetBlob).exists()).toBe(true);
+ });
+
+ it('renders embeddable component if visibility allows', () => {
+ createComponent({
+ data: {
+ snippet: {
+ visibilityLevel: SNIPPET_VISIBILITY_PUBLIC,
+ webUrl: 'http://foo.bar',
+ },
+ },
+ });
+ expect(wrapper.contains(BlobEmbeddable)).toBe(true);
+ });
+
+ it('renders correct snippet-blob components', () => {
+ createComponent({
+ data: {
+ blobs: [Blob, BinaryBlob],
+ },
+ });
+ const blobs = wrapper.findAll(SnippetBlob);
+ expect(blobs.length).toBe(2);
+ expect(blobs.at(0).props('blob')).toEqual(Blob);
+ expect(blobs.at(1).props('blob')).toEqual(BinaryBlob);
});
});
diff --git a/spec/frontend/snippets/components/snippet_blob_edit_spec.js b/spec/frontend/snippets/components/snippet_blob_edit_spec.js
index 75688e61892..009074b4558 100644
--- a/spec/frontend/snippets/components/snippet_blob_edit_spec.js
+++ b/spec/frontend/snippets/components/snippet_blob_edit_spec.js
@@ -4,78 +4,161 @@ import BlobContentEdit from '~/blob/components/blob_edit_content.vue';
import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import AxiosMockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import { joinPaths } from '~/lib/utils/url_utility';
+import waitForPromises from 'helpers/wait_for_promises';
jest.mock('~/blob/utils', () => jest.fn());
+jest.mock('~/lib/utils/url_utility', () => ({
+ getBaseURL: jest.fn().mockReturnValue('foo/'),
+ joinPaths: jest
+ .fn()
+ .mockName('joinPaths')
+ .mockReturnValue('contentApiURL'),
+}));
+
+jest.mock('~/flash');
+
+let flashSpy;
+
describe('Snippet Blob Edit component', () => {
let wrapper;
- const value = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
- const fileName = 'lorem.txt';
- const findHeader = () => wrapper.find(BlobHeaderEdit);
- const findContent = () => wrapper.find(BlobContentEdit);
+ let axiosMock;
+ const contentMock = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
+ const pathMock = 'lorem.txt';
+ const rawPathMock = 'foo/bar';
+ const blob = {
+ path: pathMock,
+ content: contentMock,
+ rawPath: rawPathMock,
+ };
+ const findComponent = component => wrapper.find(component);
- function createComponent(props = {}) {
+ function createComponent(props = {}, data = { isContentLoading: false }) {
wrapper = shallowMount(SnippetBlobEdit, {
propsData: {
- value,
- fileName,
- isLoading: false,
...props,
},
+ data() {
+ return {
+ ...data,
+ };
+ },
});
+ flashSpy = jest.spyOn(wrapper.vm, 'flashAPIFailure');
}
beforeEach(() => {
+ axiosMock = new AxiosMockAdapter(axios);
createComponent();
});
afterEach(() => {
+ axiosMock.restore();
wrapper.destroy();
});
describe('rendering', () => {
it('matches the snapshot', () => {
+ createComponent({ blob });
expect(wrapper.element).toMatchSnapshot();
});
it('renders required components', () => {
- expect(findHeader().exists()).toBe(true);
- expect(findContent().exists()).toBe(true);
+ expect(findComponent(BlobHeaderEdit).exists()).toBe(true);
+ expect(findComponent(BlobContentEdit).exists()).toBe(true);
});
- it('renders loader if isLoading equals true', () => {
- createComponent({ isLoading: true });
+ it('renders loader if existing blob is supplied but no content is fetched yet', () => {
+ createComponent({ blob }, { isContentLoading: true });
expect(wrapper.contains(GlLoadingIcon)).toBe(true);
- expect(findContent().exists()).toBe(false);
+ expect(findComponent(BlobContentEdit).exists()).toBe(false);
+ });
+
+ it('does not render loader if when blob is not supplied', () => {
+ createComponent();
+ expect(wrapper.contains(GlLoadingIcon)).toBe(false);
+ expect(findComponent(BlobContentEdit).exists()).toBe(true);
});
});
describe('functionality', () => {
- it('does not fail without content', () => {
+ it('does not fail without blob', () => {
const spy = jest.spyOn(global.console, 'error');
- createComponent({ value: undefined });
+ createComponent({ blob: undefined });
expect(spy).not.toHaveBeenCalled();
- expect(findContent().exists()).toBe(true);
+ expect(findComponent(BlobContentEdit).exists()).toBe(true);
});
- it('emits "name-change" event when the file name gets changed', () => {
- expect(wrapper.emitted('name-change')).toBeUndefined();
- const newFilename = 'foo.bar';
- findHeader().vm.$emit('input', newFilename);
+ it.each`
+ emitter | prop
+ ${BlobHeaderEdit} | ${'filePath'}
+ ${BlobContentEdit} | ${'content'}
+ `('emits "blob-updated" event when the $prop gets changed', ({ emitter, prop }) => {
+ expect(wrapper.emitted('blob-updated')).toBeUndefined();
+ const newValue = 'foo.bar';
+ findComponent(emitter).vm.$emit('input', newValue);
return nextTick().then(() => {
- expect(wrapper.emitted('name-change')[0]).toEqual([newFilename]);
+ expect(wrapper.emitted('blob-updated')[0]).toEqual([
+ expect.objectContaining({
+ [prop]: newValue,
+ }),
+ ]);
});
});
- it('emits "input" event when the file content gets changed', () => {
- expect(wrapper.emitted('input')).toBeUndefined();
- const newValue = 'foo.bar';
- findContent().vm.$emit('input', newValue);
+ describe('fetching blob content', () => {
+ const bootstrapForExistingSnippet = resp => {
+ createComponent({
+ blob: {
+ ...blob,
+ content: '',
+ },
+ });
- return nextTick().then(() => {
- expect(wrapper.emitted('input')[0]).toEqual([newValue]);
+ if (resp === 500) {
+ axiosMock.onGet('contentApiURL').reply(500);
+ } else {
+ axiosMock.onGet('contentApiURL').reply(200, contentMock);
+ }
+ };
+
+ const bootstrapForNewSnippet = () => {
+ createComponent();
+ };
+
+ it('fetches blob content with the additional query', () => {
+ bootstrapForExistingSnippet();
+
+ return waitForPromises().then(() => {
+ expect(joinPaths).toHaveBeenCalledWith('foo/', rawPathMock);
+ expect(findComponent(BlobHeaderEdit).props('value')).toBe(pathMock);
+ expect(findComponent(BlobContentEdit).props('value')).toBe(contentMock);
+ });
+ });
+
+ it('flashes the error message if fetching content fails', () => {
+ bootstrapForExistingSnippet(500);
+
+ return waitForPromises().then(() => {
+ expect(flashSpy).toHaveBeenCalled();
+ expect(findComponent(BlobContentEdit).props('value')).toBe('');
+ });
+ });
+
+ it('does not fetch content for new snippet', () => {
+ bootstrapForNewSnippet();
+
+ return waitForPromises().then(() => {
+ // we keep using waitForPromises to make sure we do not run failed test
+ expect(findComponent(BlobHeaderEdit).props('value')).toBe('');
+ expect(findComponent(BlobContentEdit).props('value')).toBe('');
+ expect(joinPaths).not.toHaveBeenCalled();
+ });
});
});
});
diff --git a/spec/frontend/snippets/components/snippet_blob_view_spec.js b/spec/frontend/snippets/components/snippet_blob_view_spec.js
index e4d8ee9b7df..c8f1c8fc8a9 100644
--- a/spec/frontend/snippets/components/snippet_blob_view_spec.js
+++ b/spec/frontend/snippets/components/snippet_blob_view_spec.js
@@ -23,13 +23,17 @@ describe('Blob Embeddable', () => {
id: 'gid://foo.bar/snippet',
webUrl: 'https://foo.bar',
visibilityLevel: SNIPPET_VISIBILITY_PUBLIC,
- blob: BlobMock,
};
const dataMock = {
activeViewerType: SimpleViewerMock.type,
};
- function createComponent(props = {}, data = dataMock, contentLoading = false) {
+ function createComponent({
+ snippetProps = {},
+ data = dataMock,
+ blob = BlobMock,
+ contentLoading = false,
+ } = {}) {
const $apollo = {
queries: {
blobContent: {
@@ -44,8 +48,9 @@ describe('Blob Embeddable', () => {
propsData: {
snippet: {
...snippet,
- ...props,
+ ...snippetProps,
},
+ blob,
},
data() {
return {
@@ -63,7 +68,6 @@ describe('Blob Embeddable', () => {
describe('rendering', () => {
it('renders correct components', () => {
createComponent();
- expect(wrapper.find(BlobEmbeddable).exists()).toBe(true);
expect(wrapper.find(BlobHeader).exists()).toBe(true);
expect(wrapper.find(BlobContent).exists()).toBe(true);
});
@@ -72,19 +76,14 @@ describe('Blob Embeddable', () => {
'does not render blob-embeddable by default',
visibilityLevel => {
createComponent({
- visibilityLevel,
+ snippetProps: {
+ visibilityLevel,
+ },
});
expect(wrapper.find(BlobEmbeddable).exists()).toBe(false);
},
);
- it('does render blob-embeddable for public snippet', () => {
- createComponent({
- visibilityLevel: SNIPPET_VISIBILITY_PUBLIC,
- });
- expect(wrapper.find(BlobEmbeddable).exists()).toBe(true);
- });
-
it('sets simple viewer correctly', () => {
createComponent();
expect(wrapper.find(SimpleViewer).exists()).toBe(true);
@@ -92,7 +91,9 @@ describe('Blob Embeddable', () => {
it('sets rich viewer correctly', () => {
const data = { ...dataMock, activeViewerType: RichViewerMock.type };
- createComponent({}, data);
+ createComponent({
+ data,
+ });
expect(wrapper.find(RichViewer).exists()).toBe(true);
});
@@ -137,7 +138,9 @@ describe('Blob Embeddable', () => {
});
it('renders simple viewer by default if URL contains hash', () => {
- createComponent({}, {});
+ createComponent({
+ data: {},
+ });
expect(wrapper.vm.activeViewerType).toBe(SimpleViewerMock.type);
expect(wrapper.find(SimpleViewer).exists()).toBe(true);
@@ -183,12 +186,11 @@ describe('Blob Embeddable', () => {
});
it(`sets '${SimpleViewerMock.type}' as active on ${BLOB_RENDER_EVENT_SHOW_SOURCE} event`, () => {
- createComponent(
- {},
- {
+ createComponent({
+ data: {
activeViewerType: RichViewerMock.type,
},
- );
+ });
findContentEl().vm.$emit(BLOB_RENDER_EVENT_SHOW_SOURCE);
expect(wrapper.vm.activeViewerType).toEqual(SimpleViewerMock.type);
diff --git a/spec/frontend/snippets/components/snippet_header_spec.js b/spec/frontend/snippets/components/snippet_header_spec.js
index 5230910b6f5..0825da92118 100644
--- a/spec/frontend/snippets/components/snippet_header_spec.js
+++ b/spec/frontend/snippets/components/snippet_header_spec.js
@@ -3,6 +3,7 @@ import DeleteSnippetMutation from '~/snippets/mutations/deleteSnippet.mutation.g
import { ApolloMutation } from 'vue-apollo';
import { GlButton, GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import { Blob, BinaryBlob } from 'jest/blob/components/mock_data';
describe('Snippet header component', () => {
let wrapper;
@@ -20,9 +21,7 @@ describe('Snippet header component', () => {
author: {
name: 'Thor Odinson',
},
- blob: {
- binary: false,
- },
+ blobs: [Blob],
};
const mutationVariables = {
mutation: DeleteSnippetMutation,
@@ -49,7 +48,6 @@ describe('Snippet header component', () => {
mutationRes = mutationTypes.RESOLVE,
snippetProps = {},
} = {}) {
- // const defaultProps = Object.assign({}, snippet, snippetProps);
const defaultProps = Object.assign(snippet, snippetProps);
if (permissions) {
Object.assign(defaultProps.userPermissions, {
@@ -131,15 +129,18 @@ describe('Snippet header component', () => {
expect(wrapper.find(GlModal).exists()).toBe(true);
});
- it('renders Edit button as disabled for binary snippets', () => {
+ it.each`
+ blobs | isDisabled | condition
+ ${[Blob]} | ${false} | ${'no binary'}
+ ${[Blob, BinaryBlob]} | ${true} | ${'several blobs. incl. a binary'}
+ ${[BinaryBlob]} | ${true} | ${'binary'}
+ `('renders Edit button when snippet contains $condition file', ({ blobs, isDisabled }) => {
createComponent({
snippetProps: {
- blob: {
- binary: true,
- },
+ blobs,
},
});
- expect(wrapper.find('[href*="edit"]').props('disabled')).toBe(true);
+ expect(wrapper.find('[href*="edit"]').props('disabled')).toBe(isDisabled);
});
describe('Delete mutation', () => {