Skip to content
Snippets Groups Projects
Unverified Commit 87da69af authored by Jay Swain's avatar Jay Swain
Browse files

Allow uploads from empty repo state

A user can upload files from the "empty repo" state.

part of:
https://gitlab.com/groups/gitlab-org/-/epics/4742
parent 0576477b
No related branches found
No related tags found
No related merge requests found
Showing
with 211 additions and 16 deletions
Loading
Loading
@@ -6,6 +6,7 @@ import { sprintf, __ } from '~/locale';
import { visitUrl } from '../lib/utils/url_utility';
import { HIDDEN_CLASS } from '../lib/utils/constants';
import csrf from '../lib/utils/csrf';
import { trackUploadFileFormSubmitted } from '~/projects/upload_file_experiment';
 
Dropzone.autoDiscover = false;
 
Loading
Loading
@@ -83,6 +84,9 @@ export default class BlobFileDropzone {
submitButton.on('click', (e) => {
e.preventDefault();
e.stopPropagation();
trackUploadFileFormSubmitted();
if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
// eslint-disable-next-line no-alert
alert(__('Please select a file'));
Loading
Loading
import Tracking from '~/tracking';
import { get } from 'lodash';
const TRACKING_CONTEXT_SCHEMA = 'iglu:com.gitlab/gitlab_experiment/jsonschema/0-3-0';
export default class ExperimentTracking {
constructor(experimentName, { label } = {}) {
this.label = label;
this.experimentData = get(window, ['gon', 'global', 'experiment', experimentName]);
}
event(action) {
if (!this.experimentData) return false;
Tracking.event(document.body.dataset.page, action, {
label: this.label,
context: {
schema: TRACKING_CONTEXT_SCHEMA,
data: this.experimentData,
},
});
}
}
Loading
Loading
@@ -12,6 +12,7 @@ import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initVueNotificationsDropdown from '~/notifications';
import notificationsDropdown from '../../../notifications_dropdown';
import Star from '../../../star';
import { initUploadFileTrigger } from '~/projects/upload_file_experiment';
 
initReadMore();
new Star(); // eslint-disable-line no-new
Loading
Loading
@@ -24,9 +25,13 @@ new UserCallout({
});
 
// Project show page loads different overview content based on user preferences
const treeSlider = document.getElementById('js-tree-list');
if (treeSlider) {
if (document.querySelector('.js-upload-blob-form')) {
initUploadForm();
initUploadFileTrigger();
}
if (document.getElementById('js-tree-list')) {
initTree();
}
 
Loading
Loading
import ExperimentTracking from '~/experiment_tracking';
const Tracking = new ExperimentTracking('empty_repo_upload', { label: 'blob-upload-modal' });
export function initUploadFileTrigger() {
$('.js-upload-file-experiment-trigger').on('click', (e) => {
e.preventDefault();
e.stopPropagation();
$('#modal-upload-blob').modal();
Tracking.event('click_upload_modal_trigger');
});
}
export function trackUploadFileFormSubmitted() {
Tracking.event('click_upload_modal_form_submit');
}
# frozen_string_literal: true
class EmptyRepoUploadExperiment < ApplicationExperiment
def control_behavior
false
end
def candidate_behavior
true
end
def track_initial_write
return unless should_track? && project.empty_repo?
track(:initial_write)
end
private
def project
context.project
end
end
Loading
Loading
@@ -11,7 +11,7 @@ def stat_anchor_attrs(anchor)
private
 
def button_attribute(anchor)
"btn-#{anchor.class_modifier || 'dashed'}"
anchor.class_modifier || 'btn-dashed'
end
 
def extra_classes(anchor)
Loading
Loading
Loading
Loading
@@ -10,6 +10,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
include BlobHelper
include ChecksCollaboration
include Gitlab::Utils::StrongMemoize
include Gitlab::Experiment::Dsl
 
presents :project
 
Loading
Loading
@@ -33,6 +34,7 @@ def statistics_anchors(show_auto_devops_callout:)
 
def statistics_buttons(show_auto_devops_callout:)
[
upload_anchor_data,
readme_anchor_data,
license_anchor_data,
changelog_anchor_data,
Loading
Loading
@@ -49,6 +51,7 @@ def empty_repo_statistics_anchors
 
def empty_repo_statistics_buttons
[
upload_anchor_data,
new_file_anchor_data,
readme_anchor_data,
license_anchor_data,
Loading
Loading
@@ -232,6 +235,24 @@ def tags_anchor_data
empty_repo? ? nil : project_tags_path(project))
end
 
def upload_anchor_data
return unless empty_repo_upload_experiment?
AnchorData.new(false,
statistic_icon('upload') + _('Upload file'),
'#modal-upload-blob',
'js-upload-file-experiment-trigger',
nil,
nil
)
end
def empty_repo_upload_experiment?
current_user.present? &&
can_current_user_push_to_default_branch? &&
experiment(:empty_repo_upload, project: project).run
end
def new_file_anchor_data
if current_user && can_current_user_push_to_default_branch?
new_file_path = empty_repo? ? ide_edit_path(project, default_branch_or_master) : project_new_blob_path(project, default_branch_or_master)
Loading
Loading
@@ -239,7 +260,7 @@ def new_file_anchor_data
AnchorData.new(false,
statistic_icon + _('New file'),
new_file_path,
'dashed')
'btn-dashed')
end
end
 
Loading
Loading
@@ -252,7 +273,7 @@ def readme_anchor_data
AnchorData.new(false,
statistic_icon('doc-text') + _('README'),
default_view != 'readme' ? readme_path : '#readme',
'default',
'btn-default',
'doc-text')
end
end
Loading
Loading
@@ -266,7 +287,7 @@ def changelog_anchor_data
AnchorData.new(false,
statistic_icon('doc-text') + _('CHANGELOG'),
changelog_path,
'default')
'btn-default')
end
end
 
Loading
Loading
@@ -277,7 +298,7 @@ def license_anchor_data
AnchorData.new(false,
icon + content_tag(:span, license_short_name, class: 'project-stat-value'),
license_path,
'default',
'btn-default',
nil,
'license')
else
Loading
Loading
@@ -302,7 +323,7 @@ def contribution_guide_anchor_data
AnchorData.new(false,
statistic_icon('doc-text') + _('CONTRIBUTING'),
contribution_guide_path,
'default')
'btn-default')
end
end
 
Loading
Loading
@@ -312,7 +333,7 @@ def autodevops_anchor_data(show_auto_devops_callout: false)
AnchorData.new(false,
statistic_icon('settings') + _('Auto DevOps enabled'),
project_settings_ci_cd_path(project, anchor: 'autodevops-settings'),
'default')
'btn-default')
else
AnchorData.new(false,
statistic_icon + _('Enable Auto DevOps'),
Loading
Loading
@@ -337,7 +358,7 @@ def kubernetes_cluster_anchor_data
AnchorData.new(false,
_('Kubernetes'),
cluster_link,
'default')
'btn-default')
end
end
end
Loading
Loading
@@ -351,7 +372,7 @@ def gitlab_ci_anchor_data
AnchorData.new(false,
statistic_icon('doc-text') + _('CI/CD configuration'),
ci_configuration_path,
'default')
'btn-default')
end
end
 
