Commit 432add1d authored by Phil Hughes's avatar Phil Hughes

Merge branch '217934-snippet-description-files' into 'master'

Include `uploadedFiles` param in the snippet Create mutation request

See merge request gitlab-org/gitlab!34221
parents 80b1aeb9 c2ef26aa
...@@ -137,18 +137,34 @@ export default { ...@@ -137,18 +137,34 @@ export default {
this.onExistingSnippetFetched(); this.onExistingSnippetFetched();
} }
}, },
getAttachedFiles() {
const fileInputs = Array.from(this.$el.querySelectorAll('[name="files[]"]'));
return fileInputs.map(node => node.value);
},
createMutation() {
return {
mutation: CreateSnippetMutation,
variables: {
input: {
...this.apiData,
uploadedFiles: this.getAttachedFiles(),
projectPath: this.projectPath,
},
},
};
},
updateMutation() {
return {
mutation: UpdateSnippetMutation,
variables: {
input: this.apiData,
},
};
},
handleFormSubmit() { handleFormSubmit() {
this.isUpdating = true; this.isUpdating = true;
this.$apollo this.$apollo
.mutate({ .mutate(this.newSnippet ? this.createMutation() : this.updateMutation())
mutation: this.newSnippet ? CreateSnippetMutation : UpdateSnippetMutation,
variables: {
input: {
...this.apiData,
projectPath: this.newSnippet ? this.projectPath : undefined,
},
},
})
.then(({ data }) => { .then(({ data }) => {
const baseObj = this.newSnippet ? data?.createSnippet : data?.updateSnippet; const baseObj = this.newSnippet ? data?.createSnippet : data?.updateSnippet;
...@@ -176,6 +192,8 @@ export default { ...@@ -176,6 +192,8 @@ export default {
<form <form
class="snippet-form js-requires-input js-quick-submit common-note-form" class="snippet-form js-requires-input js-quick-submit common-note-form"
:data-snippet-type="isProjectSnippet ? 'project' : 'personal'" :data-snippet-type="isProjectSnippet ? 'project' : 'personal'"
data-testid="snippet-edit-form"
@submit.prevent="handleFormSubmit"
> >
<gl-loading-icon <gl-loading-icon
v-if="isLoading" v-if="isLoading"
...@@ -211,17 +229,17 @@ export default { ...@@ -211,17 +229,17 @@ export default {
<form-footer-actions> <form-footer-actions>
<template #prepend> <template #prepend>
<gl-button <gl-button
type="submit"
category="primary" category="primary"
type="submit"
variant="success" variant="success"
:disabled="updatePrevented" :disabled="updatePrevented"
data-qa-selector="submit_button" data-qa-selector="submit_button"
@click.prevent="handleFormSubmit" data-testid="snippet-submit-btn"
>{{ saveButtonLabel }}</gl-button >{{ saveButtonLabel }}</gl-button
> >
</template> </template>
<template #append> <template #append>
<gl-button data-testid="snippet-cancel-btn" :href="cancelButtonHref">{{ <gl-button type="cancel" data-testid="snippet-cancel-btn" :href="cancelButtonHref">{{
__('Cancel') __('Cancel')
}}</gl-button> }}</gl-button>
</template> </template>
......
...@@ -30,7 +30,7 @@ export default { ...@@ -30,7 +30,7 @@ export default {
</script> </script>
<template> <template>
<div class="form-group js-description-input"> <div class="form-group js-description-input">
<label>{{ s__('Snippets|Description (optional)') }}</label> <label for="snippet-description">{{ s__('Snippets|Description (optional)') }}</label>
<div class="js-collapsible-input"> <div class="js-collapsible-input">
<div class="js-collapsed" :class="{ 'd-none': value }"> <div class="js-collapsed" :class="{ 'd-none': value }">
<gl-form-input <gl-form-input
...@@ -46,22 +46,26 @@ export default { ...@@ -46,22 +46,26 @@ export default {
<markdown-field <markdown-field
class="js-expanded" class="js-expanded"
:class="{ 'd-none': !value }" :class="{ 'd-none': !value }"
:add-spacing-classes="false"
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
> >
<textarea <template #textarea>
slot="textarea" <textarea
class="note-textarea js-gfm-input js-autosize markdown-area" id="snippet-description"
dir="auto" ref="textarea"
data-qa-selector="snippet_description_field" :value="value"
data-supports-quick-actions="false" class="note-textarea js-gfm-input js-autosize markdown-area"
:value="value" dir="auto"
:aria-label="__('Description')" data-qa-selector="snippet_description_field"
:placeholder="__('Write a comment or drag your files here…')" data-supports-quick-actions="false"
v-bind="$attrs" :aria-label="__('Description')"
@input="$emit('input', $event.target.value)" :placeholder="__('Write a comment or drag your files here…')"
> v-bind="$attrs"
</textarea> @input="$emit('input', $event.target.value)"
>
</textarea>
</template>
</markdown-field> </markdown-field>
</div> </div>
</div> </div>
......
---
title: Send information about attached files to the GraphQL mutation
merge_request: 34221
author:
type: fixed
...@@ -4,7 +4,9 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] = ...@@ -4,7 +4,9 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
<div <div
class="form-group js-description-input" class="form-group js-description-input"
> >
<label> <label
for="snippet-description"
>
Description (optional) Description (optional)
</label> </label>
...@@ -21,27 +23,67 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] = ...@@ -21,27 +23,67 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
/> />
</div> </div>
<markdown-field-stub <div
addspacingclasses="true" class="js-vue-markdown-field md-area position-relative js-expanded gfm-form"
canattachfile="true"
class="js-expanded"
enableautocomplete="true"
helppagepath=""
markdowndocspath="help/"
markdownpreviewpath="foo/"
note="[object Object]"
quickactionsdocspath=""
textareavalue=""
> >
<textarea <markdown-header-stub
aria-label="Description" linecontent=""
class="note-textarea js-gfm-input js-autosize markdown-area"
data-qa-selector="snippet_description_field"
data-supports-quick-actions="false"
dir="auto"
placeholder="Write a comment or drag your files here…"
/> />
</markdown-field-stub>
<div
class="md-write-holder"
>
<div
class="zen-backdrop div-dropzone-wrapper"
>
<div
class="div-dropzone js-invalid-dropzone"
>
<textarea
aria-label="Description"
class="note-textarea js-gfm-input js-autosize markdown-area"
data-qa-selector="snippet_description_field"
data-supports-quick-actions="false"
dir="auto"
id="snippet-description"
placeholder="Write a comment or drag your files here…"
style="overflow-x: hidden; word-wrap: break-word; overflow-y: hidden;"
/>
<div
class="div-dropzone-hover"
>
<i
class="fa fa-paperclip div-dropzone-icon"
/>
</div>
</div>
<a
aria-label="Leave zen mode"
class="zen-control zen-control-leave js-zen-leave gl-text-gray-700"
href="#"
>
<icon-stub
name="screen-normal"
size="16"
/>
</a>
<markdown-toolbar-stub
canattachfile="true"
markdowndocspath="help/"
quickactionsdocspath=""
/>
</div>
</div>
<div
class="js-vue-md-preview md md-preview-holder"
style="display: none;"
/>
<!---->
</div>
</div> </div>
</div> </div>
`; `;
...@@ -40,6 +40,9 @@ const newlyEditedSnippetUrl = 'http://foo.bar'; ...@@ -40,6 +40,9 @@ const newlyEditedSnippetUrl = 'http://foo.bar';
const apiError = { message: 'Ufff' }; const apiError = { message: 'Ufff' };
const mutationError = 'Bummer'; const mutationError = 'Bummer';
const attachedFilePath1 = 'foo/bar';
const attachedFilePath2 = 'alpha/beta';
const defaultProps = { const defaultProps = {
snippetGid: 'gid://gitlab/PersonalSnippet/42', snippetGid: 'gid://gitlab/PersonalSnippet/42',
markdownPreviewPath: 'http://preview.foo.bar', markdownPreviewPath: 'http://preview.foo.bar',
...@@ -120,8 +123,9 @@ describe('Snippet Edit app', () => { ...@@ -120,8 +123,9 @@ describe('Snippet Edit app', () => {
wrapper.destroy(); wrapper.destroy();
}); });
const findSubmitButton = () => wrapper.find('[type=submit]'); const findSubmitButton = () => wrapper.find('[data-testid="snippet-submit-btn"]');
const findCancellButton = () => wrapper.find('[data-testid="snippet-cancel-btn"]'); const findCancellButton = () => wrapper.find('[data-testid="snippet-cancel-btn"]');
const clickSubmitBtn = () => wrapper.find('[data-testid="snippet-edit-form"]').trigger('submit');
describe('rendering', () => { describe('rendering', () => {
it('renders loader while the query is in flight', () => { it('renders loader while the query is in flight', () => {
...@@ -289,13 +293,15 @@ describe('Snippet Edit app', () => { ...@@ -289,13 +293,15 @@ describe('Snippet Edit app', () => {
}, },
}; };
wrapper.vm.handleFormSubmit(); clickSubmitBtn();
expect(resolveMutate).toHaveBeenCalledWith(mutationPayload); expect(resolveMutate).toHaveBeenCalledWith(mutationPayload);
}); });
it('redirects to snippet view on successful mutation', () => { it('redirects to snippet view on successful mutation', () => {
createComponent(); createComponent();
wrapper.vm.handleFormSubmit(); clickSubmitBtn();
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(redirectTo).toHaveBeenCalledWith(newlyEditedSnippetUrl); expect(redirectTo).toHaveBeenCalledWith(newlyEditedSnippetUrl);
}); });
...@@ -322,7 +328,8 @@ describe('Snippet Edit app', () => { ...@@ -322,7 +328,8 @@ describe('Snippet Edit app', () => {
mutationRes: mutationTypes.RESOLVE_WITH_ERRORS, mutationRes: mutationTypes.RESOLVE_WITH_ERRORS,
}); });
wrapper.vm.handleFormSubmit(); clickSubmitBtn();
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(redirectTo).not.toHaveBeenCalled(); expect(redirectTo).not.toHaveBeenCalled();
expect(flashSpy).toHaveBeenCalledWith(mutationError); expect(flashSpy).toHaveBeenCalledWith(mutationError);
...@@ -334,7 +341,9 @@ describe('Snippet Edit app', () => { ...@@ -334,7 +341,9 @@ describe('Snippet Edit app', () => {
createComponent({ createComponent({
mutationRes: mutationTypes.REJECT, mutationRes: mutationTypes.REJECT,
}); });
wrapper.vm.handleFormSubmit();
clickSubmitBtn();
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(redirectTo).not.toHaveBeenCalled(); expect(redirectTo).not.toHaveBeenCalled();
expect(flashSpy).toHaveBeenCalledWith(apiError); expect(flashSpy).toHaveBeenCalledWith(apiError);
...@@ -354,12 +363,61 @@ describe('Snippet Edit app', () => { ...@@ -354,12 +363,61 @@ describe('Snippet Edit app', () => {
}, },
mutationRes: mutationTypes.REJECT, mutationRes: mutationTypes.REJECT,
}); });
wrapper.vm.handleFormSubmit();
clickSubmitBtn();
return waitForPromises().then(() => { return waitForPromises().then(() => {
expect(Flash).toHaveBeenCalledWith(expect.stringContaining(expectation)); expect(Flash).toHaveBeenCalledWith(expect.stringContaining(expectation));
}); });
}, },
); );
}); });
describe('correctly includes attached files into the mutation', () => {
const createMutationPayload = expectation => {
return expect.objectContaining({
variables: {
input: expect.objectContaining({ uploadedFiles: expectation }),
},
});
};
const updateMutationPayload = () => {
return expect.objectContaining({
variables: {
input: expect.not.objectContaining({ uploadedFiles: expect.anything() }),
},
});
};
it.each`
paths | expectation
${[attachedFilePath1]} | ${[attachedFilePath1]}
${[attachedFilePath1, attachedFilePath2]} | ${[attachedFilePath1, attachedFilePath2]}
${[]} | ${[]}
`(`correctly sends paths for $paths.length files`, ({ paths, expectation }) => {
createComponent({
data: {
newSnippet: true,
},
});
const fixtures = paths.map(path => {
return path ? `<input name="files[]" value="${path}">` : undefined;
});
wrapper.vm.$el.innerHTML += fixtures.join('');
clickSubmitBtn();
expect(resolveMutate).toHaveBeenCalledWith(createMutationPayload(expectation));
});
it(`neither fails nor sends 'uploadedFiles' to update mutation`, () => {
createComponent();
clickSubmitBtn();
expect(resolveMutate).toHaveBeenCalledWith(updateMutationPayload());
});
});
}); });
}); });
import SnippetDescriptionEdit from '~/snippets/components/snippet_description_edit.vue'; import SnippetDescriptionEdit from '~/snippets/components/snippet_description_edit.vue';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
describe('Snippet Description Edit component', () => { describe('Snippet Description Edit component', () => {
...@@ -15,6 +16,9 @@ describe('Snippet Description Edit component', () => { ...@@ -15,6 +16,9 @@ describe('Snippet Description Edit component', () => {
markdownPreviewPath, markdownPreviewPath,
markdownDocsPath, markdownDocsPath,
}, },
stubs: {
MarkdownField,
},
}); });
} }
......
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