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:
Diffstat (limited to 'spec/frontend/snippets/components/edit_spec.js')
-rw-r--r--spec/frontend/snippets/components/edit_spec.js279
1 files changed, 279 insertions, 0 deletions
diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js
new file mode 100644
index 00000000000..21a4ccf5a74
--- /dev/null
+++ b/spec/frontend/snippets/components/edit_spec.js
@@ -0,0 +1,279 @@
+import { shallowMount } from '@vue/test-utils';
+import axios from '~/lib/utils/axios_utils';
+
+import { GlLoadingIcon } from '@gitlab/ui';
+import { joinPaths, redirectTo } from '~/lib/utils/url_utility';
+
+import SnippetEditApp from '~/snippets/components/edit.vue';
+import SnippetDescriptionEdit from '~/snippets/components/snippet_description_edit.vue';
+import SnippetVisibilityEdit from '~/snippets/components/snippet_visibility_edit.vue';
+import SnippetBlobEdit from '~/snippets/components/snippet_blob_edit.vue';
+import TitleField from '~/vue_shared/components/form/title.vue';
+import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
+
+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'),
+}));
+
+let flashSpy;
+
+const contentMock = 'Foo Bar';
+const rawPathMock = '/foo/bar';
+const rawProjectPathMock = '/project/path';
+const newlyEditedSnippetUrl = 'http://foo.bar';
+const apiError = { message: 'Ufff' };
+
+const defaultProps = {
+ snippetGid: 'gid://gitlab/PersonalSnippet/42',
+ markdownPreviewPath: 'http://preview.foo.bar',
+ markdownDocsPath: 'http://docs.foo.bar',
+};
+
+describe('Snippet Edit app', () => {
+ let wrapper;
+ let axiosMock;
+
+ const resolveMutate = jest.fn().mockResolvedValue({
+ data: {
+ updateSnippet: {
+ errors: [],
+ snippet: {
+ webUrl: newlyEditedSnippetUrl,
+ },
+ },
+ },
+ });
+
+ const rejectMutation = jest.fn().mockRejectedValue(apiError);
+
+ const mutationTypes = {
+ RESOLVE: resolveMutate,
+ REJECT: rejectMutation,
+ };
+
+ function createComponent({
+ props = defaultProps,
+ data = {},
+ loading = false,
+ mutationRes = mutationTypes.RESOLVE,
+ } = {}) {
+ const $apollo = {
+ queries: {
+ snippet: {
+ loading,
+ },
+ },
+ mutate: mutationRes,
+ };
+
+ wrapper = shallowMount(SnippetEditApp, {
+ mocks: { $apollo },
+ stubs: {
+ FormFooterActions,
+ ApolloMutation,
+ },
+ propsData: {
+ ...props,
+ },
+ data() {
+ return data;
+ },
+ });
+
+ flashSpy = jest.spyOn(wrapper.vm, 'flashAPIFailure');
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findSubmitButton = () => wrapper.find('[type=submit]');
+
+ describe('rendering', () => {
+ it('renders loader while the query is in flight', () => {
+ createComponent({ loading: true });
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ });
+
+ it('renders all required components', () => {
+ createComponent();
+
+ expect(wrapper.contains(TitleField)).toBe(true);
+ expect(wrapper.contains(SnippetDescriptionEdit)).toBe(true);
+ expect(wrapper.contains(SnippetBlobEdit)).toBe(true);
+ expect(wrapper.contains(SnippetVisibilityEdit)).toBe(true);
+ expect(wrapper.contains(FormFooterActions)).toBe(true);
+ });
+
+ it('does not fail if there is no snippet yet (new snippet creation)', () => {
+ const snippetGid = '';
+ createComponent({
+ props: {
+ ...defaultProps,
+ snippetGid,
+ },
+ });
+
+ expect(wrapper.props('snippetGid')).toBe(snippetGid);
+ });
+
+ it.each`
+ title | content | expectation
+ ${''} | ${''} | ${true}
+ ${'foo'} | ${''} | ${true}
+ ${''} | ${'foo'} | ${true}
+ ${'foo'} | ${'bar'} | ${false}
+ `(
+ 'disables submit button unless both title and content are present',
+ ({ title, content, expectation }) => {
+ createComponent({
+ data: {
+ snippet: { title },
+ content,
+ },
+ });
+ const isBtnDisabled = Boolean(findSubmitButton().attributes('disabled'));
+ expect(isBtnDisabled).toBe(expectation);
+ },
+ );
+ });
+
+ describe('functionality', () => {
+ describe('handling of the data from GraphQL response', () => {
+ const snippet = {
+ blob: {
+ rawPath: rawPathMock,
+ },
+ };
+ const getResSchema = newSnippet => {
+ return {
+ data: {
+ snippets: {
+ edges: newSnippet ? [] : [snippet],
+ },
+ },
+ };
+ };
+
+ const bootstrapForExistingSnippet = resp => {
+ createComponent({
+ data: {
+ snippet,
+ },
+ });
+
+ 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();
+
+ 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);
+ });
+ });
+ });
+
+ describe('form submission handling', () => {
+ it.each`
+ newSnippet | projectPath | mutation | mutationName
+ ${true} | ${rawProjectPathMock} | ${CreateSnippetMutation} | ${'CreateSnippetMutation with projectPath'}
+ ${true} | ${''} | ${CreateSnippetMutation} | ${'CreateSnippetMutation without projectPath'}
+ ${false} | ${rawProjectPathMock} | ${UpdateSnippetMutation} | ${'UpdateSnippetMutation with projectPath'}
+ ${false} | ${''} | ${UpdateSnippetMutation} | ${'UpdateSnippetMutation without projectPath'}
+ `('should submit $mutationName correctly', ({ newSnippet, projectPath, mutation }) => {
+ createComponent({
+ data: {
+ newSnippet,
+ },
+ props: {
+ ...defaultProps,
+ projectPath,
+ },
+ });
+
+ const mutationPayload = {
+ mutation,
+ variables: {
+ input: newSnippet ? expect.objectContaining({ projectPath }) : expect.any(Object),
+ },
+ };
+
+ wrapper.vm.handleFormSubmit();
+ expect(resolveMutate).toHaveBeenCalledWith(mutationPayload);
+ });
+
+ it('redirects to snippet view on successful mutation', () => {
+ createComponent();
+ wrapper.vm.handleFormSubmit();
+ return waitForPromises().then(() => {
+ expect(redirectTo).toHaveBeenCalledWith(newlyEditedSnippetUrl);
+ });
+ });
+
+ it('flashes an error if mutation failed', () => {
+ createComponent({
+ mutationRes: mutationTypes.REJECT,
+ });
+ wrapper.vm.handleFormSubmit();
+ return waitForPromises().then(() => {
+ expect(redirectTo).not.toHaveBeenCalled();
+ expect(flashSpy).toHaveBeenCalledWith(apiError);
+ });
+ });
+ });
+ });
+});