Loading
Loading
Loading
Loading
@@ -77,3 +77,7 @@
%span><
git push -u origin --all
git push -u origin --tags
- if @project.empty_repo_upload_experiment?
- @ref = default_branch_name
= render 'projects/blob/upload', title: _('Upload New File'), placeholder: _('Upload New File'), button_title: _('Upload file'), form_path: project_create_blob_path(@project, @ref), method: :post
Loading
Loading
@@ -4,7 +4,7 @@
= render 'shared/commit_message_container', placeholder: placeholder
 
- if @project.empty_repo?
= hidden_field_tag 'branch_name', @ref
= hidden_field_tag 'branch_name', @ref, class: 'js-branch-name'
- else
- if can?(current_user, :push_code, @project)
.form-group.row.branch
Loading
Loading
Loading
Loading
@@ -123,6 +123,7 @@ def update_remote_mirrors(post_received, project)
 
def after_project_changes_hooks(project, user, refs, changes)
experiment(:new_project_readme, actor: user).track_initial_writes(project)
experiment(:empty_repo_upload, project: project).track_initial_write
repository_update_hook_data = Gitlab::DataBuilder::Repository.update(project, user, changes, refs)
SystemHooksService.new.execute_hooks(repository_update_hook_data, :repository_update_hooks)
Gitlab::UsageDataCounters::SourceCodeCounter.count(:pushes)
Loading
Loading
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe EmptyRepoUploadExperiment do
pending "add some examples to (or delete) #{__FILE__}"
end
# frozen_string_literal: true
 
