Commit f8b8393d authored by Brandon Labuschagne's avatar Brandon Labuschagne Committed by Martin Wortschack

Mograte bootstrap modal to vue

This is being done for the blob dropdown
zone functionality within the project
breadcrumbs
parent 8896f473
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
GlDropdownSectionHeader, GlDropdownSectionHeader,
GlDropdownItem, GlDropdownItem,
GlIcon, GlIcon,
GlModalDirective,
} from '@gitlab/ui'; } from '@gitlab/ui';
import permissionsQuery from 'shared_queries/repository/permissions.query.graphql'; import permissionsQuery from 'shared_queries/repository/permissions.query.graphql';
import { joinPaths, escapeFileUrl } from '~/lib/utils/url_utility'; import { joinPaths, escapeFileUrl } from '~/lib/utils/url_utility';
...@@ -12,12 +13,15 @@ import { __ } from '../../locale'; ...@@ -12,12 +13,15 @@ import { __ } from '../../locale';
import getRefMixin from '../mixins/get_ref'; import getRefMixin from '../mixins/get_ref';
import projectPathQuery from '../queries/project_path.query.graphql'; import projectPathQuery from '../queries/project_path.query.graphql';
import projectShortPathQuery from '../queries/project_short_path.query.graphql'; import projectShortPathQuery from '../queries/project_short_path.query.graphql';
import UploadBlobModal from './upload_blob_modal.vue';
const ROW_TYPES = { const ROW_TYPES = {
header: 'header', header: 'header',
divider: 'divider', divider: 'divider',
}; };
const UPLOAD_BLOB_MODAL_ID = 'modal-upload-blob';
export default { export default {
components: { components: {
GlDropdown, GlDropdown,
...@@ -25,6 +29,7 @@ export default { ...@@ -25,6 +29,7 @@ export default {
GlDropdownSectionHeader, GlDropdownSectionHeader,
GlDropdownItem, GlDropdownItem,
GlIcon, GlIcon,
UploadBlobModal,
}, },
apollo: { apollo: {
projectShortPath: { projectShortPath: {
...@@ -46,6 +51,9 @@ export default { ...@@ -46,6 +51,9 @@ export default {
}, },
}, },
}, },
directives: {
GlModal: GlModalDirective,
},
mixins: [getRefMixin], mixins: [getRefMixin],
props: { props: {
currentPath: { currentPath: {
...@@ -63,6 +71,21 @@ export default { ...@@ -63,6 +71,21 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
canPushCode: {
type: Boolean,
required: false,
default: false,
},
selectedBranch: {
type: String,
required: false,
default: '',
},
origionalBranch: {
type: String,
required: false,
default: '',
},
newBranchPath: { newBranchPath: {
type: String, type: String,
required: false, required: false,
...@@ -93,7 +116,13 @@ export default { ...@@ -93,7 +116,13 @@ export default {
required: false, required: false,
default: null, default: null,
}, },
uploadPath: {
type: String,
required: false,
default: '',
},
}, },
uploadBlobModalId: UPLOAD_BLOB_MODAL_ID,
data() { data() {
return { return {
projectShortPath: '', projectShortPath: '',
...@@ -126,7 +155,10 @@ export default { ...@@ -126,7 +155,10 @@ export default {
); );
}, },
canCreateMrFromFork() { canCreateMrFromFork() {
return this.userPermissions.forkProject && this.userPermissions.createMergeRequestIn; return this.userPermissions?.forkProject && this.userPermissions?.createMergeRequestIn;
},
showUploadModal() {
return this.canEditTree && !this.$apollo.queries.userPermissions.loading;
}, },
dropdownItems() { dropdownItems() {
const items = []; const items = [];
...@@ -149,10 +181,9 @@ export default { ...@@ -149,10 +181,9 @@ export default {
{ {
attrs: { attrs: {
href: '#modal-upload-blob', href: '#modal-upload-blob',
'data-target': '#modal-upload-blob',
'data-toggle': 'modal',
}, },
text: __('Upload file'), text: __('Upload file'),
modalId: UPLOAD_BLOB_MODAL_ID,
}, },
{ {
attrs: { attrs: {
...@@ -253,12 +284,26 @@ export default { ...@@ -253,12 +284,26 @@ export default {
<gl-icon name="chevron-down" :size="16" class="float-left" /> <gl-icon name="chevron-down" :size="16" class="float-left" />
</template> </template>
<template v-for="(item, i) in dropdownItems"> <template v-for="(item, i) in dropdownItems">
<component :is="getComponent(item.type)" :key="i" v-bind="item.attrs"> <component
:is="getComponent(item.type)"
:key="i"
v-bind="item.attrs"
v-gl-modal="item.modalId || null"
>
{{ item.text }} {{ item.text }}
</component> </component>
</template> </template>
</gl-dropdown> </gl-dropdown>
</li> </li>
</ol> </ol>
<upload-blob-modal
v-if="showUploadModal"
:modal-id="$options.uploadBlobModalId"
:commit-message="__('Upload New File')"
:target-branch="selectedBranch"
:origional-branch="origionalBranch"
:can-push-code="canPushCode"
:path="uploadPath"
/>
</nav> </nav>
</template> </template>
<script>
import {
GlModal,
GlForm,
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlToggle,
GlButton,
GlAlert,
} from '@gitlab/ui';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { ContentTypeMultipartFormData } from '~/lib/utils/headers';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
const PRIMARY_OPTIONS_TEXT = __('Upload file');
const SECONDARY_OPTIONS_TEXT = __('Cancel');
const MODAL_TITLE = __('Upload New File');
const COMMIT_LABEL = __('Commit message');
const TARGET_BRANCH_LABEL = __('Target branch');
const TOGGLE_CREATE_MR_LABEL = __('Start a new merge request with these changes');
const REMOVE_FILE_TEXT = __('Remove file');
const NEW_BRANCH_IN_FORK = __(
'A new branch will be created in your fork and a new merge request will be started.',
);
const ERROR_MESSAGE = __('Error uploading file. Please try again.');
export default {
components: {
GlModal,
GlForm,
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlToggle,
GlButton,
UploadDropzone,
GlAlert,
},
i18n: {
MODAL_TITLE,
COMMIT_LABEL,
TARGET_BRANCH_LABEL,
TOGGLE_CREATE_MR_LABEL,
REMOVE_FILE_TEXT,
NEW_BRANCH_IN_FORK,
},
props: {
modalId: {
type: String,
required: true,
},
commitMessage: {
type: String,
required: true,
},
targetBranch: {
type: String,
required: true,
},
origionalBranch: {
type: String,
required: true,
},
canPushCode: {
type: Boolean,
required: true,
},
path: {
type: String,
required: true,
},
},
data() {
return {
commit: this.commitMessage,
target: this.targetBranch,
createNewMr: true,
file: null,
filePreviewURL: null,
fileBinary: null,
loading: false,
};
},
computed: {
primaryOptions() {
return {
text: PRIMARY_OPTIONS_TEXT,
attributes: [
{
variant: 'success',
loading: this.loading,
disabled: !this.formCompleted || this.loading,
},
],
};
},
cancelOptions() {
return {
text: SECONDARY_OPTIONS_TEXT,
attributes: [
{
disabled: this.loading,
},
],
};
},
formattedFileSize() {
return numberToHumanSize(this.file.size);
},
showCreateNewMrToggle() {
return this.canPushCode && this.target !== this.origionalBranch;
},
formCompleted() {
return this.file && this.commit && this.target;
},
},
methods: {
setFile(file) {
this.file = file;
const fileUurlReader = new FileReader();
fileUurlReader.readAsDataURL(this.file);
fileUurlReader.onload = (e) => {
this.filePreviewURL = e.target?.result;
};
},
removeFile() {
this.file = null;
this.filePreviewURL = null;
},
uploadFile() {
this.loading = true;
const {
$route: {
params: { path },
},
} = this;
const uploadPath = joinPaths(this.path, path);
const formData = new FormData();
formData.append('branch_name', this.target);
formData.append('create_merge_request', this.createNewMr);
formData.append('commit_message', this.commit);
formData.append('file', this.file);
return axios
.post(uploadPath, formData, {
headers: {
...ContentTypeMultipartFormData,
},
})
.then((response) => {
visitUrl(response.data.filePath);
})
.catch(() => {
this.loading = false;
createFlash(ERROR_MESSAGE);
});
},
},
};
</script>
<template>
<gl-form>
<gl-modal
:modal-id="modalId"
:title="$options.i18n.MODAL_TITLE"
:action-primary="primaryOptions"
:action-cancel="cancelOptions"
@primary.prevent="uploadFile"
>
<upload-dropzone class="gl-h-200! gl-mb-4" single-file-selection @change="setFile">
<div
v-if="file"
class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-3"
>
<img v-if="filePreviewURL" :src="filePreviewURL" class="gl-h-11" />
<div>{{ formattedFileSize }}</div>
<div>{{ file.name }}</div>
<gl-button
category="tertiary"
variant="confirm"
:disabled="loading"
@click="removeFile"
>{{ $options.i18n.REMOVE_FILE_TEXT }}</gl-button
>
</div>
</upload-dropzone>
<gl-form-group :label="$options.i18n.COMMIT_LABEL" label-for="commit_message">
<gl-form-textarea v-model="commit" name="commit_message" :disabled="loading" />
</gl-form-group>
<gl-form-group
v-if="canPushCode"
:label="$options.i18n.TARGET_BRANCH_LABEL"
label-for="branch_name"
>
<gl-form-input v-model="target" :disabled="loading" name="branch_name" />
</gl-form-group>
<gl-toggle
v-if="showCreateNewMrToggle"
v-model="createNewMr"
:disabled="loading"
:label="$options.i18n.TOGGLE_CREATE_MR_LABEL"
/>
<gl-alert v-if="!canPushCode" variant="info" :dismissible="false" class="gl-mt-3">
{{ $options.i18n.NEW_BRANCH_IN_FORK }}
</gl-alert>
</gl-modal>
</gl-form>
</template>
import Vue from 'vue'; import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import { escapeFileUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link'; import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
import { parseBoolean } from '../lib/utils/common_utils';
import { escapeFileUrl } from '../lib/utils/url_utility';
import { __ } from '../locale';
import App from './components/app.vue'; import App from './components/app.vue';
import Breadcrumbs from './components/breadcrumbs.vue'; import Breadcrumbs from './components/breadcrumbs.vue';
import DirectoryDownloadLinks from './components/directory_download_links.vue'; import DirectoryDownloadLinks from './components/directory_download_links.vue';
...@@ -55,6 +55,8 @@ export default function setupVueRepositoryList() { ...@@ -55,6 +55,8 @@ export default function setupVueRepositoryList() {
const { const {
canCollaborate, canCollaborate,
canEditTree, canEditTree,
canPushCode,
selectedBranch,
newBranchPath, newBranchPath,
newTagPath, newTagPath,
newBlobPath, newBlobPath,
...@@ -65,8 +67,7 @@ export default function setupVueRepositoryList() { ...@@ -65,8 +67,7 @@ export default function setupVueRepositoryList() {
newDirPath, newDirPath,
} = breadcrumbEl.dataset; } = breadcrumbEl.dataset;
router.afterEach(({ params: { path = '/' } }) => { router.afterEach(({ params: { path } }) => {
updateFormAction('.js-upload-blob-form', uploadPath, path);
updateFormAction('.js-create-dir-form', newDirPath, path); updateFormAction('.js-create-dir-form', newDirPath, path);
}); });
...@@ -81,12 +82,16 @@ export default function setupVueRepositoryList() { ...@@ -81,12 +82,16 @@ export default function setupVueRepositoryList() {
currentPath: this.$route.params.path, currentPath: this.$route.params.path,
canCollaborate: parseBoolean(canCollaborate), canCollaborate: parseBoolean(canCollaborate),
canEditTree: parseBoolean(canEditTree), canEditTree: parseBoolean(canEditTree),
canPushCode: parseBoolean(canPushCode),
origionalBranch: ref,
selectedBranch,
newBranchPath, newBranchPath,
newTagPath, newTagPath,
newBlobPath, newBlobPath,
forkNewBlobPath, forkNewBlobPath,
forkNewDirectoryPath, forkNewDirectoryPath,
forkUploadBlobPath, forkUploadBlobPath,
uploadPath,
}, },
}); });
}, },
......
...@@ -131,6 +131,8 @@ module TreeHelper ...@@ -131,6 +131,8 @@ module TreeHelper
def breadcrumb_data_attributes def breadcrumb_data_attributes
attrs = { attrs = {
selected_branch: selected_branch,
can_push_code: can?(current_user, :push_code, @project).to_s,
can_collaborate: can_collaborate_with_project?(@project).to_s, can_collaborate: can_collaborate_with_project?(@project).to_s,
new_blob_path: project_new_blob_path(@project, @ref), new_blob_path: project_new_blob_path(@project, @ref),
upload_path: project_create_blob_path(@project, @ref), upload_path: project_create_blob_path(@project, @ref),
......
...@@ -21,5 +21,4 @@ ...@@ -21,5 +21,4 @@
#js-tree-list{ data: vue_file_list_data(project, ref) } #js-tree-list{ data: vue_file_list_data(project, ref) }
- if can_edit_tree? - if can_edit_tree?
= render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @id), method: :post
= render 'projects/blob/new_dir' = render 'projects/blob/new_dir'
---
title: Migrate bootstrap modal to GlModal for repo single file uploads
merge_request: 53623
author:
type: changed
...@@ -11966,6 +11966,9 @@ msgstr "" ...@@ -11966,6 +11966,9 @@ msgstr ""
msgid "Error uploading file" msgid "Error uploading file"
msgstr "" msgstr ""
msgid "Error uploading file. Please try again."
msgstr ""
msgid "Error uploading file: %{stripped}" msgid "Error uploading file: %{stripped}"
msgstr "" msgstr ""
...@@ -24762,6 +24765,9 @@ msgstr "" ...@@ -24762,6 +24765,9 @@ msgstr ""
msgid "Remove due date" msgid "Remove due date"
msgstr "" msgstr ""
msgid "Remove file"
msgstr ""
msgid "Remove fork relationship" msgid "Remove fork relationship"
msgstr "" msgstr ""
...@@ -28118,6 +28124,9 @@ msgstr "" ...@@ -28118,6 +28124,9 @@ msgstr ""
msgid "Start a new merge request" msgid "Start a new merge request"
msgstr "" msgstr ""
msgid "Start a new merge request with these changes"
msgstr ""
msgid "Start a review" msgid "Start a review"
msgstr "" msgstr ""
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Projects > Files > User uploads files' do RSpec.describe 'Projects > Files > User uploads files' do
include DropzoneHelper
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :repository, name: 'Shop', creator: user) } let(:project) { create(:project, :repository, name: 'Shop', creator: user) }
let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') } let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
...@@ -17,36 +15,15 @@ RSpec.describe 'Projects > Files > User uploads files' do ...@@ -17,36 +15,15 @@ RSpec.describe 'Projects > Files > User uploads files' do
context 'when a user has write access' do context 'when a user has write access' do
before do before do
visit(project_tree_path(project)) visit(project_tree_path(project))
wait_for_requests
end end
include_examples 'it uploads and commit a new text file' include_examples 'it uploads and commit a new text file'
include_examples 'it uploads and commit a new image file' include_examples 'it uploads and commit a new image file'
it 'uploads a file to a sub-directory', :js do include_examples 'it uploads a file to a sub-directory'
click_link 'files'
page.within('.repo-breadcrumb') do
expect(page).to have_content('files')
end
find('.add-to-tree').click
click_link('Upload file')
drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt'))
page.within('#modal-upload-blob') do
fill_in(:commit_message, with: 'New commit message')
end
click_button('Upload file')
expect(page).to have_content('New commit message')
page.within('.repo-breadcrumb') do
expect(page).to have_content('files')
expect(page).to have_content('doc_sample.txt')
end
end
end end
context 'when a user does not have write access' do context 'when a user does not have write access' do
......
...@@ -17,11 +17,15 @@ RSpec.describe 'Projects > Show > User uploads files' do ...@@ -17,11 +17,15 @@ RSpec.describe 'Projects > Show > User uploads files' do
context 'when a user has write access' do context 'when a user has write access' do
before do before do
visit(project_path(project)) visit(project_path(project))
wait_for_requests
end end
include_examples 'it uploads and commit a new text file' include_examples 'it uploads and commit a new text file'
include_examples 'it uploads and commit a new image file' include_examples 'it uploads and commit a new image file'
include_examples 'it uploads a file to a sub-directory'
end end
context 'when a user does not have write access' do context 'when a user does not have write access' do
......
import { GlDropdown } from '@gitlab/ui'; import { GlDropdown } from '@gitlab/ui';
import { shallowMount, RouterLinkStub } from '@vue/test-utils'; import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import Breadcrumbs from '~/repository/components/breadcrumbs.vue'; import Breadcrumbs from '~/repository/components/breadcrumbs.vue';
import UploadBlobModal from '~/repository/components/upload_blob_modal.vue';
let vm;
function factory(currentPath, extraProps = {}) {
vm = shallowMount(Breadcrumbs, {
propsData: {
currentPath,
...extraProps,
},
stubs: {
RouterLink: RouterLinkStub,
},
});
}
describe('Repository breadcrumbs component', () => { describe('Repository breadcrumbs component', () => {
let wrapper;
const factory = (currentPath, extraProps = {}) => {
const $apollo = {
queries: {
userPermissions: {
loading: true,
},
},
};
wrapper = shallowMount(Breadcrumbs, {
propsData: {
currentPath,
...extraProps,
},
stubs: {
RouterLink: RouterLinkStub,
},
mocks: { $apollo },
});
};
const findUploadBlobModal = () => wrapper.find(UploadBlobModal);
afterEach(() => { afterEach(() => {
vm.destroy(); wrapper.destroy();
}); });
it.each` it.each`
...@@ -30,13 +42,13 @@ describe('Repository breadcrumbs component', () => { ...@@ -30,13 +42,13 @@ describe('Repository breadcrumbs component', () => {
`('renders $linkCount links for path $path', ({ path, linkCount }) => { `('renders $linkCount links for path $path', ({ path, linkCount }) => {
factory(path); factory(path);
expect(vm.findAll(RouterLinkStub).length).toEqual(linkCount); expect(wrapper.findAll(RouterLinkStub).length).toEqual(linkCount);
}); });
it('escapes hash in directory path', () => { it('escapes hash in directory path', () => {
factory('app/assets/javascripts#'); factory('app/assets/javascripts#');
expect(vm.findAll(RouterLinkStub).at(3).props('to')).toEqual( expect(wrapper.findAll(RouterLinkStub).at(3).props('to')).toEqual(
'/-/tree/app/assets/javascripts%23', '/-/tree/app/assets/javascripts%23',
); );
}); });
...@@ -44,26 +56,44 @@ describe('Repository breadcrumbs component', () => { ...@@ -44,26 +56,44 @@ describe('Repository breadcrumbs component', () => {
it('renders last link as active', () => { it('renders last link as active', () => {
factory('app/assets'); factory('app/assets');
expect(vm.findAll(RouterLinkStub).at(2).attributes('aria-current')).toEqual('page'); expect(wrapper.findAll(RouterLinkStub).at(2).attributes('aria-current')).toEqual('page');
}); });
it('does not render add to tree dropdown when permissions are false', () => { it('does not render add to tree dropdown when permissions are false', async () => {
factory('/', { canCollaborate: false }); factory('/', { canCollaborate: false });
vm.setData({ userPermissions: { forkProject: false, createMergeRequestIn: false } }); wrapper.setData({ userPermissions: { forkProject: false, createMergeRequestIn: false } });
return vm.vm.$nextTick(() => { await wrapper.vm.$nextTick();
expect(vm.find(GlDropdown).exists()).toBe(false);
}); expect(wrapper.find(GlDropdown).exists()).toBe(false);
}); });
it('renders add to tree dropdown when permissions are true', () => { it('renders add to tree dropdown when permissions are true', async () => {
factory('/', { canCollaborate: true }); factory('/', { canCollaborate: true });
vm.setData({ userPermissions: { forkProject: true, createMergeRequestIn: true } }); wrapper.setData({ userPermissions: { forkProject: true, createMergeRequestIn: true } });
await wrapper.vm.$nextTick();
expect(wrapper.find(GlDropdown).exists()).toBe(true);
});
describe('renders the upload blob modal', () => {
beforeEach(() => {
factory('/', { canEditTree: true });
});
it('does not render the modal while loading', () => {
expect(findUploadBlobModal().exists()).toBe(false);
});
it('renders the modal once loaded', async () => {
wrapper.setData({ $apollo: { queries: { userPermissions: { loading: false } } } });
await wrapper.vm.$nextTick();
return vm.vm.$nextTick(() => { expect(findUploadBlobModal().exists()).toBe(true);
expect(vm.find(GlDropdown).exists()).toBe(true);
}); });
}); });
}); });
import { GlModal, GlFormInput, GlFormTextarea, GlToggle, GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status';
import { visitUrl } from '~/lib/utils/url_utility';
import UploadBlobModal from '~/repository/components/upload_blob_modal.vue';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn(),
joinPaths: () => '/new_upload',
}));
const initialProps = {
modalId: 'upload-blob',
commitMessage: 'Upload New File',
targetBranch: 'master',
origionalBranch: 'master',
canPushCode: true,
path: 'new_upload',
};
describe('UploadBlobModal', () => {
let wrapper;
let mock;
const mockEvent = { preventDefault: jest.fn() };
const createComponent = (props) => {
wrapper = shallowMount(UploadBlobModal, {
propsData: {
...initialProps,
...props,
},
mocks: {
$route: {
params: {
path: '',
},
},
},
});
};
const findModal = () => wrapper.find(GlModal);
const findAlert = () => wrapper.find(GlAlert);
const findCommitMessage = () => wrapper.find(GlFormTextarea);
const findBranchName = () => wrapper.find(GlFormInput);
const findMrToggle = () => wrapper.find(GlToggle);
const findUploadDropzone = () => wrapper.find(UploadDropzone);
const actionButtonDisabledState = () => findModal().props('actionPrimary').attributes[0].disabled;
const cancelButtonDisabledState = () => findModal().props('actionCancel').attributes[0].disabled;
const actionButtonLoadingState = () => findModal().props('actionPrimary').attributes[0].loading;
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe.each`
canPushCode | displayBranchName | displayForkedBranchMessage
${true} | ${true} | ${false}
${false} | ${false} | ${true}
`(
'canPushCode = $canPushCode',
({ canPushCode, displayBranchName, displayForkedBranchMessage }) => {
beforeEach(() => {
createComponent({ canPushCode });
});
it('displays the modal', () => {
expect(findModal().exists()).toBe(true);
});
it('includes the upload dropzone', () => {
expect(findUploadDropzone().exists()).toBe(true);
});
it('includes the commit message', () => {
expect(findCommitMessage().exists()).toBe(true);
});
it('displays the disabled upload button', () => {
expect(actionButtonDisabledState()).toBe(true);
});
it('displays the enabled cancel button', () => {
expect(cancelButtonDisabledState()).toBe(false);
});
it('does not display the MR toggle', () => {
expect(findMrToggle().exists()).toBe(false);
});
it(`${
displayForkedBranchMessage ? 'displays' : 'does not display'
} the forked branch message`, () => {
expect(findAlert().exists()).toBe(displayForkedBranchMessage);
});
it(`${displayBranchName ? 'displays' : 'does not display'} the branch name`, () => {
expect(findBranchName().exists()).toBe(displayBranchName);
});
if (canPushCode) {
describe('when changing the branch name', () => {
it('displays the MR toggle', async () => {
wrapper.setData({ target: 'Not master' });
await wrapper.vm.$nextTick();
expect(findMrToggle().exists()).toBe(true);
});
});
}
describe('completed form', () => {
beforeEach(() => {
wrapper.setData({
file: { type: 'jpg' },
filePreviewURL: 'http://file.com?format=jpg',
});
});
it('enables the upload button when the form is completed', () => {
expect(actionButtonDisabledState()).toBe(false);
});
describe('form submission', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
findModal().vm.$emit('primary', mockEvent);
});
afterEach(() => {
mock.restore();
});
it('disables the upload button', () => {
expect(actionButtonDisabledState()).toBe(true);
});
it('sets the upload button to loading', () => {
expect(actionButtonLoadingState()).toBe(true);
});
});
describe('successful response', () => {
beforeEach(async () => {
mock = new MockAdapter(axios);
mock.onPost(initialProps.path).reply(httpStatusCodes.OK, { filePath: 'blah' });
findModal().vm.$emit('primary', mockEvent);
await waitForPromises();
});
it('redirects to the uploaded file', () => {
expect(visitUrl).toHaveBeenCalled();
});
afterEach(() => {
mock.restore();
});
});
describe('error response', () => {
beforeEach(async () => {
mock = new MockAdapter(axios);
mock.onPost(initialProps.path).timeout();
findModal().vm.$emit('primary', mockEvent);
await waitForPromises();
});
it('creates a flash error', () => {
expect(createFlash).toHaveBeenCalledWith('Error uploading file. Please try again.');
});
afterEach(() => {
mock.restore();
});
});
});
},
);
});
...@@ -10,7 +10,7 @@ RSpec.shared_examples 'it uploads and commit a new text file' do ...@@ -10,7 +10,7 @@ RSpec.shared_examples 'it uploads and commit a new text file' do
wait_for_requests wait_for_requests
end end
drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) attach_file('upload_file', File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt'), make_visible: true)
page.within('#modal-upload-blob') do page.within('#modal-upload-blob') do
fill_in(:commit_message, with: 'New commit message') fill_in(:commit_message, with: 'New commit message')
...@@ -42,7 +42,7 @@ RSpec.shared_examples 'it uploads and commit a new image file' do ...@@ -42,7 +42,7 @@ RSpec.shared_examples 'it uploads and commit a new image file' do
wait_for_requests wait_for_requests
end end
drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'logo_sample.svg')) attach_file('upload_file', File.join(Rails.root, 'spec', 'fixtures', 'logo_sample.svg'), make_visible: true)
page.within('#modal-upload-blob') do page.within('#modal-upload-blob') do
fill_in(:commit_message, with: 'New commit message') fill_in(:commit_message, with: 'New commit message')
...@@ -70,9 +70,11 @@ RSpec.shared_examples 'it uploads and commit a new file to a forked project' do ...@@ -70,9 +70,11 @@ RSpec.shared_examples 'it uploads and commit a new file to a forked project' do
expect(page).to have_content(fork_message) expect(page).to have_content(fork_message)
wait_for_all_requests
find('.add-to-tree').click find('.add-to-tree').click
click_link('Upload file') click_link('Upload file')
drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt')) attach_file('upload_file', File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt'), make_visible: true)
page.within('#modal-upload-blob') do page.within('#modal-upload-blob') do
fill_in(:commit_message, with: 'New commit message') fill_in(:commit_message, with: 'New commit message')
...@@ -94,3 +96,30 @@ RSpec.shared_examples 'it uploads and commit a new file to a forked project' do ...@@ -94,3 +96,30 @@ RSpec.shared_examples 'it uploads and commit a new file to a forked project' do
expect(page).to have_content('Sed ut perspiciatis unde omnis') expect(page).to have_content('Sed ut perspiciatis unde omnis')
end end
end end
RSpec.shared_examples 'it uploads a file to a sub-directory' do
it 'uploads a file to a sub-directory', :js do
click_link 'files'
page.within('.repo-breadcrumb') do
expect(page).to have_content('files')
end
find('.add-to-tree').click
click_link('Upload file')
attach_file('upload_file', File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt'), make_visible: true)
page.within('#modal-upload-blob') do
fill_in(:commit_message, with: 'New commit message')
end
click_button('Upload file')
expect(page).to have_content('New commit message')
page.within('.repo-breadcrumb') do
expect(page).to have_content('files')
expect(page).to have_content('doc_sample.txt')
end
end
end
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