summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorMike Greiling <mike@pixelcog.com>2017-08-07 22:44:47 -0500
committerMike Greiling <mike@pixelcog.com>2017-08-07 22:44:47 -0500
commit0994bbf9dd79413b3c22381bc2d82fcc41aa13bc (patch)
tree4488184f950fb66ae761209952722bb72bf18b72 /spec
parent1375db509a4cd30d759d588ee85a8aec2771d78a (diff)
parent9e7ac48bc11141762816f157247baaf9e61618b3 (diff)
downloadgitlab-ce-0994bbf9dd79413b3c22381bc2d82fcc41aa13bc.tar.gz
Merge branch 'master' into ide
* master: (86 commits) Show all labels 33874 confidential issue redesign Exclude merge_jid on Import/Export attribute configuration Resolve "User dropdown in filtered search does not load avatar on `master`" Re-add column locked_at on migration rollback Group-level new issue & MR using previously selected project [EE Backport] Update log audit event in omniauth_callbacks_controller.rb more eagerly bail when the state is prevented Move locked_at removal to post-deployment migration Add class to other sidebars Improve mobile sidebar reduce iterations by keeping a count of remaining enablers Store & use ConvDev percentages returned by Version app Store MergeWorker JID on merge request, and clean up stuck merges Backport changes in https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2551 to CE DRY up caching in AbstractReferenceFilter Update CHANGELOG Add CHANGELOG entry Fix html structure Removes test for removed behavior Port form back to use form_tag ...
Diffstat (limited to 'spec')
-rw-r--r--spec/factories/conversational_development_index_metrics.rb10
-rw-r--r--spec/features/boards/boards_spec.rb4
-rw-r--r--spec/features/boards/sidebar_spec.rb6
-rw-r--r--spec/features/dashboard/issues_spec.rb15
-rw-r--r--spec/features/groups/empty_states_spec.rb4
-rw-r--r--spec/features/issues/create_branch_merge_request_spec.rb14
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb8
-rw-r--r--spec/features/issues_spec.rb26
-rw-r--r--spec/features/merge_requests/form_spec.rb2
-rw-r--r--spec/features/merge_requests/widget_spec.rb13
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb14
-rw-r--r--spec/features/projects_spec.rb21
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request.json4
-rw-r--r--spec/fixtures/markdown.md.erb5
-rw-r--r--spec/helpers/gitlab_routing_helper_spec.rb40
-rw-r--r--spec/helpers/milestones_routing_helper_spec.rb46
-rw-r--r--spec/javascripts/boards/issue_card_spec.js28
-rw-r--r--spec/javascripts/build_spec.js1
-rw-r--r--spec/javascripts/fixtures/project_select_combo_button.html.haml6
-rw-r--r--spec/javascripts/labels_issue_sidebar_spec.js1
-rw-r--r--spec/javascripts/project_select_combo_button_spec.js105
-rw-r--r--spec/javascripts/projects/project_import_gitlab_project_spec.js25
-rw-r--r--spec/javascripts/sidebar/confidential_edit_buttons_spec.js39
-rw-r--r--spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js39
-rw-r--r--spec/javascripts/sidebar/confidential_issue_sidebar_spec.js65
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js10
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js1
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js2
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb196
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb2
-rw-r--r--spec/lib/gitlab/daemon_spec.rb103
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb71
-rw-r--r--spec/lib/gitlab/import_export/project.json9
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb2
-rw-r--r--spec/lib/gitlab/key_fingerprint_spec.rb84
-rw-r--r--spec/lib/gitlab/metrics/influx_sampler_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/sidekiq_metrics_exporter_spec.rb101
-rw-r--r--spec/lib/gitlab/project_template_spec.rb63
-rw-r--r--spec/lib/gitlab/shell_spec.rb90
-rw-r--r--spec/migrations/calculate_conv_dev_index_percentages_spec.rb41
-rw-r--r--spec/models/conversational_development_index/metric_spec.rb11
-rw-r--r--spec/models/issue_spec.rb4
-rw-r--r--spec/models/key_spec.rb10
-rw-r--r--spec/models/merge_request_spec.rb26
-rw-r--r--spec/models/milestone_spec.rb38
-rw-r--r--spec/models/repository_spec.rb30
-rw-r--r--spec/presenters/conversational_development_index/metric_presenter_spec.rb6
-rw-r--r--spec/serializers/merge_request_entity_spec.rb2
-rw-r--r--spec/services/projects/autocomplete_service_spec.rb27
-rw-r--r--spec/services/projects/create_from_template_service_spec.rb26
-rw-r--r--spec/services/projects/import_service_spec.rb32
-rw-r--r--spec/services/submit_usage_ping_service_spec.rb7
-rw-r--r--spec/services/system_note_service_spec.rb53
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/cycle_analytics_helpers.rb4
-rw-r--r--spec/support/issuable_shared_examples.rb6
-rw-r--r--spec/support/markdown_feature.rb6
-rw-r--r--spec/support/matchers/markdown_matchers.rb2
-rw-r--r--spec/support/migrations_helpers.rb10
-rw-r--r--spec/workers/merge_worker_spec.rb11
-rw-r--r--spec/workers/new_issue_worker_spec.rb54
-rw-r--r--spec/workers/new_merge_request_worker_spec.rb56
-rw-r--r--spec/workers/stuck_merge_jobs_worker_spec.rb50
64 files changed, 1523 insertions, 271 deletions
diff --git a/spec/factories/conversational_development_index_metrics.rb b/spec/factories/conversational_development_index_metrics.rb
index a5412629195..3806c43ba15 100644
--- a/spec/factories/conversational_development_index_metrics.rb
+++ b/spec/factories/conversational_development_index_metrics.rb
@@ -2,32 +2,42 @@ FactoryGirl.define do
factory :conversational_development_index_metric, class: ConversationalDevelopmentIndex::Metric do
leader_issues 9.256
instance_issues 1.234
+ percentage_issues 13.331
leader_notes 30.33333
instance_notes 28.123
+ percentage_notes 92.713
leader_milestones 16.2456
instance_milestones 1.234
+ percentage_milestones 7.595
leader_boards 5.2123
instance_boards 3.254
+ percentage_boards 62.429
leader_merge_requests 1.2
instance_merge_requests 0.6
+ percentage_merge_requests 50.0
leader_ci_pipelines 12.1234
instance_ci_pipelines 2.344
+ percentage_ci_pipelines 19.334
leader_environments 3.3333
instance_environments 2.2222
+ percentage_environments 66.672
leader_deployments 1.200
instance_deployments 0.771
+ percentage_deployments 64.25
leader_projects_prometheus_active 0.111
instance_projects_prometheus_active 0.109
+ percentage_projects_prometheus_active 98.198
leader_service_desk_issues 15.891
instance_service_desk_issues 13.345
+ percentage_service_desk_issues 83.978
end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index c51b81c1cff..ce458431c55 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -233,7 +233,7 @@ describe 'Issue Boards', js: true do
wait_for_board_cards(4, 1)
expect(find('.board:nth-child(3)')).to have_content(issue6.title)
- expect(find('.board:nth-child(3)').all('.card').last).not_to have_content(development.title)
+ expect(find('.board:nth-child(3)').all('.card').last).to have_content(development.title)
end
it 'issue moves between lists' do
@@ -244,7 +244,7 @@ describe 'Issue Boards', js: true do
wait_for_board_cards(4, 1)
expect(find('.board:nth-child(2)')).to have_content(issue7.title)
- expect(find('.board:nth-child(2)').all('.card').first).not_to have_content(planning.title)
+ expect(find('.board:nth-child(2)').all('.card').first).to have_content(planning.title)
end
it 'issue moves from closed' do
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 373cd92793e..8d3d4ff8773 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -257,7 +257,7 @@ describe 'Issue Boards', js: true do
end
end
- expect(card).to have_selector('.label', count: 2)
+ expect(card).to have_selector('.label', count: 3)
expect(card).to have_content(bug.title)
end
@@ -283,7 +283,7 @@ describe 'Issue Boards', js: true do
end
end
- expect(card).to have_selector('.label', count: 3)
+ expect(card).to have_selector('.label', count: 4)
expect(card).to have_content(bug.title)
expect(card).to have_content(regression.title)
end
@@ -308,7 +308,7 @@ describe 'Issue Boards', js: true do
end
end
- expect(card).not_to have_selector('.label')
+ expect(card).to have_selector('.label', count: 1)
expect(card).not_to have_content(stretch.title)
end
end
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index be6f78ee607..795335aa106 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -79,12 +79,21 @@ RSpec.describe 'Dashboard Issues' do
end
end
- it 'shows the new issue page', :js do
+ it 'shows the new issue page', js: true do
find('.new-project-item-select-button').trigger('click')
+
wait_for_requests
- find('.select2-results li').click
- expect(page).to have_current_path("/#{project.path_with_namespace}/issues/new")
+ project_path = "/#{project.path_with_namespace}"
+ project_json = { name: project.name_with_namespace, url: project_path }.to_json
+
+ # similate selection, and prevent overlap by dropdown menu
+ execute_script("$('.project-item-select').val('#{project_json}').trigger('change');")
+ execute_script("$('#select2-drop-mask').remove();")
+
+ find('.new-project-item-link').trigger('click')
+
+ expect(page).to have_current_path("#{project_path}/issues/new")
page.within('#content-body') do
expect(page).to have_selector('.issue-form')
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index 7f28553c44e..243e8536168 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -38,7 +38,7 @@ feature 'Groups Merge Requests Empty States' do
it 'should show a new merge request button' do
within '.empty-state' do
- expect(page).to have_content('New merge request')
+ expect(page).to have_content('create merge request')
end
end
@@ -63,7 +63,7 @@ feature 'Groups Merge Requests Empty States' do
it 'should not show a new merge request button' do
within '.empty-state' do
- expect(page).not_to have_link('New merge request')
+ expect(page).not_to have_link('create merge request')
end
end
end
diff --git a/spec/features/issues/create_branch_merge_request_spec.rb b/spec/features/issues/create_branch_merge_request_spec.rb
index f59f687cf51..546dc7e8a49 100644
--- a/spec/features/issues/create_branch_merge_request_spec.rb
+++ b/spec/features/issues/create_branch_merge_request_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Create Branch/Merge Request Dropdown on issue page', js: true do
+feature 'Create Branch/Merge Request Dropdown on issue page', :feature, :js do
let(:user) { create(:user) }
let!(:project) { create(:project, :repository) }
let(:issue) { create(:issue, project: project, title: 'Cherry-Coloured Funk') }
@@ -14,10 +14,14 @@ feature 'Create Branch/Merge Request Dropdown on issue page', js: true do
it 'allows creating a merge request from the issue page' do
visit project_issue_path(project, issue)
- select_dropdown_option('create-mr')
-
- expect(page).to have_content('WIP: Resolve "Cherry-Coloured Funk"')
- expect(current_path).to eq(project_merge_request_path(project, MergeRequest.first))
+ perform_enqueued_jobs do
+ select_dropdown_option('create-mr')
+
+ expect(page).to have_content('WIP: Resolve "Cherry-Coloured Funk"')
+ expect(current_path).to eq(project_merge_request_path(project, MergeRequest.first))
+
+ wait_for_requests
+ end
visit project_issue_path(project, issue)
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 8e22441e0e8..af11b474842 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -130,8 +130,8 @@ feature 'Issue Sidebar' do
it 'adds new label' do
page.within('.block.labels') do
fill_in 'new_label_name', with: 'wontfix'
- page.find(".suggest-colors a", match: :first).click
- click_button 'Create'
+ page.find('.suggest-colors a', match: :first).trigger('click')
+ page.find('button', text: 'Create').trigger('click')
page.within('.dropdown-page-one') do
expect(page).to have_content 'wontfix'
@@ -142,8 +142,8 @@ feature 'Issue Sidebar' do
it 'shows error message if label title is taken' do
page.within('.block.labels') do
fill_in 'new_label_name', with: label.title
- page.find('.suggest-colors a', match: :first).click
- click_button 'Create'
+ page.find('.suggest-colors a', match: :first).trigger('click')
+ page.find('button', text: 'Create').trigger('click')
page.within('.dropdown-page-two') do
expect(page).to have_content 'Title has already been taken'
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 489baa4291f..a5bb642221c 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -706,4 +706,30 @@ describe 'Issues' do
expect(page).to have_text("updated title")
end
end
+
+ describe 'confidential issue#show', js: true do
+ it 'shows confidential sibebar information as confidential and can be turned off' do
+ issue = create(:issue, :confidential, project: project)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_css('.confidential-issue-warning')
+ expect(page).to have_css('.is-confidential')
+ expect(page).not_to have_css('.is-not-confidential')
+
+ find('.confidential-edit').click
+ expect(page).to have_css('.confidential-warning-message')
+
+ within('.confidential-warning-message') do
+ find('.btn-close').click
+ end
+
+ wait_for_requests
+
+ visit project_issue_path(project, issue)
+
+ expect(page).not_to have_css('.is-confidential')
+ expect(page).to have_css('.is-not-confidential')
+ end
+ end
end
diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb
index 6ffb05c5030..89410b0e90f 100644
--- a/spec/features/merge_requests/form_spec.rb
+++ b/spec/features/merge_requests/form_spec.rb
@@ -41,7 +41,7 @@ describe 'New/edit merge request', :js do
expect(page).to have_content user2.name
end
- click_link 'Assign to me'
+ find('a', text: 'Assign to me').trigger('click')
expect(find('input[name="merge_request[assignee_id]"]', visible: false).value).to match(user.id.to_s)
page.within '.js-assignee-search' do
expect(page).to have_content user.name
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb
index 69e31c7481f..fd991293ee9 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_requests/widget_spec.rb
@@ -219,4 +219,17 @@ describe 'Merge request', :js do
expect(page).to have_field('remove-source-branch-input', disabled: true)
end
end
+
+ context 'ongoing merge process' do
+ it 'shows Merging state' do
+ allow_any_instance_of(MergeRequest).to receive(:merge_ongoing?).and_return(true)
+
+ visit project_merge_request_path(project, merge_request)
+
+ wait_for_requests
+
+ expect(page).not_to have_button('Merge')
+ expect(page).to have_content('This merge request is in the process of being merged')
+ end
+ end
end
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index c0cfb9eafe2..24e7843db63 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -29,8 +29,9 @@ feature 'Import/Export - project import integration test', js: true do
fill_in :project_path, with: 'test-project-path', visible: true
click_link 'GitLab export'
- expect(page).to have_content('GitLab project export')
+ expect(page).to have_content('Import an exported GitLab project')
expect(URI.parse(current_url).query).to eq("namespace_id=#{namespace.id}&path=test-project-path")
+ expect(Gitlab::ImportExport).to receive(:import_upload_path).with(filename: /\A\h{32}_test-project-path\z/).and_call_original
attach_file('file', file)
@@ -60,17 +61,6 @@ feature 'Import/Export - project import integration test', js: true do
expect(page).to have_content('Project could not be imported')
end
end
-
- scenario 'project with no name' do
- create(:project, namespace: namespace)
-
- visit new_project_path
-
- select2(namespace.id, from: '#project_namespace_id')
-
- # Check for tooltip disabled import button
- expect(find('.import_gitlab_project')['title']).to eq('Please enter a valid project name.')
- end
end
context 'when limited to the default user namespace' do
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index dbcdac902d5..7e4d53332e5 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -1,6 +1,27 @@
require 'spec_helper'
feature 'Project' do
+ describe 'creating from template' do
+ let(:user) { create(:user) }
+ let(:template) { Gitlab::ProjectTemplate.find(:rails) }
+
+ before do
+ sign_in user
+ visit new_project_path
+ end
+
+ it "allows creation from templates" do
+ page.choose(template.name)
+ fill_in("project_path", with: template.name)
+
+ page.within '#content-body' do
+ click_button "Create project"
+ end
+
+ expect(page).to have_content 'This project Loading..'
+ end
+ end
+
describe 'description' do
let(:project) { create(:project, :repository) }
let(:path) { project_path(project) }
diff --git a/spec/fixtures/api/schemas/entities/merge_request.json b/spec/fixtures/api/schemas/entities/merge_request.json
index 7ffa82fc4bd..2f12b671dec 100644
--- a/spec/fixtures/api/schemas/entities/merge_request.json
+++ b/spec/fixtures/api/schemas/entities/merge_request.json
@@ -19,7 +19,6 @@
"human_time_estimate": { "type": ["integer", "null"] },
"human_total_time_spent": { "type": ["integer", "null"] },
"in_progress_merge_commit_sha": { "type": ["string", "null"] },
- "locked_at": { "type": ["string", "null"] },
"merge_error": { "type": ["string", "null"] },
"merge_commit_sha": { "type": ["string", "null"] },
"merge_params": { "type": ["object", "null"] },
@@ -94,7 +93,8 @@
"commit_change_content_path": { "type": "string" },
"remove_wip_path": { "type": "string" },
"commits_count": { "type": "integer" },
- "remove_source_branch": { "type": ["boolean", "null"] }
+ "remove_source_branch": { "type": ["boolean", "null"] },
+ "merge_ongoing": { "type": "boolean" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 58b43805705..4f46e40ce7a 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -227,8 +227,11 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- Milestone in another project: <%= xmilestone.to_reference(project) %>
- Ignored in code: `<%= simple_milestone.to_reference %>`
- Ignored in links: [Link to <%= simple_milestone.to_reference %>](#milestone-link)
-- Milestone by URL: <%= urls.project_milestone_url(milestone.project, milestone) %>
+- Milestone by URL: <%= urls.milestone_url(milestone) %>
- Link to milestone by URL: [Milestone](<%= milestone.to_reference %>)
+- Group milestone by name: <%= Milestone.reference_prefix %><%= group_milestone.name %>
+- Group milestone by name in quotes: <%= group_milestone.to_reference(format: :name) %>
+- Group milestone by URL is ignore: <%= urls.milestone_url(group_milestone) %>
### Task Lists
diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb
index 537e457513f..a44b200c5da 100644
--- a/spec/helpers/gitlab_routing_helper_spec.rb
+++ b/spec/helpers/gitlab_routing_helper_spec.rb
@@ -63,44 +63,4 @@ describe GitlabRoutingHelper do
it { expect(resend_invite_group_member_path(group_member)).to eq resend_invite_group_group_member_path(group_member.source, group_member) }
end
end
-
- describe '#milestone_path' do
- context 'for a group milestone' do
- let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) }
-
- it 'links to the group milestone page' do
- expect(milestone_path(milestone))
- .to eq(group_milestone_path(group, milestone))
- end
- end
-
- context 'for a project milestone' do
- let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) }
-
- it 'links to the project milestone page' do
- expect(milestone_path(milestone))
- .to eq(project_milestone_path(project, milestone))
- end
- end
- end
-
- describe '#milestone_url' do
- context 'for a group milestone' do
- let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) }
-
- it 'links to the group milestone page' do
- expect(milestone_url(milestone))
- .to eq(group_milestone_url(group, milestone))
- end
- end
-
- context 'for a project milestone' do
- let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) }
-
- it 'links to the project milestone page' do
- expect(milestone_url(milestone))
- .to eq(project_milestone_url(project, milestone))
- end
- end
- end
end
diff --git a/spec/helpers/milestones_routing_helper_spec.rb b/spec/helpers/milestones_routing_helper_spec.rb
new file mode 100644
index 00000000000..dc13a43c2ab
--- /dev/null
+++ b/spec/helpers/milestones_routing_helper_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe MilestonesRoutingHelper do
+ let(:project) { build_stubbed(:project) }
+ let(:group) { build_stubbed(:group) }
+
+ describe '#milestone_path' do
+ context 'for a group milestone' do
+ let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) }
+
+ it 'links to the group milestone page' do
+ expect(milestone_path(milestone))
+ .to eq(group_milestone_path(group, milestone))
+ end
+ end
+
+ context 'for a project milestone' do
+ let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) }
+
+ it 'links to the project milestone page' do
+ expect(milestone_path(milestone))
+ .to eq(project_milestone_path(project, milestone))
+ end
+ end
+ end
+
+ describe '#milestone_url' do
+ context 'for a group milestone' do
+ let(:milestone) { build_stubbed(:milestone, group: group, iid: 1) }
+
+ it 'links to the group milestone page' do
+ expect(milestone_url(milestone))
+ .to eq(group_milestone_url(group, milestone))
+ end
+ end
+
+ context 'for a project milestone' do
+ let(:milestone) { build_stubbed(:milestone, project: project, iid: 1) }
+
+ it 'links to the project milestone page' do
+ expect(milestone_url(milestone))
+ .to eq(project_milestone_url(project, milestone))
+ end
+ end
+ end
+end
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index bd9b4fbfdd3..69cfcbbce5a 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -238,12 +238,6 @@ describe('Issue card component', () => {
});
describe('labels', () => {
- it('does not render any', () => {
- expect(
- component.$el.querySelector('.label'),
- ).toBeNull();
- });
-
describe('exists', () => {
beforeEach((done) => {
component.issue.addLabel(label1);
@@ -251,16 +245,21 @@ describe('Issue card component', () => {
Vue.nextTick(() => done());
});
- it('does not render list label', () => {
+ it('renders list label', () => {
expect(
component.$el.querySelectorAll('.label').length,
- ).toBe(1);
+ ).toBe(2);
});
it('renders label', () => {
+ const nodes = [];
+ component.$el.querySelectorAll('.label').forEach((label) => {
+ nodes.push(label.title);
+ });
+
expect(
- component.$el.querySelector('.label').textContent,
- ).toContain(label1.title);
+ nodes.includes(label1.description),
+ ).toBe(true);
});
it('sets label description as title', () => {
@@ -270,9 +269,14 @@ describe('Issue card component', () => {
});
it('sets background color of button', () => {
+ const nodes = [];
+ component.$el.querySelectorAll('.label').forEach((label) => {
+ nodes.push(label.style.backgroundColor);
+ });
+
expect(
- component.$el.querySelector('.label').style.backgroundColor,
- ).toContain(label1.color);
+ nodes.includes(label1.color),
+ ).toBe(true);
});
});
});
diff --git a/spec/javascripts/build_spec.js b/spec/javascripts/build_spec.js
index be90dbdd88a..35149611095 100644
--- a/spec/javascripts/build_spec.js
+++ b/spec/javascripts/build_spec.js
@@ -5,7 +5,6 @@ import '~/lib/utils/datetime_utility';
import '~/lib/utils/url_utility';
import '~/build';
import '~/breakpoints';
-import 'vendor/jquery.nicescroll';
describe('Build', () => {
const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1`;
diff --git a/spec/javascripts/fixtures/project_select_combo_button.html.haml b/spec/javascripts/fixtures/project_select_combo_button.html.haml
new file mode 100644
index 00000000000..54bc1a59279
--- /dev/null
+++ b/spec/javascripts/fixtures/project_select_combo_button.html.haml
@@ -0,0 +1,6 @@
+.project-item-select-holder
+ %input.project-item-select{ data: { group_id: '12345' , relative_path: 'issues/new' } }
+ %a.new-project-item-link{ data: { label: 'New issue' }, href: ''}
+ %i.fa.fa-spinner.spin
+ %a.new-project-item-select-button
+ %i.fa.fa-caret-down
diff --git a/spec/javascripts/labels_issue_sidebar_spec.js b/spec/javascripts/labels_issue_sidebar_spec.js
index c99f379b871..e47adc49224 100644
--- a/spec/javascripts/labels_issue_sidebar_spec.js
+++ b/spec/javascripts/labels_issue_sidebar_spec.js
@@ -4,7 +4,6 @@
import '~/gl_dropdown';
import 'select2';
-import 'vendor/jquery.nicescroll';
import '~/api';
import '~/create_label';
import '~/issuable_context';
diff --git a/spec/javascripts/project_select_combo_button_spec.js b/spec/javascripts/project_select_combo_button_spec.js
new file mode 100644
index 00000000000..e10a5a3bef6
--- /dev/null
+++ b/spec/javascripts/project_select_combo_button_spec.js
@@ -0,0 +1,105 @@
+import ProjectSelectComboButton from '~/project_select_combo_button';
+
+const fixturePath = 'static/project_select_combo_button.html.raw';
+
+describe('Project Select Combo Button', function () {
+ preloadFixtures(fixturePath);
+
+ beforeEach(function () {
+ this.defaults = {
+ label: 'Select project to create issue',
+ groupId: 12345,
+ projectMeta: {
+ name: 'My Cool Project',
+ url: 'http://mycoolproject.com',
+ },
+ newProjectMeta: {
+ name: 'My Other Cool Project',
+ url: 'http://myothercoolproject.com',
+ },
+ localStorageKey: 'group-12345-new-issue-recent-project',
+ relativePath: 'issues/new',
+ };
+
+ loadFixtures(fixturePath);
+
+ this.newItemBtn = document.querySelector('.new-project-item-link');
+ this.projectSelectInput = document.querySelector('.project-item-select');
+ });
+
+ describe('on page load when localStorage is empty', function () {
+ beforeEach(function () {
+ this.comboButton = new ProjectSelectComboButton(this.projectSelectInput);
+ });
+
+ it('newItemBtn is disabled', function () {
+ expect(this.newItemBtn.hasAttribute('disabled')).toBe(true);
+ expect(this.newItemBtn.classList.contains('disabled')).toBe(true);
+ });
+
+ it('newItemBtn href is null', function () {
+ expect(this.newItemBtn.getAttribute('href')).toBe('');
+ });
+
+ it('newItemBtn text is the plain default label', function () {
+ expect(this.newItemBtn.textContent).toBe(this.defaults.label);
+ });
+ });
+
+ describe('on page load when localStorage is filled', function () {
+ beforeEach(function () {
+ window.localStorage
+ .setItem(this.defaults.localStorageKey, JSON.stringify(this.defaults.projectMeta));
+ this.comboButton = new ProjectSelectComboButton(this.projectSelectInput);
+ });
+
+ it('newItemBtn is not disabled', function () {
+ expect(this.newItemBtn.hasAttribute('disabled')).toBe(false);
+ expect(this.newItemBtn.classList.contains('disabled')).toBe(false);
+ });
+
+ it('newItemBtn href is correctly set', function () {
+ expect(this.newItemBtn.getAttribute('href')).toBe(this.defaults.projectMeta.url);
+ });
+
+ it('newItemBtn text is the cached label', function () {
+ expect(this.newItemBtn.textContent)
+ .toBe(`New issue in ${this.defaults.projectMeta.name}`);
+ });
+
+ afterEach(function () {
+ window.localStorage.clear();
+ });
+ });
+
+ describe('after selecting a new project', function () {
+ beforeEach(function () {
+ this.comboButton = new ProjectSelectComboButton(this.projectSelectInput);
+
+ // mock the effect of selecting an item from the projects dropdown (select2)
+ $('.project-item-select')
+ .val(JSON.stringify(this.defaults.newProjectMeta))
+ .trigger('change');
+ });
+
+ it('newItemBtn is not disabled', function () {
+ expect(this.newItemBtn.hasAttribute('disabled')).toBe(false);
+ expect(this.newItemBtn.classList.contains('disabled')).toBe(false);
+ });
+
+ it('newItemBtn href is correctly set', function () {
+ expect(this.newItemBtn.getAttribute('href'))
+ .toBe('http://myothercoolproject.com/issues/new');
+ });
+
+ it('newItemBtn text is the selected project label', function () {
+ expect(this.newItemBtn.textContent)
+ .toBe(`New issue in ${this.defaults.newProjectMeta.name}`);
+ });
+
+ afterEach(function () {
+ window.localStorage.clear();
+ });
+ });
+});
+
diff --git a/spec/javascripts/projects/project_import_gitlab_project_spec.js b/spec/javascripts/projects/project_import_gitlab_project_spec.js
new file mode 100644
index 00000000000..2f1aae109e3
--- /dev/null
+++ b/spec/javascripts/projects/project_import_gitlab_project_spec.js
@@ -0,0 +1,25 @@
+import projectImportGitlab from '~/projects/project_import_gitlab_project';
+
+describe('Import Gitlab project', () => {
+ let projectName;
+ beforeEach(() => {
+ projectName = 'project';
+ window.history.pushState({}, null, `?path=${projectName}`);
+
+ setFixtures(`
+ <input class="js-path-name" />
+ `);
+
+ projectImportGitlab.bindEvents();
+ });
+
+ afterEach(() => {
+ window.history.pushState({}, null, '');
+ });
+
+ describe('path name', () => {
+ it('should fill in the project name derived from the previously filled project name', () => {
+ expect(document.querySelector('.js-path-name').value).toEqual(projectName);
+ });
+ });
+});
diff --git a/spec/javascripts/sidebar/confidential_edit_buttons_spec.js b/spec/javascripts/sidebar/confidential_edit_buttons_spec.js
new file mode 100644
index 00000000000..482be466aad
--- /dev/null
+++ b/spec/javascripts/sidebar/confidential_edit_buttons_spec.js
@@ -0,0 +1,39 @@
+import Vue from 'vue';
+import editFormButtons from '~/sidebar/components/confidential/edit_form_buttons.vue';
+
+describe('Edit Form Buttons', () => {
+ let vm1;
+ let vm2;
+
+ beforeEach(() => {
+ const Component = Vue.extend(editFormButtons);
+ const toggleForm = () => { };
+ const updateConfidentialAttribute = () => { };
+
+ vm1 = new Component({
+ propsData: {
+ isConfidential: true,
+ toggleForm,
+ updateConfidentialAttribute,
+ },
+ }).$mount();
+
+ vm2 = new Component({
+ propsData: {
+ isConfidential: false,
+ toggleForm,
+ updateConfidentialAttribute,
+ },
+ }).$mount();
+ });
+
+ it('renders on or off text based on confidentiality', () => {
+ expect(
+ vm1.$el.innerHTML.includes('Turn Off'),
+ ).toBe(true);
+
+ expect(
+ vm2.$el.innerHTML.includes('Turn On'),
+ ).toBe(true);
+ });
+});
diff --git a/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js b/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js
new file mode 100644
index 00000000000..724f5126945
--- /dev/null
+++ b/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js
@@ -0,0 +1,39 @@
+import Vue from 'vue';
+import editForm from '~/sidebar/components/confidential/edit_form.vue';
+
+describe('Edit Form Dropdown', () => {
+ let vm1;
+ let vm2;
+
+ beforeEach(() => {
+ const Component = Vue.extend(editForm);
+ const toggleForm = () => { };
+ const updateConfidentialAttribute = () => { };
+
+ vm1 = new Component({
+ propsData: {
+ isConfidential: true,
+ toggleForm,
+ updateConfidentialAttribute,
+ },
+ }).$mount();
+
+ vm2 = new Component({
+ propsData: {
+ isConfidential: false,
+ toggleForm,
+ updateConfidentialAttribute,
+ },
+ }).$mount();
+ });
+
+ it('renders on the appropriate warning text', () => {
+ expect(
+ vm1.$el.innerHTML.includes('You are going to turn off the confidentiality.'),
+ ).toBe(true);
+
+ expect(
+ vm2.$el.innerHTML.includes('You are going to turn on the confidentiality.'),
+ ).toBe(true);
+ });
+});
diff --git a/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js
new file mode 100644
index 00000000000..90eac1ed1ab
--- /dev/null
+++ b/spec/javascripts/sidebar/confidential_issue_sidebar_spec.js
@@ -0,0 +1,65 @@
+import Vue from 'vue';
+import confidentialIssueSidebar from '~/sidebar/components/confidential/confidential_issue_sidebar.vue';
+
+describe('Confidential Issue Sidebar Block', () => {
+ let vm1;
+ let vm2;
+
+ beforeEach(() => {
+ const Component = Vue.extend(confidentialIssueSidebar);
+ const service = {
+ update: () => new Promise((resolve, reject) => {
+ resolve(true);
+ reject('failed!');
+ }),
+ };
+
+ vm1 = new Component({
+ propsData: {
+ isConfidential: true,
+ isEditable: true,
+ service,
+ },
+ }).$mount();
+
+ vm2 = new Component({
+ propsData: {
+ isConfidential: false,
+ isEditable: false,
+ service,
+ },
+ }).$mount();
+ });
+
+ it('shows if confidential and/or editable', () => {
+ expect(
+ vm1.$el.innerHTML.includes('Edit'),
+ ).toBe(true);
+
+ expect(
+ vm1.$el.innerHTML.includes('This issue is confidential'),
+ ).toBe(true);
+
+ expect(
+ vm2.$el.innerHTML.includes('None'),
+ ).toBe(true);
+ });
+
+ it('displays the edit form when editable', (done) => {
+ expect(vm1.edit).toBe(false);
+
+ vm1.$el.querySelector('.confidential-edit').click();
+
+ expect(vm1.edit).toBe(true);
+
+ setTimeout(() => {
+ expect(
+ vm1.$el
+ .innerHTML
+ .includes('You are going to turn off the confidentiality.'),
+ ).toBe(true);
+
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js
index fb2ef606604..237035648cf 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_locked_spec.js
@@ -1,10 +1,10 @@
import Vue from 'vue';
-import lockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_locked';
+import mergingComponent from '~/vue_merge_request_widget/components/states/mr_widget_merging';
-describe('MRWidgetLocked', () => {
+describe('MRWidgetMerging', () => {
describe('props', () => {
it('should have props', () => {
- const { mr } = lockedComponent.props;
+ const { mr } = mergingComponent.props;
expect(mr.type instanceof Object).toBeTruthy();
expect(mr.required).toBeTruthy();
@@ -13,7 +13,7 @@ describe('MRWidgetLocked', () => {
describe('template', () => {
it('should have correct elements', () => {
- const Component = Vue.extend(lockedComponent);
+ const Component = Vue.extend(mergingComponent);
const mr = {
targetBranchPath: '/branch-path',
targetBranch: 'branch',
@@ -24,7 +24,7 @@ describe('MRWidgetLocked', () => {
}).$el;
expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.innerText).toContain('it is locked');
+ expect(el.innerText).toContain('This merge request is in the process of being merged');
expect(el.innerText).toContain('changes will be merged into');
expect(el.querySelector('.label-branch a').getAttribute('href')).toEqual(mr.targetBranchPath);
expect(el.querySelector('.label-branch a').textContent).toContain(mr.targetBranch);
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index ad2f28b24f0..0795d0aaa82 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -20,7 +20,6 @@ export default {
"human_time_estimate": null,
"human_total_time_spent": null,
"in_progress_merge_commit_sha": null,
- "locked_at": null,
"merge_commit_sha": "53027d060246c8f47e4a9310fb332aa52f221775",
"merge_error": null,
"merge_params": {
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 3a0c50b750f..669ee248bf1 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -342,7 +342,7 @@ describe('mrWidgetOptions', () => {
expect(comps['mr-widget-related-links']).toBeDefined();
expect(comps['mr-widget-merged']).toBeDefined();
expect(comps['mr-widget-closed']).toBeDefined();
- expect(comps['mr-widget-locked']).toBeDefined();
+ expect(comps['mr-widget-merging']).toBeDefined();
expect(comps['mr-widget-failed-to-merge']).toBeDefined();
expect(comps['mr-widget-wip']).toBeDefined();
expect(comps['mr-widget-archived']).toBeDefined();
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index 5db77566513..ebd6c79077e 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -3,57 +3,57 @@ require 'spec_helper'
describe Banzai::Filter::MilestoneReferenceFilter do
include FilterSpecHelper
- let(:project) { create(:project, :public) }
- let(:milestone) { create(:milestone, project: project) }
- let(:reference) { milestone.to_reference }
+ let(:group) { create(:group, :public) }
+ let(:project) { create(:project, :public, group: group) }
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
end
- %w(pre code a style).each do |elem|
- it "ignores valid references contained inside '#{elem}' element" do
- exp = act = "<#{elem}>milestone #{milestone.to_reference}</#{elem}>"
- expect(reference_filter(act).to_html).to eq exp
+ shared_examples 'reference parsing' do
+ %w(pre code a style).each do |elem|
+ it "ignores valid references contained inside '#{elem}' element" do
+ exp = act = "<#{elem}>milestone #{reference}</#{elem}>"
+ expect(reference_filter(act).to_html).to eq exp
+ end
end
- end
- it 'includes default classes' do
- doc = reference_filter("Milestone #{reference}")
- expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-milestone has-tooltip'
- end
+ it 'includes default classes' do
+ doc = reference_filter("Milestone #{reference}")
- it 'includes a data-project attribute' do
- doc = reference_filter("Milestone #{reference}")
- link = doc.css('a').first
+ expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-milestone has-tooltip'
+ end
- expect(link).to have_attribute('data-project')
- expect(link.attr('data-project')).to eq project.id.to_s
- end
+ it 'includes a data-project attribute' do
+ doc = reference_filter("Milestone #{reference}")
+ link = doc.css('a').first
- it 'includes a data-milestone attribute' do
- doc = reference_filter("See #{reference}")
- link = doc.css('a').first
+ expect(link).to have_attribute('data-project')
+ expect(link.attr('data-project')).to eq project.id.to_s
+ end
- expect(link).to have_attribute('data-milestone')
- expect(link.attr('data-milestone')).to eq milestone.id.to_s
- end
+ it 'includes a data-milestone attribute' do
+ doc = reference_filter("See #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-milestone')
+ expect(link.attr('data-milestone')).to eq milestone.id.to_s
+ end
- it 'supports an :only_path context' do
- doc = reference_filter("Milestone #{reference}", only_path: true)
- link = doc.css('a').first.attr('href')
+ it 'supports an :only_path context' do
+ doc = reference_filter("Milestone #{reference}", only_path: true)
+ link = doc.css('a').first.attr('href')
- expect(link).not_to match %r(https?://)
- expect(link).to eq urls
- .project_milestone_path(project, milestone)
+ expect(link).not_to match %r(https?://)
+ expect(link).to eq urls.milestone_path(milestone)
+ end
end
- context 'Integer-based references' do
+ shared_examples 'Integer-based references' do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls
- .project_milestone_url(project, milestone)
+ expect(doc.css('a').first.attr('href')).to eq urls.milestone_url(milestone)
end
it 'links with adjacent text' do
@@ -68,15 +68,17 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
end
- context 'String-based single-word references' do
- let(:milestone) { create(:milestone, name: 'gfm', project: project) }
+ shared_examples 'String-based single-word references' do
let(:reference) { "#{Milestone.reference_prefix}#{milestone.name}" }
+ before do
+ milestone.update!(name: 'gfm')
+ end
+
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls
- .project_milestone_url(project, milestone)
+ expect(doc.css('a').first.attr('href')).to eq urls.milestone_url(milestone)
expect(doc.text).to eq 'See gfm'
end
@@ -92,15 +94,17 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
end
- context 'String-based multi-word references in quotes' do
- let(:milestone) { create(:milestone, name: 'gfm references', project: project) }
+ shared_examples 'String-based multi-word references in quotes' do
let(:reference) { milestone.to_reference(format: :name) }
+ before do
+ milestone.update!(name: 'gfm references')
+ end
+
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.attr('href')).to eq urls
- .project_milestone_url(project, milestone)
+ expect(doc.css('a').first.attr('href')).to eq urls.milestone_url(milestone)
expect(doc.text).to eq 'See gfm references'
end
@@ -116,23 +120,27 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
end
- describe 'referencing a milestone in a link href' do
- let(:reference) { %Q{<a href="#{milestone.to_reference}">Milestone</a>} }
+ shared_examples 'referencing a milestone in a link href' do
+ let(:unquoted_reference) { "#{Milestone.reference_prefix}#{milestone.name}" }
+ let(:link_reference) { %Q{<a href="#{unquoted_reference}">Milestone</a>} }
+
+ before do
+ milestone.update!(name: 'gfm')
+ end
it 'links to a valid reference' do
- doc = reference_filter("See #{reference}")
+ doc = reference_filter("See #{link_reference}")
- expect(doc.css('a').first.attr('href')).to eq urls
- .project_milestone_url(project, milestone)
+ expect(doc.css('a').first.attr('href')).to eq urls.milestone_url(milestone)
end
it 'links with adjacent text' do
- doc = reference_filter("Milestone (#{reference}.)")
+ doc = reference_filter("Milestone (#{link_reference}.)")
expect(doc.to_html).to match(%r(\(<a.+>Milestone</a>\.\)))
end
it 'includes a data-project attribute' do
- doc = reference_filter("Milestone #{reference}")
+ doc = reference_filter("Milestone #{link_reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
@@ -140,7 +148,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
it 'includes a data-milestone attribute' do
- doc = reference_filter("See #{reference}")
+ doc = reference_filter("See #{link_reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-milestone')
@@ -148,7 +156,35 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
end
- describe 'cross-project / cross-namespace complete reference' do
+ shared_examples 'linking to a milestone as the entire link' do
+ let(:unquoted_reference) { "#{Milestone.reference_prefix}#{milestone.name}" }
+ let(:link) { urls.milestone_url(milestone) }
+ let(:link_reference) { %Q{<a href="#{link}">#{link}</a>} }
+
+ it 'replaces the link text with the milestone reference' do
+ doc = reference_filter("See #{link}")
+
+ expect(doc.css('a').first.text).to eq(unquoted_reference)
+ end
+
+ it 'includes a data-project attribute' do
+ doc = reference_filter("Milestone #{link_reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-project')
+ expect(link.attr('data-project')).to eq project.id.to_s
+ end
+
+ it 'includes a data-milestone attribute' do
+ doc = reference_filter("See #{link_reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-milestone')
+ expect(link.attr('data-milestone')).to eq milestone.id.to_s
+ end
+ end
+
+ shared_examples 'cross-project / cross-namespace complete reference' do
let(:namespace) { create(:namespace) }
let(:another_project) { create(:project, :public, namespace: namespace) }
let(:milestone) { create(:milestone, project: another_project) }
@@ -184,7 +220,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
end
- describe 'cross-project / same-namespace complete reference' do
+ shared_examples 'cross-project / same-namespace complete reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:project, :public, namespace: namespace) }
let(:another_project) { create(:project, :public, namespace: namespace) }
@@ -221,7 +257,7 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
end
- describe 'cross project shorthand reference' do
+ shared_examples 'cross project shorthand reference' do
let(:namespace) { create(:namespace) }
let(:project) { create(:project, :public, namespace: namespace) }
let(:another_project) { create(:project, :public, namespace: namespace) }
@@ -258,27 +294,53 @@ describe Banzai::Filter::MilestoneReferenceFilter do
end
end
- describe 'cross project milestone references' do
- let(:another_project) { create(:project, :public) }
- let(:project_path) { another_project.full_path }
- let(:milestone) { create(:milestone, project: another_project) }
- let(:reference) { milestone.to_reference(project) }
+ context 'project milestones' do
+ let(:milestone) { create(:milestone, project: project) }
+ let(:reference) { milestone.to_reference }
- let!(:result) { reference_filter("See #{reference}") }
+ include_examples 'reference parsing'
- it 'points to referenced project milestone page' do
- expect(result.css('a').first.attr('href')).to eq urls
- .project_milestone_url(another_project, milestone)
+ it_behaves_like 'Integer-based references'
+ it_behaves_like 'String-based single-word references'
+ it_behaves_like 'String-based multi-word references in quotes'
+ it_behaves_like 'referencing a milestone in a link href'
+ it_behaves_like 'cross-project / cross-namespace complete reference'
+ it_behaves_like 'cross-project / same-namespace complete reference'
+ it_behaves_like 'cross project shorthand reference'
+ end
+
+ context 'group milestones' do
+ let(:milestone) { create(:milestone, group: group) }
+ let(:reference) { milestone.to_reference(format: :name) }
+
+ include_examples 'reference parsing'
+
+ it_behaves_like 'String-based single-word references'
+ it_behaves_like 'String-based multi-word references in quotes'
+ it_behaves_like 'referencing a milestone in a link href'
+
+ it 'does not support references by IID' do
+ doc = reference_filter("See #{Milestone.reference_prefix}#{milestone.iid}")
+
+ expect(doc.css('a')).to be_empty
end
- it 'contains cross project content' do
- expect(result.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+ it 'does not support references by link' do
+ doc = reference_filter("See #{urls.milestone_url(milestone)}")
+
+ expect(doc.css('a').first.text).to eq(urls.milestone_url(milestone))
end
- it 'escapes the name attribute' do
- allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
- doc = reference_filter("See #{reference}")
- expect(doc.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+ it 'does not support cross-project references' do
+ another_group = create(:group)
+ another_project = create(:project, :public, group: group)
+ project_reference = another_project.to_reference(project)
+
+ milestone.update!(group: another_group)
+
+ doc = reference_filter("See #{project_reference}#{reference}")
+
+ expect(doc.css('a')).to be_empty
end
end
end
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index d7d6a37f7cf..a66347ead76 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -54,7 +54,7 @@ describe Gitlab::BitbucketImport::Importer do
create(
:project,
import_source: project_identifier,
- import_data: ProjectImportData.new(credentials: data)
+ import_data_attributes: { credentials: data }
)
end
diff --git a/spec/lib/gitlab/daemon_spec.rb b/spec/lib/gitlab/daemon_spec.rb
new file mode 100644
index 00000000000..c519984a267
--- /dev/null
+++ b/spec/lib/gitlab/daemon_spec.rb
@@ -0,0 +1,103 @@
+require 'spec_helper'
+
+describe Gitlab::Daemon do
+ subject { described_class.new }
+
+ before do
+ allow(subject).to receive(:start_working)
+ allow(subject).to receive(:stop_working)
+ end
+
+ describe '.instance' do
+ before do
+ allow(Kernel).to receive(:at_exit)
+ end
+
+ after(:each) do
+ described_class.instance_variable_set(:@instance, nil)
+ end
+
+ it 'provides instance of Daemon' do
+ expect(described_class.instance).to be_instance_of(described_class)
+ end
+
+ it 'subsequent invocations provide the same instance' do
+ expect(described_class.instance).to eq(described_class.instance)
+ end
+
+ it 'creates at_exit hook when instance is created' do
+ expect(described_class.instance).not_to be_nil
+
+ expect(Kernel).to have_received(:at_exit)
+ end
+ end
+
+ describe 'when Daemon is enabled' do
+ before do
+ allow(subject).to receive(:enabled?).and_return(true)
+ end
+
+ describe 'when Daemon is stopped' do
+ describe '#start' do
+ it 'starts the Daemon' do
+ expect { subject.start.join }.to change { subject.thread? }.from(false).to(true)
+
+ expect(subject).to have_received(:start_working)
+ end
+ end
+
+ describe '#stop' do
+ it "doesn't shutdown stopped Daemon" do
+ expect { subject.stop }.not_to change { subject.thread? }
+
+ expect(subject).not_to have_received(:start_working)
+ end
+ end
+ end
+
+ describe 'when Daemon is running' do
+ before do
+ subject.start.join
+ end
+
+ describe '#start' do
+ it "doesn't start running Daemon" do
+ expect { subject.start.join }.not_to change { subject.thread? }
+
+ expect(subject).to have_received(:start_working).once
+ end
+ end
+
+ describe '#stop' do
+ it 'shutdowns Daemon' do
+ expect { subject.stop }.to change { subject.thread? }.from(true).to(false)
+
+ expect(subject).to have_received(:stop_working)
+ end
+ end
+ end
+ end
+
+ describe 'when Daemon is disabled' do
+ before do
+ allow(subject).to receive(:enabled?).and_return(false)
+ end
+
+ describe '#start' do
+ it "doesn't start working" do
+ expect(subject.start).to be_nil
+ expect { subject.start }.not_to change { subject.thread? }
+
+ expect(subject).not_to have_received(:start_working)
+ end
+ end
+
+ describe '#stop' do
+ it "doesn't stop working" do
+ expect { subject.stop }.not_to change { subject.thread? }
+
+ expect(subject).not_to have_received(:stop_working)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 18320bb23b9..dfab0c2fe85 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -152,6 +152,77 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
+ describe '.batch' do
+ let(:blob_references) do
+ [
+ [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
+ [SeedRepo::Commit::ID, 'six']
+ ]
+ end
+
+ subject { described_class.batch(repository, blob_references) }
+
+ it { expect(subject.size).to eq(blob_references.size) }
+
+ context 'first blob' do
+ let(:blob) { subject[0] }
+
+ it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
+ it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
+ it { expect(blob.path).to eq("files/ruby/popen.rb") }
+ it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
+ it { expect(blob.size).to eq(669) }
+ it { expect(blob.mode).to eq("100644") }
+ end
+
+ context 'second blob' do
+ let(:blob) { subject[1] }
+
+ it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
+ it { expect(blob.data).to eq('') }
+ it 'does not mark the blob as binary' do
+ expect(blob).not_to be_binary
+ end
+ end
+
+ context 'limiting' do
+ subject { described_class.batch(repository, blob_references, blob_size_limit: blob_size_limit) }
+
+ context 'default' do
+ let(:blob_size_limit) { nil }
+
+ it 'limits to MAX_DATA_DISPLAY_SIZE' do
+ stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
+
+ expect(subject.first.data.size).to eq(100)
+ end
+ end
+
+ context 'positive' do
+ let(:blob_size_limit) { 10 }
+
+ it { expect(subject.first.data.size).to eq(10) }
+ end
+
+ context 'zero' do
+ let(:blob_size_limit) { 0 }
+
+ it { expect(subject.first.data).to eq('') }
+ end
+
+ context 'negative' do
+ let(:blob_size_limit) { -1 }
+
+ it 'ignores MAX_DATA_DISPLAY_SIZE' do
+ stub_const('Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE', 100)
+
+ expect(subject.first.data.size).to eq(669)
+ end
+ end
+ end
+ end
+
describe 'encoding' do
context 'file with russian text' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/russian.rb") }
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 469a014e4d2..4e631e13410 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2534,7 +2534,6 @@
"iid": 9,
"description": null,
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
@@ -2983,7 +2982,6 @@
"iid": 8,
"description": null,
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
@@ -3267,7 +3265,6 @@
"iid": 7,
"description": "Et commodi deserunt aspernatur vero rerum. Ut non dolorum alias in odit est libero. Voluptatibus eos in et vitae repudiandae facilis ex mollitia.",
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
@@ -3551,7 +3548,6 @@
"iid": 6,
"description": "Dicta magnam non voluptates nam dignissimos nostrum deserunt. Dolorum et suscipit iure quae doloremque. Necessitatibus saepe aut labore sed.",
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
@@ -4241,7 +4237,6 @@
"iid": 5,
"description": "Est eaque quasi qui qui. Similique voluptatem impedit iusto ratione reprehenderit. Itaque est illum ut nulla aut.",
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
@@ -4789,7 +4784,6 @@
"iid": 4,
"description": "Nam magnam odit velit rerum. Sapiente dolore sunt saepe debitis. Culpa maiores ut ad dolores dolorem et.",
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
@@ -5288,7 +5282,6 @@
"iid": 3,
"description": "Libero nesciunt mollitia quis odit eos vero quasi. Iure voluptatem ut sint pariatur voluptates ut aut. Laborum possimus unde illum ipsum eum.",
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
@@ -5548,7 +5541,6 @@
"iid": 2,
"description": "Ut dolor quia aliquid dolore et nisi. Est minus suscipit enim quaerat sapiente consequatur rerum. Eveniet provident consequatur dolor accusantium reiciendis.",
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
@@ -6238,7 +6230,6 @@
"iid": 1,
"description": "Eveniet nihil ratione veniam similique qui aut sapiente tempora. Sed praesentium iusto dignissimos possimus id repudiandae quo nihil. Qui doloremque autem et iure fugit.",
"position": 0,
- "locked_at": null,
"updated_by_id": null,
"merge_error": null,
"merge_params": {
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 11f4c16ff96..4dce48f8079 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -145,7 +145,6 @@ MergeRequest:
- iid
- description
- position
-- locked_at
- updated_by_id
- merge_error
- merge_params
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index b3b5e5e7e33..c5725f47453 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -56,7 +56,7 @@ describe Gitlab::ImportSources do
describe '.importer' do
import_sources = {
- 'github' => Gitlab::GithubImport::Importer,
+ 'github' => Github::Import,
'bitbucket' => Gitlab::BitbucketImport::Importer,
'gitlab' => Gitlab::GitlabImport::Importer,
'google_code' => Gitlab::GoogleCodeImport::Importer,
diff --git a/spec/lib/gitlab/key_fingerprint_spec.rb b/spec/lib/gitlab/key_fingerprint_spec.rb
index d7bebaca675..f5fd5a96bc9 100644
--- a/spec/lib/gitlab/key_fingerprint_spec.rb
+++ b/spec/lib/gitlab/key_fingerprint_spec.rb
@@ -1,12 +1,82 @@
-require "spec_helper"
+require 'spec_helper'
-describe Gitlab::KeyFingerprint do
- let(:key) { "ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=" }
- let(:fingerprint) { "3f:a2:ee:de:b5:de:53:c3:aa:2f:9c:45:24:4c:47:7b" }
+describe Gitlab::KeyFingerprint, lib: true do
+ KEYS = {
+ rsa:
+ 'example.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC5z65PwQ1GE6foJgwk' \
+ '9rmQi/glaXbUeVa5uvQpnZ3Z5+forcI7aTngh3aZ/H2UDP2L70TGy7kKNyp0J3a8/OdG' \
+ 'Z08y5yi3JlbjFARO1NyoFEjw2H1SJxeJ43L6zmvTlu+hlK1jSAlidl7enS0ufTlzEEj4' \
+ 'iJcuTPKdVzKRgZuTRVm9woWNVKqIrdRC0rJiTinERnfSAp/vNYERMuaoN4oJt8p/NEek' \
+ 'rmFoDsQOsyDW5RAnCnjWUU+jFBKDpfkJQ1U2n6BjJewC9dl6ODK639l3yN4WOLZEk4tN' \
+ 'UysfbGeF3rmMeflaD6O1Jplpv3YhwVGFNKa7fMq6k3Z0tszTJPYh',
+ ecdsa:
+ 'example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAI' \
+ 'bmlzdHAyNTYAAABBBKTJy43NZzJSfNxpv/e2E6Zy3qoHoTQbmOsU5FEfpWfWa1MdTeXQ' \
+ 'YvKOi+qz/1AaNx6BK421jGu74JCDJtiZWT8=',
+ ed25519:
+ '@revoked example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjq' \
+ 'uxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf',
+ dss:
+ 'example.com ssh-dss AAAAB3NzaC1kc3MAAACBAP1/U4EddRIpUt9KnC7s5Of2EbdS' \
+ 'PO9EAMMeP4C2USZpRV1AIlH7WT2NWPq/xfW6MPbLm1Vs14E7gB00b/JmYLdrmVClpJ+f' \
+ '6AR7ECLCT7up1/63xhv4O1fnxqimFQ8E+4P208UewwI1VBNaFpEy9nXzrith1yrv8iID' \
+ 'GZ3RSAHHAAAAFQCXYFCPFSMLzLKSuYKi64QL8Fgc9QAAAIEA9+GghdabPd7LvKtcNrhX' \
+ 'uXmUr7v6OuqC+VdMCz0HgmdRWVeOutRZT+ZxBxCBgLRJFnEj6EwoFhO3zwkyjMim4TwW' \
+ 'eotUfI0o4KOuHiuzpnWRbqN/C/ohNWLx+2J6ASQ7zKTxvqhRkImog9/hWuWfBpKLZl6A' \
+ 'e1UlZAFMO/7PSSoAAACBAJcQ4JODqhuGbXIEpqxetm7PWbdbCcr3y/GzIZ066pRovpL6' \
+ 'qm3qCVIym4cyChxWwb8qlyCIi+YRUUWm1z/wiBYT2Vf3S4FXBnyymCkKEaV/EY7+jd4X' \
+ '1bXI58OD2u+bLCB/sInM4fGB8CZUIWT9nJH0Ve9jJUge2ms348/QOJ1+'
+ }.freeze
- describe "#fingerprint" do
- it "generates the key's fingerprint" do
- expect(described_class.new(key).fingerprint).to eq(fingerprint)
+ MD5_FINGERPRINTS = {
+ rsa: '06:b2:8a:92:df:0e:11:2c:ca:7b:8f:a4:ba:6e:4b:fd',
+ ecdsa: '45:ff:5b:98:9a:b6:8a:41:13:c1:30:8b:09:5e:7b:4e',
+ ed25519: '2e:65:6a:c8:cf:bf:b2:8b:9a:bd:6d:9f:11:5c:12:16',
+ dss: '57:98:86:02:5f:9c:f4:9b:ad:5a:1e:51:92:0e:fd:2b'
+ }.freeze
+
+ BIT_COUNTS = {
+ rsa: 2048,
+ ecdsa: 256,
+ ed25519: 256,
+ dss: 1024
+ }.freeze
+
+ describe '#type' do
+ KEYS.each do |type, key|
+ it "calculates the type of #{type} keys" do
+ calculated_type = described_class.new(key).type
+
+ expect(calculated_type).to eq(type.to_s.upcase)
+ end
+ end
+ end
+
+ describe '#fingerprint' do
+ KEYS.each do |type, key|
+ it "calculates the MD5 fingerprint for #{type} keys" do
+ fp = described_class.new(key).fingerprint
+
+ expect(fp).to eq(MD5_FINGERPRINTS[type])
+ end
+ end
+ end
+
+ describe '#bits' do
+ KEYS.each do |type, key|
+ it "calculates the number of bits in #{type} keys" do
+ bits = described_class.new(key).bits
+
+ expect(bits).to eq(BIT_COUNTS[type])
+ end
+ end
+ end
+
+ describe '#key' do
+ it 'carries the unmodified key data' do
+ key = described_class.new(KEYS[:rsa]).key
+
+ expect(key).to eq(KEYS[:rsa])
end
end
end
diff --git a/spec/lib/gitlab/metrics/influx_sampler_spec.rb b/spec/lib/gitlab/metrics/influx_sampler_spec.rb
index 0bc68d64276..999a9536d82 100644
--- a/spec/lib/gitlab/metrics/influx_sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/influx_sampler_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::Metrics::InfluxSampler do
it 'runs once and gathers a sample at a given interval' do
expect(sampler).to receive(:sleep).with(a_kind_of(Numeric)).twice
expect(sampler).to receive(:sample).once
- expect(sampler).to receive(:running).and_return(false, true, false)
+ expect(sampler).to receive(:running).and_return(true, false)
sampler.start.join
end
diff --git a/spec/lib/gitlab/metrics/sidekiq_metrics_exporter_spec.rb b/spec/lib/gitlab/metrics/sidekiq_metrics_exporter_spec.rb
new file mode 100644
index 00000000000..6721e02fb85
--- /dev/null
+++ b/spec/lib/gitlab/metrics/sidekiq_metrics_exporter_spec.rb
@@ -0,0 +1,101 @@
+require 'spec_helper'
+
+describe Gitlab::Metrics::SidekiqMetricsExporter do
+ let(:exporter) { described_class.new }
+ let(:server) { double('server') }
+
+ before do
+ allow(::WEBrick::HTTPServer).to receive(:new).and_return(server)
+ allow(server).to receive(:mount)
+ allow(server).to receive(:start)
+ allow(server).to receive(:shutdown)
+ end
+
+ describe 'when exporter is enabled' do
+ before do
+ allow(Settings.monitoring.sidekiq_exporter).to receive(:enabled).and_return(true)
+ end
+
+ describe 'when exporter is stopped' do
+ describe '#start' do
+ it 'starts the exporter' do
+ expect { exporter.start.join }.to change { exporter.thread? }.from(false).to(true)
+
+ expect(server).to have_received(:start)
+ end
+
+ describe 'with custom settings' do
+ let(:port) { 99999 }
+ let(:address) { 'sidekiq_exporter_address' }
+
+ before do
+ allow(Settings.monitoring.sidekiq_exporter).to receive(:port).and_return(port)
+ allow(Settings.monitoring.sidekiq_exporter).to receive(:address).and_return(address)
+ end
+
+ it 'starts server with port and address from settings' do
+ exporter.start.join
+
+ expect(::WEBrick::HTTPServer).to have_received(:new).with(
+ Port: port,
+ BindAddress: address
+ )
+ end
+ end
+ end
+
+ describe '#stop' do
+ it "doesn't shutdown stopped server" do
+ expect { exporter.stop }.not_to change { exporter.thread? }
+
+ expect(server).not_to have_received(:shutdown)
+ end
+ end
+ end
+
+ describe 'when exporter is running' do
+ before do
+ exporter.start.join
+ end
+
+ describe '#start' do
+ it "doesn't start running server" do
+ expect { exporter.start.join }.not_to change { exporter.thread? }
+
+ expect(server).to have_received(:start).once
+ end
+ end
+
+ describe '#stop' do
+ it 'shutdowns server' do
+ expect { exporter.stop }.to change { exporter.thread? }.from(true).to(false)
+
+ expect(server).to have_received(:shutdown)
+ end
+ end
+ end
+ end
+
+ describe 'when exporter is disabled' do
+ before do
+ allow(Settings.monitoring.sidekiq_exporter).to receive(:enabled).and_return(false)
+ end
+
+ describe '#start' do
+ it "doesn't start" do
+ expect(exporter.start).to be_nil
+ expect { exporter.start }.not_to change { exporter.thread? }
+
+ expect(server).not_to have_received(:start)
+ end
+ end
+
+ describe '#stop' do
+ it "doesn't shutdown" do
+ expect { exporter.stop }.not_to change { exporter.thread? }
+
+ expect(server).not_to have_received(:shutdown)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb
new file mode 100644
index 00000000000..12e75cdd5d0
--- /dev/null
+++ b/spec/lib/gitlab/project_template_spec.rb
@@ -0,0 +1,63 @@
+require 'spec_helper'
+
+describe Gitlab::ProjectTemplate do
+ describe '.all' do
+ it 'returns a all templates' do
+ expected = [
+ described_class.new('rails', 'Ruby on Rails')
+ ]
+
+ expect(described_class.all).to be_an(Array)
+ expect(described_class.all).to eq(expected)
+ end
+ end
+
+ describe '.find' do
+ subject { described_class.find(query) }
+
+ context 'when there is a match' do
+ let(:query) { :rails }
+
+ it { is_expected.to be_a(described_class) }
+ end
+
+ context 'when there is no match' do
+ let(:query) { 'no-match' }
+
+ it { is_expected.to be(nil) }
+ end
+ end
+
+ describe 'instance methods' do
+ subject { described_class.new('phoenix', 'Phoenix Framework') }
+
+ it { is_expected.to respond_to(:logo, :file, :archive_path) }
+ end
+
+ describe 'validate all templates' do
+ set(:admin) { create(:admin) }
+
+ described_class.all.each do |template|
+ it "#{template.name} has a valid archive" do
+ archive = template.archive_path
+
+ expect(File.exist?(archive)).to be(true)
+ end
+
+ context 'with valid parameters' do
+ it 'can be imported' do
+ params = {
+ template_name: template.name,
+ namespace_id: admin.namespace.id,
+ path: template.name
+ }
+
+ project = Projects::CreateFromTemplateService.new(admin, params).execute
+
+ expect(project).to be_valid
+ expect(project).to be_persisted
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index b90d8dede0f..2345874cf10 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -174,20 +174,94 @@ describe Gitlab::Shell do
end
describe '#fetch_remote' do
+ def fetch_remote(ssh_auth = nil)
+ gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage', ssh_auth: ssh_auth)
+ end
+
+ def expect_popen(vars = {})
+ popen_args = [
+ projects_path,
+ 'fetch-remote',
+ 'current/storage',
+ 'project/path.git',
+ 'new/storage',
+ Gitlab.config.gitlab_shell.git_timeout.to_s
+ ]
+
+ expect(Gitlab::Popen).to receive(:popen).with(popen_args, nil, popen_vars.merge(vars))
+ end
+
+ def build_ssh_auth(opts = {})
+ defaults = {
+ ssh_import?: true,
+ ssh_key_auth?: false,
+ ssh_known_hosts: nil,
+ ssh_private_key: nil
+ }
+
+ double(:ssh_auth, defaults.merge(opts))
+ end
+
it 'returns true when the command succeeds' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800'],
- nil, popen_vars).and_return([nil, 0])
+ expect_popen.and_return([nil, 0])
- expect(gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage')).to be true
+ expect(fetch_remote).to be_truthy
end
it 'raises an exception when the command fails' do
- expect(Gitlab::Popen).to receive(:popen)
- .with([projects_path, 'fetch-remote', 'current/storage', 'project/path.git', 'new/storage', '800'],
- nil, popen_vars).and_return(["error", 1])
+ expect_popen.and_return(["error", 1])
+
+ expect { fetch_remote }.to raise_error(Gitlab::Shell::Error, "error")
+ end
+
+ context 'SSH auth' do
+ it 'passes the SSH key if specified' do
+ expect_popen('GITLAB_SHELL_SSH_KEY' => 'foo').and_return([nil, 0])
+
+ ssh_auth = build_ssh_auth(ssh_key_auth?: true, ssh_private_key: 'foo')
+
+ expect(fetch_remote(ssh_auth)).to be_truthy
+ end
+
+ it 'does not pass an empty SSH key' do
+ expect_popen.and_return([nil, 0])
+
+ ssh_auth = build_ssh_auth(ssh_key_auth: true, ssh_private_key: '')
+
+ expect(fetch_remote(ssh_auth)).to be_truthy
+ end
+
+ it 'does not pass the key unless SSH key auth is to be used' do
+ expect_popen.and_return([nil, 0])
+
+ ssh_auth = build_ssh_auth(ssh_key_auth: false, ssh_private_key: 'foo')
+
+ expect(fetch_remote(ssh_auth)).to be_truthy
+ end
+
+ it 'passes the known_hosts data if specified' do
+ expect_popen('GITLAB_SHELL_KNOWN_HOSTS' => 'foo').and_return([nil, 0])
+
+ ssh_auth = build_ssh_auth(ssh_known_hosts: 'foo')
+
+ expect(fetch_remote(ssh_auth)).to be_truthy
+ end
+
+ it 'does not pass empty known_hosts data' do
+ expect_popen.and_return([nil, 0])
+
+ ssh_auth = build_ssh_auth(ssh_known_hosts: '')
+
+ expect(fetch_remote(ssh_auth)).to be_truthy
+ end
+
+ it 'does not pass known_hosts data unless SSH is to be used' do
+ expect_popen(popen_vars).and_return([nil, 0])
+
+ ssh_auth = build_ssh_auth(ssh_import?: false, ssh_known_hosts: 'foo')
- expect { gitlab_shell.fetch_remote('current/storage', 'project/path', 'new/storage') }.to raise_error(Gitlab::Shell::Error, "error")
+ expect(fetch_remote(ssh_auth)).to be_truthy
+ end
end
end
diff --git a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
new file mode 100644
index 00000000000..597d8eab51c
--- /dev/null
+++ b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
@@ -0,0 +1,41 @@
+# encoding: utf-8
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170803090603_calculate_conv_dev_index_percentages.rb')
+
+describe CalculateConvDevIndexPercentages, truncate: true do
+ let(:migration) { described_class.new }
+ let!(:conv_dev_index) do
+ create(:conversational_development_index_metric,
+ leader_notes: 0,
+ instance_milestones: 0,
+ percentage_issues: 0,
+ percentage_notes: 0,
+ percentage_milestones: 0,
+ percentage_boards: 0,
+ percentage_merge_requests: 0,
+ percentage_ci_pipelines: 0,
+ percentage_environments: 0,
+ percentage_deployments: 0,
+ percentage_projects_prometheus_active: 0,
+ percentage_service_desk_issues: 0)
+ end
+
+ describe '#up' do
+ it 'calculates percentages correctly' do
+ migration.up
+ conv_dev_index.reload
+
+ expect(conv_dev_index.percentage_issues).to be_within(0.1).of(13.3)
+ expect(conv_dev_index.percentage_notes).to be_zero # leader 0
+ expect(conv_dev_index.percentage_milestones).to be_zero # instance 0
+ expect(conv_dev_index.percentage_boards).to be_within(0.1).of(62.4)
+ expect(conv_dev_index.percentage_merge_requests).to eq(50.0)
+ expect(conv_dev_index.percentage_ci_pipelines).to be_within(0.1).of(19.3)
+ expect(conv_dev_index.percentage_environments).to be_within(0.1).of(66.7)
+ expect(conv_dev_index.percentage_deployments).to be_within(0.1).of(64.2)
+ expect(conv_dev_index.percentage_projects_prometheus_active).to be_within(0.1).of(98.2)
+ expect(conv_dev_index.percentage_service_desk_issues).to be_within(0.1).of(84.0)
+ end
+ end
+end
diff --git a/spec/models/conversational_development_index/metric_spec.rb b/spec/models/conversational_development_index/metric_spec.rb
new file mode 100644
index 00000000000..b3193619503
--- /dev/null
+++ b/spec/models/conversational_development_index/metric_spec.rb
@@ -0,0 +1,11 @@
+require 'rails_helper'
+
+describe ConversationalDevelopmentIndex::Metric do
+ let(:conv_dev_index) { create(:conversational_development_index_metric) }
+
+ describe '#percentage_score' do
+ it 'returns stored percentage score' do
+ expect(conv_dev_index.percentage_score('issues')).to eq(13.331)
+ end
+ end
+end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index fa22eee3dea..c055863d298 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -191,14 +191,10 @@ describe Issue do
end
it 'returns the merge request to close this issue' do
- mr
-
expect(issue.closed_by_merge_requests(mr.author)).to eq([mr])
end
it "returns an empty array when the merge request is closed already" do
- closed_mr
-
expect(issue.closed_by_merge_requests(closed_mr.author)).to eq([])
end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index 0daeb337168..3508391c721 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -83,15 +83,6 @@ describe Key, :mailer do
expect(build(:key)).to be_valid
end
- it 'rejects an unfingerprintable key that contains a space' do
- key = build(:key)
-
- # Not always the middle, but close enough
- key.key = key.key[0..100] + ' ' + key.key[101..-1]
-
- expect(key).not_to be_valid
- end
-
it 'accepts a key with newline charecters after stripping them' do
key = build(:key)
key.key = key.key.insert(100, "\n")
@@ -102,7 +93,6 @@ describe Key, :mailer do
it 'rejects the unfingerprintable key (not a key)' do
expect(build(:key, key: 'ssh-rsa an-invalid-key==')).not_to be_valid
end
-
end
context 'callbacks' do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 3402c260f27..a1a3e70a7d2 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1369,6 +1369,32 @@ describe MergeRequest do
end
end
+ describe '#merge_ongoing?' do
+ it 'returns true when merge process is ongoing for merge_jid' do
+ merge_request = create(:merge_request, merge_jid: 'foo')
+
+ allow(Gitlab::SidekiqStatus).to receive(:num_running).with(['foo']).and_return(1)
+
+ expect(merge_request.merge_ongoing?).to be(true)
+ end
+
+ it 'returns false when no merge process running for merge_jid' do
+ merge_request = build(:merge_request, merge_jid: 'foo')
+
+ allow(Gitlab::SidekiqStatus).to receive(:num_running).with(['foo']).and_return(0)
+
+ expect(merge_request.merge_ongoing?).to be(false)
+ end
+
+ it 'returns false when merge_jid is nil' do
+ merge_request = build(:merge_request, merge_jid: nil)
+
+ expect(Gitlab::SidekiqStatus).not_to receive(:num_running)
+
+ expect(merge_request.merge_ongoing?).to be(false)
+ end
+ end
+
describe "#closed_without_fork?" do
let(:project) { create(:project) }
let(:fork_project) { create(:project, forked_from_project: project) }
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index b48aa9558d5..d3da0107d5c 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -230,16 +230,40 @@ describe Milestone do
end
describe '#to_reference' do
- let(:project) { build(:project, name: 'sample-project') }
- let(:milestone) { build(:milestone, iid: 1, project: project) }
+ let(:group) { build_stubbed(:group) }
+ let(:project) { build_stubbed(:project, name: 'sample-project') }
+ let(:another_project) { build_stubbed(:project, name: 'another-project', namespace: project.namespace) }
+
+ context 'for a project milestone' do
+ let(:milestone) { build_stubbed(:milestone, iid: 1, project: project, name: 'milestone') }
+
+ it 'returns a String reference to the object' do
+ expect(milestone.to_reference).to eq '%1'
+ end
+
+ it 'returns a reference by name when the format is set to :name' do
+ expect(milestone.to_reference(format: :name)).to eq '%"milestone"'
+ end
- it 'returns a String reference to the object' do
- expect(milestone.to_reference).to eq "%1"
+ it 'supports a cross-project reference' do
+ expect(milestone.to_reference(another_project)).to eq 'sample-project%1'
+ end
end
- it 'supports a cross-project reference' do
- another_project = build(:project, name: 'another-project', namespace: project.namespace)
- expect(milestone.to_reference(another_project)).to eq "sample-project%1"
+ context 'for a group milestone' do
+ let(:milestone) { build_stubbed(:milestone, iid: 1, group: group, name: 'milestone') }
+
+ it 'returns nil with the default format' do
+ expect(milestone.to_reference).to be_nil
+ end
+
+ it 'returns a reference by name when the format is set to :name' do
+ expect(milestone.to_reference(format: :name)).to eq '%"milestone"'
+ end
+
+ it 'does not supports cross-project references' do
+ expect(milestone.to_reference(another_project, format: :name)).to eq '%"milestone"'
+ end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 4ddda5b638c..cfa77648338 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -234,19 +234,31 @@ describe Repository, models: true do
end
describe '#find_commits_by_message' do
- it 'returns commits with messages containing a given string' do
- commit_ids = repository.find_commits_by_message('submodule').map(&:id)
+ shared_examples 'finding commits by message' do
+ it 'returns commits with messages containing a given string' do
+ commit_ids = repository.find_commits_by_message('submodule').map(&:id)
- expect(commit_ids).to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
- expect(commit_ids).to include('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
- expect(commit_ids).to include('cfe32cf61b73a0d5e9f13e774abde7ff789b1660')
- expect(commit_ids).not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e')
+ expect(commit_ids).to include(
+ '5937ac0a7beb003549fc5fd26fc247adbce4a52e',
+ '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9',
+ 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660'
+ )
+ expect(commit_ids).not_to include('913c66a37b4a45b9769037c55c2d238bd0942d2e')
+ end
+
+ it 'is case insensitive' do
+ commit_ids = repository.find_commits_by_message('SUBMODULE').map(&:id)
+
+ expect(commit_ids).to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ end
end
- it 'is case insensitive' do
- commit_ids = repository.find_commits_by_message('SUBMODULE').map(&:id)
+ context 'when Gitaly commits_by_message feature is enabled' do
+ it_behaves_like 'finding commits by message'
+ end
- expect(commit_ids).to include('5937ac0a7beb003549fc5fd26fc247adbce4a52e')
+ context 'when Gitaly commits_by_message feature is disabled', skip_gitaly_mock: true do
+ it_behaves_like 'finding commits by message'
end
describe 'when storage is broken', broken_storage: true do
diff --git a/spec/presenters/conversational_development_index/metric_presenter_spec.rb b/spec/presenters/conversational_development_index/metric_presenter_spec.rb
index 1e015c71f5b..81eb05a9a6b 100644
--- a/spec/presenters/conversational_development_index/metric_presenter_spec.rb
+++ b/spec/presenters/conversational_development_index/metric_presenter_spec.rb
@@ -8,9 +8,9 @@ describe ConversationalDevelopmentIndex::MetricPresenter do
it 'includes instance score, leader score and percentage score' do
issues_card = subject.cards.first
- expect(issues_card.instance_score).to eq 1.234
- expect(issues_card.leader_score).to eq 9.256
- expect(issues_card.percentage_score).to be_within(0.1).of(13.3)
+ expect(issues_card.instance_score).to eq(1.234)
+ expect(issues_card.leader_score).to eq(9.256)
+ expect(issues_card.percentage_score).to eq(13.331)
end
end
diff --git a/spec/serializers/merge_request_entity_spec.rb b/spec/serializers/merge_request_entity_spec.rb
index 18cd9e9c006..a2fd5b7daae 100644
--- a/spec/serializers/merge_request_entity_spec.rb
+++ b/spec/serializers/merge_request_entity_spec.rb
@@ -47,7 +47,7 @@ describe MergeRequestEntity do
:cancel_merge_when_pipeline_succeeds_path,
:create_issue_to_resolve_discussions_path,
:source_branch_path, :target_branch_commits_path,
- :target_branch_tree_path, :commits_count)
+ :target_branch_tree_path, :commits_count, :merge_ongoing)
end
it 'has email_patches_path' do
diff --git a/spec/services/projects/autocomplete_service_spec.rb b/spec/services/projects/autocomplete_service_spec.rb
index c1f098530bf..426593be428 100644
--- a/spec/services/projects/autocomplete_service_spec.rb
+++ b/spec/services/projects/autocomplete_service_spec.rb
@@ -88,4 +88,31 @@ describe Projects::AutocompleteService do
end
end
end
+
+ describe '#milestones' do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let!(:group_milestone) { create(:milestone, group: group) }
+ let!(:project_milestone) { create(:milestone, project: project) }
+
+ let(:milestone_titles) { described_class.new(project, user).milestones.map(&:title) }
+
+ it 'includes project and group milestones' do
+ expect(milestone_titles).to eq([group_milestone.title, project_milestone.title])
+ end
+
+ it 'does not include closed milestones' do
+ group_milestone.close
+
+ expect(milestone_titles).to eq([project_milestone.title])
+ end
+
+ it 'does not include milestones from other projects in the group' do
+ other_project = create(:project, group: group)
+ project_milestone.update!(project: other_project)
+
+ expect(milestone_titles).to eq([group_milestone.title])
+ end
+ end
end
diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb
new file mode 100644
index 00000000000..9919ec254c6
--- /dev/null
+++ b/spec/services/projects/create_from_template_service_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe Projects::CreateFromTemplateService do
+ let(:user) { create(:user) }
+ let(:project_params) do
+ {
+ path: user.to_param,
+ template_name: 'rails'
+ }
+ end
+
+ subject { described_class.new(user, project_params) }
+
+ it 'calls the importer service' do
+ expect_any_instance_of(Projects::GitlabProjectsImportService).to receive(:execute)
+
+ subject.execute
+ end
+
+ it 'returns the project thats created' do
+ project = subject.execute
+
+ expect(project).to be_saved
+ expect(project.scheduled?).to be(true)
+ end
+end
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index c0ab1ea704d..034065aab00 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -38,8 +38,7 @@ describe Projects::ImportService do
context 'with a Github repository' do
it 'succeeds if repository import is successfully' do
- expect_any_instance_of(Repository).to receive(:fetch_remote).and_return(true)
- expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(true)
+ expect_any_instance_of(Github::Import).to receive(:execute).and_return(true)
result = subject.execute
@@ -52,16 +51,7 @@ describe Projects::ImportService do
result = subject.execute
expect(result[:status]).to eq :error
- expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.full_path} - Failed to import the repository"
- end
-
- it 'does not remove the GitHub remote' do
- expect_any_instance_of(Repository).to receive(:fetch_remote).and_return(true)
- expect_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(true)
-
- subject.execute
-
- expect(project.repository.raw_repository.remote_names).to include('github')
+ expect(result[:message]).to eq "Error importing repository #{project.import_url} into #{project.path_with_namespace} - The remote data could not be imported."
end
end
@@ -102,8 +92,7 @@ describe Projects::ImportService do
end
it 'succeeds if importer succeeds' do
- allow_any_instance_of(Repository).to receive(:fetch_remote).and_return(true)
- allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(true)
+ allow_any_instance_of(Github::Import).to receive(:execute).and_return(true)
result = subject.execute
@@ -111,10 +100,7 @@ describe Projects::ImportService do
end
it 'flushes various caches' do
- allow_any_instance_of(Repository).to receive(:fetch_remote)
- .and_return(true)
-
- allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute)
+ allow_any_instance_of(Github::Import).to receive(:execute)
.and_return(true)
expect_any_instance_of(Repository).to receive(:expire_content_cache)
@@ -123,8 +109,7 @@ describe Projects::ImportService do
end
it 'fails if importer fails' do
- allow_any_instance_of(Repository).to receive(:fetch_remote).and_return(true)
- allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_return(false)
+ allow_any_instance_of(Github::Import).to receive(:execute).and_return(false)
result = subject.execute
@@ -133,8 +118,7 @@ describe Projects::ImportService do
end
it 'fails if importer raise an error' do
- allow_any_instance_of(Gitlab::Shell).to receive(:fetch_remote).and_return(true)
- allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).and_raise(Projects::ImportService::Error.new('Github: failed to connect API'))
+ allow_any_instance_of(Github::Import).to receive(:execute).and_raise(Projects::ImportService::Error.new('Github: failed to connect API'))
result = subject.execute
@@ -143,9 +127,9 @@ describe Projects::ImportService do
end
it 'expires content cache after error' do
- allow_any_instance_of(Project).to receive(:repository_exists?).and_return(false, true)
+ allow_any_instance_of(Project).to receive(:repository_exists?).and_return(false)
- expect_any_instance_of(Gitlab::Shell).to receive(:fetch_remote).and_raise(Gitlab::Shell::Error.new('Failed to import the repository'))
+ expect_any_instance_of(Repository).to receive(:fetch_remote).and_raise(Gitlab::Shell::Error.new)
expect_any_instance_of(Repository).to receive(:expire_content_cache)
subject.execute
diff --git a/spec/services/submit_usage_ping_service_spec.rb b/spec/services/submit_usage_ping_service_spec.rb
index 817fa4262d5..c8a6fc1a99b 100644
--- a/spec/services/submit_usage_ping_service_spec.rb
+++ b/spec/services/submit_usage_ping_service_spec.rb
@@ -46,6 +46,8 @@ describe SubmitUsagePingService do
.by(1)
expect(ConversationalDevelopmentIndex::Metric.last.leader_issues).to eq 10.2
+ expect(ConversationalDevelopmentIndex::Metric.last.instance_issues).to eq 3.2
+ expect(ConversationalDevelopmentIndex::Metric.last.percentage_issues).to eq 31.37
end
end
@@ -60,6 +62,7 @@ describe SubmitUsagePingService do
conv_index: {
leader_issues: 10.2,
instance_issues: 3.2,
+ percentage_issues: 31.37,
leader_notes: 25.3,
instance_notes: 23.2,
@@ -86,7 +89,9 @@ describe SubmitUsagePingService do
instance_projects_prometheus_active: 0.30,
leader_service_desk_issues: 15.8,
- instance_service_desk_issues: 15.1
+ instance_service_desk_issues: 15.1,
+
+ non_existing_column: 'value'
}
}
end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index e3805160b04..8f1eb4863d9 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -3,7 +3,8 @@ require 'spec_helper'
describe SystemNoteService do
include Gitlab::Routing
- let(:project) { create(:project) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
let(:author) { create(:user) }
let(:noteable) { create(:issue, project: project) }
let(:issue) { noteable }
@@ -242,25 +243,51 @@ describe SystemNoteService do
end
describe '.change_milestone' do
- subject { described_class.change_milestone(noteable, project, author, milestone) }
+ context 'for a project milestone' do
+ subject { described_class.change_milestone(noteable, project, author, milestone) }
- let(:milestone) { create(:milestone, project: project) }
+ let(:milestone) { create(:milestone, project: project) }
- it_behaves_like 'a system note' do
- let(:action) { 'milestone' }
- end
+ it_behaves_like 'a system note' do
+ let(:action) { 'milestone' }
+ end
- context 'when milestone added' do
- it 'sets the note text' do
- expect(subject.note).to eq "changed milestone to #{milestone.to_reference}"
+ context 'when milestone added' do
+ it 'sets the note text' do
+ expect(subject.note).to eq "changed milestone to #{milestone.to_reference}"
+ end
+ end
+
+ context 'when milestone removed' do
+ let(:milestone) { nil }
+
+ it 'sets the note text' do
+ expect(subject.note).to eq 'removed milestone'
+ end
end
end
- context 'when milestone removed' do
- let(:milestone) { nil }
+ context 'for a group milestone' do
+ subject { described_class.change_milestone(noteable, project, author, milestone) }
- it 'sets the note text' do
- expect(subject.note).to eq 'removed milestone'
+ let(:milestone) { create(:milestone, group: group) }
+
+ it_behaves_like 'a system note' do
+ let(:action) { 'milestone' }
+ end
+
+ context 'when milestone added' do
+ it 'sets the note text to use the milestone name' do
+ expect(subject.note).to eq "changed milestone to #{milestone.to_reference(format: :name)}"
+ end
+ end
+
+ context 'when milestone removed' do
+ let(:milestone) { nil }
+
+ it 'sets the note text' do
+ expect(subject.note).to eq 'removed milestone'
+ end
end
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 06769b241ad..0ba6ed56314 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -134,13 +134,13 @@ RSpec.configure do |config|
ActiveRecord::Migrator
.migrate(migrations_paths, previous_migration.version)
- ActiveRecord::Base.descendants.each(&:reset_column_information)
+ reset_column_in_migration_models
end
config.after(:example, :migration) do
ActiveRecord::Migrator.migrate(migrations_paths)
- ActiveRecord::Base.descendants.each(&:reset_column_information)
+ reset_column_in_migration_models
end
config.around(:each, :nested_groups) do |example|
diff --git a/spec/support/cycle_analytics_helpers.rb b/spec/support/cycle_analytics_helpers.rb
index c0a5491a430..30911e7fa86 100644
--- a/spec/support/cycle_analytics_helpers.rb
+++ b/spec/support/cycle_analytics_helpers.rb
@@ -41,7 +41,9 @@ module CycleAnalyticsHelpers
target_branch: 'master'
}
- MergeRequests::CreateService.new(project, user, opts).execute
+ mr = MergeRequests::CreateService.new(project, user, opts).execute
+ NewMergeRequestWorker.new.perform(mr, user)
+ mr
end
def merge_merge_requests_closing_issue(issue)
diff --git a/spec/support/issuable_shared_examples.rb b/spec/support/issuable_shared_examples.rb
index 970fe10db2b..42f3b4db23c 100644
--- a/spec/support/issuable_shared_examples.rb
+++ b/spec/support/issuable_shared_examples.rb
@@ -21,15 +21,15 @@ shared_examples 'system notes for milestones' do
create(:group_member, group: group, user: user)
end
- it 'does not create system note' do
+ it 'creates a system note' do
expect do
update_issuable(milestone: group_milestone)
- end.not_to change { Note.system.count }
+ end.to change { Note.system.count }.by(1)
end
end
context 'project milestones' do
- it 'creates system note' do
+ it 'creates a system note' do
expect do
update_issuable(milestone: create(:milestone))
end.to change { Note.system.count }.by(1)
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index 21a054af4e1..c90359d7cfa 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -23,7 +23,7 @@ class MarkdownFeature
# Direct references ----------------------------------------------------------
def project
- @project ||= create(:project, :repository).tap do |project|
+ @project ||= create(:project, :repository, group: group).tap do |project|
project.team << [user, :master]
end
end
@@ -75,6 +75,10 @@ class MarkdownFeature
@milestone ||= create(:milestone, name: 'next goal', project: project)
end
+ def group_milestone
+ @group_milestone ||= create(:milestone, name: 'group-milestone', group: group)
+ end
+
# Cross-references -----------------------------------------------------------
def xproject
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index 7afa57fb76b..d12b2757427 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -155,7 +155,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-milestone', count: 6)
+ expect(actual).to have_selector('a.gfm.gfm-milestone', count: 8)
end
end
diff --git a/spec/support/migrations_helpers.rb b/spec/support/migrations_helpers.rb
index 91fbb4eaf48..aabdad13047 100644
--- a/spec/support/migrations_helpers.rb
+++ b/spec/support/migrations_helpers.rb
@@ -15,6 +15,16 @@ module MigrationsHelpers
ActiveRecord::Migrator.migrations(migrations_paths)
end
+ def reset_column_in_migration_models
+ described_class.constants.sort.each do |name|
+ const = described_class.const_get(name)
+
+ if const.is_a?(Class) && const < ActiveRecord::Base
+ const.reset_column_information
+ end
+ end
+ end
+
def previous_migration
migrations.each_cons(2) do |previous, migration|
break previous if migration.name == described_class.name
diff --git a/spec/workers/merge_worker_spec.rb b/spec/workers/merge_worker_spec.rb
index 303193bab9b..ee51000161a 100644
--- a/spec/workers/merge_worker_spec.rb
+++ b/spec/workers/merge_worker_spec.rb
@@ -27,4 +27,15 @@ describe MergeWorker do
expect(source_project.repository.branch_names).not_to include('markdown')
end
end
+
+ it 'persists merge_jid' do
+ merge_request = create(:merge_request, merge_jid: nil)
+ user = create(:user)
+ worker = described_class.new
+
+ allow(worker).to receive(:jid) { '999' }
+
+ expect { worker.perform(merge_request.id, user.id, {}) }
+ .to change { merge_request.reload.merge_jid }.from(nil).to('999')
+ end
end
diff --git a/spec/workers/new_issue_worker_spec.rb b/spec/workers/new_issue_worker_spec.rb
new file mode 100644
index 00000000000..ed49ce57c0b
--- /dev/null
+++ b/spec/workers/new_issue_worker_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+
+describe NewIssueWorker do
+ describe '#perform' do
+ let(:worker) { described_class.new }
+
+ context 'when an issue not found' do
+ it 'does not call Services' do
+ expect(EventCreateService).not_to receive(:new)
+ expect(NotificationService).not_to receive(:new)
+
+ worker.perform(99, create(:user).id)
+ end
+
+ it 'logs an error' do
+ expect(Rails.logger).to receive(:error).with('NewIssueWorker: couldn\'t find Issue with ID=99, skipping job')
+
+ worker.perform(99, create(:user).id)
+ end
+ end
+
+ context 'when a user not found' do
+ it 'does not call Services' do
+ expect(EventCreateService).not_to receive(:new)
+ expect(NotificationService).not_to receive(:new)
+
+ worker.perform(create(:issue).id, 99)
+ end
+
+ it 'logs an error' do
+ expect(Rails.logger).to receive(:error).with('NewIssueWorker: couldn\'t find User with ID=99, skipping job')
+
+ worker.perform(create(:issue).id, 99)
+ end
+ end
+
+ context 'when everything is ok' do
+ let(:project) { create(:project, :public) }
+ let(:mentioned) { create(:user) }
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, project: project, description: "issue for #{mentioned.to_reference}") }
+
+ it 'creates a new event record' do
+ expect{ worker.perform(issue.id, user.id) }.to change { Event.count }.from(0).to(1)
+ end
+
+ it 'creates a notification for the assignee' do
+ expect(Notify).to receive(:new_issue_email).with(mentioned.id, issue.id).and_return(double(deliver_later: true))
+
+ worker.perform(issue.id, user.id)
+ end
+ end
+ end
+end
diff --git a/spec/workers/new_merge_request_worker_spec.rb b/spec/workers/new_merge_request_worker_spec.rb
new file mode 100644
index 00000000000..85af6184d39
--- /dev/null
+++ b/spec/workers/new_merge_request_worker_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe NewMergeRequestWorker do
+ describe '#perform' do
+ let(:worker) { described_class.new }
+
+ context 'when a merge request not found' do
+ it 'does not call Services' do
+ expect(EventCreateService).not_to receive(:new)
+ expect(NotificationService).not_to receive(:new)
+
+ worker.perform(99, create(:user).id)
+ end
+
+ it 'logs an error' do
+ expect(Rails.logger).to receive(:error).with('NewMergeRequestWorker: couldn\'t find MergeRequest with ID=99, skipping job')
+
+ worker.perform(99, create(:user).id)
+ end
+ end
+
+ context 'when a user not found' do
+ it 'does not call Services' do
+ expect(EventCreateService).not_to receive(:new)
+ expect(NotificationService).not_to receive(:new)
+
+ worker.perform(create(:merge_request).id, 99)
+ end
+
+ it 'logs an error' do
+ expect(Rails.logger).to receive(:error).with('NewMergeRequestWorker: couldn\'t find User with ID=99, skipping job')
+
+ worker.perform(create(:merge_request).id, 99)
+ end
+ end
+
+ context 'when everything is ok' do
+ let(:project) { create(:project, :public) }
+ let(:mentioned) { create(:user) }
+ let(:user) { create(:user) }
+ let(:merge_request) do
+ create(:merge_request, source_project: project, description: "mr for #{mentioned.to_reference}")
+ end
+
+ it 'creates a new event record' do
+ expect{ worker.perform(merge_request.id, user.id) }.to change { Event.count }.from(0).to(1)
+ end
+
+ it 'creates a notification for the assignee' do
+ expect(Notify).to receive(:new_merge_request_email).with(mentioned.id, merge_request.id).and_return(double(deliver_later: true))
+
+ worker.perform(merge_request.id, user.id)
+ end
+ end
+ end
+end
diff --git a/spec/workers/stuck_merge_jobs_worker_spec.rb b/spec/workers/stuck_merge_jobs_worker_spec.rb
new file mode 100644
index 00000000000..a5ad78393c9
--- /dev/null
+++ b/spec/workers/stuck_merge_jobs_worker_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe StuckMergeJobsWorker do
+ describe 'perform' do
+ let(:worker) { described_class.new }
+
+ context 'merge job identified as completed' do
+ it 'updates merge request to merged when locked but has merge_commit_sha' do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(%w(123 456))
+ mr_with_sha = create(:merge_request, :locked, merge_jid: '123', state: :locked, merge_commit_sha: 'foo-bar-baz')
+ mr_without_sha = create(:merge_request, :locked, merge_jid: '123', state: :locked, merge_commit_sha: nil)
+
+ worker.perform
+
+ expect(mr_with_sha.reload).to be_merged
+ expect(mr_without_sha.reload).to be_opened
+ end
+
+ it 'updates merge request to opened when locked but has not been merged' do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(%w(123))
+ merge_request = create(:merge_request, :locked, merge_jid: '123', state: :locked)
+
+ worker.perform
+
+ expect(merge_request.reload).to be_opened
+ end
+
+ it 'logs updated stuck merge job ids' do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(%w(123 456))
+
+ create(:merge_request, :locked, merge_jid: '123')
+ create(:merge_request, :locked, merge_jid: '456')
+
+ expect(Rails).to receive_message_chain(:logger, :info).with('Updated state of locked merge jobs. JIDs: 123, 456')
+
+ worker.perform
+ end
+ end
+
+ context 'merge job not identified as completed' do
+ it 'does not change merge request state when job is not completed yet' do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([])
+
+ merge_request = create(:merge_request, :locked, merge_jid: '123')
+
+ expect { worker.perform }.not_to change { merge_request.reload.state }.from('locked')
+ end
+ end
+ end
+end