Commit c907a1ab authored by Brandon Labuschagne's avatar Brandon Labuschagne Committed by Mayra Cabrera

Migrate upload file experiment

The migration is from the old bootstrap modal
to the new gitlab ui GlModal based component.

This experiment is behind a feature flag.
parent 9b101bf3
......@@ -3,7 +3,6 @@
import Dropzone from 'dropzone';
import $ from 'jquery';
import { sprintf, __ } from '~/locale';
import { trackUploadFileFormSubmitted } from '~/projects/upload_file_experiment';
import { HIDDEN_CLASS } from '../lib/utils/constants';
import csrf from '../lib/utils/csrf';
import { visitUrl } from '../lib/utils/url_utility';
......@@ -85,8 +84,6 @@ export default class BlobFileDropzone {
e.preventDefault();
e.stopPropagation();
trackUploadFileFormSubmitted();
if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
// eslint-disable-next-line no-alert
alert(__('Please select a file'));
......
......@@ -4,7 +4,6 @@ import $ from 'jquery';
import initPopover from '~/blob/suggest_gitlab_ci_yml';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { disableButtonIfEmptyField, setCookie } from '~/lib/utils/common_utils';
import { initUploadFileTrigger } from '~/projects/upload_file_experiment';
import Tracking from '~/tracking';
import BlobFileDropzone from '../blob/blob_file_dropzone';
import NewCommitForm from '../new_commit_form';
......@@ -48,7 +47,6 @@ export const initUploadForm = () => {
new NewCommitForm(uploadBlobForm);
disableButtonIfEmptyField(uploadBlobForm.find('.js-commit-message'), '.btn-upload-file');
initUploadFileTrigger();
}
};
......
......@@ -5,6 +5,7 @@ import BlobViewer from '~/blob/viewer/index';
import { initUploadForm } from '~/blob_edit/blob_bundle';
import leaveByUrl from '~/namespaces/leave_by_url';
import initVueNotificationsDropdown from '~/notifications';
import { initUploadFileTrigger } from '~/projects/upload_file_experiment';
import initReadMore from '~/read_more';
import UserCallout from '~/user_callout';
import Star from '../../../star';
......@@ -41,3 +42,5 @@ leaveByUrl('project');
initVueNotificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new
initUploadFileTrigger();
<script>
import { GlButton, GlModalDirective } from '@gitlab/ui';
import UploadBlobModal from '~/repository/components/upload_blob_modal.vue';
import { trackFileUploadEvent } from '../upload_file_experiment_tracking';
const UPLOAD_BLOB_MODAL_ID = 'details-modal-upload-blob';
......@@ -16,7 +17,7 @@ export default {
targetBranch: {
default: '',
},
origionalBranch: {
originalBranch: {
default: '',
},
canPushCode: {
......@@ -29,19 +30,28 @@ export default {
default: '',
},
},
methods: {
trackOpenModal() {
trackFileUploadEvent('click_upload_modal_trigger');
},
},
uploadBlobModalId: UPLOAD_BLOB_MODAL_ID,
};
</script>
<template>
<span>
<gl-button v-gl-modal="$options.uploadBlobModalId" icon="upload">{{
__('Upload File')
}}</gl-button>
<gl-button
v-gl-modal="$options.uploadBlobModalId"
icon="upload"
data-testid="upload-file-button"
@click="trackOpenModal"
>{{ __('Upload File') }}</gl-button
>
<upload-blob-modal
:modal-id="$options.uploadBlobModalId"
:commit-message="__('Upload New File')"
:target-branch="targetBranch"
:origional-branch="origionalBranch"
:original-branch="originalBranch"
:can-push-code="canPushCode"
:path="path"
/>
......
import ExperimentTracking from '~/experimentation/experiment_tracking';
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import createRouter from '~/repository/router';
import UploadButton from './details/upload_button.vue';
function trackEvent(eventName) {
const isEmpty = Boolean(document.querySelector('.project-home-panel.empty-project'));
const property = isEmpty ? 'empty' : 'nonempty';
const label = 'blob-upload-modal';
const Tracking = new ExperimentTracking('empty_repo_upload', { label, property });
export const initUploadFileTrigger = () => {
const uploadFileTriggerEl = document.querySelector('.js-upload-file-experiment-trigger');
Tracking.event(eventName);
}
if (!uploadFileTriggerEl) return false;
export function initUploadFileTrigger() {
const uploadFileTriggerEl = document.querySelector('.js-upload-file-experiment-trigger');
const {
targetBranch,
originalBranch,
canPushCode,
path,
projectPath,
} = uploadFileTriggerEl.dataset;
if (uploadFileTriggerEl) {
uploadFileTriggerEl.addEventListener('click', () => {
trackEvent('click_upload_modal_trigger');
return new Vue({
el: uploadFileTriggerEl,
router: createRouter(projectPath, originalBranch),
provide: {
targetBranch,
originalBranch,
canPushCode: parseBoolean(canPushCode),
path,
projectPath,
},
render(h) {
return h(UploadButton);
},
});
}
}
export function trackUploadFileFormSubmitted() {
trackEvent('click_upload_modal_form_submit');
}
};
import ExperimentTracking from '~/experimentation/experiment_tracking';
export const trackFileUploadEvent = (eventName) => {
const isEmpty = Boolean(document.querySelector('.project-home-panel.empty-project'));
const property = isEmpty ? 'empty' : 'nonempty';
const label = 'blob-upload-modal';
const FileUploadTracking = new ExperimentTracking('empty_repo_upload', { label, property });
FileUploadTracking.event(eventName);
};
......@@ -15,6 +15,7 @@ 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 { trackFileUploadEvent } from '~/projects/upload_file_experiment_tracking';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
const PRIMARY_OPTIONS_TEXT = __('Upload file');
......@@ -62,7 +63,7 @@ export default {
type: String,
required: true,
},
origionalBranch: {
originalBranch: {
type: String,
required: true,
},
......@@ -113,7 +114,7 @@ export default {
return numberToHumanSize(this.file.size);
},
showCreateNewMrToggle() {
return this.canPushCode && this.target !== this.origionalBranch;
return this.canPushCode && this.target !== this.originalBranch;
},
formCompleted() {
return this.file && this.commit && this.target;
......@@ -158,6 +159,7 @@ export default {
},
})
.then((response) => {
trackFileUploadEvent('click_upload_modal_form_submit');
visitUrl(response.data.filePath);
})
.catch(() => {
......
......@@ -253,8 +253,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
nil,
nil,
{
'toggle' => 'modal',
'target' => '#modal-upload-blob'
'target_branch' => default_branch_or_master,
'original_branch' => default_branch_or_master,
'can_push_code' => 'true',
'path' => project_create_blob_path(project, default_branch_or_master),
'project_path' => project.path
}
)
end
......
import $ from 'jquery';
import BlobFileDropzone from '~/blob/blob_file_dropzone';
import { trackUploadFileFormSubmitted } from '~/projects/upload_file_experiment';
jest.mock('~/projects/upload_file_experiment', () => ({
trackUploadFileFormSubmitted: jest.fn(),
}));
describe('BlobFileDropzone', () => {
let dropzone;
......@@ -45,13 +40,5 @@ describe('BlobFileDropzone', () => {
expect(replaceFileButton.is(':disabled')).toEqual(true);
expect(dropzone.processQueue).toHaveBeenCalled();
});
it('calls the tracking event', () => {
jest.spyOn(window, 'alert').mockImplementation(() => {});
replaceFileButton.click();
expect(trackUploadFileFormSubmitted).toHaveBeenCalled();
});
});
});
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import UploadButton from '~/projects/details/upload_button.vue';
import { trackFileUploadEvent } from '~/projects/upload_file_experiment_tracking';
import UploadBlobModal from '~/repository/components/upload_blob_modal.vue';
jest.mock('~/projects/upload_file_experiment_tracking');
const MODAL_ID = 'details-modal-upload-blob';
describe('UploadButton', () => {
......@@ -47,6 +50,10 @@ describe('UploadButton', () => {
wrapper.find(GlButton).vm.$emit('click');
});
it('tracks the click_upload_modal_trigger event', () => {
expect(trackFileUploadEvent).toHaveBeenCalledWith('click_upload_modal_trigger');
});
it('opens the modal', () => {
expect(glModalDirective).toHaveBeenCalledWith(MODAL_ID);
});
......
import ExperimentTracking from '~/experimentation/experiment_tracking';
import * as UploadFileExperiment from '~/projects/upload_file_experiment';
import { trackFileUploadEvent } from '~/projects/upload_file_experiment_tracking';
jest.mock('~/experimentation/experiment_tracking');
const fixture = `<a class='js-upload-file-experiment-trigger' data-toggle='modal' data-target='#modal-upload-blob'></a><div id='modal-upload-blob'></div><div class='project-home-panel empty-project'></div>`;
const findModal = () => document.querySelector('[aria-modal="true"]');
const findTrigger = () => document.querySelector('.js-upload-file-experiment-trigger');
const eventName = 'click_upload_modal_form_submit';
const fixture = `<a class='js-upload-file-experiment-trigger'></a><div class='project-home-panel empty-project'></div>`;
beforeEach(() => {
document.body.innerHTML = fixture;
......@@ -15,23 +14,26 @@ afterEach(() => {
document.body.innerHTML = '';
});
describe('trackUploadFileFormSubmitted', () => {
it('initializes ExperimentTracking with the correct arguments and calls the tracking event with correct arguments', () => {
UploadFileExperiment.trackUploadFileFormSubmitted();
describe('trackFileUploadEvent', () => {
it('initializes ExperimentTracking with the correct tracking event', () => {
trackFileUploadEvent(eventName);
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(eventName);
});
it('calls ExperimentTracking with the correct arguments', () => {
trackFileUploadEvent(eventName);
expect(ExperimentTracking).toHaveBeenCalledWith('empty_repo_upload', {
label: 'blob-upload-modal',
property: 'empty',
});
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith(
'click_upload_modal_form_submit',
);
});
it('initializes ExperimentTracking with the correct arguments when the project is not empty', () => {
it('calls ExperimentTracking with the correct arguments when the project is not empty', () => {
document.querySelector('.empty-project').remove();
UploadFileExperiment.trackUploadFileFormSubmitted();
trackFileUploadEvent(eventName);
expect(ExperimentTracking).toHaveBeenCalledWith('empty_repo_upload', {
label: 'blob-upload-modal',
......@@ -39,14 +41,3 @@ describe('trackUploadFileFormSubmitted', () => {
});
});
});
describe('initUploadFileTrigger', () => {
it('calls modal and tracks event', () => {
UploadFileExperiment.initUploadFileTrigger();
expect(findModal()).not.toExist();
findTrigger().click();
expect(findModal()).toExist();
expect(ExperimentTracking.prototype.event).toHaveBeenCalledWith('click_upload_modal_trigger');
});
});
......@@ -6,9 +6,11 @@ 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 { trackFileUploadEvent } from '~/projects/upload_file_experiment_tracking';
import UploadBlobModal from '~/repository/components/upload_blob_modal.vue';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
jest.mock('~/projects/upload_file_experiment_tracking');
jest.mock('~/flash');
jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn(),
......@@ -19,7 +21,7 @@ const initialProps = {
modalId: 'upload-blob',
commitMessage: 'Upload New File',
targetBranch: 'master',
origionalBranch: 'master',
originalBranch: 'master',
canPushCode: true,
path: 'new_upload',
};
......@@ -160,6 +162,10 @@ describe('UploadBlobModal', () => {
await waitForPromises();
});
it('tracks the click_upload_modal_trigger event when opening the modal', () => {
expect(trackFileUploadEvent).toHaveBeenCalledWith('click_upload_modal_form_submit');
});
it('redirects to the uploaded file', () => {
expect(visitUrl).toHaveBeenCalled();
});
......@@ -179,6 +185,10 @@ describe('UploadBlobModal', () => {
await waitForPromises();
});
it('does not track an event', () => {
expect(trackFileUploadEvent).not.toHaveBeenCalled();
});
it('creates a flash error', () => {
expect(createFlash).toHaveBeenCalledWith('Error uploading file. Please try again.');
});
......
......@@ -563,6 +563,51 @@ RSpec.describe ProjectPresenter do
end
end
end
describe '#upload_anchor_data' do
context 'with empty_repo_upload enabled' do
before do
stub_experiments(empty_repo_upload: :candidate)
end
context 'user can push to branch' do
before do
project.add_developer(user)
end
it 'returns upload_anchor_data' do
expect(presenter.upload_anchor_data).to have_attributes(
is_link: false,
label: a_string_including('Upload file'),
data: {
"can_push_code" => "true",
"original_branch" => "master",
"path" => "/#{project.full_path}/-/create/master",
"project_path" => project.path,
"target_branch" => "master"
}
)
end
end
context 'user cannot push to branch' do
it 'returns nil' do
expect(presenter.upload_anchor_data).to be_nil
end
end
end
context 'with empty_repo_upload disabled' do
before do
stub_experiments(empty_repo_upload: :control)
project.add_developer(user)
end
it 'returns nil' do
expect(presenter.upload_anchor_data).to be_nil
end
end
end
end
describe '#statistics_buttons' do
......
......@@ -97,18 +97,16 @@ end
RSpec.shared_examples 'uploads and commits a new text file via "upload file" button' do
it 'uploads and commits a new text file via "upload file" button', :js do
find('.js-upload-file-experiment-trigger', text: 'Upload file').click
find('[data-testid="upload-file-button"]').click
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('#details-modal-upload-blob') do
fill_in(:commit_message, with: 'New commit message')
end
click_button('Upload file')
wait_for_requests
expect(page).to have_content('New commit message')
expect(page).to have_content('Lorem ipsum dolor sit amet')
expect(page).to have_content('Sed ut perspiciatis unde omnis')
......
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