require 'spec_helper'
require 'gitlab/experiment/rspec'
 
RSpec.describe 'Projects > Show > User uploads files' do
include DropzoneHelper
Loading
Loading
@@ -33,4 +34,35 @@
 
include_examples 'it uploads and commit a new file to a forked project'
end
context 'with an empty repo' do
let(:project) { create(:project, :empty_repo, creator: user) }
context 'when in the empty_repo_upload experiment' do
before do
stub_experiments(empty_repo_upload: :candidate)
visit(project_path(project))
end
it 'uploads and commit a new text file', :js do
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')
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')
end
end
end
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', () => {
preloadFixtures('blob/show.html');
Loading
Loading
@@ -38,6 +43,7 @@ describe('BlobFileDropzone', () => {
replaceFileButton.click();
 
expect(window.alert).not.toHaveBeenCalled();
expect(trackUploadFileFormSubmitted).toHaveBeenCalled();
expect(replaceFileButton.is(':disabled')).toEqual(true);
expect(dropzone.processQueue).toHaveBeenCalled();
});
Loading
Loading
Loading
Loading
@@ -18,7 +18,7 @@
 
context 'when anchor is not a link' do
context 'when class_modifier is set' do
let(:anchor) { anchor_klass.new(false, nil, nil, 'default') }
let(:anchor) { anchor_klass.new(false, nil, nil, 'btn-default') }
 
it 'returns the proper attributes' do
expect(subject[:class]).to include('gl-button btn btn-default')
Loading
Loading
# frozen_string_literal: true
 
require 'spec_helper'
require 'gitlab/experiment/rspec'
 
RSpec.describe ProjectPresenter do
let(:user) { create(:user) }
Loading
Loading
@@ -350,7 +351,7 @@
is_link: false,
label: a_string_including("New file"),
link: presenter.project_new_blob_path(project, 'master'),
class_modifier: 'dashed'
class_modifier: 'btn-dashed'
)
end
 
Loading
Loading
@@ -597,10 +598,12 @@
context 'for a developer' do
before do
project.add_developer(user)
stub_experiments(empty_repo_upload: :candidate)
end
 
it 'orders the items correctly' do
expect(empty_repo_statistics_buttons.map(&:label)).to start_with(
a_string_including('Upload'),
a_string_including('New'),
a_string_including('README'),
a_string_including('LICENSE'),
Loading
Loading
@@ -694,4 +697,44 @@
end
end
end
describe '#empty_repo_upload_experiment?' do
subject { presenter.empty_repo_upload_experiment? }
let(:project) { create(:project_empty_repo) }
context 'when no current_user' do
let(:user) { nil }
it { is_expected.to be false }
end
context 'when current_user is provided' do
context 'and user is not a developer' do
it { is_expected.to be false }
end
context 'and user is a maintainer' do
before do
project.add_maintainer(user)
end
context 'and project is in control group' do
before do
stub_experiments(empty_repo_upload: :control)
end
it { is_expected.to be false }
end
context 'and project is in candidate group' do
before do
stub_experiments(empty_repo_upload: :candidate)
end
it { is_expected.to be true }
end
end
end
end
end
Loading
Loading
@@ -3,7 +3,11 @@
RSpec.shared_examples 'it uploads and commit a new text file' do
it 'uploads and commit a new text file', :js do
find('.add-to-tree').click
click_link('Upload file')
page.within('.dropdown-menu') do
click_link('Upload file')
end
drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt'))
 
page.within('#modal-upload-blob') do
Loading
Loading
@@ -29,7 +33,11 @@
RSpec.shared_examples 'it uploads and commit a new image file' do
it 'uploads and commit a new image file', :js do
find('.add-to-tree').click
click_link('Upload file')
page.within('.dropdown-menu') do
click_link('Upload file')
end
drop_in_dropzone(File.join(Rails.root, 'spec', 'fixtures', 'logo_sample.svg'))
 
page.within('#modal-upload-blob') do
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment