summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb23
-rw-r--r--spec/controllers/projects/milestones_controller_spec.rb28
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb2
-rw-r--r--spec/factories/ci/builds.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb23
-rw-r--r--spec/features/groups/milestone_spec.rb13
-rw-r--r--spec/features/issues/form_spec.rb49
-rw-r--r--spec/features/issues_spec.rb108
-rw-r--r--spec/features/projects/members/share_with_group_spec.rb2
-rw-r--r--spec/features/projects/services/user_activates_packagist_spec.rb24
-rw-r--r--spec/features/projects/services/user_views_services_spec.rb1
-rw-r--r--spec/features/projects/tree/upload_file_spec.rb48
-rw-r--r--spec/features/security/project/internal_access_spec.rb15
-rw-r--r--spec/features/security/project/private_access_spec.rb15
-rw-r--r--spec/features/security/project/public_access_spec.rb15
-rw-r--r--spec/javascripts/gl_form_spec.js4
-rw-r--r--spec/javascripts/header_spec.js3
-rw-r--r--spec/javascripts/issuable_context_spec.js3
-rw-r--r--spec/javascripts/issuable_spec.js102
-rw-r--r--spec/javascripts/labels_issue_sidebar_spec.js3
-rw-r--r--spec/javascripts/merge_request_notes_spec.js2
-rw-r--r--spec/javascripts/notes/components/issue_comment_form_spec.js6
-rw-r--r--spec/javascripts/notes_spec.js2
-rw-r--r--spec/javascripts/repo/components/new_dropdown/index_spec.js56
-rw-r--r--spec/javascripts/repo/components/new_dropdown/modal_spec.js6
-rw-r--r--spec/javascripts/repo/components/new_dropdown/upload_spec.js100
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js46
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js39
-rw-r--r--spec/lib/additional_email_headers_interceptor_spec.rb21
-rw-r--r--spec/lib/gitlab/database_spec.rb22
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb37
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb32
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb26
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/models/email_spec.rb8
-rw-r--r--spec/models/merge_request_diff_commit_spec.rb83
-rw-r--r--spec/models/merge_request_spec.rb10
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb39
-rw-r--r--spec/models/project_services/packagist_service_spec.rb46
-rw-r--r--spec/models/project_spec.rb68
-rw-r--r--spec/models/project_wiki_spec.rb38
-rw-r--r--spec/models/repository_spec.rb38
-rw-r--r--spec/models/wiki_page_spec.rb2
-rw-r--r--spec/requests/api/merge_requests_spec.rb24
-rw-r--r--spec/requests/api/runner_spec.rb16
-rw-r--r--spec/routing/project_routing_spec.rb19
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb49
-rw-r--r--spec/services/milestones/promote_service_spec.rb77
-rw-r--r--spec/services/projects/hashed_storage_migration_service_spec.rb2
-rw-r--r--spec/services/system_hooks_service_spec.rb6
-rw-r--r--spec/support/update_invalid_issuable.rb27
-rw-r--r--spec/uploaders/file_uploader_spec.rb52
-rw-r--r--spec/workers/stuck_merge_jobs_worker_spec.rb9
53 files changed, 1233 insertions, 259 deletions
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 6f48f091a20..aecdfb50759 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -557,6 +557,29 @@ describe Projects::IssuesController do
end
end
end
+
+ describe 'GET #edit' do
+ it_behaves_like 'restricted action', success: 200
+
+ def go(id:)
+ get :edit,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: id
+ end
+ end
+
+ describe 'PUT #update' do
+ it_behaves_like 'restricted action', success: 302
+
+ def go(id:)
+ put :update,
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: id,
+ issue: { title: 'New title' }
+ end
+ end
end
describe 'POST #create' do
diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb
index 573530d0db0..209979e642d 100644
--- a/spec/controllers/projects/milestones_controller_spec.rb
+++ b/spec/controllers/projects/milestones_controller_spec.rb
@@ -86,4 +86,32 @@ describe Projects::MilestonesController do
expect(last_note).to eq('removed milestone')
end
end
+
+ describe '#promote' do
+ context 'promotion succeeds' do
+ before do
+ group = create(:group)
+ group.add_developer(user)
+ milestone.project.update(namespace: group)
+ end
+
+ it 'shows group milestone' do
+ post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid
+
+ group_milestone = assigns(:milestone)
+
+ expect(response).to redirect_to(group_milestone_path(project.group, group_milestone.iid))
+ expect(flash[:notice]).to eq('Milestone has been promoted to group milestone.')
+ end
+ end
+
+ context 'promotion fails' do
+ it 'shows project milestone' do
+ post :promote, namespace_id: project.namespace.id, project_id: project.id, id: milestone.iid
+
+ expect(response).to redirect_to(project_milestone_path(project, milestone))
+ expect(flash[:alert]).to eq('Promotion failed - Project does not belong to a group.')
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index f1ad5dd84ff..1604a2da485 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -46,7 +46,7 @@ describe Projects::PipelinesController do
context 'when performing gitaly calls', :request_store do
it 'limits the Gitaly requests' do
- expect { subject }.to change { Gitlab::GitalyClient.get_request_count }.by(10)
+ expect { subject }.to change { Gitlab::GitalyClient.get_request_count }.by(8)
end
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index c2b59239af9..cf38066dedc 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -119,7 +119,7 @@ FactoryGirl.define do
finished_at nil
end
- factory :ci_build_tag do
+ trait :tag do
tag true
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index c490dce7ab0..85561511101 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -95,6 +95,29 @@ feature 'Admin updates settings' do
expect(find_field('ED25519 SSH keys').value).to eq(forbidden)
end
+ scenario 'Change Performance Bar settings' do
+ group = create(:group)
+
+ check 'Enable the Performance Bar'
+ fill_in 'Allowed group', with: group.path
+
+ click_on 'Save'
+
+ expect(page).to have_content 'Application settings saved successfully'
+
+ expect(find_field('Enable the Performance Bar')).to be_checked
+ expect(find_field('Allowed group').value).to eq group.path
+
+ uncheck 'Enable the Performance Bar'
+
+ click_on 'Save'
+
+ expect(page).to have_content 'Application settings saved successfully'
+
+ expect(find_field('Enable the Performance Bar')).not_to be_checked
+ expect(find_field('Allowed group').value).to be_nil
+ end
+
def check_all_events
page.check('Active')
page.check('Push')
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 12aa54a3da1..1b41b3842c8 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -19,9 +19,9 @@ feature 'Group milestones', :js do
end
it 'renders description preview' do
- form = find('.gfm-form')
+ description = find('.note-textarea')
- form.fill_in(:milestone_description, with: '')
+ description.native.send_keys('')
click_link('Preview')
@@ -31,7 +31,7 @@ feature 'Group milestones', :js do
click_link('Write')
- form.fill_in(:milestone_description, with: ':+1: Nice')
+ description.native.send_keys(':+1: Nice')
click_link('Preview')
@@ -51,6 +51,13 @@ feature 'Group milestones', :js do
expect(find('.start_date')).to have_content(Date.today.at_beginning_of_month.strftime('%b %-d, %Y'))
end
+
+ it 'description input does not support autocomplete' do
+ description = find('.note-textarea')
+ description.native.send_keys('!')
+
+ expect(page).not_to have_selector('.atwho-view')
+ end
end
context 'milestones list' do
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 8ce470fc288..2db6f9a2982 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -218,15 +218,54 @@ describe 'New/edit issue', :js do
context 'edit issue' do
before do
- visit project_issue_path(project, issue)
- page.within('.content .issuable-actions') do
- click_on 'Edit'
+ visit edit_project_issue_path(project, issue)
+ end
+
+ it 'allows user to update issue' do
+ expect(find('input[name="issue[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
+ expect(find('input[name="issue[milestone_id]"]', visible: false).value).to match(milestone.id.to_s)
+ expect(find('a', text: 'Assign to me', visible: false)).not_to be_visible
+
+ page.within '.js-user-search' do
+ expect(page).to have_content user.name
+ end
+
+ page.within '.js-milestone-select' do
+ expect(page).to have_content milestone.title
+ end
+
+ click_button 'Labels'
+ page.within '.dropdown-menu-labels' do
+ click_link label.title
+ click_link label2.title
+ end
+ page.within '.js-label-select' do
+ expect(page).to have_content label.title
+ end
+ expect(page.all('input[name="issue[label_ids][]"]', visible: false)[1].value).to match(label.id.to_s)
+ expect(page.all('input[name="issue[label_ids][]"]', visible: false)[2].value).to match(label2.id.to_s)
+
+ click_button 'Save changes'
+
+ page.within '.issuable-sidebar' do
+ page.within '.assignee' do
+ expect(page).to have_content user.name
+ end
+
+ page.within '.milestone' do
+ expect(page).to have_content milestone.title
+ end
+
+ page.within '.labels' do
+ expect(page).to have_content label.title
+ expect(page).to have_content label2.title
+ end
end
end
it 'description has autocomplete' do
- find_field('issue-description').native.send_keys('')
- fill_in 'issue-description', with: '@'
+ find('#issue_description').native.send_keys('')
+ fill_in 'issue_description', with: '@'
expect(page).to have_selector('.atwho-view')
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 25e99774575..d4fd3a50008 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Issues', :js do
+describe 'Issues' do
include DropzoneHelper
include IssueHelpers
include SortingHelper
@@ -24,15 +24,109 @@ describe 'Issues', :js do
end
before do
- visit project_issue_path(project, issue)
- page.within('.content .issuable-actions') do
- find('.issuable-edit').click
- end
- find('.issue-details .content-block .js-zen-enter').click
+ visit edit_project_issue_path(project, issue)
+ find('.js-zen-enter').click
end
it 'opens new issue popup' do
- expect(page).to have_content(issue.description)
+ expect(page).to have_content("Issue ##{issue.iid}")
+ end
+ end
+
+ describe 'Editing issue assignee' do
+ let!(:issue) do
+ create(:issue,
+ author: user,
+ assignees: [user],
+ project: project)
+ end
+
+ it 'allows user to select unassigned', :js do
+ visit edit_project_issue_path(project, issue)
+
+ expect(page).to have_content "Assignee #{user.name}"
+
+ first('.js-user-search').click
+ click_link 'Unassigned'
+
+ click_button 'Save changes'
+
+ page.within('.assignee') do
+ expect(page).to have_content 'No assignee - assign yourself'
+ end
+
+ expect(issue.reload.assignees).to be_empty
+ end
+ end
+
+ describe 'due date', :js do
+ context 'on new form' do
+ before do
+ visit new_project_issue_path(project)
+ end
+
+ it 'saves with due date' do
+ date = Date.today.at_beginning_of_month
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+ find('#issuable-due-date').click
+
+ page.within '.pika-single' do
+ click_button date.day
+ end
+
+ expect(find('#issuable-due-date').value).to eq date.to_s
+
+ click_button 'Submit issue'
+
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
+ end
+ end
+
+ context 'on edit form' do
+ let(:issue) { create(:issue, author: user, project: project, due_date: Date.today.at_beginning_of_month.to_s) }
+
+ before do
+ visit edit_project_issue_path(project, issue)
+ end
+
+ it 'saves with due date' do
+ date = Date.today.at_beginning_of_month
+
+ expect(find('#issuable-due-date').value).to eq date.to_s
+
+ date = date.tomorrow
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+ find('#issuable-due-date').click
+
+ page.within '.pika-single' do
+ click_button date.day
+ end
+
+ expect(find('#issuable-due-date').value).to eq date.to_s
+
+ click_button 'Save changes'
+
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
+ end
+
+ it 'warns about version conflict' do
+ issue.update(title: "New title")
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+
+ click_button 'Save changes'
+
+ expect(page).to have_content 'Someone edited the issue the same time you did'
+ end
end
end
diff --git a/spec/features/projects/members/share_with_group_spec.rb b/spec/features/projects/members/share_with_group_spec.rb
index 3b368f8e25d..63b5df5a8f5 100644
--- a/spec/features/projects/members/share_with_group_spec.rb
+++ b/spec/features/projects/members/share_with_group_spec.rb
@@ -149,7 +149,7 @@ feature 'Project > Members > Share with Group', :js do
create(:group).add_owner(master)
visit project_settings_members_path(project)
- execute_script 'GroupsSelect.PER_PAGE = 1;'
+ execute_script 'GROUP_SELECT_PER_PAGE = 1;'
open_select2 '#link_group_id'
end
diff --git a/spec/features/projects/services/user_activates_packagist_spec.rb b/spec/features/projects/services/user_activates_packagist_spec.rb
new file mode 100644
index 00000000000..b0cc818f093
--- /dev/null
+++ b/spec/features/projects/services/user_activates_packagist_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe 'User activates Packagist' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+
+ visit(project_settings_integrations_path(project))
+
+ click_link('Packagist')
+ end
+
+ it 'activates service' do
+ check('Active')
+ fill_in('Username', with: 'theUser')
+ fill_in('Token', with: 'verySecret')
+ click_button('Save')
+
+ expect(page).to have_content('Packagist activated.')
+ end
+end
diff --git a/spec/features/projects/services/user_views_services_spec.rb b/spec/features/projects/services/user_views_services_spec.rb
index f86591c2633..5c5e8b66642 100644
--- a/spec/features/projects/services/user_views_services_spec.rb
+++ b/spec/features/projects/services/user_views_services_spec.rb
@@ -21,5 +21,6 @@ describe 'User views services' do
expect(page).to have_content('JetBrains TeamCity')
expect(page).to have_content('Asana')
expect(page).to have_content('Irker (IRC gateway)')
+ expect(page).to have_content('Packagist')
end
end
diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb
new file mode 100644
index 00000000000..7dbe4fd0aa5
--- /dev/null
+++ b/spec/features/projects/tree/upload_file_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+feature 'Multi-file editor upload file', :js do
+ include WaitForRequests
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:txt_file) { File.join(Rails.root, 'spec', 'fixtures', 'doc_sample.txt') }
+ let(:img_file) { File.join(Rails.root, 'spec', 'fixtures', 'dk.png') }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+
+ page.driver.set_cookie('new_repo', 'true')
+
+ visit project_tree_path(project, :master)
+
+ wait_for_requests
+ end
+
+ it 'uploads text file' do
+ find('.add-to-tree').click
+
+ # make the field visible so capybara can use it
+ execute_script('document.querySelector("#file-upload").classList.remove("hidden")')
+ attach_file('file-upload', txt_file)
+
+ find('.add-to-tree').click
+
+ expect(page).to have_selector('.repo-tab', text: 'doc_sample.txt')
+ expect(page).to have_content(File.open(txt_file, &:readline))
+ end
+
+ it 'uploads image file' do
+ find('.add-to-tree').click
+
+ # make the field visible so capybara can use it
+ execute_script('document.querySelector("#file-upload").classList.remove("hidden")')
+ attach_file('file-upload', img_file)
+
+ find('.add-to-tree').click
+
+ expect(page).to have_selector('.repo-tab', text: 'dk.png')
+ expect(page).not_to have_selector('.monaco-editor')
+ expect(page).to have_content('The source could not be displayed for this temporary file.')
+ end
+end
diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb
index d70cf1527e7..a7928857b7d 100644
--- a/spec/features/security/project/internal_access_spec.rb
+++ b/spec/features/security/project/internal_access_spec.rb
@@ -181,6 +181,21 @@ describe "Internal Project Access" do
it { is_expected.to be_denied_for(:visitor) }
end
+ describe "GET /:project_path/issues/:id/edit" do
+ let(:issue) { create(:issue, project: project) }
+ subject { edit_project_issue_path(project, issue) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
describe "GET /:project_path/snippets" do
subject { project_snippets_path(project) }
diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb
index ea130606545..a4396b20afd 100644
--- a/spec/features/security/project/private_access_spec.rb
+++ b/spec/features/security/project/private_access_spec.rb
@@ -181,6 +181,21 @@ describe "Private Project Access" do
it { is_expected.to be_denied_for(:visitor) }
end
+ describe "GET /:project_path/issues/:id/edit" do
+ let(:issue) { create(:issue, project: project) }
+ subject { edit_project_issue_path(project, issue) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
describe "GET /:project_path/snippets" do
subject { project_snippets_path(project) }
diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb
index d15f5af66c9..fccdeb0e5b7 100644
--- a/spec/features/security/project/public_access_spec.rb
+++ b/spec/features/security/project/public_access_spec.rb
@@ -394,6 +394,21 @@ describe "Public Project Access" do
it { is_expected.to be_allowed_for(:visitor) }
end
+ describe "GET /:project_path/issues/:id/edit" do
+ let(:issue) { create(:issue, project: project) }
+ subject { edit_project_issue_path(project, issue) }
+
+ it { is_expected.to be_allowed_for(:admin) }
+ it { is_expected.to be_allowed_for(:owner).of(project) }
+ it { is_expected.to be_allowed_for(:master).of(project) }
+ it { is_expected.to be_allowed_for(:developer).of(project) }
+ it { is_expected.to be_allowed_for(:reporter).of(project) }
+ it { is_expected.to be_denied_for(:guest).of(project) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
+
describe "GET /:project_path/snippets" do
subject { project_snippets_path(project) }
diff --git a/spec/javascripts/gl_form_spec.js b/spec/javascripts/gl_form_spec.js
index 124fc030774..5a8009e57fd 100644
--- a/spec/javascripts/gl_form_spec.js
+++ b/spec/javascripts/gl_form_spec.js
@@ -1,9 +1,9 @@
-import autosize from 'vendor/autosize';
+import Autosize from 'autosize';
import GLForm from '~/gl_form';
import '~/lib/utils/text_utility';
import '~/lib/utils/common_utils';
-window.autosize = autosize;
+window.autosize = Autosize;
describe('GLForm', () => {
describe('when instantiated', function () {
diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js
index 4751eb868a4..2443ffd48f3 100644
--- a/spec/javascripts/header_spec.js
+++ b/spec/javascripts/header_spec.js
@@ -1,4 +1,4 @@
-import '~/header';
+import initTodoToggle from '~/header';
describe('Header', function () {
const todosPendingCount = '.todos-count';
@@ -14,6 +14,7 @@ describe('Header', function () {
preloadFixtures(fixtureTemplate);
beforeEach(() => {
+ initTodoToggle();
loadFixtures(fixtureTemplate);
});
diff --git a/spec/javascripts/issuable_context_spec.js b/spec/javascripts/issuable_context_spec.js
index bcb2b7b24a0..f266209027a 100644
--- a/spec/javascripts/issuable_context_spec.js
+++ b/spec/javascripts/issuable_context_spec.js
@@ -1,6 +1,5 @@
-/* global IssuableContext */
-import '~/issuable_context';
import $ from 'jquery';
+import IssuableContext from '~/issuable_context';
describe('IssuableContext', () => {
describe('toggleHiddenParticipants', () => {
diff --git a/spec/javascripts/issuable_spec.js b/spec/javascripts/issuable_spec.js
index 45f55395d3a..ceee08d47c5 100644
--- a/spec/javascripts/issuable_spec.js
+++ b/spec/javascripts/issuable_spec.js
@@ -1,80 +1,44 @@
-/* global IssuableIndex */
-
-import '~/lib/utils/url_utility';
-import '~/issuable_index';
-
-(() => {
- const BASE_URL = '/user/project/issues?scope=all&state=closed';
- const DEFAULT_PARAMS = '&utf8=%E2%9C%93';
-
- function updateForm(formValues, form) {
- $.each(formValues, (id, value) => {
- $(`#${id}`, form).val(value);
- });
- }
-
- function resetForm(form) {
- $('input[name!="utf8"]', form).each((index, input) => {
- input.setAttribute('value', '');
+import IssuableIndex from '~/issuable_index';
+
+describe('Issuable', () => {
+ let Issuable;
+ describe('initBulkUpdate', () => {
+ it('should not set bulkUpdateSidebar', () => {
+ Issuable = new IssuableIndex('issue_');
+ expect(Issuable.bulkUpdateSidebar).not.toBeDefined();
});
- }
- describe('Issuable', () => {
- preloadFixtures('static/issuable_filter.html.raw');
+ it('should set bulkUpdateSidebar', () => {
+ const element = document.createElement('div');
+ element.classList.add('issues-bulk-update');
+ document.body.appendChild(element);
- beforeEach(() => {
- loadFixtures('static/issuable_filter.html.raw');
- IssuableIndex.init();
- });
-
- it('should be defined', () => {
- expect(window.IssuableIndex).toBeDefined();
+ Issuable = new IssuableIndex('issue_');
+ expect(Issuable.bulkUpdateSidebar).toBeDefined();
});
+ });
- describe('filtering', () => {
- let $filtersForm;
-
- beforeEach(() => {
- $filtersForm = $('.js-filter-form');
- loadFixtures('static/issuable_filter.html.raw');
- resetForm($filtersForm);
- });
-
- it('should contain only the default parameters', () => {
- spyOn(gl.utils, 'visitUrl');
-
- IssuableIndex.filterResults($filtersForm);
-
- expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + DEFAULT_PARAMS);
- });
-
- it('should filter for the phrase "broken"', () => {
- spyOn(gl.utils, 'visitUrl');
-
- updateForm({ search: 'broken' }, $filtersForm);
- IssuableIndex.filterResults($filtersForm);
- const params = `${DEFAULT_PARAMS}&search=broken`;
-
- expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
- });
-
- it('should keep query parameters after modifying filter', () => {
- spyOn(gl.utils, 'visitUrl');
+ describe('resetIncomingEmailToken', () => {
+ beforeEach(() => {
+ const element = document.createElement('a');
+ element.classList.add('incoming-email-token-reset');
+ element.setAttribute('href', 'foo');
+ document.body.appendChild(element);
- // initial filter
- updateForm({ milestone_title: 'v1.0' }, $filtersForm);
+ const input = document.createElement('input');
+ input.setAttribute('id', 'issue_email');
+ document.body.appendChild(input);
- IssuableIndex.filterResults($filtersForm);
- let params = `${DEFAULT_PARAMS}&milestone_title=v1.0`;
- expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
+ Issuable = new IssuableIndex('issue_');
+ });
- // update filter
- updateForm({ label_name: 'Frontend' }, $filtersForm);
+ it('should send request to reset email token', () => {
+ spyOn(jQuery, 'ajax').and.callThrough();
+ document.querySelector('.incoming-email-token-reset').click();
- IssuableIndex.filterResults($filtersForm);
- params = `${DEFAULT_PARAMS}&milestone_title=v1.0&label_name=Frontend`;
- expect(gl.utils.visitUrl).toHaveBeenCalledWith(BASE_URL + params);
- });
+ expect(jQuery.ajax).toHaveBeenCalled();
+ expect(jQuery.ajax.calls.argsFor(0)[0].url).toEqual('foo');
});
});
-})();
+});
+
diff --git a/spec/javascripts/labels_issue_sidebar_spec.js b/spec/javascripts/labels_issue_sidebar_spec.js
index e47adc49224..4e66304e0ad 100644
--- a/spec/javascripts/labels_issue_sidebar_spec.js
+++ b/spec/javascripts/labels_issue_sidebar_spec.js
@@ -1,12 +1,11 @@
/* eslint-disable no-new */
-/* global IssuableContext */
+import IssuableContext from '~/issuable_context';
/* global LabelsSelect */
import '~/gl_dropdown';
import 'select2';
import '~/api';
import '~/create_label';
-import '~/issuable_context';
import '~/users_select';
import '~/labels_select';
diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js
index ac6ace48108..6054b75d0b8 100644
--- a/spec/javascripts/merge_request_notes_spec.js
+++ b/spec/javascripts/merge_request_notes_spec.js
@@ -1,6 +1,6 @@
/* global Notes */
-import 'vendor/autosize';
+import 'autosize';
import '~/gl_form';
import '~/lib/utils/text_utility';
import '~/render_gfm';
diff --git a/spec/javascripts/notes/components/issue_comment_form_spec.js b/spec/javascripts/notes/components/issue_comment_form_spec.js
index 3f659af5c3b..a26fc8f63cc 100644
--- a/spec/javascripts/notes/components/issue_comment_form_spec.js
+++ b/spec/javascripts/notes/components/issue_comment_form_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import autosize from 'vendor/autosize';
+import Autosize from 'autosize';
import store from '~/notes/stores';
import issueCommentForm from '~/notes/components/issue_comment_form.vue';
import { loggedOutIssueData, notesDataMock, userDataMock, issueDataMock } from '../mock_data';
@@ -97,14 +97,14 @@ describe('issue_comment_form component', () => {
});
it('should resize textarea after note discarded', (done) => {
- spyOn(autosize, 'update');
+ spyOn(Autosize, 'update');
spyOn(vm, 'discard').and.callThrough();
vm.note = 'foo';
vm.discard();
Vue.nextTick(() => {
- expect(autosize.update).toHaveBeenCalled();
+ expect(Autosize.update).toHaveBeenCalled();
done();
});
});
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 4546b88e44d..53d8faae911 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable space-before-function-paren, no-unused-expressions, no-var, object-shorthand, comma-dangle, max-len */
/* global Notes */
-import 'vendor/autosize';
+import 'autosize';
import '~/gl_form';
import '~/lib/utils/text_utility';
import '~/render_gfm';
diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js
index ddbfdab582d..ddffef53300 100644
--- a/spec/javascripts/repo/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js
@@ -74,25 +74,38 @@ describe('new dropdown component', () => {
it('closes modal after creating file', () => {
vm.openModal = true;
- eventHub.$emit('createNewEntry', 'testing', type);
+ eventHub.$emit('createNewEntry', {
+ name: 'testing',
+ type,
+ toggleModal: true,
+ });
expect(vm.openModal).toBeFalsy();
});
it('sets editMode to true', () => {
- eventHub.$emit('createNewEntry', 'testing', type);
+ eventHub.$emit('createNewEntry', {
+ name: 'testing',
+ type,
+ });
expect(RepoStore.editMode).toBeTruthy();
});
it('toggles blob view', () => {
- eventHub.$emit('createNewEntry', 'testing', type);
+ eventHub.$emit('createNewEntry', {
+ name: 'testing',
+ type,
+ });
expect(RepoStore.isPreviewView()).toBeFalsy();
});
it('adds file into activeFiles', () => {
- eventHub.$emit('createNewEntry', 'testing', type);
+ eventHub.$emit('createNewEntry', {
+ name: 'testing',
+ type,
+ });
expect(RepoStore.openedFiles.length).toBe(1);
});
@@ -100,7 +113,10 @@ describe('new dropdown component', () => {
it(`creates ${type} in the current stores path`, () => {
RepoStore.path = 'testing';
- eventHub.$emit('createNewEntry', 'testing/app', type);
+ eventHub.$emit('createNewEntry', {
+ name: 'testing/app',
+ type,
+ });
expect(RepoStore.files[0].path).toBe('testing/app');
expect(RepoStore.files[0].name).toBe('app');
@@ -116,7 +132,10 @@ describe('new dropdown component', () => {
describe('file', () => {
it('creates new file', () => {
- eventHub.$emit('createNewEntry', 'testing', 'blob');
+ eventHub.$emit('createNewEntry', {
+ name: 'testing',
+ type: 'blob',
+ });
expect(RepoStore.files.length).toBe(1);
expect(RepoStore.files[0].name).toBe('testing');
@@ -129,7 +148,10 @@ describe('new dropdown component', () => {
name: 'testing',
}));
- eventHub.$emit('createNewEntry', 'testing', 'blob');
+ eventHub.$emit('createNewEntry', {
+ name: 'testing',
+ type: 'blob',
+ });
expect(RepoStore.files.length).toBe(1);
expect(RepoStore.files[0].name).toBe('testing');
@@ -140,7 +162,10 @@ describe('new dropdown component', () => {
describe('tree', () => {
it('creates new tree', () => {
- eventHub.$emit('createNewEntry', 'testing', 'tree');
+ eventHub.$emit('createNewEntry', {
+ name: 'testing',
+ type: 'tree',
+ });
expect(RepoStore.files.length).toBe(1);
expect(RepoStore.files[0].name).toBe('testing');
@@ -151,7 +176,10 @@ describe('new dropdown component', () => {
});
it('creates multiple trees when entryName has slashes', () => {
- eventHub.$emit('createNewEntry', 'app/test', 'tree');
+ eventHub.$emit('createNewEntry', {
+ name: 'app/test',
+ type: 'tree',
+ });
expect(RepoStore.files.length).toBe(1);
expect(RepoStore.files[0].name).toBe('app');
@@ -164,7 +192,10 @@ describe('new dropdown component', () => {
name: 'app',
}));
- eventHub.$emit('createNewEntry', 'app/test', 'tree');
+ eventHub.$emit('createNewEntry', {
+ name: 'app/test',
+ type: 'tree',
+ });
expect(RepoStore.files.length).toBe(1);
expect(RepoStore.files[0].name).toBe('app');
@@ -179,7 +210,10 @@ describe('new dropdown component', () => {
name: 'app',
}));
- eventHub.$emit('createNewEntry', 'app', 'tree');
+ eventHub.$emit('createNewEntry', {
+ name: 'app',
+ type: 'tree',
+ });
expect(RepoStore.files.length).toBe(1);
expect(RepoStore.files[0].name).toBe('app');
diff --git a/spec/javascripts/repo/components/new_dropdown/modal_spec.js b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
index 4c5cdc47c6e..d9fd9b9a595 100644
--- a/spec/javascripts/repo/components/new_dropdown/modal_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
@@ -70,7 +70,11 @@ describe('new file modal component', () => {
vm.createEntryInStore();
- expect(eventHub.$emit).toHaveBeenCalledWith('createNewEntry', 'testing', 'tree');
+ expect(eventHub.$emit).toHaveBeenCalledWith('createNewEntry', {
+ name: 'testing',
+ type: 'tree',
+ toggleModal: true,
+ });
});
});
});
diff --git a/spec/javascripts/repo/components/new_dropdown/upload_spec.js b/spec/javascripts/repo/components/new_dropdown/upload_spec.js
new file mode 100644
index 00000000000..31878e9d327
--- /dev/null
+++ b/spec/javascripts/repo/components/new_dropdown/upload_spec.js
@@ -0,0 +1,100 @@
+import Vue from 'vue';
+import upload from '~/repo/components/new_dropdown/upload.vue';
+import eventHub from '~/repo/event_hub';
+import createComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('new dropdown upload', () => {
+ let vm;
+
+ beforeEach(() => {
+ const Component = Vue.extend(upload);
+
+ vm = createComponent(Component, {
+ currentPath: '',
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('readFile', () => {
+ beforeEach(() => {
+ spyOn(FileReader.prototype, 'readAsText');
+ spyOn(FileReader.prototype, 'readAsDataURL');
+ });
+
+ it('calls readAsText for text files', () => {
+ const file = {
+ type: 'text/html',
+ };
+
+ vm.readFile(file);
+
+ expect(FileReader.prototype.readAsText).toHaveBeenCalledWith(file);
+ });
+
+ it('calls readAsDataURL for non-text files', () => {
+ const file = {
+ type: 'images/png',
+ };
+
+ vm.readFile(file);
+
+ expect(FileReader.prototype.readAsDataURL).toHaveBeenCalledWith(file);
+ });
+ });
+
+ describe('createFile', () => {
+ const target = {
+ result: 'content',
+ };
+ const binaryTarget = {
+ result: 'base64,base64content',
+ };
+ const file = {
+ name: 'file',
+ };
+
+ beforeEach(() => {
+ spyOn(eventHub, '$emit');
+ });
+
+ it('emits createNewEntry event', () => {
+ vm.createFile(target, file, true);
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('createNewEntry', {
+ name: 'file',
+ type: 'blob',
+ content: 'content',
+ toggleModal: false,
+ base64: false,
+ }, true);
+ });
+
+ it('createNewEntry event name contains current path', () => {
+ vm.currentPath = 'testing';
+ vm.createFile(target, file, true);
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('createNewEntry', {
+ name: 'testing/file',
+ type: 'blob',
+ content: 'content',
+ toggleModal: false,
+ base64: false,
+ }, true);
+ });
+
+ it('splits content on base64 if binary', () => {
+ vm.createFile(binaryTarget, file, false);
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('createNewEntry', {
+ name: 'file',
+ type: 'blob',
+ content: 'base64content',
+ toggleModal: false,
+ base64: true,
+ }, false);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index d7019ea408b..df3d29ee1f9 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -43,6 +43,10 @@ describe('MRWidgetReadyToMerge', () => {
vm = createComponent();
});
+ afterEach(() => {
+ vm.$destroy();
+ });
+
describe('props', () => {
it('should have props', () => {
const { mr, service } = readyToMergeComponent.props;
@@ -495,6 +499,48 @@ describe('MRWidgetReadyToMerge', () => {
});
});
+ describe('Merge controls', () => {
+ describe('when allowed to merge', () => {
+ beforeEach(() => {
+ vm = createComponent({
+ mr: { isMergeAllowed: true },
+ });
+ });
+
+ it('shows remove source branch checkbox', () => {
+ expect(vm.$el.querySelector('.js-remove-source-branch-checkbox')).toBeDefined();
+ });
+
+ it('shows modify commit message button', () => {
+ expect(vm.$el.querySelector('.js-modify-commit-message-button')).toBeDefined();
+ });
+
+ it('does not show message about needing to resolve items', () => {
+ expect(vm.$el.querySelector('.js-resolve-mr-widget-items-message')).toBeNull();
+ });
+ });
+
+ describe('when not allowed to merge', () => {
+ beforeEach(() => {
+ vm = createComponent({
+ mr: { isMergeAllowed: false },
+ });
+ });
+
+ it('does not show remove source branch checkbox', () => {
+ expect(vm.$el.querySelector('.js-remove-source-branch-checkbox')).toBeNull();
+ });
+
+ it('does not show modify commit message button', () => {
+ expect(vm.$el.querySelector('.js-modify-commit-message-button')).toBeNull();
+ });
+
+ it('shows message to resolve all items before being allowed to merge', () => {
+ expect(vm.$el.querySelector('.js-resolve-mr-widget-items-message')).toBeDefined();
+ });
+ });
+ });
+
describe('Commit message area', () => {
it('when using merge commits, should show "Modify commit message" button', () => {
const customVm = createComponent({
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
index 52e450e9ba5..8450ad9dbcb 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_link_spec.js
@@ -11,6 +11,7 @@ describe('User Avatar Link Component', function () {
imgCssClasses: 'myextraavatarclass',
tooltipText: 'tooltip text',
tooltipPlacement: 'bottom',
+ username: 'username',
};
const UserAvatarLinkComponent = Vue.extend(UserAvatarLink);
@@ -47,4 +48,42 @@ describe('User Avatar Link Component', function () {
expect(this.userAvatarLink[key]).toBeDefined();
});
});
+
+ describe('no username', function () {
+ beforeEach(function (done) {
+ this.userAvatarLink.username = '';
+
+ Vue.nextTick(done);
+ });
+
+ it('should only render image tag in link', function () {
+ const childElements = this.userAvatarLink.$el.childNodes;
+ expect(childElements[0].tagName).toBe('IMG');
+
+ // Vue will render the hidden component as <!---->
+ expect(childElements[1].tagName).toBeUndefined();
+ });
+
+ it('should render avatar image tooltip', function () {
+ expect(this.userAvatarLink.$el.querySelector('img').dataset.originalTitle).toEqual(this.propsData.tooltipText);
+ });
+ });
+
+ describe('username', function () {
+ it('should not render avatar image tooltip', function () {
+ expect(this.userAvatarLink.$el.querySelector('img').dataset.originalTitle).toEqual('');
+ });
+
+ it('should render username prop in <span>', function () {
+ expect(this.userAvatarLink.$el.querySelector('span').innerText.trim()).toEqual(this.propsData.username);
+ });
+
+ it('should render text tooltip for <span>', function () {
+ expect(this.userAvatarLink.$el.querySelector('span').dataset.originalTitle).toEqual(this.propsData.tooltipText);
+ });
+
+ it('should render text tooltip placement for <span>', function () {
+ expect(this.userAvatarLink.$el.querySelector('span').getAttribute('tooltip-placement')).toEqual(this.propsData.tooltipPlacement);
+ });
+ });
});
diff --git a/spec/lib/additional_email_headers_interceptor_spec.rb b/spec/lib/additional_email_headers_interceptor_spec.rb
index 580450eef1e..b5c1a360ba9 100644
--- a/spec/lib/additional_email_headers_interceptor_spec.rb
+++ b/spec/lib/additional_email_headers_interceptor_spec.rb
@@ -1,12 +1,29 @@
require 'spec_helper'
describe AdditionalEmailHeadersInterceptor do
- it 'adds Auto-Submitted header' do
- mail = ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello').deliver
+ let(:mail) do
+ ActionMailer::Base.mail(to: 'test@mail.com', from: 'info@mail.com', body: 'hello')
+ end
+
+ before do
+ mail.deliver_now
+ end
+ it 'adds Auto-Submitted header' do
expect(mail.header['To'].value).to eq('test@mail.com')
expect(mail.header['From'].value).to eq('info@mail.com')
expect(mail.header['Auto-Submitted'].value).to eq('auto-generated')
expect(mail.header['X-Auto-Response-Suppress'].value).to eq('All')
end
+
+ context 'when the same mail object is sent twice' do
+ before do
+ mail.deliver_now
+ end
+
+ it 'does not add the Auto-Submitted header twice' do
+ expect(mail.header['Auto-Submitted'].value).to eq('auto-generated')
+ expect(mail.header['X-Auto-Response-Suppress'].value).to eq('All')
+ end
+ end
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 5fa94999d25..7aeb85b8f5a 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -256,4 +256,26 @@ describe Gitlab::Database do
expect(described_class.false_value).to eq 0
end
end
+
+ describe '#sanitize_timestamp' do
+ let(:max_timestamp) { Time.at((1 << 31) - 1) }
+
+ subject { described_class.sanitize_timestamp(timestamp) }
+
+ context 'with a timestamp smaller than MAX_TIMESTAMP_VALUE' do
+ let(:timestamp) { max_timestamp - 10.years }
+
+ it 'returns the given timestamp' do
+ expect(subject).to eq(timestamp)
+ end
+ end
+
+ context 'with a timestamp larger than MAX_TIMESTAMP_VALUE' do
+ let(:timestamp) { max_timestamp + 1.second }
+
+ it 'returns MAX_TIMESTAMP_VALUE' do
+ expect(subject).to eq(max_timestamp)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index 245f24e96d4..677eb373d22 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -364,6 +364,43 @@ describe Gitlab::Diff::Position do
end
end
+ describe "position for a missing ref" do
+ let(:diff_refs) do
+ Gitlab::Diff::DiffRefs.new(
+ base_sha: "not_existing_sha",
+ head_sha: "existing_sha"
+ )
+ end
+
+ subject do
+ described_class.new(
+ old_path: "files/ruby/feature.rb",
+ new_path: "files/ruby/feature.rb",
+ old_line: 3,
+ new_line: nil,
+ diff_refs: diff_refs
+ )
+ end
+
+ describe "#diff_file" do
+ it "does not raise exception" do
+ expect { subject.diff_file(project.repository) }.not_to raise_error
+ end
+ end
+
+ describe "#diff_line" do
+ it "does not raise exception" do
+ expect { subject.diff_line(project.repository) }.not_to raise_error
+ end
+ end
+
+ describe "#line_code" do
+ it "does not raise exception" do
+ expect { subject.line_code(project.repository) }.not_to raise_error
+ end
+ end
+ end
+
describe "position for a file in the initial commit" do
let(:commit) { project.commit("1a0b36b3cdad1d2ee32457c102a8c0b7056fa863") }
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index 318a7b7a332..708870060e7 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -7,6 +7,38 @@ describe Gitlab::Git::Branch, seed_helper: true do
it { is_expected.to be_kind_of Array }
+ describe '.find' do
+ subject { described_class.find(repository, branch) }
+
+ before do
+ allow(repository).to receive(:find_branch).with(branch)
+ .and_call_original
+ end
+
+ context 'when finding branch via branch name' do
+ let(:branch) { 'master' }
+
+ it 'returns a branch object' do
+ expect(subject).to be_a(described_class)
+ expect(subject.name).to eq(branch)
+
+ expect(repository).to have_received(:find_branch).with(branch)
+ end
+ end
+
+ context 'when the branch is already a branch' do
+ let(:commit) { repository.commit('master') }
+ let(:branch) { described_class.new(repository, 'master', commit.sha, commit) }
+
+ it 'returns a branch object' do
+ expect(subject).to be_a(described_class)
+ expect(subject).to eq(branch)
+
+ expect(repository).not_to have_received(:find_branch).with(branch)
+ end
+ end
+ end
+
describe '#size' do
subject { super().size }
it { is_expected.to eq(SeedRepo::Repo::BRANCHES.size) }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index e3d320631bc..b161d25b96c 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1135,6 +1135,32 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#merged_branch_names' do
+ context 'when branch names are passed' do
+ it 'only returns the names we are asking' do
+ names = repository.merged_branch_names(%w[merge-test])
+
+ expect(names).to contain_exactly('merge-test')
+ end
+
+ it 'does not return unmerged branch names' do
+ names = repository.merged_branch_names(%w[feature])
+
+ expect(names).to be_empty
+ end
+ end
+
+ context 'when no branch names are specified' do
+ it 'returns all merged branch names' do
+ names = repository.merged_branch_names
+
+ expect(names).to include('merge-test')
+ expect(names).to include('fix-mode')
+ expect(names).not_to include('feature')
+ end
+ end
+ end
+
describe "#ls_files" do
let(:master_file_paths) { repository.ls_files("master") }
let(:not_existed_branch) { repository.ls_files("not_existed_branch") }
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 29baa70d5ae..3824bb0757c 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -195,6 +195,7 @@ project:
- mattermost_slash_commands_service
- slack_slash_commands_service
- irker_service
+- packagist_service
- pivotaltracker_service
- prometheus_service
- hipchat_service
diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb
index b32dd31ae6d..47eb0717c0c 100644
--- a/spec/models/email_spec.rb
+++ b/spec/models/email_spec.rb
@@ -40,4 +40,12 @@ describe Email do
expect(user.emails.confirmed.count).to eq 1
end
end
+
+ describe 'delegation' do
+ let(:user) { create(:user) }
+
+ it 'delegates to :user' do
+ expect(build(:email, user: user).username).to eq user.username
+ end
+ end
end
diff --git a/spec/models/merge_request_diff_commit_spec.rb b/spec/models/merge_request_diff_commit_spec.rb
index 9d4a0ecf8c0..7709cf43200 100644
--- a/spec/models/merge_request_diff_commit_spec.rb
+++ b/spec/models/merge_request_diff_commit_spec.rb
@@ -2,14 +2,93 @@ require 'rails_helper'
describe MergeRequestDiffCommit do
let(:merge_request) { create(:merge_request) }
- subject { merge_request.commits.first }
+ let(:project) { merge_request.project }
describe '#to_hash' do
+ subject { merge_request.commits.first }
+
it 'returns the same results as Commit#to_hash, except for parent_ids' do
- commit_from_repo = merge_request.project.repository.commit(subject.sha)
+ commit_from_repo = project.repository.commit(subject.sha)
commit_from_repo_hash = commit_from_repo.to_hash.merge(parent_ids: [])
expect(subject.to_hash).to eq(commit_from_repo_hash)
end
end
+
+ describe '.create_bulk' do
+ let(:sha_attribute) { Gitlab::Database::ShaAttribute.new }
+ let(:merge_request_diff_id) { merge_request.merge_request_diff.id }
+ let(:commits) do
+ [
+ project.commit('5937ac0a7beb003549fc5fd26fc247adbce4a52e'),
+ project.commit('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
+ ]
+ end
+ let(:rows) do
+ [
+ {
+ "message": "Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets \u003cdmitriy.zaporozhets@gmail.com\u003e\n",
+ "authored_date": "2014-02-27T10:01:38.000+01:00".to_time,
+ "author_name": "Dmitriy Zaporozhets",
+ "author_email": "dmitriy.zaporozhets@gmail.com",
+ "committed_date": "2014-02-27T10:01:38.000+01:00".to_time,
+ "committer_name": "Dmitriy Zaporozhets",
+ "committer_email": "dmitriy.zaporozhets@gmail.com",
+ "merge_request_diff_id": merge_request_diff_id,
+ "relative_order": 0,
+ "sha": sha_attribute.type_cast_for_database('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ },
+ {
+ "message": "Change some files\n\nSigned-off-by: Dmitriy Zaporozhets \u003cdmitriy.zaporozhets@gmail.com\u003e\n",
+ "authored_date": "2014-02-27T09:57:31.000+01:00".to_time,
+ "author_name": "Dmitriy Zaporozhets",
+ "author_email": "dmitriy.zaporozhets@gmail.com",
+ "committed_date": "2014-02-27T09:57:31.000+01:00".to_time,
+ "committer_name": "Dmitriy Zaporozhets",
+ "committer_email": "dmitriy.zaporozhets@gmail.com",
+ "merge_request_diff_id": merge_request_diff_id,
+ "relative_order": 1,
+ "sha": sha_attribute.type_cast_for_database('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
+ }
+ ]
+ end
+
+ subject { described_class.create_bulk(merge_request_diff_id, commits) }
+
+ it 'inserts the commits into the database en masse' do
+ expect(Gitlab::Database).to receive(:bulk_insert)
+ .with(described_class.table_name, rows)
+
+ subject
+ end
+
+ context 'with dates larger than the DB limit' do
+ let(:commits) do
+ # This commit's date is "Sun Aug 17 07:12:55 292278994 +0000"
+ [project.commit('ba3343bc4fa403a8dfbfcab7fc1a8c29ee34bd69')]
+ end
+ let(:timestamp) { Time.at((1 << 31) - 1) }
+ let(:rows) do
+ [{
+ "message": "Weird commit date\n",
+ "authored_date": timestamp,
+ "author_name": "Alejandro Rodríguez",
+ "author_email": "alejorro70@gmail.com",
+ "committed_date": timestamp,
+ "committer_name": "Alejandro Rodríguez",
+ "committer_email": "alejorro70@gmail.com",
+ "merge_request_diff_id": merge_request_diff_id,
+ "relative_order": 0,
+ "sha": sha_attribute.type_cast_for_database('ba3343bc4fa403a8dfbfcab7fc1a8c29ee34bd69')
+ }]
+ end
+
+ it 'uses a sanitized date' do
+ expect(Gitlab::Database).to receive(:bulk_insert)
+ .with(described_class.table_name, rows)
+
+ subject
+ end
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 73e038b61ed..476a2697605 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -86,7 +86,7 @@ describe MergeRequest do
context 'when the target branch does not exist' do
before do
- project.repository.raw_repository.delete_branch(subject.target_branch)
+ project.repository.rm_branch(subject.author, subject.target_branch)
end
it 'returns nil' do
@@ -1388,7 +1388,7 @@ describe MergeRequest do
context 'when the target branch does not exist' do
before do
- subject.project.repository.raw_repository.delete_branch(subject.target_branch)
+ subject.project.repository.rm_branch(subject.author, subject.target_branch)
end
it 'returns nil' do
@@ -1460,6 +1460,12 @@ describe MergeRequest do
end
describe '#merge_ongoing?' do
+ it 'returns true when the merge request is locked' do
+ merge_request = build_stubbed(:merge_request, state: :locked)
+
+ expect(merge_request.merge_ongoing?).to be(true)
+ end
+
it 'returns true when merge_id, MR is not merged and it has no running job' do
merge_request = build_stubbed(:merge_request, state: :open, merge_jid: 'foo')
allow(Gitlab::SidekiqStatus).to receive(:running?).with('foo') { true }
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index 2298dcab55f..00de536a18b 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -99,45 +99,34 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
describe '#actual_namespace' do
subject { service.actual_namespace }
- it "returns the default namespace" do
- is_expected.to eq(service.send(:default_namespace))
- end
-
- context 'when namespace is specified' do
- before do
- service.namespace = 'my-namespace'
+ shared_examples 'a correctly formatted namespace' do
+ it 'returns a valid Kubernetes namespace name' do
+ expect(subject).to match(Gitlab::Regex.kubernetes_namespace_regex)
+ expect(subject).to eq(expected_namespace)
end
+ end
- it "returns the user-namespace" do
- is_expected.to eq('my-namespace')
- end
+ it_behaves_like 'a correctly formatted namespace' do
+ let(:expected_namespace) { service.send(:default_namespace) }
end
- context 'when service is not assigned to project' do
+ context 'when the project path contains forbidden characters' do
before do
- service.project = nil
+ project.path = '-a_Strange.Path--forSure'
end
- it "does not return namespace" do
- is_expected.to be_nil
+ it_behaves_like 'a correctly formatted namespace' do
+ let(:expected_namespace) { "a-strange-path--forsure-#{project.id}" }
end
end
- end
-
- describe '#actual_namespace' do
- subject { service.actual_namespace }
-
- it "returns the default namespace" do
- is_expected.to eq(service.send(:default_namespace))
- end
context 'when namespace is specified' do
before do
service.namespace = 'my-namespace'
end
- it "returns the user-namespace" do
- is_expected.to eq('my-namespace')
+ it_behaves_like 'a correctly formatted namespace' do
+ let(:expected_namespace) { 'my-namespace' }
end
end
@@ -146,7 +135,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
service.project = nil
end
- it "does not return namespace" do
+ it 'does not return namespace' do
is_expected.to be_nil
end
end
diff --git a/spec/models/project_services/packagist_service_spec.rb b/spec/models/project_services/packagist_service_spec.rb
new file mode 100644
index 00000000000..6acee311700
--- /dev/null
+++ b/spec/models/project_services/packagist_service_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe PackagistService do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ let(:project) { create(:project) }
+
+ let(:packagist_server) { 'https://packagist.example.com' }
+ let(:packagist_username) { 'theUser' }
+ let(:packagist_token) { 'verySecret' }
+ let(:packagist_hook_url) do
+ "#{packagist_server}/api/update-package?username=#{packagist_username}&apiToken=#{packagist_token}"
+ end
+
+ let(:packagist_params) do
+ {
+ active: true,
+ project: project,
+ properties: {
+ username: packagist_username,
+ token: packagist_token,
+ server: packagist_server
+ }
+ }
+ end
+
+ describe '#execute' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
+ let(:packagist_service) { described_class.create(packagist_params) }
+
+ before do
+ stub_request(:post, packagist_hook_url)
+ end
+
+ it 'calls Packagist API' do
+ packagist_service.execute(push_sample_data)
+
+ expect(a_request(:post, packagist_hook_url)).to have_been_made.once
+ end
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 74eba7e33f6..ed6e42d476e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -24,6 +24,7 @@ describe Project do
it { is_expected.to have_one(:slack_service) }
it { is_expected.to have_one(:microsoft_teams_service) }
it { is_expected.to have_one(:mattermost_service) }
+ it { is_expected.to have_one(:packagist_service) }
it { is_expected.to have_one(:pushover_service) }
it { is_expected.to have_one(:asana_service) }
it { is_expected.to have_many(:boards) }
@@ -2452,6 +2453,7 @@ describe Project do
context 'legacy storage' do
let(:project) { create(:project, :repository) }
let(:gitlab_shell) { Gitlab::Shell.new }
+ let(:project_storage) { project.send(:storage) }
before do
allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
@@ -2493,7 +2495,7 @@ describe Project do
describe '#hashed_storage?' do
it 'returns false' do
- expect(project.hashed_storage?).to be_falsey
+ expect(project.hashed_storage?(:repository)).to be_falsey
end
end
@@ -2546,6 +2548,30 @@ describe Project do
it { expect { subject }.to raise_error(StandardError) }
end
+
+ context 'gitlab pages' do
+ before do
+ expect(project_storage).to receive(:rename_repo) { true }
+ end
+
+ it 'moves pages folder to new location' do
+ expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
+
+ project.rename_repo
+ end
+ end
+
+ context 'attachments' do
+ before do
+ expect(project_storage).to receive(:rename_repo) { true }
+ end
+
+ it 'moves uploads folder to new location' do
+ expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)
+
+ project.rename_repo
+ end
+ end
end
describe '#pages_path' do
@@ -2605,8 +2631,14 @@ describe Project do
end
describe '#hashed_storage?' do
- it 'returns true' do
- expect(project.hashed_storage?).to be_truthy
+ it 'returns true if rolled out' do
+ expect(project.hashed_storage?(:attachments)).to be_truthy
+ end
+
+ it 'returns false when not rolled out yet' do
+ project.storage_version = 1
+
+ expect(project.hashed_storage?(:attachments)).to be_falsey
end
end
@@ -2649,10 +2681,6 @@ describe Project do
.to receive(:execute_hooks_for)
.with(project, :rename)
- expect_any_instance_of(Gitlab::UploadsTransfer)
- .to receive(:rename_project)
- .with('foo', project.path, project.namespace.full_path)
-
expect(project).to receive(:expire_caches_before_rename)
expect(project).to receive(:expires_full_path_cache)
@@ -2673,6 +2701,32 @@ describe Project do
it { expect { subject }.to raise_error(StandardError) }
end
+
+ context 'gitlab pages' do
+ it 'moves pages folder to new location' do
+ expect_any_instance_of(Gitlab::PagesTransfer).to receive(:rename_project)
+
+ project.rename_repo
+ end
+ end
+
+ context 'attachments' do
+ it 'keeps uploads folder location unchanged' do
+ expect_any_instance_of(Gitlab::UploadsTransfer).not_to receive(:rename_project)
+
+ project.rename_repo
+ end
+
+ context 'when not rolled out' do
+ let(:project) { create(:project, :repository, storage_version: 1) }
+
+ it 'moves pages folder to new location' do
+ expect_any_instance_of(Gitlab::UploadsTransfer).to receive(:rename_project)
+
+ project.rename_repo
+ end
+ end
+ end
end
describe '#pages_path' do
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index f10d9383ae2..95d58b96f33 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -265,23 +265,33 @@ describe ProjectWiki do
end
describe "#delete_page" do
- before do
- create_page("index", "some content")
- @page = subject.wiki.page(title: "index")
- end
+ shared_examples 'deleting a wiki page' do
+ before do
+ create_page("index", "some content")
+ @page = subject.wiki.page(title: "index")
+ end
- it "deletes the page" do
- subject.delete_page(@page)
- expect(subject.pages.count).to eq(0)
- end
+ it "deletes the page" do
+ subject.delete_page(@page)
+ expect(subject.pages.count).to eq(0)
+ end
- it 'updates project activity' do
- subject.delete_page(@page)
+ it 'updates project activity' do
+ subject.delete_page(@page)
- project.reload
+ project.reload
- expect(project.last_activity_at).to be_within(1.minute).of(Time.now)
- expect(project.last_repository_updated_at).to be_within(1.minute).of(Time.now)
+ expect(project.last_activity_at).to be_within(1.minute).of(Time.now)
+ expect(project.last_repository_updated_at).to be_within(1.minute).of(Time.now)
+ end
+ end
+
+ context 'when Gitaly wiki_delete_page is enabled' do
+ it_behaves_like 'deleting a wiki page'
+ end
+
+ context 'when Gitaly wiki_delete_page is disabled', :skip_gitaly_mock do
+ it_behaves_like 'deleting a wiki page'
end
end
@@ -343,6 +353,6 @@ describe ProjectWiki do
end
def destroy_page(page)
- subject.delete_page(page, commit_details)
+ subject.delete_page(page, "test commit")
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 874368ada67..d7c07676911 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -299,6 +299,24 @@ describe Repository do
it { is_expected.to be_falsey }
end
+
+ context 'when pre-loaded merged branches are provided' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:branch, :pre_loaded, :expected) do
+ 'not-merged-branch' | ['branch-merged'] | false
+ 'branch-merged' | ['not-merged-branch'] | false
+ 'branch-merged' | ['branch-merged'] | true
+ 'not-merged-branch' | ['not-merged-branch'] | false
+ 'master' | ['master'] | false
+ end
+
+ with_them do
+ subject { repository.merged_to_root_ref?(branch, pre_loaded) }
+
+ it { is_expected.to eq(expected) }
+ end
+ end
end
describe '#can_be_merged?' do
@@ -2260,4 +2278,24 @@ describe Repository do
end
end
end
+
+ describe 'commit cache' do
+ set(:project) { create(:project, :repository) }
+
+ it 'caches based on SHA' do
+ # Gets the commit oid, and warms the cache
+ oid = project.commit.id
+
+ expect(Gitlab::Git::Commit).not_to receive(:find).once
+
+ project.commit_by(oid: oid)
+ end
+
+ it 'caches nil values' do
+ expect(Gitlab::Git::Commit).to receive(:find).once
+
+ project.commit_by(oid: '1' * 40)
+ project.commit_by(oid: '1' * 40)
+ end
+ end
end
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 1f14d06997e..a7227b38850 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -402,7 +402,7 @@ describe WikiPage do
def destroy_page(title)
page = wiki.wiki.page(title: title)
- wiki.delete_page(page, commit_details)
+ wiki.delete_page(page, "test commit")
end
def get_slugs(page_or_dir)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 28b1404a4f7..024cfe8b372 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -1061,6 +1061,30 @@ describe API::MergeRequests do
end
end
+ describe 'POST :id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds' do
+ before do
+ ::MergeRequests::MergeWhenPipelineSucceedsService.new(merge_request.target_project, user).execute(merge_request)
+ end
+
+ it 'removes the merge_when_pipeline_succeeds status' do
+ post api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/cancel_merge_when_pipeline_succeeds", user)
+
+ expect(response).to have_gitlab_http_status(201)
+ end
+
+ it 'returns 404 if the merge request is not found' do
+ post api("/projects/#{project.id}/merge_requests/123/merge_when_pipeline_succeeds", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns 404 if the merge request id is used instead of iid' do
+ post api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge_when_pipeline_succeeds", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
describe 'Time tracking' do
let(:issuable) { merge_request }
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 5fd76fce7df..47f4ccd4887 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -385,7 +385,7 @@ describe API::Runner do
end
context 'when job is made for tag' do
- let!(:job) { create(:ci_build_tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
it 'sets branch as ref_type' do
request_job
@@ -436,8 +436,8 @@ describe API::Runner do
end
context 'when project and pipeline have multiple jobs' do
- let!(:job) { create(:ci_build_tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
- let!(:job2) { create(:ci_build_tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job2) { create(:ci_build, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
let!(:test_job) { create(:ci_build, pipeline: pipeline, name: 'deploy', stage: 'deploy', stage_idx: 1) }
before do
@@ -458,7 +458,7 @@ describe API::Runner do
end
context 'when pipeline have jobs with artifacts' do
- let!(:job) { create(:ci_build_tag, :artifacts, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :tag, :artifacts, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
let!(:test_job) { create(:ci_build, pipeline: pipeline, name: 'deploy', stage: 'deploy', stage_idx: 1) }
before do
@@ -478,8 +478,8 @@ describe API::Runner do
end
context 'when explicit dependencies are defined' do
- let!(:job) { create(:ci_build_tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
- let!(:job2) { create(:ci_build_tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job2) { create(:ci_build, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
let!(:test_job) do
create(:ci_build, pipeline: pipeline, token: 'test-job-token', name: 'deploy',
stage: 'deploy', stage_idx: 1,
@@ -502,8 +502,8 @@ describe API::Runner do
end
context 'when dependencies is an empty array' do
- let!(:job) { create(:ci_build_tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
- let!(:job2) { create(:ci_build_tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
+ let!(:job) { create(:ci_build, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
+ let!(:job2) { create(:ci_build, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
let!(:empty_dependencies_job) do
create(:ci_build, pipeline: pipeline, token: 'test-job-token', name: 'empty_dependencies_job',
stage: 'deploy', stage_idx: 1,
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index 39d44245c3f..fb1281a6b42 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -426,18 +426,23 @@ describe 'project routing' do
end
end
- # project_milestones GET /:project_id/milestones(.:format) milestones#index
- # POST /:project_id/milestones(.:format) milestones#create
- # new_project_milestone GET /:project_id/milestones/new(.:format) milestones#new
- # edit_project_milestone GET /:project_id/milestones/:id/edit(.:format) milestones#edit
- # project_milestone GET /:project_id/milestones/:id(.:format) milestones#show
- # PUT /:project_id/milestones/:id(.:format) milestones#update
- # DELETE /:project_id/milestones/:id(.:format) milestones#destroy
+ # project_milestones GET /:project_id/milestones(.:format) milestones#index
+ # POST /:project_id/milestones(.:format) milestones#create
+ # new_project_milestone GET /:project_id/milestones/new(.:format) milestones#new
+ # edit_project_milestone GET /:project_id/milestones/:id/edit(.:format) milestones#edit
+ # project_milestone GET /:project_id/milestones/:id(.:format) milestones#show
+ # PUT /:project_id/milestones/:id(.:format) milestones#update
+ # DELETE /:project_id/milestones/:id(.:format) milestones#destroy
+ # promote_project_milestone POST /:project_id/milestones/:id/promote milestones#promote
describe Projects::MilestonesController, 'routing' do
it_behaves_like 'RESTful project resources' do
let(:controller) { 'milestones' }
let(:actions) { [:index, :create, :new, :edit, :show, :update] }
end
+
+ it 'to #promote' do
+ expect(post('/gitlab/gitlabhq/milestones/1/promote')).to route_to('projects/milestones#promote', namespace_id: 'gitlab', project_id: 'gitlabhq', id: "1")
+ end
end
# project_labels GET /:project_id/labels(.:format) labels#index
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index d1043f99b5a..ac196e92601 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -12,55 +12,6 @@ describe MergeRequests::MergeService do
end
describe '#execute' do
- context 'MergeRequest#merge_jid' do
- let(:service) do
- described_class.new(project, user, commit_message: 'Awesome message')
- end
-
- before do
- merge_request.update_column(:merge_jid, 'hash-123')
- end
-
- it 'is cleaned when no error is raised' do
- service.execute(merge_request)
-
- expect(merge_request.reload.merge_jid).to be_nil
- end
-
- it 'is cleaned when expected error is raised' do
- allow(service).to receive(:commit).and_raise(described_class::MergeError)
-
- service.execute(merge_request)
-
- expect(merge_request.reload.merge_jid).to be_nil
- end
-
- it 'is cleaned when merge request is not mergeable' do
- allow(merge_request).to receive(:mergeable?).and_return(false)
-
- service.execute(merge_request)
-
- expect(merge_request.reload.merge_jid).to be_nil
- end
-
- it 'is cleaned when no source is found' do
- allow(merge_request).to receive(:diff_head_sha).and_return(nil)
-
- service.execute(merge_request)
-
- expect(merge_request.reload.merge_jid).to be_nil
- end
-
- it 'is not cleaned when unexpected error is raised' do
- service = described_class.new(project, user, commit_message: 'Awesome message')
- allow(service).to receive(:commit).and_raise(StandardError)
-
- expect { service.execute(merge_request) }.to raise_error(StandardError)
-
- expect(merge_request.reload.merge_jid).to be_present
- end
- end
-
context 'valid params' do
let(:service) { described_class.new(project, user, commit_message: 'Awesome message') }
diff --git a/spec/services/milestones/promote_service_spec.rb b/spec/services/milestones/promote_service_spec.rb
new file mode 100644
index 00000000000..9f2df6d6d19
--- /dev/null
+++ b/spec/services/milestones/promote_service_spec.rb
@@ -0,0 +1,77 @@
+require 'spec_helper'
+
+describe Milestones::PromoteService do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, namespace: group) }
+ let(:user) { create(:user) }
+ let(:milestone_title) { 'project milestone' }
+ let(:milestone) { create(:milestone, project: project, title: milestone_title) }
+ let(:service) { described_class.new(project, user) }
+
+ describe '#execute' do
+ before do
+ group.add_master(user)
+ end
+
+ context 'validations' do
+ it 'raises error if milestone does not belong to a project' do
+ allow(milestone).to receive(:project_milestone?).and_return(false)
+
+ expect { service.execute(milestone) }.to raise_error(described_class::PromoteMilestoneError)
+ end
+
+ it 'raises error if project does not belong to a group' do
+ project.update(namespace: user.namespace)
+
+ expect { service.execute(milestone) }.to raise_error(described_class::PromoteMilestoneError)
+ end
+ end
+
+ context 'without duplicated milestone titles across projects' do
+ it 'promotes project milestone to group milestone' do
+ promoted_milestone = service.execute(milestone)
+
+ expect(promoted_milestone).to be_group_milestone
+ end
+
+ it 'sets issuables with new promoted milestone' do
+ issue = create(:issue, milestone: milestone, project: project)
+ merge_request = create(:merge_request, milestone: milestone, source_project: project)
+
+ promoted_milestone = service.execute(milestone)
+
+ expect(promoted_milestone).to be_group_milestone
+ expect(issue.reload.milestone).to eq(promoted_milestone)
+ expect(merge_request.reload.milestone).to eq(promoted_milestone)
+ end
+ end
+
+ context 'with duplicated milestone titles across projects' do
+ let(:project_2) { create(:project, namespace: group) }
+ let!(:milestone_2) { create(:milestone, project: project_2, title: milestone_title) }
+
+ it 'deletes project milestones with the same title' do
+ promoted_milestone = service.execute(milestone)
+
+ expect(promoted_milestone).to be_group_milestone
+ expect(promoted_milestone).to be_valid
+ expect(Milestone.exists?(milestone.id)).to be_falsy
+ expect(Milestone.exists?(milestone_2.id)).to be_falsy
+ end
+
+ it 'sets all issuables with new promoted milestone' do
+ issue = create(:issue, milestone: milestone, project: project)
+ issue_2 = create(:issue, milestone: milestone_2, project: project_2)
+ merge_request = create(:merge_request, milestone: milestone, source_project: project)
+ merge_request_2 = create(:merge_request, milestone: milestone_2, source_project: project_2)
+
+ promoted_milestone = service.execute(milestone)
+
+ expect(issue.reload.milestone).to eq(promoted_milestone)
+ expect(issue_2.reload.milestone).to eq(promoted_milestone)
+ expect(merge_request.reload.milestone).to eq(promoted_milestone)
+ expect(merge_request_2.reload.milestone).to eq(promoted_milestone)
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/hashed_storage_migration_service_spec.rb b/spec/services/projects/hashed_storage_migration_service_spec.rb
index aa1988d29d6..b71b47c59b6 100644
--- a/spec/services/projects/hashed_storage_migration_service_spec.rb
+++ b/spec/services/projects/hashed_storage_migration_service_spec.rb
@@ -23,7 +23,7 @@ describe Projects::HashedStorageMigrationService do
it 'updates project to be hashed and not read-only' do
service.execute
- expect(project.hashed_storage?).to be_truthy
+ expect(project.hashed_storage?(:repository)).to be_truthy
expect(project.repository_read_only).to be_falsey
end
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 8b5d9187785..8f7aea533dc 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -63,6 +63,12 @@ describe SystemHooksService do
:group_id, :user_id, :user_username, :user_name, :user_email, :group_access
)
end
+
+ it 'includes the correct project visibility level' do
+ data = event_data(project, :create)
+
+ expect(data[:project_visibility]).to eq('private')
+ end
end
context 'event names' do
diff --git a/spec/support/update_invalid_issuable.rb b/spec/support/update_invalid_issuable.rb
index 50a1d4a56e2..1490287681b 100644
--- a/spec/support/update_invalid_issuable.rb
+++ b/spec/support/update_invalid_issuable.rb
@@ -25,13 +25,11 @@ shared_examples 'update invalid issuable' do |klass|
.and_raise(ActiveRecord::StaleObjectError.new(issuable, :save))
end
- if klass == MergeRequest
- it 'renders edit when format is html' do
- put :update, params
+ it 'renders edit when format is html' do
+ put :update, params
- expect(response).to render_template(:edit)
- expect(assigns[:conflict]).to be_truthy
- end
+ expect(response).to render_template(:edit)
+ expect(assigns[:conflict]).to be_truthy
end
it 'renders json error message when format is json' do
@@ -44,17 +42,16 @@ shared_examples 'update invalid issuable' do |klass|
end
end
- if klass == MergeRequest
- context 'when updating an invalid issuable' do
- before do
- params[:merge_request][:title] = ""
- end
+ context 'when updating an invalid issuable' do
+ before do
+ key = klass == Issue ? :issue : :merge_request
+ params[key][:title] = ""
+ end
- it 'renders edit when merge request is invalid' do
- put :update, params
+ it 'renders edit when merge request is invalid' do
+ put :update, params
- expect(response).to render_template(:edit)
- end
+ expect(response).to render_template(:edit)
end
end
end
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index 2492d56a5cf..f52b2bab05b 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -3,25 +3,51 @@ require 'spec_helper'
describe FileUploader do
let(:uploader) { described_class.new(build_stubbed(:project)) }
- describe '.absolute_path' do
- it 'returns the correct absolute path by building it dynamically' do
- project = build_stubbed(:project)
- upload = double(model: project, path: 'secret/foo.jpg')
+ context 'legacy storage' do
+ let(:project) { build_stubbed(:project) }
- dynamic_segment = project.path_with_namespace
+ describe '.absolute_path' do
+ it 'returns the correct absolute path by building it dynamically' do
+ upload = double(model: project, path: 'secret/foo.jpg')
- expect(described_class.absolute_path(upload))
- .to end_with("#{dynamic_segment}/secret/foo.jpg")
+ dynamic_segment = project.full_path
+
+ expect(described_class.absolute_path(upload))
+ .to end_with("#{dynamic_segment}/secret/foo.jpg")
+ end
+ end
+
+ describe "#store_dir" do
+ it "stores in the namespace path" do
+ uploader = described_class.new(project)
+
+ expect(uploader.store_dir).to include(project.full_path)
+ expect(uploader.store_dir).not_to include("system")
+ end
end
end
- describe "#store_dir" do
- it "stores in the namespace path" do
- project = build_stubbed(:project)
- uploader = described_class.new(project)
+ context 'hashed storage' do
+ let(:project) { build_stubbed(:project, :hashed) }
+
+ describe '.absolute_path' do
+ it 'returns the correct absolute path by building it dynamically' do
+ upload = double(model: project, path: 'secret/foo.jpg')
+
+ dynamic_segment = project.disk_path
+
+ expect(described_class.absolute_path(upload))
+ .to end_with("#{dynamic_segment}/secret/foo.jpg")
+ end
+ end
+
+ describe "#store_dir" do
+ it "stores in the namespace path" do
+ uploader = described_class.new(project)
- expect(uploader.store_dir).to include(project.path_with_namespace)
- expect(uploader.store_dir).not_to include("system")
+ expect(uploader.store_dir).to include(project.disk_path)
+ expect(uploader.store_dir).not_to include("system")
+ end
end
end
diff --git a/spec/workers/stuck_merge_jobs_worker_spec.rb b/spec/workers/stuck_merge_jobs_worker_spec.rb
index a5ad78393c9..f8b55e873df 100644
--- a/spec/workers/stuck_merge_jobs_worker_spec.rb
+++ b/spec/workers/stuck_merge_jobs_worker_spec.rb
@@ -12,8 +12,13 @@ describe StuckMergeJobsWorker do
worker.perform
- expect(mr_with_sha.reload).to be_merged
- expect(mr_without_sha.reload).to be_opened
+ mr_with_sha.reload
+ mr_without_sha.reload
+
+ expect(mr_with_sha).to be_merged
+ expect(mr_without_sha).to be_opened
+ expect(mr_with_sha.merge_jid).to be_present
+ expect(mr_without_sha.merge_jid).to be_nil
end
it 'updates merge request to opened when locked but has not been merged' do