summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorAhmad Hassan <ahmad.hassan612@gmail.com>2018-12-17 13:18:50 +0200
committerAhmad Hassan <ahmad.hassan612@gmail.com>2018-12-17 13:18:50 +0200
commitd6886506a0bb8b7db3e6f4fa7dc4d5aa1632b84f (patch)
tree04b5625a927fb37e684d026cb5161b78f993b72e /spec
parentdfc54352c001e8544972c3d40bfc82e55a11c6a0 (diff)
parent32b6129d8cef406b1cafa455fcf0bf83ddba46de (diff)
downloadgitlab-ce-d6886506a0bb8b7db3e6f4fa7dc4d5aa1632b84f.tar.gz
Merge remote-tracking branch 'origin/master' into support-gitaly-tls
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/application_controller_spec.rb20
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb6
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb8
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb18
-rw-r--r--spec/controllers/projects/services_controller_spec.rb38
-rw-r--r--spec/controllers/uploads_controller_spec.rb6
-rw-r--r--spec/db/schema_spec.rb3
-rw-r--r--spec/factories/ci/bridge.rb17
-rw-r--r--spec/factories/releases.rb1
-rw-r--r--spec/factories/suggestions.rb20
-rw-r--r--spec/features/help_pages_spec.rb18
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb10
-rw-r--r--spec/features/merge_request/user_awards_emoji_spec.rb12
-rw-r--r--spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb2
-rw-r--r--spec/features/merge_request/user_suggests_changes_on_diff_spec.rb85
-rw-r--r--spec/features/projects/labels/user_views_labels_spec.rb8
-rw-r--r--spec/finders/cluster_ancestors_finder_spec.rb77
-rw-r--r--spec/fixtures/api/schemas/entities/diff_line.json3
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json3
-rw-r--r--spec/frontend/dummy_spec.js1
-rw-r--r--spec/frontend/vue_shared/components/notes/timeline_entry_item_spec.js (renamed from spec/javascripts/vue_shared/components/notes/timeline_entry_item_spec.js)0
-rw-r--r--spec/helpers/emails_helper_spec.rb55
-rw-r--r--spec/helpers/sorting_helper_spec.rb6
-rw-r--r--spec/helpers/storage_helper_spec.rb6
-rw-r--r--spec/helpers/version_check_helper_spec.rb12
-rw-r--r--spec/javascripts/blob_edit/blob_bundle_spec.js7
-rw-r--r--spec/javascripts/boards/boards_store_spec.js7
-rw-r--r--spec/javascripts/boards/components/issue_due_date_spec.js7
-rw-r--r--spec/javascripts/boards/issue_spec.js18
-rw-r--r--spec/javascripts/diffs/components/app_spec.js91
-rw-r--r--spec/javascripts/diffs/components/diff_content_spec.js40
-rw-r--r--spec/javascripts/diffs/components/no_changes_spec.js41
-rw-r--r--spec/javascripts/diffs/mock_data/diff_discussions.js15
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js85
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js126
-rw-r--r--spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js43
-rw-r--r--spec/javascripts/notes/components/note_app_spec.js23
-rw-r--r--spec/javascripts/releases/components/release_block_spec.js148
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js25
-rw-r--r--spec/javascripts/vue_shared/components/markdown/field_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/markdown/header_spec.js15
-rw-r--r--spec/javascripts/vue_shared/components/markdown/suggestion_diff_header_spec.js69
-rw-r--r--spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js79
-rw-r--r--spec/javascripts/vue_shared/components/markdown/suggestions_spec.js125
-rw-r--r--spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js13
-rw-r--r--spec/lib/backup/repository_spec.rb13
-rw-r--r--spec/lib/banzai/filter/suggestion_filter_spec.rb35
-rw-r--r--spec/lib/banzai/suggestions_parser_spec.rb32
-rw-r--r--spec/lib/constraints/feature_constrainer_spec.rb11
-rw-r--r--spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb20
-rw-r--r--spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/config/entry/except_policy_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/config/entry/global_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/config/entry/jobs_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/config/entry/only_policy_spec.rb15
-rw-r--r--spec/lib/gitlab/ci/config/entry/policy_spec.rb167
-rw-r--r--spec/lib/gitlab/ci/parsers_spec.rb (renamed from spec/lib/gitlab/ci/parsers/test_spec.rb)8
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb7
-rw-r--r--spec/lib/gitlab/database_spec.rb15
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb89
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb7
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml3
-rw-r--r--spec/lib/gitlab/sql/glob_spec.rb3
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb1
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb2
-rw-r--r--spec/mailers/notify_spec.rb10
-rw-r--r--spec/migrations/backfill_releases_name_with_tag_name_spec.rb23
-rw-r--r--spec/models/ci/bridge_spec.rb25
-rw-r--r--spec/models/clusters/applications/knative_spec.rb6
-rw-r--r--spec/models/diff_note_spec.rb18
-rw-r--r--spec/models/merge_request_diff_spec.rb2
-rw-r--r--spec/models/merge_request_spec.rb24
-rw-r--r--spec/models/notification_setting_spec.rb7
-rw-r--r--spec/models/project_import_data_spec.rb42
-rw-r--r--spec/models/project_spec.rb23
-rw-r--r--spec/models/release_spec.rb1
-rw-r--r--spec/models/remote_mirror_spec.rb39
-rw-r--r--spec/models/suggestion_spec.rb57
-rw-r--r--spec/models/user_spec.rb1
-rw-r--r--spec/presenters/clusters/cluster_presenter_spec.rb140
-rw-r--r--spec/requests/api/internal_spec.rb15
-rw-r--r--spec/requests/api/suggestions_spec.rb83
-rw-r--r--spec/rubocop/cop/inject_enterprise_edition_module_spec.rb133
-rw-r--r--spec/rubocop/cop/migration/add_timestamps_spec.rb6
-rw-r--r--spec/rubocop/cop/migration/datetime_spec.rb8
-rw-r--r--spec/rubocop/cop/migration/timestamps_spec.rb6
-rw-r--r--spec/serializers/issue_board_entity_spec.rb31
-rw-r--r--spec/serializers/suggestion_entity_spec.rb23
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb31
-rw-r--r--spec/services/create_release_service_spec.rb5
-rw-r--r--spec/services/notes/update_service_spec.rb23
-rw-r--r--spec/services/notification_service_spec.rb33
-rw-r--r--spec/services/preview_markdown_service_spec.rb25
-rw-r--r--spec/services/projects/lfs_pointers/lfs_download_service_spec.rb12
-rw-r--r--spec/services/suggestions/apply_service_spec.rb229
-rw-r--r--spec/services/suggestions/create_service_spec.rb110
-rw-r--r--spec/spec_helper.rb10
-rw-r--r--spec/support/carrierwave.rb2
-rw-r--r--spec/support/db_cleaner.rb6
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb10
-rw-r--r--spec/support/helpers/email_helpers.rb9
-rw-r--r--spec/support/helpers/fake_migration_classes.rb2
-rw-r--r--spec/support/helpers/test_request_helpers.rb6
-rw-r--r--spec/support/services/migrate_to_ghost_user_service_shared_examples.rb2
-rw-r--r--spec/support/setup_builds_storage.rb2
-rw-r--r--spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb32
-rw-r--r--spec/support/shared_examples/notify_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/only_except_policy_examples.rb167
-rw-r--r--spec/support/shared_examples/serializers/diff_file_entity_examples.rb2
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb1
-rw-r--r--spec/workers/repository_update_remote_mirror_worker_spec.rb13
116 files changed, 2745 insertions, 626 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index c2bd7fd9808..945c9f97fc3 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -551,14 +551,7 @@ describe ApplicationController do
subject { get :index, text: "hi \255" }
it 'renders 412' do
- if Gitlab.rails5?
- expect { subject }.to raise_error(ActionController::BadRequest)
- else
- subject
-
- expect(response).to have_gitlab_http_status(412)
- expect(response).to render_template :precondition_failed
- end
+ expect { subject }.to raise_error(ActionController::BadRequest)
end
end
@@ -566,16 +559,7 @@ describe ApplicationController do
subject { get :index, text: "hi \255", format: :js }
it 'renders 412' do
- if Gitlab.rails5?
- expect { subject }.to raise_error(ActionController::BadRequest)
- else
- subject
-
- json_response = JSON.parse(response.body)
-
- expect(response).to have_gitlab_http_status(412)
- expect(json_response['error']).to eq('Invalid UTF-8')
- end
+ expect { subject }.to raise_error(ActionController::BadRequest)
end
end
end
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
index 80631d2efb0..16ccf405247 100644
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ b/spec/controllers/boards/lists_controller_spec.rb
@@ -163,11 +163,7 @@ describe Boards::ListsController do
list: { position: position },
format: :json }
- if Gitlab.rails5?
- patch :update, params: params, as: :json
- else
- patch :update, params
- end
+ patch :update, params: params, as: :json
end
end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 02930edbf72..6240ab6d867 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -42,6 +42,8 @@ describe Projects::IssuesController do
it_behaves_like "issuables list meta-data", :issue
+ it_behaves_like 'set sort order from user preference'
+
it "returns index" do
get :index, namespace_id: project.namespace, project_id: project
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 7f15da859e5..e837c99d19c 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -160,6 +160,8 @@ describe Projects::MergeRequestsController do
it_behaves_like "issuables list meta-data", :merge_request
+ it_behaves_like 'set sort order from user preference'
+
context 'when page param' do
let(:last_page) { project.merge_requests.page().total_pages }
let!(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
@@ -355,11 +357,7 @@ describe Projects::MergeRequestsController do
context 'when the sha parameter matches the source SHA' do
def merge_with_sha(params = {})
post_params = base_params.merge(sha: merge_request.diff_head_sha).merge(params)
- if Gitlab.rails5?
- post :merge, params: post_params, as: :json
- else
- post :merge, post_params
- end
+ post :merge, params: post_params, as: :json
end
it 'returns :success' do
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index 7179423dde2..08e2c957d69 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -310,19 +310,11 @@ describe Projects::PipelineSchedulesController do
end
def go
- if Gitlab.rails5?
- put :update, params: { namespace_id: project.namespace.to_param,
- project_id: project,
- id: pipeline_schedule,
- schedule: schedule },
- as: :html
-
- else
- put :update, namespace_id: project.namespace.to_param,
- project_id: project,
- id: pipeline_schedule,
- schedule: schedule
- end
+ put :update, params: { namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: pipeline_schedule,
+ schedule: schedule },
+ as: :html
end
end
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index 45cea8c1351..0941af6779f 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -3,9 +3,8 @@ require 'spec_helper'
describe Projects::ServicesController do
let(:project) { create(:project, :repository) }
let(:user) { create(:user) }
- let(:service) { create(:hipchat_service, project: project) }
- let(:hipchat_client) { { '#room' => double(send: true) } }
- let(:service_params) { { token: 'hipchat_token_p', room: '#room' } }
+ let(:service) { create(:jira_service, project: project) }
+ let(:service_params) { { username: 'username', password: 'password', url: 'http://example.com' } }
before do
sign_in(user)
@@ -24,13 +23,13 @@ describe Projects::ServicesController do
end
context 'when validations fail' do
- let(:service_params) { { active: 'true', token: '' } }
+ let(:service_params) { { active: 'true', url: '' } }
it 'returns error messages in JSON response' do
- put :test, namespace_id: project.namespace, project_id: project, id: :hipchat, service: service_params
+ put :test, namespace_id: project.namespace, project_id: project, id: service.to_param, service: service_params
expect(json_response['message']).to eq "Validations failed."
- expect(json_response['service_response']).to eq "Token can't be blank"
+ expect(json_response['service_response']).to include "Url can't be blank"
expect(response).to have_gitlab_http_status(200)
end
end
@@ -52,7 +51,8 @@ describe Projects::ServicesController do
end
it 'returns success' do
- expect(HipChat::Client).to receive(:new).with('hipchat_token_p', anything).and_return(hipchat_client)
+ stub_request(:get, 'http://example.com/rest/api/2/serverInfo')
+ .to_return(status: 200, body: '{}')
put :test, namespace_id: project.namespace, project_id: project, id: service.to_param, service: service_params
@@ -61,7 +61,8 @@ describe Projects::ServicesController do
end
it 'returns success' do
- expect(HipChat::Client).to receive(:new).with('hipchat_token_p', anything).and_return(hipchat_client)
+ stub_request(:get, 'http://example.com/rest/api/2/serverInfo')
+ .to_return(status: 200, body: '{}')
put :test, namespace_id: project.namespace, project_id: project, id: service.to_param, service: service_params
@@ -76,12 +77,16 @@ describe Projects::ServicesController do
it 'persist the object' do
do_put
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_empty
expect(BuildkiteService.first).to be_present
end
it 'creates the ServiceHook object' do
do_put
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_empty
expect(BuildkiteService.first.service_hook).to be_present
end
@@ -96,13 +101,18 @@ describe Projects::ServicesController do
context 'failure' do
it 'returns success status code and the error message' do
- expect(HipChat::Client).to receive(:new).with('hipchat_token_p', anything).and_raise('Bad test')
+ stub_request(:get, 'http://example.com/rest/api/2/serverInfo')
+ .to_return(status: 404)
put :test, namespace_id: project.namespace, project_id: project, id: service.to_param, service: service_params
- expect(response.status).to eq(200)
- expect(JSON.parse(response.body))
- .to eq('error' => true, 'message' => 'Test failed.', 'service_response' => 'Bad test', 'test_failed' => true)
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to eq(
+ 'error' => true,
+ 'message' => 'Test failed.',
+ 'service_response' => '',
+ 'test_failed' => true
+ )
end
end
end
@@ -114,7 +124,7 @@ describe Projects::ServicesController do
namespace_id: project.namespace, project_id: project, id: service.to_param, service: { active: true }
expect(response).to redirect_to(project_settings_integrations_path(project))
- expect(flash[:notice]).to eq 'HipChat activated.'
+ expect(flash[:notice]).to eq 'JIRA activated.'
end
end
@@ -123,7 +133,7 @@ describe Projects::ServicesController do
put :update,
namespace_id: project.namespace, project_id: project, id: service.to_param, service: { active: false }
- expect(flash[:notice]).to eq 'HipChat settings saved, but not activated.'
+ expect(flash[:notice]).to eq 'JIRA settings saved, but not activated.'
end
end
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 6420b70a54f..832649e5886 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -8,11 +8,7 @@ end
shared_examples 'content not cached without revalidation and no-store' do
it 'ensures content will not be cached without revalidation' do
# Fixed in newer versions of ActivePack, it will only output a single `private`.
- if Gitlab.rails5?
- expect(subject['Cache-Control']).to eq('max-age=0, private, must-revalidate, no-store')
- else
- expect(subject['Cache-Control']).to eq('max-age=0, private, must-revalidate, private, no-store')
- end
+ expect(subject['Cache-Control']).to eq('max-age=0, private, must-revalidate, no-store')
end
end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index e8584846b56..7c505ee0d43 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -54,7 +54,8 @@ describe 'Database schema' do
user_agent_details: %w[subject_id],
users: %w[color_scheme_id created_by_id theme_id],
users_star_projects: %w[user_id],
- web_hooks: %w[service_id]
+ web_hooks: %w[service_id],
+ suggestions: %w[commit_id]
}.with_indifferent_access.freeze
context 'for table' do
diff --git a/spec/factories/ci/bridge.rb b/spec/factories/ci/bridge.rb
new file mode 100644
index 00000000000..5f83b80ad7b
--- /dev/null
+++ b/spec/factories/ci/bridge.rb
@@ -0,0 +1,17 @@
+FactoryBot.define do
+ factory :ci_bridge, class: Ci::Bridge do
+ name ' bridge'
+ stage 'test'
+ stage_idx 0
+ ref 'master'
+ tag false
+ created_at 'Di 29. Okt 09:50:00 CET 2013'
+ status :success
+
+ pipeline factory: :ci_pipeline
+
+ after(:build) do |bridge, evaluator|
+ bridge.project ||= bridge.pipeline.project
+ end
+ end
+end
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
index d80c65cf8bb..18047c74a5d 100644
--- a/spec/factories/releases.rb
+++ b/spec/factories/releases.rb
@@ -1,6 +1,7 @@
FactoryBot.define do
factory :release do
tag "v1.1.0"
+ name { tag }
description "Awesome release"
project
end
diff --git a/spec/factories/suggestions.rb b/spec/factories/suggestions.rb
new file mode 100644
index 00000000000..307523cc061
--- /dev/null
+++ b/spec/factories/suggestions.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :suggestion do
+ relative_order 0
+ association :note, factory: :diff_note_on_merge_request
+ from_content " vars = {\n"
+ to_content " vars = [\n"
+
+ trait :unappliable do
+ from_content "foo"
+ to_content "foo"
+ end
+
+ trait :applied do
+ applied true
+ commit_id { RepoHelpers.sample_commit.id }
+ end
+ end
+end
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index c29dfb01381..e24b1f4349d 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe 'Help Pages' do
@@ -52,23 +54,21 @@ describe 'Help Pages' do
end
end
- context 'in a production environment with version check enabled', :js do
+ context 'in a production environment with version check enabled' do
before do
- allow(Rails.env).to receive(:production?) { true }
stub_application_setting(version_check_enabled: true)
- allow_any_instance_of(VersionCheck).to receive(:url) { '/version-check-url' }
+
+ allow(Rails.env).to receive(:production?).and_return(true)
+ allow(VersionCheck).to receive(:url).and_return('/version-check-url')
sign_in(create(:user))
visit help_path
end
it 'has a version check image' do
- expect(find('.js-version-status-badge', visible: false)['src']).to end_with('/version-check-url')
- end
-
- it 'hides the version check image if the image request fails' do
- # We use '--load-images=yes' with poltergeist so the image fails to load
- expect(page).to have_selector('.js-version-status-badge', visible: false)
+ # Check `data-src` due to lazy image loading
+ expect(find('.js-version-status-badge', visible: false)['data-src'])
+ .to end_with('/version-check-url')
end
end
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index 687a6f1eafc..830d56035aa 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe "User creates issue" do
@@ -64,10 +66,10 @@ describe "User creates issue" do
end
context "with labels" do
- LABEL_TITLES = %w(bug feature enhancement).freeze
+ let(:label_titles) { %w(bug feature enhancement) }
before do
- LABEL_TITLES.each do |title|
+ label_titles.each do |title|
create(:label, project: project, title: title)
end
end
@@ -77,13 +79,13 @@ describe "User creates issue" do
fill_in("Title", with: issue_title)
click_button("Label")
- click_link(LABEL_TITLES.first)
+ click_link(label_titles.first)
click_button("Submit issue")
expect(page).to have_content(issue_title)
.and have_content(user.name)
.and have_content(project.name)
- .and have_content(LABEL_TITLES.first)
+ .and have_content(label_titles.first)
end
end
end
diff --git a/spec/features/merge_request/user_awards_emoji_spec.rb b/spec/features/merge_request/user_awards_emoji_spec.rb
index 859a4c65562..93376bc8ce0 100644
--- a/spec/features/merge_request/user_awards_emoji_spec.rb
+++ b/spec/features/merge_request/user_awards_emoji_spec.rb
@@ -4,11 +4,14 @@ describe 'Merge request > User awards emoji', :js do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project, author: create(:user)) }
+ let!(:note) { create(:note, noteable: merge_request, project: merge_request.project) }
describe 'logged in' do
before do
sign_in(user)
visit project_merge_request_path(project, merge_request)
+
+ wait_for_requests
end
it 'adds award to merge request' do
@@ -36,6 +39,15 @@ describe 'Merge request > User awards emoji', :js do
expect(page).to have_selector('.emoji-menu', count: 1)
end
+ it 'adds awards to note' do
+ first('.js-note-emoji').click
+ first('.emoji-menu .js-emoji-btn').click
+
+ wait_for_requests
+
+ expect(page).to have_selector('.js-awards-block')
+ end
+
describe 'the project is archived' do
let(:project) { create(:project, :public, :repository, :archived) }
diff --git a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
index 53ed5d78598..29b3d2b629b 100644
--- a/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb
@@ -88,6 +88,8 @@ describe 'Merge request > User merges when pipeline succeeds', :js do
describe 'enabling Merge when pipeline succeeds via dropdown' do
it 'activates the Merge when pipeline succeeds feature' do
+ wait_for_requests
+
find('.js-merge-moment').click
click_link 'Merge when pipeline succeeds'
diff --git a/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
new file mode 100644
index 00000000000..c19e299097e
--- /dev/null
+++ b/spec/features/merge_request/user_suggests_changes_on_diff_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'User comments on a diff', :js do
+ include MergeRequestDiffHelpers
+ include RepoHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) do
+ create(:merge_request_with_diffs, source_project: project, target_project: project, source_branch: 'merge-test')
+ end
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit(diffs_project_merge_request_path(project, merge_request))
+ end
+
+ context 'single suggestion note' do
+ it 'suggestion is presented' do
+ click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ page.within('.diff-discussions') do
+ expect(page).to have_button('Apply suggestion')
+ expect(page).to have_content('Suggested change')
+ expect(page).to have_content(' url = https://github.com/gitlabhq/gitlab-shell.git')
+ expect(page).to have_content('# change to a comment')
+ end
+ end
+
+ it 'suggestion is appliable' do
+ click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in('note_note', with: "```suggestion\n# change to a comment\n```")
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ page.within('.diff-discussions') do
+ expect(page).not_to have_content('Applied')
+
+ click_button('Apply suggestion')
+ wait_for_requests
+
+ expect(page).to have_content('Applied')
+ end
+ end
+ end
+
+ context 'multiple suggestions in a single note' do
+ it 'suggestions are presented' do
+ click_diff_line(find("[id='#{sample_compare.changes[1][:line_code]}']"))
+
+ page.within('.js-discussion-note-form') do
+ fill_in('note_note', with: "```suggestion\n# change to a comment\n```\n```suggestion\n# or that\n```")
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ page.within('.diff-discussions') do
+ suggestion_1 = page.all(:css, '.md-suggestion-diff')[0]
+ suggestion_2 = page.all(:css, '.md-suggestion-diff')[1]
+
+ expect(suggestion_1).to have_content(' url = https://github.com/gitlabhq/gitlab-shell.git')
+ expect(suggestion_1).to have_content('# change to a comment')
+
+ expect(suggestion_2).to have_content(' url = https://github.com/gitlabhq/gitlab-shell.git')
+ expect(suggestion_2).to have_content('# or that')
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/labels/user_views_labels_spec.rb b/spec/features/projects/labels/user_views_labels_spec.rb
index 0cbeca4e392..2c8267764bd 100644
--- a/spec/features/projects/labels/user_views_labels_spec.rb
+++ b/spec/features/projects/labels/user_views_labels_spec.rb
@@ -1,13 +1,15 @@
+# frozen_string_literal: true
+
require "spec_helper"
describe "User views labels" do
set(:project) { create(:project_empty_repo, :public) }
set(:user) { create(:user) }
- LABEL_TITLES = %w[bug enhancement feature].freeze
+ let(:label_titles) { %w[bug enhancement feature] }
before do
- LABEL_TITLES.each { |title| create(:label, project: project, title: title) }
+ label_titles.each { |title| create(:label, project: project, title: title) }
project.add_guest(user)
sign_in(user)
@@ -17,7 +19,7 @@ describe "User views labels" do
it "shows all labels" do
page.within('.other-labels .manage-labels-list') do
- LABEL_TITLES.each { |title| expect(page).to have_content(title) }
+ label_titles.each { |title| expect(page).to have_content(title) }
end
end
end
diff --git a/spec/finders/cluster_ancestors_finder_spec.rb b/spec/finders/cluster_ancestors_finder_spec.rb
new file mode 100644
index 00000000000..332086c42e2
--- /dev/null
+++ b/spec/finders/cluster_ancestors_finder_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ClusterAncestorsFinder, '#execute' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let(:user) { create(:user) }
+
+ let!(:project_cluster) do
+ create(:cluster, :provided_by_user, cluster_type: :project_type, projects: [project])
+ end
+
+ let!(:group_cluster) do
+ create(:cluster, :provided_by_user, cluster_type: :group_type, groups: [group])
+ end
+
+ subject { described_class.new(clusterable, user).execute }
+
+ context 'for a project' do
+ let(:clusterable) { project }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'returns the project clusters followed by group clusters' do
+ is_expected.to eq([project_cluster, group_cluster])
+ end
+
+ context 'nested groups', :nested_groups do
+ let(:group) { create(:group, parent: parent_group) }
+ let(:parent_group) { create(:group) }
+
+ let!(:parent_group_cluster) do
+ create(:cluster, :provided_by_user, cluster_type: :group_type, groups: [parent_group])
+ end
+
+ it 'returns the project clusters followed by group clusters ordered ascending the hierarchy' do
+ is_expected.to eq([project_cluster, group_cluster, parent_group_cluster])
+ end
+ end
+ end
+
+ context 'user cannot read clusters for clusterable' do
+ let(:clusterable) { project }
+
+ it 'returns nothing' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'for a group' do
+ let(:clusterable) { group }
+
+ before do
+ group.add_maintainer(user)
+ end
+
+ it 'returns the list of group clusters' do
+ is_expected.to eq([group_cluster])
+ end
+
+ context 'nested groups', :nested_groups do
+ let(:group) { create(:group, parent: parent_group) }
+ let(:parent_group) { create(:group) }
+
+ let!(:parent_group_cluster) do
+ create(:cluster, :provided_by_user, cluster_type: :group_type, groups: [parent_group])
+ end
+
+ it 'returns the list of group clusters ordered ascending the hierarchy' do
+ is_expected.to eq([group_cluster, parent_group_cluster])
+ end
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/entities/diff_line.json b/spec/fixtures/api/schemas/entities/diff_line.json
index 66e8b443e1b..9657004cd2d 100644
--- a/spec/fixtures/api/schemas/entities/diff_line.json
+++ b/spec/fixtures/api/schemas/entities/diff_line.json
@@ -8,7 +8,8 @@
"new_line": { "type": ["integer", "null"] },
"text": { "type": ["string"] },
"rich_text": { "type": ["string"] },
- "meta_data": { "type": ["object", "null"] }
+ "meta_data": { "type": ["object", "null"] },
+ "can_receive_suggestion": { "type": "boolean" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index 35971d564d5..193ab6821a5 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -119,7 +119,8 @@
"can_push_to_source_branch": { "type": "boolean" },
"rebase_path": { "type": ["string", "null"] },
"squash": { "type": "boolean" },
- "test_reports_path": { "type": ["string", "null"] }
+ "test_reports_path": { "type": ["string", "null"] },
+ "can_receive_suggestion": { "type": "boolean" }
},
"additionalProperties": false
}
diff --git a/spec/frontend/dummy_spec.js b/spec/frontend/dummy_spec.js
deleted file mode 100644
index 2bfef25e9c6..00000000000
--- a/spec/frontend/dummy_spec.js
+++ /dev/null
@@ -1 +0,0 @@
-it('does nothing', () => {});
diff --git a/spec/javascripts/vue_shared/components/notes/timeline_entry_item_spec.js b/spec/frontend/vue_shared/components/notes/timeline_entry_item_spec.js
index c15635f2105..c15635f2105 100644
--- a/spec/javascripts/vue_shared/components/notes/timeline_entry_item_spec.js
+++ b/spec/frontend/vue_shared/components/notes/timeline_entry_item_spec.js
diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb
index 139387e0b24..3820cf5cb9d 100644
--- a/spec/helpers/emails_helper_spec.rb
+++ b/spec/helpers/emails_helper_spec.rb
@@ -73,4 +73,59 @@ describe EmailsHelper do
end
end
end
+
+ describe '#create_list_id_string' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:full_path, :list_id_path) do
+ "01234" | "01234"
+ "5/0123" | "012.."
+ "45/012" | "012.."
+ "012" | "012"
+ "23/01" | "01.23"
+ "2/01" | "01.2"
+ "234/01" | "01.."
+ "4/2/0" | "0.2.4"
+ "45/2/0" | "0.2.."
+ "5/23/0" | "0.."
+ "0-2/5" | "5.0-2"
+ "0_2/5" | "5.0-2"
+ "0.2/5" | "5.0-2"
+ end
+
+ with_them do
+ it 'ellipcizes different variants' do
+ project = double("project")
+ allow(project).to receive(:full_path).and_return(full_path)
+ allow(project).to receive(:id).and_return(12345)
+ # Set a max length that gives only 5 chars for the project full path
+ max_length = "12345..#{Gitlab.config.gitlab.host}".length + 5
+ list_id = create_list_id_string(project, max_length)
+
+ expect(list_id).to eq("12345.#{list_id_path}.#{Gitlab.config.gitlab.host}")
+ expect(list_id).to satisfy { |s| s.length <= max_length }
+ end
+ end
+ end
+
+ describe 'Create realistic List-Id identifier' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:full_path, :list_id_path) do
+ "gitlab-org/gitlab-ce" | "gitlab-ce.gitlab-org"
+ "project-name/subproject_name/my.project" | "my-project.subproject-name.project-name"
+ end
+
+ with_them do
+ it 'Produces the right List-Id' do
+ project = double("project")
+ allow(project).to receive(:full_path).and_return(full_path)
+ allow(project).to receive(:id).and_return(12345)
+ list_id = create_list_id_string(project)
+
+ expect(list_id).to eq("12345.#{list_id_path}.#{Gitlab.config.gitlab.host}")
+ expect(list_id).to satisfy { |s| s.length <= 255 }
+ end
+ end
+ end
end
diff --git a/spec/helpers/sorting_helper_spec.rb b/spec/helpers/sorting_helper_spec.rb
index cba0d93e144..f405268d198 100644
--- a/spec/helpers/sorting_helper_spec.rb
+++ b/spec/helpers/sorting_helper_spec.rb
@@ -21,7 +21,11 @@ describe SortingHelper do
describe '#issuable_sort_direction_button' do
before do
- allow(self).to receive(:request).and_return(double(path: 'http://test.com', query_parameters: {}))
+ allow(self).to receive(:request).and_return(double(path: 'http://test.com', query_parameters: { label_name: 'test_label' }))
+ end
+
+ it 'keeps label filter param' do
+ expect(issuable_sort_direction_button('created_date')).to include('label_name=test_label')
end
it 'returns icon with sort-highest when sort is created_date' do
diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb
index c580b78c908..03df9deafa1 100644
--- a/spec/helpers/storage_helper_spec.rb
+++ b/spec/helpers/storage_helper_spec.rb
@@ -15,11 +15,7 @@ describe StorageHelper do
end
it "uses commas as thousands separator" do
- if Gitlab.rails5?
- expect(helper.storage_counter(100_000_000_000_000_000_000_000)).to eq("86,736.2 EB")
- else
- expect(helper.storage_counter(100_000_000_000_000_000)).to eq("90,949.5 TB")
- end
+ expect(helper.storage_counter(100_000_000_000_000_000_000_000)).to eq("86,736.2 EB")
end
end
end
diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb
index 9d4e34abef5..bfec7ad4bba 100644
--- a/spec/helpers/version_check_helper_spec.rb
+++ b/spec/helpers/version_check_helper_spec.rb
@@ -13,21 +13,21 @@ describe VersionCheckHelper do
before do
allow(Rails.env).to receive(:production?) { true }
allow(Gitlab::CurrentSettings.current_application_settings).to receive(:version_check_enabled) { true }
- allow_any_instance_of(VersionCheck).to receive(:url) { 'https://version.host.com/check.svg?gitlab_info=xxx' }
-
- @image_tag = helper.version_status_badge
+ allow(VersionCheck).to receive(:url) { 'https://version.host.com/check.svg?gitlab_info=xxx' }
end
it 'should return an image tag' do
- expect(@image_tag).to match(/^<img/)
+ expect(helper.version_status_badge).to start_with('<img')
end
it 'should have a js prefixed css class' do
- expect(@image_tag).to match(/class="js-version-status-badge lazy"/)
+ expect(helper.version_status_badge)
+ .to match(/class="js-version-status-badge lazy"/)
end
it 'should have a VersionCheck url as the src' do
- expect(@image_tag).to match(%r{src="https://version\.host\.com/check\.svg\?gitlab_info=xxx"})
+ expect(helper.version_status_badge)
+ .to include(%{src="https://version.host.com/check.svg?gitlab_info=xxx"})
end
end
end
diff --git a/spec/javascripts/blob_edit/blob_bundle_spec.js b/spec/javascripts/blob_edit/blob_bundle_spec.js
index 759d170af77..57f60a4a3dd 100644
--- a/spec/javascripts/blob_edit/blob_bundle_spec.js
+++ b/spec/javascripts/blob_edit/blob_bundle_spec.js
@@ -14,6 +14,7 @@ describe('EditBlob', () => {
setFixtures(`
<div class="js-edit-blob-form">
<button class="js-commit-button"></button>
+ <a class="btn btn-cancel" href="#"></a>
</div>`);
blobBundle();
});
@@ -27,4 +28,10 @@ describe('EditBlob', () => {
expect(window.onbeforeunload).toBeNull();
});
+
+ it('removes beforeunload listener when cancel link is clicked', () => {
+ $('.btn.btn-cancel').click();
+
+ expect(window.onbeforeunload).toBeNull();
+ });
});
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 54f1edfb1f9..22f192bc7f3 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -65,6 +65,13 @@ describe('Store', () => {
expect(list).toBeDefined();
});
+ it('finds list by label ID', () => {
+ boardsStore.addList(listObj);
+ const list = boardsStore.findListByLabelId(listObj.label.id);
+
+ expect(list.id).toBe(listObj.id);
+ });
+
it('gets issue when new list added', done => {
boardsStore.addList(listObj);
const list = boardsStore.findList('id', listObj.id);
diff --git a/spec/javascripts/boards/components/issue_due_date_spec.js b/spec/javascripts/boards/components/issue_due_date_spec.js
index 9e49330c052..054cf8c5b7d 100644
--- a/spec/javascripts/boards/components/issue_due_date_spec.js
+++ b/spec/javascripts/boards/components/issue_due_date_spec.js
@@ -49,10 +49,11 @@ describe('Issue Due Date component', () => {
it('should render month and day for other dates', () => {
date.setDate(date.getDate() + 17);
vm = createComponent(date);
+ const today = new Date();
+ const isDueInCurrentYear = today.getFullYear() === date.getFullYear();
+ const format = isDueInCurrentYear ? 'mmm d' : 'mmm d, yyyy';
- expect(vm.$el.querySelector('time').textContent.trim()).toEqual(
- dateFormat(date, 'mmm d', true),
- );
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual(dateFormat(date, format, true));
});
it('should contain the correct `.text-danger` css class for overdue issue', () => {
diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js
index 437ab4bb3df..54fb0e8228b 100644
--- a/spec/javascripts/boards/issue_spec.js
+++ b/spec/javascripts/boards/issue_spec.js
@@ -55,15 +55,27 @@ describe('Issue model', () => {
expect(issue.labels.length).toBe(2);
});
- it('does not add existing label', () => {
+ it('does not add label if label id exists', () => {
+ issue.addLabel({
+ id: 1,
+ title: 'test 2',
+ color: 'blue',
+ description: 'testing',
+ });
+
+ expect(issue.labels.length).toBe(1);
+ expect(issue.labels[0].color).toBe('red');
+ });
+
+ it('adds other label with same title', () => {
issue.addLabel({
id: 2,
title: 'test',
color: 'blue',
- description: 'bugs!',
+ description: 'other test',
});
- expect(issue.labels.length).toBe(1);
+ expect(issue.labels.length).toBe(2);
});
it('finds label', () => {
diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js
index 1e2f7ff4fd8..a2cbc0f3c72 100644
--- a/spec/javascripts/diffs/components/app_spec.js
+++ b/spec/javascripts/diffs/components/app_spec.js
@@ -1,33 +1,44 @@
-import Vue from 'vue';
-import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import Vuex from 'vuex';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
import { TEST_HOST } from 'spec/test_constants';
import App from '~/diffs/components/app.vue';
+import NoChanges from '~/diffs/components/no_changes.vue';
+import DiffFile from '~/diffs/components/diff_file.vue';
import createDiffsStore from '../create_diffs_store';
describe('diffs/components/app', () => {
const oldMrTabs = window.mrTabs;
- const Component = Vue.extend(App);
-
+ let store;
let vm;
- beforeEach(() => {
- // setup globals (needed for component to mount :/)
- window.mrTabs = jasmine.createSpyObj('mrTabs', ['resetViewContainer']);
- window.mrTabs.expandViewContainer = jasmine.createSpy();
- window.location.hash = 'ABC_123';
+ function createComponent(props = {}, extendStore = () => {}) {
+ const localVue = createLocalVue();
- // setup component
- const store = createDiffsStore();
+ localVue.use(Vuex);
+
+ store = createDiffsStore();
store.state.diffs.isLoading = false;
- vm = mountComponentWithStore(Component, {
- store,
- props: {
+ extendStore(store);
+
+ vm = shallowMount(localVue.extend(App), {
+ localVue,
+ propsData: {
endpoint: `${TEST_HOST}/diff/endpoint`,
projectPath: 'namespace/project',
currentUser: {},
+ changesEmptyStateIllustration: '',
+ ...props,
},
+ store,
});
+ }
+
+ beforeEach(() => {
+ // setup globals (needed for component to mount :/)
+ window.mrTabs = jasmine.createSpyObj('mrTabs', ['resetViewContainer']);
+ window.mrTabs.expandViewContainer = jasmine.createSpy();
+ window.location.hash = 'ABC_123';
});
afterEach(() => {
@@ -35,21 +46,53 @@ describe('diffs/components/app', () => {
window.mrTabs = oldMrTabs;
// reset component
- vm.$destroy();
+ vm.destroy();
});
it('does not show commit info', () => {
- expect(vm.$el).not.toContainElement('.blob-commit-info');
+ createComponent();
+
+ expect(vm.contains('.blob-commit-info')).toBe(false);
});
it('sets highlighted row if hash exists in location object', done => {
- vm.$props.shouldShow = true;
-
- vm.$nextTick()
- .then(() => {
- expect(vm.$store.state.diffs.highlightedRow).toBe('ABC_123');
- })
- .then(done)
- .catch(done.fail);
+ createComponent({
+ shouldShow: true,
+ });
+
+ // Component uses $nextTick so we wait until that has finished
+ setTimeout(() => {
+ expect(store.state.diffs.highlightedRow).toBe('ABC_123');
+
+ done();
+ });
+ });
+
+ describe('empty state', () => {
+ it('renders empty state when no diff files exist', () => {
+ createComponent();
+
+ expect(vm.contains(NoChanges)).toBe(true);
+ });
+
+ it('does not render empty state when diff files exist', () => {
+ createComponent({}, () => {
+ store.state.diffs.diffFiles.push({
+ id: 1,
+ });
+ });
+
+ expect(vm.contains(NoChanges)).toBe(false);
+ expect(vm.findAll(DiffFile).length).toBe(1);
+ });
+
+ it('does not render empty state when versions match', () => {
+ createComponent({}, () => {
+ store.state.diffs.startVersion = { version_index: 1 };
+ store.state.diffs.mergeRequestDiff = { version_index: 1 };
+ });
+
+ expect(vm.contains(NoChanges)).toBe(false);
+ });
});
});
diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js
index c25f6167163..9e158327a77 100644
--- a/spec/javascripts/diffs/components/diff_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_content_spec.js
@@ -17,6 +17,7 @@ describe('DiffContent', () => {
current_user: {
can_create_note: false,
},
+ preview_note_path: 'path/to/preview',
};
vm = mountComponentWithStore(Component, {
@@ -49,6 +50,45 @@ describe('DiffContent', () => {
});
});
+ describe('empty files', () => {
+ beforeEach(() => {
+ vm.diffFile.empty = true;
+ vm.diffFile.highlighted_diff_lines = [];
+ vm.diffFile.parallel_diff_lines = [];
+ });
+
+ it('should render a message', done => {
+ vm.$nextTick(() => {
+ const block = vm.$el.querySelector('.diff-viewer .nothing-here-block');
+
+ expect(block).not.toBe(null);
+ expect(block.textContent.trim()).toContain('Empty file');
+
+ done();
+ });
+ });
+
+ it('should not render multiple messages', done => {
+ vm.diffFile.mode_changed = true;
+ vm.diffFile.b_mode = '100755';
+ vm.diffFile.viewer.name = 'mode_changed';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelectorAll('.nothing-here-block').length).toBe(1);
+
+ done();
+ });
+ });
+
+ it('should not render diff table', done => {
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('table')).toBe(null);
+
+ done();
+ });
+ });
+ });
+
describe('Non-Text diffs', () => {
beforeEach(() => {
vm.diffFile.viewer.name = 'image';
diff --git a/spec/javascripts/diffs/components/no_changes_spec.js b/spec/javascripts/diffs/components/no_changes_spec.js
index 7237274eb43..e45d34bf9d5 100644
--- a/spec/javascripts/diffs/components/no_changes_spec.js
+++ b/spec/javascripts/diffs/components/no_changes_spec.js
@@ -1 +1,40 @@
-// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import Vuex from 'vuex';
+import { createStore } from '~/mr_notes/stores';
+import NoChanges from '~/diffs/components/no_changes.vue';
+
+describe('Diff no changes empty state', () => {
+ let vm;
+
+ function createComponent(extendStore = () => {}) {
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+
+ const store = createStore();
+ extendStore(store);
+
+ vm = shallowMount(localVue.extend(NoChanges), {
+ localVue,
+ store,
+ propsData: {
+ changesEmptyStateIllustration: '',
+ },
+ });
+ }
+
+ afterEach(() => {
+ vm.destroy();
+ });
+
+ it('prevents XSS', () => {
+ createComponent(store => {
+ // eslint-disable-next-line no-param-reassign
+ store.state.notes.noteableData = {
+ source_branch: '<script>alert("test");</script>',
+ target_branch: '<script>alert("test");</script>',
+ };
+ });
+
+ expect(vm.contains('script')).toBe(false);
+ });
+});
diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js
index 44313caba29..c1e9f791925 100644
--- a/spec/javascripts/diffs/mock_data/diff_discussions.js
+++ b/spec/javascripts/diffs/mock_data/diff_discussions.js
@@ -487,8 +487,19 @@ export default {
],
},
diff_discussion: true,
- truncated_diff_lines:
- '<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="1">\n1\n</td>\n<td class="line_content new noteable_line"><span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n</td>\n</tr>\n<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="2">\n2\n</td>\n<td class="line_content new noteable_line"><span id="LC2" class="line" lang="plaintext"></span>\n</td>\n</tr>\n',
+ truncated_diff_lines: [
+ {
+ text: 'line',
+ rich_text:
+ '<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="1">\n1\n</td>\n<td class="line_content new noteable_line"><span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n</td>\n</tr>\n<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="2">\n2\n</td>\n<td class="line_content new noteable_line"><span id="LC2" class="line" lang="plaintext"></span>\n</td>\n</tr>\n',
+ can_receive_suggestion: true,
+ line_code: '6f209374f7e565f771b95720abf46024c41d1885_1_1',
+ type: 'new',
+ old_line: null,
+ new_line: 1,
+ meta_data: null,
+ },
+ ],
};
export const imageDiffDiscussions = [
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index 23e8761bc55..f3449bec6ec 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -277,6 +277,87 @@ describe('DiffsStoreMutations', () => {
expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
});
+ it('updates existing discussion', () => {
+ const diffPosition = {
+ base_sha: 'ed13df29948c41ba367caa757ab3ec4892509910',
+ head_sha: 'b921914f9a834ac47e6fd9420f78db0f83559130',
+ new_line: null,
+ new_path: '500-lines-4.txt',
+ old_line: 5,
+ old_path: '500-lines-4.txt',
+ start_sha: 'ed13df29948c41ba367caa757ab3ec4892509910',
+ };
+
+ const state = {
+ latestDiff: true,
+ diffFiles: [
+ {
+ file_hash: 'ABC',
+ parallel_diff_lines: [
+ {
+ left: {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ right: {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ },
+ ],
+ highlighted_diff_lines: [
+ {
+ line_code: 'ABC_1',
+ discussions: [],
+ },
+ ],
+ },
+ ],
+ };
+ const discussion = {
+ id: 1,
+ line_code: 'ABC_1',
+ diff_discussion: true,
+ resolvable: true,
+ original_position: diffPosition,
+ position: diffPosition,
+ diff_file: {
+ file_hash: state.diffFiles[0].file_hash,
+ },
+ };
+
+ const diffPositionByLineCode = {
+ ABC_1: diffPosition,
+ };
+
+ mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, {
+ discussion,
+ diffPositionByLineCode,
+ });
+
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions.length).toEqual(1);
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].id).toEqual(1);
+ expect(state.diffFiles[0].parallel_diff_lines[0].right.discussions).toEqual([]);
+
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions.length).toEqual(1);
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].id).toEqual(1);
+
+ mutations[types.SET_LINE_DISCUSSIONS_FOR_FILE](state, {
+ discussion: {
+ ...discussion,
+ resolved: true,
+ notes: ['test'],
+ },
+ diffPositionByLineCode,
+ });
+
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].notes.length).toBe(1);
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].notes.length).toBe(1);
+
+ expect(state.diffFiles[0].parallel_diff_lines[0].left.discussions[0].resolved).toBe(true);
+ expect(state.diffFiles[0].highlighted_diff_lines[0].discussions[0].resolved).toBe(true);
+ });
+
it('should add legacy discussions to the given line', () => {
const diffPosition = {
base_sha: 'ed13df29948c41ba367caa757ab3ec4892509910',
@@ -356,10 +437,12 @@ describe('DiffsStoreMutations', () => {
{
id: 1,
line_code: 'ABC_1',
+ notes: [],
},
{
id: 2,
line_code: 'ABC_1',
+ notes: [],
},
],
},
@@ -376,10 +459,12 @@ describe('DiffsStoreMutations', () => {
{
id: 1,
line_code: 'ABC_1',
+ notes: [],
},
{
id: 2,
line_code: 'ABC_1',
+ notes: [],
},
],
},
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js
index 6605b0a30d7..cfd0b96ec43 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js
@@ -211,132 +211,6 @@ describe('Dropdown Utils', () => {
});
});
- describe('mergeDuplicateLabels', () => {
- const dataMap = {
- label: {
- title: 'label',
- color: '#FFFFFF',
- },
- };
-
- it('should add label to dataMap if it is not a duplicate', () => {
- const newLabel = {
- title: 'new-label',
- color: '#000000',
- };
-
- const updated = DropdownUtils.mergeDuplicateLabels(dataMap, newLabel);
-
- expect(updated[newLabel.title]).toEqual(newLabel);
- });
-
- it('should merge colors if label is a duplicate', () => {
- const duplicate = {
- title: 'label',
- color: '#000000',
- };
-
- const updated = DropdownUtils.mergeDuplicateLabels(dataMap, duplicate);
-
- expect(updated.label.multipleColors).toEqual([dataMap.label.color, duplicate.color]);
- });
- });
-
- describe('duplicateLabelColor', () => {
- it('should linear-gradient 2 colors', () => {
- const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']);
-
- expect(gradient).toEqual(
- 'linear-gradient(#FFFFFF 0%, #FFFFFF 50%, #000000 50%, #000000 100%)',
- );
- });
-
- it('should linear-gradient 3 colors', () => {
- const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333']);
-
- expect(gradient).toEqual(
- 'linear-gradient(#FFFFFF 0%, #FFFFFF 33%, #000000 33%, #000000 66%, #333333 66%, #333333 100%)',
- );
- });
-
- it('should linear-gradient 4 colors', () => {
- const gradient = DropdownUtils.duplicateLabelColor([
- '#FFFFFF',
- '#000000',
- '#333333',
- '#DDDDDD',
- ]);
-
- expect(gradient).toEqual(
- 'linear-gradient(#FFFFFF 0%, #FFFFFF 25%, #000000 25%, #000000 50%, #333333 50%, #333333 75%, #DDDDDD 75%, #DDDDDD 100%)',
- );
- });
-
- it('should not linear-gradient more than 4 colors', () => {
- const gradient = DropdownUtils.duplicateLabelColor([
- '#FFFFFF',
- '#000000',
- '#333333',
- '#DDDDDD',
- '#EEEEEE',
- ]);
-
- expect(gradient.indexOf('#EEEEEE')).toBe(-1);
- });
- });
-
- describe('duplicateLabelPreprocessing', () => {
- it('should set preprocessed to true', () => {
- const results = DropdownUtils.duplicateLabelPreprocessing([]);
-
- expect(results.preprocessed).toEqual(true);
- });
-
- it('should not mutate existing data if there are no duplicates', () => {
- const data = [
- {
- title: 'label1',
- color: '#FFFFFF',
- },
- {
- title: 'label2',
- color: '#000000',
- },
- ];
- const results = DropdownUtils.duplicateLabelPreprocessing(data);
-
- expect(results.length).toEqual(2);
- expect(results[0]).toEqual(data[0]);
- expect(results[1]).toEqual(data[1]);
- });
-
- describe('duplicate labels', () => {
- const data = [
- {
- title: 'label',
- color: '#FFFFFF',
- },
- {
- title: 'label',
- color: '#000000',
- },
- ];
- const results = DropdownUtils.duplicateLabelPreprocessing(data);
-
- it('should merge duplicate labels', () => {
- expect(results.length).toEqual(1);
- });
-
- it('should convert multiple colored labels into linear-gradient', () => {
- expect(results[0].color).toEqual(DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']));
- });
-
- it('should set multiple colored label text color to black', () => {
- expect(results[0].text_color).toEqual('#000000');
- });
- });
- });
-
describe('setDataValueIfSelected', () => {
beforeEach(() => {
spyOn(FilteredSearchDropdownManager, 'addWordToInput').and.callFake(() => {});
diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
index 4f561df7943..9aa3cbaa231 100644
--- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
@@ -909,16 +909,6 @@ describe('Filtered Search Visual Tokens', () => {
expect(token.style.backgroundColor).not.toEqual(originalBackgroundColor);
});
- it('should not set backgroundColor when it is a linear-gradient', () => {
- const token = subject.setTokenStyle(
- bugLabelToken,
- 'linear-gradient(135deg, red, blue)',
- 'white',
- );
-
- expect(token.style.backgroundColor).toEqual(bugLabelToken.style.backgroundColor);
- });
-
it('should set textColor', () => {
const token = subject.setTokenStyle(bugLabelToken, 'white', 'black');
@@ -935,39 +925,6 @@ describe('Filtered Search Visual Tokens', () => {
});
});
- describe('preprocessLabel', () => {
- const endpoint = 'endpoint';
-
- it('does not preprocess more than once', () => {
- let labels = [];
-
- spyOn(DropdownUtils, 'duplicateLabelPreprocessing').and.callFake(() => []);
-
- labels = FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
- FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
-
- expect(DropdownUtils.duplicateLabelPreprocessing.calls.count()).toEqual(1);
- });
-
- describe('not preprocessed before', () => {
- it('returns preprocessed labels', () => {
- let labels = [];
-
- expect(labels.preprocessed).not.toEqual(true);
- labels = FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
-
- expect(labels.preprocessed).toEqual(true);
- });
-
- it('overrides AjaxCache with preprocessed results', () => {
- spyOn(AjaxCache, 'override').and.callFake(() => {});
- FilteredSearchVisualTokens.preprocessLabel(endpoint, []);
-
- expect(AjaxCache.override.calls.count()).toEqual(1);
- });
- });
- });
-
describe('updateLabelTokenColor', () => {
const jsonFixtureName = 'labels/project_labels.json';
const dummyEndpoint = '/dummy/endpoint';
diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js
index 0081f42c330..22bee049f9c 100644
--- a/spec/javascripts/notes/components/note_app_spec.js
+++ b/spec/javascripts/notes/components/note_app_spec.js
@@ -30,6 +30,8 @@ describe('note_app', () => {
jasmine.addMatchers(vueMatchers);
$('body').attr('data-page', 'projects:merge_requests:show');
+ setFixtures('<div class="js-vue-notes-event"><div id="app"></div></div>');
+
const IssueNotesApp = Vue.extend(notesApp);
store = createStore();
@@ -43,6 +45,7 @@ describe('note_app', () => {
return mountComponentWithStore(IssueNotesApp, {
props,
store,
+ el: document.getElementById('app'),
});
};
});
@@ -283,4 +286,24 @@ describe('note_app', () => {
}, 0);
});
});
+
+ describe('emoji awards', () => {
+ it('dispatches toggleAward after toggleAward event', () => {
+ const toggleAwardEvent = new CustomEvent('toggleAward', {
+ detail: {
+ awardName: 'test',
+ noteId: 1,
+ },
+ });
+
+ spyOn(vm.$store, 'dispatch');
+
+ vm.$el.parentElement.dispatchEvent(toggleAwardEvent);
+
+ expect(vm.$store.dispatch).toHaveBeenCalledWith('toggleAward', {
+ awardName: 'test',
+ noteId: 1,
+ });
+ });
+ });
});
diff --git a/spec/javascripts/releases/components/release_block_spec.js b/spec/javascripts/releases/components/release_block_spec.js
new file mode 100644
index 00000000000..c0cd15b7507
--- /dev/null
+++ b/spec/javascripts/releases/components/release_block_spec.js
@@ -0,0 +1,148 @@
+import Vue from 'vue';
+import component from '~/releases/components/release_block.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Release block', () => {
+ const Component = Vue.extend(component);
+
+ const release = {
+ name: 'Bionic Beaver',
+ tag_name: '18.04',
+ description: '## changelog\n\n* line 1\n* line2',
+ description_html: '<div><h2>changelog</h2><ul><li>line1</li<li>line 2</li></ul></div>',
+ author_name: 'Release bot',
+ author_email: 'release-bot@example.com',
+ created_at: '2012-05-28T05:00:00-07:00',
+ commit: {
+ id: '2695effb5807a22ff3d138d593fd856244e155e7',
+ short_id: '2695effb',
+ title: 'Initial commit',
+ created_at: '2017-07-26T11:08:53.000+02:00',
+ parent_ids: ['2a4b78934375d7f53875269ffd4f45fd83a84ebe'],
+ message: 'Initial commit',
+ author_name: 'John Smith',
+ author_email: 'john@example.com',
+ authored_date: '2012-05-28T04:42:42-07:00',
+ committer_name: 'Jack Smith',
+ committer_email: 'jack@example.com',
+ committed_date: '2012-05-28T04:42:42-07:00',
+ },
+ assets: {
+ count: 6,
+ sources: [
+ {
+ format: 'zip',
+ url: 'https://gitlab.com/gitlab-org/gitlab-ce/-/archive/v11.3.12/gitlab-ce-v11.3.12.zip',
+ },
+ {
+ format: 'tar.gz',
+ url:
+ 'https://gitlab.com/gitlab-org/gitlab-ce/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar.gz',
+ },
+ {
+ format: 'tar.bz2',
+ url:
+ 'https://gitlab.com/gitlab-org/gitlab-ce/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar.bz2',
+ },
+ {
+ format: 'tar',
+ url: 'https://gitlab.com/gitlab-org/gitlab-ce/-/archive/v11.3.12/gitlab-ce-v11.3.12.tar',
+ },
+ ],
+ links: [
+ {
+ name: 'release-18.04.dmg',
+ url: 'https://my-external-hosting.example.com/scrambled-url/',
+ external: true,
+ },
+ {
+ name: 'binary-linux-amd64',
+ url:
+ 'https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/v11.6.0-rc4/download?job=rspec-mysql+41%2F50',
+ external: false,
+ },
+ ],
+ },
+ };
+
+ const props = {
+ name: release.name,
+ tag: release.tag_name,
+ commit: release.commit,
+ description: release.description_html,
+ author: {
+ avatar_url: 'uploads/-/system/user/avatar/johndoe/avatar.png',
+ id: 482476,
+ name: 'John Doe',
+ path: '/johndoe',
+ state: 'active',
+ status_tooltip_html: null,
+ username: 'johndoe',
+ web_url: 'https://gitlab.com/johndoe',
+ },
+ createdAt: release.created_at,
+ assetsCount: release.assets.count,
+ sources: release.assets.sources,
+ links: release.assets.links,
+ };
+
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders release name', () => {
+ expect(vm.$el.textContent).toContain(release.name);
+ });
+
+ it('renders commit sha', () => {
+ expect(vm.$el.textContent).toContain(release.commit.short_id);
+ });
+
+ it('renders tag name', () => {
+ expect(vm.$el.textContent).toContain(release.tag_name);
+ });
+
+ it('renders release date', () => {
+ expect(vm.$el.textContent).toContain(timeagoMixin.methods.timeFormated(release.created_at));
+ });
+
+ it('renders number of assets provided', () => {
+ expect(vm.$el.querySelector('.js-assets-count').textContent).toContain(release.assets.count);
+ });
+
+ it('renders dropdown with the sources', () => {
+ expect(vm.$el.querySelectorAll('.js-sources-dropdown li').length).toEqual(
+ release.assets.sources.length,
+ );
+
+ expect(vm.$el.querySelector('.js-sources-dropdown li a').getAttribute('href')).toEqual(
+ release.assets.sources[0].url,
+ );
+
+ expect(vm.$el.querySelector('.js-sources-dropdown li a').textContent).toContain(
+ release.assets.sources[0].format,
+ );
+ });
+
+ it('renders list with the links provided', () => {
+ expect(vm.$el.querySelectorAll('.js-assets-list li').length).toEqual(
+ release.assets.links.length,
+ );
+
+ expect(vm.$el.querySelector('.js-assets-list li a').getAttribute('href')).toEqual(
+ release.assets.links[0].url,
+ );
+
+ expect(vm.$el.querySelector('.js-assets-list li a').textContent).toContain(
+ release.assets.links[0].name,
+ );
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
index 300133dc602..212519743aa 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
@@ -114,7 +114,7 @@ describe('Merge request widget rebase component', () => {
// Wait for the eventHub to be called
.then(vm.$nextTick())
.then(() => {
- expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetUpdateRequested');
+ expect(eventHub.$emit).toHaveBeenCalledWith('MRWidgetRebaseSuccess');
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
index 9d34bdd1084..61ef26cd080 100644
--- a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
@@ -35,7 +35,7 @@ describe('getStateKey', () => {
expect(bound()).toEqual('mergeWhenPipelineSucceeds');
- context.hasSHAChanged = true;
+ context.isSHAMismatch = true;
expect(bound()).toEqual('shaMismatch');
diff --git a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
index f5079147f60..c226704694c 100644
--- a/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
+++ b/spec/javascripts/vue_mr_widget/stores/mr_widget_store_spec.js
@@ -3,23 +3,30 @@ import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
import mockData from '../mock_data';
describe('MergeRequestStore', () => {
- describe('setData', () => {
- let store;
+ let store;
- beforeEach(() => {
- store = new MergeRequestStore(mockData);
- });
+ beforeEach(() => {
+ store = new MergeRequestStore(mockData);
+ });
- it('should set hasSHAChanged when the diff SHA changes', () => {
+ describe('setData', () => {
+ it('should set isSHAMismatch when the diff SHA changes', () => {
store.setData({ ...mockData, diff_head_sha: 'a-different-string' });
- expect(store.hasSHAChanged).toBe(true);
+ expect(store.isSHAMismatch).toBe(true);
});
- it('should not set hasSHAChanged when other data changes', () => {
+ it('should not set isSHAMismatch when other data changes', () => {
store.setData({ ...mockData, work_in_progress: !mockData.work_in_progress });
- expect(store.hasSHAChanged).toBe(false);
+ expect(store.isSHAMismatch).toBe(false);
+ });
+
+ it('should update cached sha after rebasing', () => {
+ store.setData({ ...mockData, diff_head_sha: 'abc123' }, true);
+
+ expect(store.isSHAMismatch).toBe(false);
+ expect(store.sha).toBe('abc123');
});
describe('isPipelinePassing', () => {
diff --git a/spec/javascripts/vue_shared/components/markdown/field_spec.js b/spec/javascripts/vue_shared/components/markdown/field_spec.js
index abb17440c0e..79e0e756a7a 100644
--- a/spec/javascripts/vue_shared/components/markdown/field_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/field_spec.js
@@ -80,7 +80,7 @@ describe('Markdown field component', () => {
previewLink.click();
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.md-preview').textContent.trim()).toContain('Loading...');
+ expect(vm.$el.querySelector('.md-preview').textContent.trim()).toContain('Loading…');
done();
});
diff --git a/spec/javascripts/vue_shared/components/markdown/header_spec.js b/spec/javascripts/vue_shared/components/markdown/header_spec.js
index 59613faa49f..e733a95288e 100644
--- a/spec/javascripts/vue_shared/components/markdown/header_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/header_spec.js
@@ -28,6 +28,7 @@ describe('Markdown field header component', () => {
'Add a numbered list',
'Add a task list',
'Add a table',
+ 'Insert suggestion',
'Go full screen',
];
const elements = vm.$el.querySelectorAll('.toolbar-btn');
@@ -93,4 +94,18 @@ describe('Markdown field header component', () => {
'| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |',
);
});
+
+ it('renders suggestion template', () => {
+ vm.lineContent = 'Some content';
+
+ expect(vm.mdSuggestion).toEqual('```suggestion\n{text}\n```');
+ });
+
+ it('does not render suggestion button if `canSuggest` is set to false', () => {
+ vm.canSuggest = false;
+
+ Vue.nextTick(() => {
+ expect(vm.$el.querySelector('.qa-suggestion-btn')).toBe(null);
+ });
+ });
});
diff --git a/spec/javascripts/vue_shared/components/markdown/suggestion_diff_header_spec.js b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_header_spec.js
new file mode 100644
index 00000000000..8187b3204b1
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_header_spec.js
@@ -0,0 +1,69 @@
+import Vue from 'vue';
+import SuggestionDiffHeaderComponent from '~/vue_shared/components/markdown/suggestion_diff_header.vue';
+
+const MOCK_DATA = {
+ canApply: true,
+ isApplied: false,
+ helpPagePath: 'path_to_docs',
+};
+
+describe('Suggestion Diff component', () => {
+ let vm;
+
+ function createComponent(propsData) {
+ const Component = Vue.extend(SuggestionDiffHeaderComponent);
+
+ return new Component({
+ propsData,
+ }).$mount();
+ }
+
+ beforeEach(done => {
+ vm = createComponent(MOCK_DATA);
+ Vue.nextTick(done);
+ });
+
+ describe('init', () => {
+ it('renders a suggestion header', () => {
+ const header = vm.$el.querySelector('.qa-suggestion-diff-header');
+
+ expect(header).not.toBeNull();
+ expect(header.innerHTML.includes('Suggested change')).toBe(true);
+ });
+
+ it('renders an apply button', () => {
+ const applyBtn = vm.$el.querySelector('.qa-apply-btn');
+
+ expect(applyBtn).not.toBeNull();
+ expect(applyBtn.innerHTML.includes('Apply suggestion')).toBe(true);
+ });
+
+ it('does not render an apply button if `canApply` is set to false', () => {
+ const props = Object.assign(MOCK_DATA, { canApply: false });
+
+ vm = createComponent(props);
+
+ expect(vm.$el.querySelector('.qa-apply-btn')).toBeNull();
+ });
+ });
+
+ describe('applySuggestion', () => {
+ it('emits when the apply button is clicked', () => {
+ const props = Object.assign(MOCK_DATA, { canApply: true });
+
+ vm = createComponent(props);
+ spyOn(vm, '$emit');
+ vm.applySuggestion();
+
+ expect(vm.$emit).toHaveBeenCalled();
+ });
+
+ it('does not emit when the canApply is set to false', () => {
+ spyOn(vm, '$emit');
+ vm.canApply = false;
+ vm.applySuggestion();
+
+ expect(vm.$emit).not.toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js
new file mode 100644
index 00000000000..d4ed8f2f7a4
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/markdown/suggestion_diff_spec.js
@@ -0,0 +1,79 @@
+import Vue from 'vue';
+import SuggestionDiffComponent from '~/vue_shared/components/markdown/suggestion_diff.vue';
+
+const MOCK_DATA = {
+ canApply: true,
+ newLines: [
+ { content: 'Line 1\n', lineNumber: 1 },
+ { content: 'Line 2\n', lineNumber: 2 },
+ { content: 'Line 3\n', lineNumber: 3 },
+ ],
+ fromLine: 1,
+ fromContent: 'Old content',
+ suggestion: {
+ id: 1,
+ },
+ helpPagePath: 'path_to_docs',
+};
+
+describe('Suggestion Diff component', () => {
+ let vm;
+
+ beforeEach(done => {
+ const Component = Vue.extend(SuggestionDiffComponent);
+
+ vm = new Component({
+ propsData: MOCK_DATA,
+ }).$mount();
+
+ Vue.nextTick(done);
+ });
+
+ describe('init', () => {
+ it('renders a suggestion header', () => {
+ expect(vm.$el.querySelector('.qa-suggestion-diff-header')).not.toBeNull();
+ });
+
+ it('renders a diff table', () => {
+ expect(vm.$el.querySelector('table.md-suggestion-diff')).not.toBeNull();
+ });
+
+ it('renders the oldLineNumber', () => {
+ const fromLine = vm.$el.querySelector('.qa-old-diff-line-number').innerHTML;
+
+ expect(parseInt(fromLine, 10)).toBe(vm.fromLine);
+ });
+
+ it('renders the oldLineContent', () => {
+ const fromContent = vm.$el.querySelector('.line_content.old').innerHTML;
+
+ expect(fromContent.includes(vm.fromContent)).toBe(true);
+ });
+
+ it('renders the contents of newLines', () => {
+ const newLines = vm.$el.querySelectorAll('.line_holder.new');
+
+ newLines.forEach((line, i) => {
+ expect(newLines[i].innerHTML.includes(vm.newLines[i].content)).toBe(true);
+ });
+ });
+
+ it('renders a line number for each line', () => {
+ const newLineNumbers = vm.$el.querySelectorAll('.qa-new-diff-line-number');
+
+ newLineNumbers.forEach((line, i) => {
+ expect(newLineNumbers[i].innerHTML.includes(vm.newLines[i].lineNumber)).toBe(true);
+ });
+ });
+ });
+
+ describe('applySuggestion', () => {
+ it('emits apply event when applySuggestion is called', () => {
+ const callback = () => {};
+ spyOn(vm, '$emit');
+ vm.applySuggestion(callback);
+
+ expect(vm.$emit).toHaveBeenCalledWith('apply', { suggestionId: vm.suggestion.id, callback });
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/markdown/suggestions_spec.js b/spec/javascripts/vue_shared/components/markdown/suggestions_spec.js
new file mode 100644
index 00000000000..ab1b747c360
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/markdown/suggestions_spec.js
@@ -0,0 +1,125 @@
+import Vue from 'vue';
+import SuggestionsComponent from '~/vue_shared/components/markdown/suggestions.vue';
+
+const MOCK_DATA = {
+ fromLine: 1,
+ fromContent: 'Old content',
+ suggestions: [],
+ noteHtml: `
+ <div class="suggestion">
+ <div class="line">Suggestion 1</div>
+ </div>
+
+ <div class="suggestion">
+ <div class="line">Suggestion 2</div>
+ </div>
+ `,
+ isApplied: false,
+ helpPagePath: 'path_to_docs',
+};
+
+const generateLine = content => {
+ const line = document.createElement('div');
+ line.className = 'line';
+ line.innerHTML = content;
+
+ return line;
+};
+
+const generateMockLines = () => {
+ const line1 = generateLine('Line 1');
+ const line2 = generateLine('Line 2');
+ const line3 = generateLine('Line 3');
+ const container = document.createElement('div');
+
+ container.appendChild(line1);
+ container.appendChild(line2);
+ container.appendChild(line3);
+
+ return container;
+};
+
+describe('Suggestion component', () => {
+ let vm;
+ let extractedLines;
+ let diffTable;
+
+ beforeEach(done => {
+ const Component = Vue.extend(SuggestionsComponent);
+
+ vm = new Component({
+ propsData: MOCK_DATA,
+ }).$mount();
+
+ extractedLines = vm.extractNewLines(generateMockLines());
+ diffTable = vm.generateDiff(extractedLines).$mount().$el;
+
+ spyOn(vm, 'renderSuggestions');
+ vm.renderSuggestions();
+ Vue.nextTick(done);
+ });
+
+ describe('mounted', () => {
+ it('renders a flash container', () => {
+ expect(vm.$el.querySelector('.flash-container')).not.toBeNull();
+ });
+
+ it('renders a container for suggestions', () => {
+ expect(vm.$refs.container).not.toBeNull();
+ });
+
+ it('renders suggestions', () => {
+ expect(vm.renderSuggestions).toHaveBeenCalled();
+ expect(vm.$el.innerHTML.includes('Suggestion 1')).toBe(true);
+ expect(vm.$el.innerHTML.includes('Suggestion 2')).toBe(true);
+ });
+ });
+
+ describe('extractNewLines', () => {
+ it('extracts suggested lines', () => {
+ const expectedReturn = [
+ { content: 'Line 1\n', lineNumber: 1 },
+ { content: 'Line 2\n', lineNumber: 2 },
+ { content: 'Line 3\n', lineNumber: 3 },
+ ];
+
+ expect(vm.extractNewLines(generateMockLines())).toEqual(expectedReturn);
+ });
+
+ it('increments line number for each extracted line', () => {
+ expect(extractedLines[0].lineNumber).toEqual(1);
+ expect(extractedLines[1].lineNumber).toEqual(2);
+ expect(extractedLines[2].lineNumber).toEqual(3);
+ });
+
+ it('returns empty array if no lines are found', () => {
+ const el = document.createElement('div');
+
+ expect(vm.extractNewLines(el)).toEqual([]);
+ });
+ });
+
+ describe('generateDiff', () => {
+ it('generates a diff table', () => {
+ expect(diffTable.querySelector('.md-suggestion-diff')).not.toBeNull();
+ });
+
+ it('generates a diff table that contains contents of `oldLineContent`', () => {
+ expect(diffTable.innerHTML.includes(vm.fromContent)).toBe(true);
+ });
+
+ it('generates a diff table that contains contents the suggested lines', () => {
+ extractedLines.forEach((line, i) => {
+ expect(diffTable.innerHTML.includes(extractedLines[i].content)).toBe(true);
+ });
+ });
+
+ it('generates a diff table with the correct line number for each suggested line', () => {
+ const lines = diffTable.getElementsByClassName('qa-new-diff-line-number');
+
+ expect([...lines][0].innerHTML).toBe('1');
+ expect([...lines][1].innerHTML).toBe('2');
+ expect([...lines][2].innerHTML).toBe('3');
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
index 1578b0f81f9..e16ab156679 100644
--- a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
+++ b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
@@ -101,6 +101,19 @@ describe('User Popover Component', () => {
expect(vm.$el.textContent).toContain('Engineer at GitLab');
});
+
+ it('should not encode special characters when we have bio and organization', () => {
+ const testProps = Object.assign({}, DEFAULT_PROPS);
+ testProps.user.bio = 'Manager & Team Lead';
+ testProps.user.organization = 'GitLab';
+
+ vm = mountComponent(UserPopover, {
+ ...DEFAULT_PROPS,
+ target: document.querySelector('.js-user-link'),
+ });
+
+ expect(vm.$el.textContent).toContain('Manager & Team Lead at GitLab');
+ });
});
describe('status data', () => {
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index fdeea814bb2..5ace5c5b1a2 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -67,6 +67,19 @@ describe Backup::Repository do
end
end
end
+
+ context 'restoring object pools' do
+ it 'schedules restoring of the pool' do
+ pool_repository = create(:pool_repository, :failed)
+ pool_repository.delete_object_pool
+
+ subject.restore
+
+ pool_repository.reload
+ expect(pool_repository).not_to be_failed
+ expect(pool_repository.object_pool.exists?).to be(true)
+ end
+ end
end
describe '#prepare_directories', :seed_helper do
diff --git a/spec/lib/banzai/filter/suggestion_filter_spec.rb b/spec/lib/banzai/filter/suggestion_filter_spec.rb
new file mode 100644
index 00000000000..55a141bf315
--- /dev/null
+++ b/spec/lib/banzai/filter/suggestion_filter_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Filter::SuggestionFilter do
+ include FilterSpecHelper
+
+ let(:input) { "<pre class='code highlight js-syntax-highlight suggestion'><code>foo\n</code></pre>" }
+ let(:default_context) do
+ { suggestions_filter_enabled: true }
+ end
+
+ it 'includes `js-render-suggestion` class' do
+ doc = filter(input, default_context)
+ result = doc.css('code').first
+
+ expect(result[:class]).to include('js-render-suggestion')
+ end
+
+ it 'includes no `js-render-suggestion` when feature disabled' do
+ stub_feature_flags(diff_suggestions: false)
+
+ doc = filter(input, default_context)
+ result = doc.css('code').first
+
+ expect(result[:class]).to be_nil
+ end
+
+ it 'includes no `js-render-suggestion` when filter is disabled' do
+ doc = filter(input)
+ result = doc.css('code').first
+
+ expect(result[:class]).to be_nil
+ end
+end
diff --git a/spec/lib/banzai/suggestions_parser_spec.rb b/spec/lib/banzai/suggestions_parser_spec.rb
new file mode 100644
index 00000000000..79658d710ce
--- /dev/null
+++ b/spec/lib/banzai/suggestions_parser_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::SuggestionsParser do
+ describe '.parse' do
+ it 'returns a list of suggestion contents' do
+ markdown = <<-MARKDOWN.strip_heredoc
+ ```suggestion
+ foo
+ bar
+ ```
+
+ ```
+ nothing
+ ```
+
+ ```suggestion
+ xpto
+ baz
+ ```
+
+ ```thing
+ this is not a suggestion, it's a thing
+ ```
+ MARKDOWN
+
+ expect(described_class.parse(markdown)).to eq([" foo\n bar",
+ " xpto\n baz"])
+ end
+ end
+end
diff --git a/spec/lib/constraints/feature_constrainer_spec.rb b/spec/lib/constraints/feature_constrainer_spec.rb
new file mode 100644
index 00000000000..42efc164f81
--- /dev/null
+++ b/spec/lib/constraints/feature_constrainer_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe Constraints::FeatureConstrainer do
+ describe '#matches' do
+ it 'calls Feature.enabled? with the correct arguments' do
+ expect(Feature).to receive(:enabled?).with(:feature_name, "an object", default_enabled: true)
+
+ described_class.new(:feature_name, "an object", default_enabled: true).matches?(double('request'))
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb
index 5ce84c61042..7c7e58d6bb7 100644
--- a/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_build_stage_spec.rb
@@ -6,8 +6,18 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201
let(:stages) { table(:ci_stages) }
let(:jobs) { table(:ci_builds) }
- STATUSES = { created: 0, pending: 1, running: 2, success: 3,
- failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze
+ let(:statuses) do
+ {
+ created: 0,
+ pending: 1,
+ running: 2,
+ success: 3,
+ failed: 4,
+ canceled: 5,
+ skipped: 6,
+ manual: 7
+ }
+ end
before do
projects.create!(id: 123, name: 'gitlab', path: 'gitlab-ce')
@@ -36,9 +46,9 @@ describe Gitlab::BackgroundMigration::MigrateBuildStage, :migration, schema: 201
expect(stages.all.pluck(:name)).to match_array %w[test build deploy]
expect(jobs.where(stage_id: nil)).to be_one
expect(jobs.find_by(stage_id: nil).id).to eq 6
- expect(stages.all.pluck(:status)).to match_array [STATUSES[:success],
- STATUSES[:failed],
- STATUSES[:pending]]
+ expect(stages.all.pluck(:status)).to match_array [statuses[:success],
+ statuses[:failed],
+ statuses[:pending]]
end
it 'recovers from unique constraint violation only twice' do
diff --git a/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb b/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb
index 878158910be..89b56906ed0 100644
--- a/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_stage_status_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::BackgroundMigration::MigrateStageStatus, :migration, schema: 20170711145320 do
@@ -6,8 +8,18 @@ describe Gitlab::BackgroundMigration::MigrateStageStatus, :migration, schema: 20
let(:stages) { table(:ci_stages) }
let(:jobs) { table(:ci_builds) }
- STATUSES = { created: 0, pending: 1, running: 2, success: 3,
- failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze
+ let(:statuses) do
+ {
+ created: 0,
+ pending: 1,
+ running: 2,
+ success: 3,
+ failed: 4,
+ canceled: 5,
+ skipped: 6,
+ manual: 7
+ }
+ end
before do
projects.create!(id: 1, name: 'gitlab1', path: 'gitlab1')
@@ -26,8 +38,8 @@ describe Gitlab::BackgroundMigration::MigrateStageStatus, :migration, schema: 20
it 'sets a correct stage status' do
described_class.new.perform(1, 2)
- expect(stages.first.status).to eq STATUSES[:running]
- expect(stages.second.status).to eq STATUSES[:failed]
+ expect(stages.first.status).to eq statuses[:running]
+ expect(stages.second.status).to eq statuses[:failed]
end
end
@@ -35,8 +47,8 @@ describe Gitlab::BackgroundMigration::MigrateStageStatus, :migration, schema: 20
it 'sets a skipped stage status' do
described_class.new.perform(1, 2)
- expect(stages.first.status).to eq STATUSES[:skipped]
- expect(stages.second.status).to eq STATUSES[:skipped]
+ expect(stages.first.status).to eq statuses[:skipped]
+ expect(stages.second.status).to eq statuses[:skipped]
end
end
@@ -50,8 +62,8 @@ describe Gitlab::BackgroundMigration::MigrateStageStatus, :migration, schema: 20
it 'sets a correct stage status' do
described_class.new.perform(1, 2)
- expect(stages.first.status).to eq STATUSES[:canceled]
- expect(stages.second.status).to eq STATUSES[:success]
+ expect(stages.first.status).to eq statuses[:canceled]
+ expect(stages.second.status).to eq statuses[:success]
end
end
@@ -65,8 +77,8 @@ describe Gitlab::BackgroundMigration::MigrateStageStatus, :migration, schema: 20
it 'sets a correct stage status' do
described_class.new.perform(1, 2)
- expect(stages.first.status).to eq STATUSES[:manual]
- expect(stages.second.status).to eq STATUSES[:success]
+ expect(stages.first.status).to eq statuses[:manual]
+ expect(stages.second.status).to eq statuses[:success]
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/except_policy_spec.rb b/spec/lib/gitlab/ci/config/entry/except_policy_spec.rb
deleted file mode 100644
index d036bf2f4d1..00000000000
--- a/spec/lib/gitlab/ci/config/entry/except_policy_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::Ci::Config::Entry::ExceptPolicy do
- let(:entry) { described_class.new(config) }
-
- it_behaves_like 'correct only except policy'
-
- describe '.default' do
- it 'does not have a default value' do
- expect(described_class.default).to be_nil
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb
index 12f4b9dc624..61d78f86b51 100644
--- a/spec/lib/gitlab/ci/config/entry/global_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb
@@ -161,7 +161,8 @@ describe Gitlab::Ci::Config::Entry::Global do
variables: { 'VAR' => 'value' },
ignore: false,
after_script: ['make clean'],
- only: { refs: %w[branches tags] } },
+ only: { refs: %w[branches tags] },
+ except: {} },
spinach: { name: :spinach,
before_script: [],
script: %w[spinach],
@@ -173,7 +174,8 @@ describe Gitlab::Ci::Config::Entry::Global do
variables: {},
ignore: false,
after_script: ['make clean'],
- only: { refs: %w[branches tags] } }
+ only: { refs: %w[branches tags] },
+ except: {} }
)
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index c1f4a060063..8e32cede3b5 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -259,7 +259,8 @@ describe Gitlab::Ci::Config::Entry::Job do
stage: 'test',
ignore: false,
after_script: %w[cleanup],
- only: { refs: %w[branches tags] })
+ only: { refs: %w[branches tags] },
+ except: {})
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
index 2a753408f54..1a2c30d3571 100644
--- a/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/jobs_spec.rb
@@ -68,13 +68,15 @@ describe Gitlab::Ci::Config::Entry::Jobs do
commands: 'rspec',
ignore: false,
stage: 'test',
- only: { refs: %w[branches tags] } },
+ only: { refs: %w[branches tags] },
+ except: {} },
spinach: { name: :spinach,
script: %w[spinach],
commands: 'spinach',
ignore: false,
stage: 'test',
- only: { refs: %w[branches tags] } })
+ only: { refs: %w[branches tags] },
+ except: {} })
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/only_policy_spec.rb b/spec/lib/gitlab/ci/config/entry/only_policy_spec.rb
deleted file mode 100644
index 5518b68e51a..00000000000
--- a/spec/lib/gitlab/ci/config/entry/only_policy_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::Ci::Config::Entry::OnlyPolicy do
- let(:entry) { described_class.new(config) }
-
- it_behaves_like 'correct only except policy'
-
- describe '.default' do
- it 'haa a default value' do
- expect(described_class.default).to eq( { refs: %w[branches tags] } )
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/config/entry/policy_spec.rb b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
index cf40a22af2e..83001b7fdd8 100644
--- a/spec/lib/gitlab/ci/config/entry/policy_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/policy_spec.rb
@@ -1,8 +1,173 @@
-require 'spec_helper'
+require 'fast_spec_helper'
+require_dependency 'active_model'
describe Gitlab::Ci::Config::Entry::Policy do
let(:entry) { described_class.new(config) }
+ context 'when using simplified policy' do
+ describe 'validations' do
+ context 'when entry config value is valid' do
+ context 'when config is a branch or tag name' do
+ let(:config) { %w[master feature/branch] }
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ describe '#value' do
+ it 'returns refs hash' do
+ expect(entry.value).to eq(refs: config)
+ end
+ end
+ end
+
+ context 'when config is a regexp' do
+ let(:config) { ['/^issue-.*$/'] }
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ context 'when config is a special keyword' do
+ let(:config) { %w[tags triggers branches] }
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+ end
+
+ context 'when entry value is not valid' do
+ let(:config) { [1] }
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include /policy config should be an array of strings or regexps/
+ end
+ end
+ end
+ end
+ end
+
+ context 'when using complex policy' do
+ context 'when specifying refs policy' do
+ let(:config) { { refs: ['master'] } }
+
+ it 'is a correct configuraton' do
+ expect(entry).to be_valid
+ expect(entry.value).to eq(refs: %w[master])
+ end
+ end
+
+ context 'when specifying kubernetes policy' do
+ let(:config) { { kubernetes: 'active' } }
+
+ it 'is a correct configuraton' do
+ expect(entry).to be_valid
+ expect(entry.value).to eq(kubernetes: 'active')
+ end
+ end
+
+ context 'when specifying invalid kubernetes policy' do
+ let(:config) { { kubernetes: 'something' } }
+
+ it 'reports an error about invalid policy' do
+ expect(entry.errors).to include /unknown value: something/
+ end
+ end
+
+ context 'when specifying valid variables expressions policy' do
+ let(:config) { { variables: ['$VAR == null'] } }
+
+ it 'is a correct configuraton' do
+ expect(entry).to be_valid
+ expect(entry.value).to eq(config)
+ end
+ end
+
+ context 'when specifying variables expressions in invalid format' do
+ let(:config) { { variables: '$MY_VAR' } }
+
+ it 'reports an error about invalid format' do
+ expect(entry.errors).to include /should be an array of strings/
+ end
+ end
+
+ context 'when specifying invalid variables expressions statement' do
+ let(:config) { { variables: ['$MY_VAR =='] } }
+
+ it 'reports an error about invalid statement' do
+ expect(entry.errors).to include /invalid expression syntax/
+ end
+ end
+
+ context 'when specifying invalid variables expressions token' do
+ let(:config) { { variables: ['$MY_VAR == 123'] } }
+
+ it 'reports an error about invalid expression' do
+ expect(entry.errors).to include /invalid expression syntax/
+ end
+ end
+
+ context 'when using invalid variables expressions regexp' do
+ let(:config) { { variables: ['$MY_VAR =~ /some ( thing/'] } }
+
+ it 'reports an error about invalid expression' do
+ expect(entry.errors).to include /invalid expression syntax/
+ end
+ end
+
+ context 'when specifying a valid changes policy' do
+ let(:config) { { changes: %w[some/* paths/**/*.rb] } }
+
+ it 'is a correct configuraton' do
+ expect(entry).to be_valid
+ expect(entry.value).to eq(config)
+ end
+ end
+
+ context 'when changes policy is invalid' do
+ let(:config) { { changes: [1, 2] } }
+
+ it 'returns errors' do
+ expect(entry.errors).to include /changes should be an array of strings/
+ end
+ end
+
+ context 'when specifying unknown policy' do
+ let(:config) { { refs: ['master'], invalid: :something } }
+
+ it 'returns error about invalid key' do
+ expect(entry.errors).to include /unknown keys: invalid/
+ end
+ end
+
+ context 'when policy is empty' do
+ let(:config) { {} }
+
+ it 'is not a valid configuration' do
+ expect(entry.errors).to include /can't be blank/
+ end
+ end
+ end
+
+ context 'when policy strategy does not match' do
+ let(:config) { 'string strategy' }
+
+ it 'returns information about errors' do
+ expect(entry.errors)
+ .to include /has to be either an array of conditions or a hash/
+ end
+ end
+
describe '.default' do
it 'does not have a default value' do
expect(described_class.default).to be_nil
diff --git a/spec/lib/gitlab/ci/parsers/test_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb
index 0b85b432677..4b647bffe59 100644
--- a/spec/lib/gitlab/ci/parsers/test_spec.rb
+++ b/spec/lib/gitlab/ci/parsers_spec.rb
@@ -1,6 +1,8 @@
+# frozen_string_literal: true
+
require 'spec_helper'
-describe Gitlab::Ci::Parsers::Test do
+describe Gitlab::Ci::Parsers do
describe '.fabricate!' do
subject { described_class.fabricate!(file_type) }
@@ -8,7 +10,7 @@ describe Gitlab::Ci::Parsers::Test do
let(:file_type) { 'junit' }
it 'fabricates the class' do
- is_expected.to be_a(described_class::Junit)
+ is_expected.to be_a(described_class::Test::Junit)
end
end
@@ -16,7 +18,7 @@ describe Gitlab::Ci::Parsers::Test do
let(:file_type) { 'undefined' }
it 'raises an error' do
- expect { subject }.to raise_error(Gitlab::Ci::Parsers::Test::ParserNotFoundError)
+ expect { subject }.to raise_error(Gitlab::Ci::Parsers::ParserNotFoundError)
end
end
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 23f27939dd2..4e83b27e4a5 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -1338,12 +1338,7 @@ describe Gitlab::Database::MigrationHelpers do
end
describe '#index_exists_by_name?' do
- # TODO: remove rails5-only after removing rails4 tests
- # rails 4 can not handle multiple indexes on the same column set if
- # index was added by 't.index' - t.index is used by default in schema.rb in
- # rails 5. Let's run this test only in rails 5 env:
- # see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21492#note_113602758
- it 'returns true if an index exists', :rails5 do
+ it 'returns true if an index exists' do
expect(model.index_exists_by_name?(:projects, 'index_projects_on_path'))
.to be_truthy
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index fc295b2deff..60106ee3c0b 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -400,13 +400,8 @@ describe Gitlab::Database do
describe '.cached_table_exists?' do
it 'only retrieves data once per table' do
- if Gitlab.rails5?
- expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:projects).once.and_call_original
- expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:bogus_table_name).once.and_call_original
- else
- expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original
- expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original
- end
+ expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:projects).once.and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:bogus_table_name).once.and_call_original
2.times do
expect(described_class.cached_table_exists?(:projects)).to be_truthy
@@ -462,8 +457,7 @@ describe Gitlab::Database do
expect(described_class.db_read_only?).to be_truthy
end
- # TODO: remove rails5-only tag after removing rails4 tests
- it 'detects a read only database', :rails5 do
+ it 'detects a read only database' do
allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => true }])
expect(described_class.db_read_only?).to be_truthy
@@ -475,8 +469,7 @@ describe Gitlab::Database do
expect(described_class.db_read_only?).to be_falsey
end
- # TODO: remove rails5-only tag after removing rails4 tests
- it 'detects a read write database', :rails5 do
+ it 'detects a read write database' do
allow(ActiveRecord::Base.connection).to receive(:execute).with('SELECT pg_is_in_recovery()').and_return([{ "pg_is_in_recovery" => false }])
expect(described_class.db_read_only?).to be_falsey
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 3417896e259..b15d22c634a 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -583,6 +583,12 @@ describe Gitlab::Diff::File do
end
end
+ describe '#empty?' do
+ it 'returns true' do
+ expect(diff_file.empty?).to be_truthy
+ end
+ end
+
describe '#different_type?' do
it 'returns false' do
expect(diff_file).not_to be_different_type
@@ -662,4 +668,87 @@ describe Gitlab::Diff::File do
end
end
end
+
+ describe '#empty?' do
+ let(:project) do
+ create(:project, :custom_repo, files: {})
+ end
+ let(:branch_name) { 'master' }
+
+ def create_file(file_name, content)
+ Files::CreateService.new(
+ project,
+ project.owner,
+ commit_message: 'Update',
+ start_branch: branch_name,
+ branch_name: branch_name,
+ file_path: file_name,
+ file_content: content
+ ).execute
+
+ project.commit(branch_name).diffs.diff_files.first
+ end
+
+ def update_file(file_name, content)
+ Files::UpdateService.new(
+ project,
+ project.owner,
+ commit_message: 'Update',
+ start_branch: branch_name,
+ branch_name: branch_name,
+ file_path: file_name,
+ file_content: content
+ ).execute
+
+ project.commit(branch_name).diffs.diff_files.first
+ end
+
+ def delete_file(file_name)
+ Files::DeleteService.new(
+ project,
+ project.owner,
+ commit_message: 'Update',
+ start_branch: branch_name,
+ branch_name: branch_name,
+ file_path: file_name
+ ).execute
+
+ project.commit(branch_name).diffs.diff_files.first
+ end
+
+ context 'when empty file is created' do
+ it 'returns true' do
+ diff_file = create_file('empty.md', '')
+
+ expect(diff_file.empty?).to be_truthy
+ end
+ end
+
+ context 'when empty file is deleted' do
+ it 'returns true' do
+ create_file('empty.md', '')
+ diff_file = delete_file('empty.md')
+
+ expect(diff_file.empty?).to be_truthy
+ end
+ end
+
+ context 'when file with content is truncated' do
+ it 'returns false' do
+ create_file('with-content.md', 'file content')
+ diff_file = update_file('with-content.md', '')
+
+ expect(diff_file.empty?).to be_falsey
+ end
+ end
+
+ context 'when empty file has content added' do
+ it 'returns false' do
+ create_file('empty.md', '')
+ diff_file = update_file('empty.md', 'new content')
+
+ expect(diff_file.empty?).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
index efca8564894..25684ea9e2c 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
@@ -240,12 +240,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
.and_return(user.id)
end
- # TODO: remove rails5-only after removing rails4 tests
- # rails 4 can not handle multiple indexes on the same column set if
- # index was added by 't.index' - t.index is used by default in schema.rb in
- # rails 5. Let's run this test only in rails 5 env:
- # see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21492#note_113602758
- it 'returns the existing merge request', :rails5 do
+ it 'returns the existing merge request' do
mr1, exists1 = importer.create_merge_request
mr2, exists2 = importer.create_merge_request
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index bae5b21c26f..c8c74883640 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -37,6 +37,7 @@ notes:
- events
- system_note_metadata
- note_diff_file
+- suggestions
label_links:
- target
- label
@@ -63,6 +64,7 @@ snippets:
- award_emoji
- user_agent_detail
releases:
+- author
- project
project_members:
- created_by
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index d3bfde181bc..24b1f2d995b 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -112,8 +112,11 @@ ProjectSnippet:
- visibility_level
Release:
- id
+- name
- tag
+- sha
- description
+- author_id
- project_id
- created_at
- updated_at
diff --git a/spec/lib/gitlab/sql/glob_spec.rb b/spec/lib/gitlab/sql/glob_spec.rb
index 1cf8935bfe3..3147b52dcc5 100644
--- a/spec/lib/gitlab/sql/glob_spec.rb
+++ b/spec/lib/gitlab/sql/glob_spec.rb
@@ -35,9 +35,8 @@ describe Gitlab::SQL::Glob do
value = query("SELECT #{quote(string)} LIKE #{pattern}")
.rows.flatten.first
- check = Gitlab.rails5? ? true : 't'
case value
- when check, 1
+ when true, 1
true
else
false
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index deb19fe1a4b..2a09f581f68 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -117,6 +117,7 @@ describe Gitlab::UsageData do
releases
remote_mirrors
snippets
+ suggestions
todos
uploads
web_hooks
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index b3f55a2e1bd..7213eee5675 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -246,7 +246,6 @@ describe Gitlab::Workhorse do
GL_ID: "user-#{user.id}",
GL_USERNAME: user.username,
GL_REPOSITORY: "project-#{project.id}",
- RepoPath: repo_path,
ShowAllRefs: false
}
end
@@ -261,7 +260,6 @@ describe Gitlab::Workhorse do
GL_ID: "user-#{user.id}",
GL_USERNAME: user.username,
GL_REPOSITORY: "wiki-#{project.id}",
- RepoPath: repo_path,
ShowAllRefs: false
}
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 1d17aec0ded..f6e5c9d33ac 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -4,6 +4,7 @@ require 'email_spec'
describe Notify do
include EmailSpec::Helpers
include EmailSpec::Matchers
+ include EmailHelpers
include RepoHelpers
include_context 'gitlab email notification'
@@ -27,15 +28,6 @@ describe Notify do
description: 'My awesome description!')
end
- def have_referable_subject(referable, reply: false)
- prefix = (referable.project ? "#{referable.project.name} | " : '').freeze
- prefix = "Re: #{prefix}" if reply
-
- suffix = "#{referable.title} (#{referable.to_reference})"
-
- have_subject [prefix, suffix].compact.join
- end
-
context 'for a project' do
shared_examples 'an assignee email' do
it 'is sent to the assignee as the author' do
diff --git a/spec/migrations/backfill_releases_name_with_tag_name_spec.rb b/spec/migrations/backfill_releases_name_with_tag_name_spec.rb
new file mode 100644
index 00000000000..6f436de84b7
--- /dev/null
+++ b/spec/migrations/backfill_releases_name_with_tag_name_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20181212104941_backfill_releases_name_with_tag_name.rb')
+
+describe BackfillReleasesNameWithTagName, :migration do
+ let(:releases) { table(:releases) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+
+ let(:namespace) { namespaces.create(name: 'foo', path: 'foo') }
+ let(:project) { projects.create!(namespace_id: namespace.id) }
+ let(:release) { releases.create!(project_id: project.id, tag: 'v1.0.0') }
+
+ it 'defaults name to tag value' do
+ expect(release.tag).to be_present
+
+ migrate!
+
+ release.reload
+ expect(release.name).to eq(release.tag)
+ end
+end
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
new file mode 100644
index 00000000000..741cdfef1a5
--- /dev/null
+++ b/spec/models/ci/bridge_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Ci::Bridge do
+ set(:project) { create(:project) }
+ set(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let(:bridge) do
+ create(:ci_bridge, pipeline: pipeline)
+ end
+
+ describe '#tags' do
+ it 'only has a bridge tag' do
+ expect(bridge.tags).to eq [:bridge]
+ end
+ end
+
+ describe '#detailed_status' do
+ let(:user) { create(:user) }
+ let(:status) { bridge.detailed_status(user) }
+
+ it 'returns detailed status object' do
+ expect(status).to be_a Gitlab::Ci::Status::Success
+ end
+ end
+end
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index a1579b90436..809880f5969 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -33,10 +33,10 @@ describe Clusters::Applications::Knative do
end
context 'application install previously errored with older version' do
- let(:application) { create(:clusters_applications_knative, :scheduled, version: '0.1.3') }
+ let(:application) { create(:clusters_applications_knative, :scheduled, version: '0.2.2') }
it 'updates the application version' do
- expect(application.reload.version).to eq('0.1.3')
+ expect(application.reload.version).to eq('0.2.2')
end
end
end
@@ -105,7 +105,7 @@ describe Clusters::Applications::Knative do
it 'should be initialized with knative arguments' do
expect(subject.name).to eq('knative')
expect(subject.chart).to eq('knative/knative')
- expect(subject.version).to eq('0.1.3')
+ expect(subject.version).to eq('0.2.2')
expect(subject.files).to eq(knative.files)
end
end
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index 8624f0daa4d..40ce8ab736a 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -318,6 +318,24 @@ describe DiffNote do
end
end
+ describe '#supports_suggestion?' do
+ context 'when noteable does not support suggestions' do
+ it 'returns false' do
+ allow(subject.noteable).to receive(:supports_suggestion?) { false }
+
+ expect(subject.supports_suggestion?).to be(false)
+ end
+ end
+
+ context 'when line is not suggestible' do
+ it 'returns false' do
+ allow_any_instance_of(Gitlab::Diff::Line).to receive(:suggestible?) { false }
+
+ expect(subject.supports_suggestion?).to be(false)
+ end
+ end
+ end
+
describe "image diff notes" do
let(:path) { "files/images/any_image.png" }
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index cbe60b3a4a5..33e984dc399 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -105,7 +105,7 @@ describe MergeRequestDiff do
context 'when the raw diffs are empty' do
before do
- MergeRequestDiffFile.delete_all(merge_request_diff_id: diff_with_commits.id)
+ MergeRequestDiffFile.where(merge_request_diff_id: diff_with_commits.id).delete_all
end
it 'returns an empty DiffCollection' do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index c3152d2021b..bf4117fbcaf 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1339,6 +1339,30 @@ describe MergeRequest do
end
end
+ describe '#calculate_reactive_cache' do
+ let(:project) { create(:project, :repository) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ subject { merge_request.calculate_reactive_cache(service_class_name) }
+
+ context 'when given an unknown service class name' do
+ let(:service_class_name) { 'Integer' }
+
+ it 'raises a NameError exception' do
+ expect { subject }.to raise_error(NameError, service_class_name)
+ end
+ end
+
+ context 'when given a known service class name' do
+ let(:service_class_name) { 'Ci::CompareTestReportsService' }
+
+ it 'does not raises a NameError exception' do
+ allow_any_instance_of(service_class_name.constantize).to receive(:execute).and_return(nil)
+
+ expect { subject }.not_to raise_error(NameError)
+ end
+ end
+ end
+
describe '#compare_test_reports' do
subject { merge_request.compare_test_reports }
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 771d834c4bc..c8ab564e3bc 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -42,12 +42,7 @@ RSpec.describe NotificationSetting do
expect(notification_setting.new_issue).to eq(true)
expect(notification_setting.close_issue).to eq(true)
expect(notification_setting.merge_merge_request).to eq(true)
-
- # In Rails 5 assigning a value which is not explicitly `true` or `false` ("nil" in this case)
- # to a boolean column transforms it to `true`.
- # In Rails 4 it transforms the value to `false` with deprecation warning.
- # Replace `eq(Gitlab.rails5?)` with `eq(true)` when removing rails5? code.
- expect(notification_setting.close_merge_request).to eq(Gitlab.rails5?)
+ expect(notification_setting.close_merge_request).to eq(true)
expect(notification_setting.reopen_merge_request).to eq(false)
end
end
diff --git a/spec/models/project_import_data_spec.rb b/spec/models/project_import_data_spec.rb
new file mode 100644
index 00000000000..e9910c0a5d1
--- /dev/null
+++ b/spec/models/project_import_data_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectImportData do
+ describe '#merge_data' do
+ it 'writes the Hash to the attribute if it is nil' do
+ row = described_class.new
+
+ row.merge_data('number' => 10)
+
+ expect(row.data).to eq({ 'number' => 10 })
+ end
+
+ it 'merges the Hash into an existing Hash if one was present' do
+ row = described_class.new(data: { 'number' => 10 })
+
+ row.merge_data('foo' => 'bar')
+
+ expect(row.data).to eq({ 'number' => 10, 'foo' => 'bar' })
+ end
+ end
+
+ describe '#merge_credentials' do
+ it 'writes the Hash to the attribute if it is nil' do
+ row = described_class.new
+
+ row.merge_credentials('number' => 10)
+
+ expect(row.credentials).to eq({ 'number' => 10 })
+ end
+
+ it 'merges the Hash into an existing Hash if one was present' do
+ row = described_class.new
+
+ row.credentials = { 'number' => 10 }
+ row.merge_credentials('foo' => 'bar')
+
+ expect(row.credentials).to eq({ 'number' => 10, 'foo' => 'bar' })
+ end
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 9e5b06b745a..5e63f14b720 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -4102,6 +4102,29 @@ describe Project do
end
end
+ describe '#object_pool_params' do
+ let(:project) { create(:project, :repository, :public) }
+
+ subject { project.object_pool_params }
+
+ before do
+ stub_application_setting(hashed_storage_enabled: true)
+ end
+
+ context 'when the objects cannot be pooled' do
+ let(:project) { create(:project, :repository, :private) }
+
+ it { is_expected.to be_empty }
+ end
+
+ context 'when a pool is created' do
+ it 'returns that pool repository' do
+ expect(subject).not_to be_empty
+ expect(subject[:pool_repository]).to be_persisted
+ end
+ end
+ end
+
describe '#git_objects_poolable?' do
subject { project }
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
index 3f86347c3ae..51725eeacac 100644
--- a/spec/models/release_spec.rb
+++ b/spec/models/release_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe Release do
describe 'associations' do
it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:author).class_name('User') }
end
describe 'validation' do
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index b12ca79847c..5d3c25062d5 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-describe RemoteMirror do
+describe RemoteMirror, :mailer do
include GitHelpers
describe 'URL validation' do
@@ -137,6 +137,43 @@ describe RemoteMirror do
end
end
+ describe '#mark_as_failed' do
+ let(:remote_mirror) { create(:remote_mirror) }
+ let(:error_message) { 'http://user:pass@test.com/root/repoC.git/' }
+ let(:sanitized_error_message) { 'http://*****:*****@test.com/root/repoC.git/' }
+
+ subject do
+ remote_mirror.update_start
+ remote_mirror.mark_as_failed(error_message)
+ end
+
+ it 'sets the update_status to failed' do
+ subject
+
+ expect(remote_mirror.reload.update_status).to eq('failed')
+ end
+
+ it 'saves the sanitized error' do
+ subject
+
+ expect(remote_mirror.last_error).to eq(sanitized_error_message)
+ end
+
+ context 'notifications' do
+ let(:user) { create(:user) }
+
+ before do
+ remote_mirror.project.add_maintainer(user)
+ end
+
+ it 'notifies the project maintainers' do
+ perform_enqueued_jobs { subject }
+
+ should_email(user)
+ end
+ end
+ end
+
context 'when remote mirror gets destroyed' do
it 'removes remote' do
mirror = create_mirror(url: 'http://foo:bar@test.com')
diff --git a/spec/models/suggestion_spec.rb b/spec/models/suggestion_spec.rb
new file mode 100644
index 00000000000..cafc725dddb
--- /dev/null
+++ b/spec/models/suggestion_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Suggestion do
+ let(:suggestion) { create(:suggestion) }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:note) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:note) }
+
+ context 'when suggestion is applied' do
+ before do
+ allow(subject).to receive(:applied?).and_return(true)
+ end
+
+ it { is_expected.to validate_presence_of(:commit_id) }
+ end
+ end
+
+ describe '#appliable?' do
+ context 'when note does not support suggestions' do
+ it 'returns false' do
+ expect_next_instance_of(DiffNote) do |note|
+ allow(note).to receive(:supports_suggestion?) { false }
+ end
+
+ expect(suggestion).not_to be_appliable
+ end
+ end
+
+ context 'when patch is already applied' do
+ let(:suggestion) { create(:suggestion, :applied) }
+
+ it 'returns false' do
+ expect(suggestion).not_to be_appliable
+ end
+ end
+
+ context 'when merge request is not opened' do
+ let(:merge_request) { create(:merge_request, :merged) }
+ let(:note) do
+ create(:diff_note_on_merge_request, project: merge_request.project,
+ noteable: merge_request)
+ end
+
+ let(:suggestion) { create(:suggestion, note: note) }
+
+ it 'returns false' do
+ expect(suggestion).not_to be_appliable
+ end
+ end
+ end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index ff075e65c76..8b3021113bc 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -45,6 +45,7 @@ describe User do
it { is_expected.to have_many(:uploads) }
it { is_expected.to have_many(:reported_abuse_reports).dependent(:destroy).class_name('AbuseReport') }
it { is_expected.to have_many(:custom_attributes).class_name('UserCustomAttribute') }
+ it { is_expected.to have_many(:releases).dependent(:nullify) }
describe "#abuse_report" do
let(:current_user) { create(:user) }
diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb
index 72c5eac3ede..754ba0a594c 100644
--- a/spec/presenters/clusters/cluster_presenter_spec.rb
+++ b/spec/presenters/clusters/cluster_presenter_spec.rb
@@ -4,9 +4,10 @@ describe Clusters::ClusterPresenter do
include Gitlab::Routing.url_helpers
let(:cluster) { create(:cluster, :provided_by_gcp, :project) }
+ let(:user) { create(:user) }
subject(:presenter) do
- described_class.new(cluster)
+ described_class.new(cluster, current_user: user)
end
it 'inherits from Gitlab::View::Presenter::Delegated' do
@@ -27,6 +28,129 @@ describe Clusters::ClusterPresenter do
end
end
+ describe '#item_link' do
+ let(:clusterable_presenter) { double('ClusterablePresenter', subject: clusterable) }
+
+ subject { presenter.item_link(clusterable_presenter) }
+
+ context 'for a group cluster' do
+ let(:cluster) { create(:cluster, cluster_type: :group_type, groups: [group]) }
+ let(:group) { create(:group, name: 'Foo') }
+ let(:cluster_link) { "<a href=\"#{group_cluster_path(cluster.group, cluster)}\">#{cluster.name}</a>" }
+
+ before do
+ group.add_maintainer(user)
+ end
+
+ shared_examples 'ancestor clusters' do
+ context 'ancestor clusters', :nested_groups do
+ let(:root_group) { create(:group, name: 'Root Group') }
+ let(:parent) { create(:group, name: 'parent', parent: root_group) }
+ let(:child) { create(:group, name: 'child', parent: parent) }
+ let(:group) { create(:group, name: 'group', parent: child) }
+
+ before do
+ root_group.add_maintainer(user)
+ end
+
+ context 'top level group cluster' do
+ let(:cluster) { create(:cluster, cluster_type: :group_type, groups: [root_group]) }
+
+ it 'returns full group names and link for cluster' do
+ expect(subject).to eq("Root Group / #{cluster_link}")
+ end
+
+ it 'is html safe' do
+ expect(presenter).to receive(:sanitize).with('Root Group').and_call_original
+
+ expect(subject).to be_html_safe
+ end
+ end
+
+ context 'first level group cluster' do
+ let(:cluster) { create(:cluster, cluster_type: :group_type, groups: [parent]) }
+
+ it 'returns full group names and link for cluster' do
+ expect(subject).to eq("Root Group / parent / #{cluster_link}")
+ end
+
+ it 'is html safe' do
+ expect(presenter).to receive(:sanitize).with('Root Group / parent').and_call_original
+
+ expect(subject).to be_html_safe
+ end
+ end
+
+ context 'second level group cluster' do
+ let(:cluster) { create(:cluster, cluster_type: :group_type, groups: [child]) }
+
+ let(:ellipsis_h) do
+ /.*ellipsis_h.*/
+ end
+
+ it 'returns clipped group names and link for cluster' do
+ expect(subject).to match("Root Group / #{ellipsis_h} / child / #{cluster_link}")
+ end
+
+ it 'is html safe' do
+ expect(presenter).to receive(:sanitize).with('Root Group / parent / child').and_call_original
+
+ expect(subject).to be_html_safe
+ end
+ end
+ end
+ end
+
+ context 'for a project clusterable' do
+ let(:clusterable) { project }
+ let(:project) { create(:project, group: group) }
+
+ it 'returns the group name and the link for cluster' do
+ expect(subject).to eq("Foo / #{cluster_link}")
+ end
+
+ it 'is html safe' do
+ expect(presenter).to receive(:sanitize).with('Foo').and_call_original
+
+ expect(subject).to be_html_safe
+ end
+
+ include_examples 'ancestor clusters'
+ end
+
+ context 'for the group clusterable for the cluster' do
+ let(:clusterable) { group }
+
+ it 'returns link for cluster' do
+ expect(subject).to eq(cluster_link)
+ end
+
+ include_examples 'ancestor clusters'
+
+ it 'is html safe' do
+ expect(subject).to be_html_safe
+ end
+ end
+ end
+
+ context 'for a project cluster' do
+ let(:cluster) { create(:cluster, :project) }
+ let(:cluster_link) { "<a href=\"#{project_cluster_path(cluster.project, cluster)}\">#{cluster.name}</a>" }
+
+ before do
+ cluster.project.add_maintainer(user)
+ end
+
+ context 'for the project clusterable' do
+ let(:clusterable) { cluster.project }
+
+ it 'returns link for cluster' do
+ expect(subject).to eq(cluster_link)
+ end
+ end
+ end
+ end
+
describe '#gke_cluster_url' do
subject { described_class.new(cluster).gke_cluster_url }
@@ -74,6 +198,20 @@ describe Clusters::ClusterPresenter do
end
end
+ describe '#cluster_type_description' do
+ subject { described_class.new(cluster).cluster_type_description }
+
+ context 'project_type cluster' do
+ it { is_expected.to eq('Project cluster') }
+ end
+
+ context 'group_type cluster' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, :group) }
+
+ it { is_expected.to eq('Group cluster') }
+ end
+ end
+
describe '#show_path' do
subject { described_class.new(cluster).show_path }
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 2ebcb787d06..3304dfdd909 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -969,17 +969,10 @@ describe API::Internal do
env: env
}
- if Gitlab.rails5?
- post(
- api("/internal/allowed"),
- params: params
- )
- else
- post(
- api("/internal/allowed"),
- params
- )
- end
+ post(
+ api("/internal/allowed"),
+ params: params
+ )
end
def archive(key, project)
diff --git a/spec/requests/api/suggestions_spec.rb b/spec/requests/api/suggestions_spec.rb
new file mode 100644
index 00000000000..8e9f737fbd5
--- /dev/null
+++ b/spec/requests/api/suggestions_spec.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe API::Suggestions do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ let(:merge_request) do
+ create(:merge_request, source_project: project,
+ target_project: project)
+ end
+
+ let(:position) do
+ Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 9,
+ diff_refs: merge_request.diff_refs)
+ end
+
+ let(:diff_note) do
+ create(:diff_note_on_merge_request, noteable: merge_request,
+ position: position,
+ project: project)
+ end
+
+ describe "PUT /suggestions/:id/apply" do
+ let(:url) { "/suggestions/#{suggestion.id}/apply" }
+
+ context 'when successfully applies patch' do
+ let(:suggestion) do
+ create(:suggestion, note: diff_note,
+ from_content: " raise RuntimeError, \"System commands must be given as an array of strings\"\n",
+ to_content: " raise RuntimeError, 'Explosion'\n # explosion?")
+ end
+
+ it 'returns 200 with json content' do
+ project.add_maintainer(user)
+
+ put api(url, user), id: suggestion.id
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response)
+ .to include('id', 'from_original_line', 'to_original_line',
+ 'from_line', 'to_line', 'appliable', 'applied',
+ 'from_content', 'to_content')
+ end
+ end
+
+ context 'when not able to apply patch' do
+ let(:suggestion) do
+ create(:suggestion, :unappliable, note: diff_note)
+ end
+
+ it 'returns 400 with json content' do
+ project.add_maintainer(user)
+
+ put api(url, user), id: suggestion.id
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response).to eq({ 'message' => 'Suggestion is not appliable' })
+ end
+ end
+
+ context 'when unauthorized' do
+ let(:suggestion) do
+ create(:suggestion, note: diff_note,
+ from_content: " raise RuntimeError, \"System commands must be given as an array of strings\"\n",
+ to_content: " raise RuntimeError, 'Explosion'\n # explosion?")
+ end
+
+ it 'returns 403 with json content' do
+ project.add_reporter(user)
+
+ put api(url, user), id: suggestion.id
+
+ expect(response).to have_gitlab_http_status(403)
+ expect(json_response).to eq({ 'message' => '403 Forbidden' })
+ end
+ end
+ end
+end
diff --git a/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb b/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb
new file mode 100644
index 00000000000..08ffc3c3a53
--- /dev/null
+++ b/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'rubocop'
+require 'rubocop/rspec/support'
+require_relative '../../../rubocop/cop/inject_enterprise_edition_module'
+
+describe RuboCop::Cop::InjectEnterpriseEditionModule do
+ include CopHelper
+
+ subject(:cop) { described_class.new }
+
+ it 'flags the use of `prepend EE` in the middle of a file' do
+ expect_offense(<<~SOURCE)
+ class Foo
+ prepend EE::Foo
+ ^^^^^^^^^^^^^^^ Injecting EE modules must be done on the last line of this file, outside of any class or module definitions
+ end
+ SOURCE
+ end
+
+ it 'flags the use of `prepend ::EE` in the middle of a file' do
+ expect_offense(<<~SOURCE)
+ class Foo
+ prepend ::EE::Foo
+ ^^^^^^^^^^^^^^^^^ Injecting EE modules must be done on the last line of this file, outside of any class or module definitions
+ end
+ SOURCE
+ end
+
+ it 'flags the use of `include EE` in the middle of a file' do
+ expect_offense(<<~SOURCE)
+ class Foo
+ include EE::Foo
+ ^^^^^^^^^^^^^^^ Injecting EE modules must be done on the last line of this file, outside of any class or module definitions
+ end
+ SOURCE
+ end
+
+ it 'flags the use of `include ::EE` in the middle of a file' do
+ expect_offense(<<~SOURCE)
+ class Foo
+ include ::EE::Foo
+ ^^^^^^^^^^^^^^^^^ Injecting EE modules must be done on the last line of this file, outside of any class or module definitions
+ end
+ SOURCE
+ end
+
+ it 'flags the use of `extend EE` in the middle of a file' do
+ expect_offense(<<~SOURCE)
+ class Foo
+ extend EE::Foo
+ ^^^^^^^^^^^^^^ Injecting EE modules must be done on the last line of this file, outside of any class or module definitions
+ end
+ SOURCE
+ end
+
+ it 'flags the use of `extend ::EE` in the middle of a file' do
+ expect_offense(<<~SOURCE)
+ class Foo
+ extend ::EE::Foo
+ ^^^^^^^^^^^^^^^^ Injecting EE modules must be done on the last line of this file, outside of any class or module definitions
+ end
+ SOURCE
+ end
+
+ it 'does not flag prepending of regular modules' do
+ expect_no_offenses(<<~SOURCE)
+ class Foo
+ prepend Foo
+ end
+ SOURCE
+ end
+
+ it 'does not flag including of regular modules' do
+ expect_no_offenses(<<~SOURCE)
+ class Foo
+ include Foo
+ end
+ SOURCE
+ end
+
+ it 'does not flag extending using regular modules' do
+ expect_no_offenses(<<~SOURCE)
+ class Foo
+ extend Foo
+ end
+ SOURCE
+ end
+
+ it 'does not flag the use of `prepend EE` on the last line' do
+ expect_no_offenses(<<~SOURCE)
+ class Foo
+ end
+
+ Foo.prepend(EE::Foo)
+ SOURCE
+ end
+
+ it 'does not flag the use of `include EE` on the last line' do
+ expect_no_offenses(<<~SOURCE)
+ class Foo
+ end
+
+ Foo.include(EE::Foo)
+ SOURCE
+ end
+
+ it 'does not flag the use of `extend EE` on the last line' do
+ expect_no_offenses(<<~SOURCE)
+ class Foo
+ end
+
+ Foo.extend(EE::Foo)
+ SOURCE
+ end
+
+ it 'autocorrects offenses by just disabling the Cop' do
+ source = <<~SOURCE
+ class Foo
+ prepend EE::Foo
+ include Bar
+ end
+ SOURCE
+
+ expect(autocorrect_source(source)).to eq(<<~SOURCE)
+ class Foo
+ prepend EE::Foo # rubocop: disable Cop/InjectEnterpriseEditionModule
+ include Bar
+ end
+ SOURCE
+ end
+end
diff --git a/spec/rubocop/cop/migration/add_timestamps_spec.rb b/spec/rubocop/cop/migration/add_timestamps_spec.rb
index 3a41c91add2..fae0177d5f5 100644
--- a/spec/rubocop/cop/migration/add_timestamps_spec.rb
+++ b/spec/rubocop/cop/migration/add_timestamps_spec.rb
@@ -11,7 +11,7 @@ describe RuboCop::Cop::Migration::AddTimestamps do
subject(:cop) { described_class.new }
let(:migration_with_add_timestamps) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
@@ -24,7 +24,7 @@ describe RuboCop::Cop::Migration::AddTimestamps do
let(:migration_without_add_timestamps) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
@@ -36,7 +36,7 @@ describe RuboCop::Cop::Migration::AddTimestamps do
let(:migration_with_add_timestamps_with_timezone) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
diff --git a/spec/rubocop/cop/migration/datetime_spec.rb b/spec/rubocop/cop/migration/datetime_spec.rb
index 9e844325371..f2d9483d8d3 100644
--- a/spec/rubocop/cop/migration/datetime_spec.rb
+++ b/spec/rubocop/cop/migration/datetime_spec.rb
@@ -12,7 +12,7 @@ describe RuboCop::Cop::Migration::Datetime do
let(:migration_with_datetime) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
@@ -25,7 +25,7 @@ describe RuboCop::Cop::Migration::Datetime do
let(:migration_with_timestamp) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
@@ -38,7 +38,7 @@ describe RuboCop::Cop::Migration::Datetime do
let(:migration_without_datetime) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
@@ -50,7 +50,7 @@ describe RuboCop::Cop::Migration::Datetime do
let(:migration_with_datetime_with_timezone) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
diff --git a/spec/rubocop/cop/migration/timestamps_spec.rb b/spec/rubocop/cop/migration/timestamps_spec.rb
index 685bdb21803..1812818692a 100644
--- a/spec/rubocop/cop/migration/timestamps_spec.rb
+++ b/spec/rubocop/cop/migration/timestamps_spec.rb
@@ -11,7 +11,7 @@ describe RuboCop::Cop::Migration::Timestamps do
subject(:cop) { described_class.new }
let(:migration_with_timestamps) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
@@ -27,7 +27,7 @@ describe RuboCop::Cop::Migration::Timestamps do
let(:migration_without_timestamps) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
@@ -42,7 +42,7 @@ describe RuboCop::Cop::Migration::Timestamps do
let(:migration_with_timestamps_with_timezone) do
%q(
- class Users < ActiveRecord::Migration
+ class Users < ActiveRecord::Migration[4.2]
DOWNTIME = false
def change
diff --git a/spec/serializers/issue_board_entity_spec.rb b/spec/serializers/issue_board_entity_spec.rb
index 06d9d3657e6..f6fa2a794f6 100644
--- a/spec/serializers/issue_board_entity_spec.rb
+++ b/spec/serializers/issue_board_entity_spec.rb
@@ -3,21 +3,40 @@
require 'spec_helper'
describe IssueBoardEntity do
- let(:project) { create(:project) }
- let(:resource) { create(:issue, project: project) }
- let(:user) { create(:user) }
-
- let(:request) { double('request', current_user: user) }
+ let(:project) { create(:project) }
+ let(:resource) { create(:issue, project: project) }
+ let(:user) { create(:user) }
+ let(:milestone) { create(:milestone, project: project) }
+ let(:label) { create(:label, project: project, title: 'Test Label') }
+ let(:request) { double('request', current_user: user) }
subject { described_class.new(resource, request: request).as_json }
it 'has basic attributes' do
expect(subject).to include(:id, :iid, :title, :confidential, :due_date, :project_id, :relative_position,
- :project, :labels)
+ :labels, :assignees, project: hash_including(:id, :path))
end
it 'has path and endpoints' do
expect(subject).to include(:reference_path, :real_path, :issue_sidebar_endpoint,
:toggle_subscription_endpoint, :assignable_labels_endpoint)
end
+
+ it 'has milestone attributes' do
+ resource.milestone = milestone
+
+ expect(subject).to include(milestone: hash_including(:id, :title))
+ end
+
+ it 'has assignee attributes' do
+ resource.assignees = [user]
+
+ expect(subject).to include(assignees: array_including(hash_including(:id, :name, :username, :avatar_url)))
+ end
+
+ it 'has label attributes' do
+ resource.labels = [label]
+
+ expect(subject).to include(labels: array_including(hash_including(:id, :title, :color, :description, :text_color, :priority)))
+ end
end
diff --git a/spec/serializers/suggestion_entity_spec.rb b/spec/serializers/suggestion_entity_spec.rb
new file mode 100644
index 00000000000..047571f161c
--- /dev/null
+++ b/spec/serializers/suggestion_entity_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe SuggestionEntity do
+ include RepoHelpers
+
+ let(:user) { create(:user) }
+ let(:request) { double('request', current_user: user) }
+ let(:suggestion) { create(:suggestion) }
+ let(:entity) { described_class.new(suggestion, request: request) }
+
+ subject { entity.as_json }
+
+ it 'exposes correct attributes' do
+ expect(subject).to include(:id, :from_original_line, :to_original_line, :from_line,
+ :to_line, :appliable, :applied, :from_content, :to_content)
+ end
+
+ it 'exposes current user abilities' do
+ expect(subject[:current_user]).to include(:can_apply)
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 8b8021ecbc8..ffa47d527f7 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -840,6 +840,37 @@ describe Ci::CreatePipelineService do
end
end
+ context "when config uses variables for only keyword" do
+ let(:config) do
+ {
+ build: {
+ stage: 'build',
+ script: 'echo',
+ only: {
+ variables: %w($CI)
+ }
+ }
+ }
+ end
+
+ context 'when merge request is specified' do
+ let(:merge_request) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: ref_name,
+ target_project: project,
+ target_branch: 'master')
+ end
+
+ it 'does not create a merge request pipeline' do
+ expect(pipeline).not_to be_persisted
+
+ expect(pipeline.errors[:base])
+ .to eq(['No stages / jobs for this pipeline.'])
+ end
+ end
+ end
+
context "when config has 'except: [tags]'" do
let(:config) do
{
diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb
index ac0a0458f56..1a2dd0b39ee 100644
--- a/spec/services/create_release_service_spec.rb
+++ b/spec/services/create_release_service_spec.rb
@@ -6,6 +6,8 @@ describe CreateReleaseService do
let(:tag_name) { project.repository.tag_names.first }
let(:description) { 'Awesome release!' }
let(:service) { described_class.new(project, user) }
+ let(:tag) { project.repository.find_tag(tag_name) }
+ let(:sha) { tag.dereferenced_target.sha }
it 'creates a new release' do
result = service.execute(tag_name, description)
@@ -13,6 +15,9 @@ describe CreateReleaseService do
release = project.releases.find_by(tag: tag_name)
expect(release).not_to be_nil
expect(release.description).to eq(description)
+ expect(release.name).to eq(tag_name)
+ expect(release.sha).to eq(sha)
+ expect(release.author).to eq(user)
end
it 'raises an error if the tag does not exist' do
diff --git a/spec/services/notes/update_service_spec.rb b/spec/services/notes/update_service_spec.rb
index 533dcdcd6cd..fd9bff46a06 100644
--- a/spec/services/notes/update_service_spec.rb
+++ b/spec/services/notes/update_service_spec.rb
@@ -20,6 +20,29 @@ describe Notes::UpdateService do
@note.reload
end
+ context 'suggestions' do
+ it 'refreshes note suggestions' do
+ markdown = <<-MARKDOWN.strip_heredoc
+ ```suggestion
+ foo
+ ```
+
+ ```suggestion
+ bar
+ ```
+ MARKDOWN
+
+ suggestion = create(:suggestion)
+ note = suggestion.note
+
+ expect { described_class.new(project, user, note: markdown).execute(note) }
+ .to change { note.suggestions.count }.from(1).to(2)
+
+ expect(note.suggestions.order(:relative_order).map(&:to_content))
+ .to eq([" foo\n", " bar\n"])
+ end
+ end
+
context 'todos' do
let!(:todo) { create(:todo, :assigned, user: user, project: project, target: issue, author: user2) }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 0f6c2604984..68ac3a00ab0 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -2167,6 +2167,39 @@ describe NotificationService, :mailer do
end
end
+ context 'Remote mirror notifications' do
+ describe '#remote_mirror_update_failed' do
+ let(:project) { create(:project) }
+ let(:remote_mirror) { create(:remote_mirror, project: project) }
+ let(:u_blocked) { create(:user, :blocked) }
+ let(:u_silence) { create_user_with_notification(:disabled, 'silent-maintainer', project) }
+ let(:u_owner) { project.owner }
+ let(:u_maintainer1) { create(:user) }
+ let(:u_maintainer2) { create(:user) }
+ let(:u_developer) { create(:user) }
+
+ before do
+ project.add_maintainer(u_blocked)
+ project.add_maintainer(u_silence)
+ project.add_maintainer(u_maintainer1)
+ project.add_maintainer(u_maintainer2)
+ project.add_developer(u_developer)
+
+ # Mock remote update
+ allow(project.repository).to receive(:async_remove_remote)
+ allow(project.repository).to receive(:add_remote)
+
+ reset_delivered_emails!
+ end
+
+ it 'emails current watching maintainers' do
+ notification.remote_mirror_update_failed(remote_mirror)
+
+ should_only_email(u_maintainer1, u_maintainer2, u_owner)
+ end
+ end
+ end
+
def build_team(project)
@u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create_global_setting_for(create(:user), :participating)
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index b69977c812a..458cb8f1f31 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -19,6 +19,31 @@ describe PreviewMarkdownService do
end
end
+ describe 'suggestions' do
+ let(:params) { { text: "```suggestion\nfoo\n```", preview_suggestions: preview_suggestions } }
+ let(:service) { described_class.new(project, user, params) }
+
+ context 'when preview markdown param is present' do
+ let(:preview_suggestions) { true }
+
+ it 'returns users referenced in text' do
+ result = service.execute
+
+ expect(result[:suggestions]).to eq(['foo'])
+ end
+ end
+
+ context 'when preview markdown param is not present' do
+ let(:preview_suggestions) { false }
+
+ it 'returns users referenced in text' do
+ result = service.execute
+
+ expect(result[:suggestions]).to eq([])
+ end
+ end
+ end
+
context 'new note with quick actions' do
let(:issue) { create(:issue, project: project) }
let(:params) do
diff --git a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
index 6af5bfc7689..d7d7f1874eb 100644
--- a/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
+++ b/spec/services/projects/lfs_pointers/lfs_download_service_spec.rb
@@ -54,6 +54,18 @@ describe Projects::LfsPointers::LfsDownloadService do
end
end
+ context 'when a bad URL is used' do
+ where(download_link: ['/etc/passwd', 'ftp://example.com', 'http://127.0.0.2'])
+
+ with_them do
+ it 'does not download the file' do
+ expect(subject).not_to receive(:download_and_save_file)
+
+ expect { subject.execute(oid, download_link) }.not_to change { LfsObject.count }
+ end
+ end
+ end
+
context 'when an lfs object with the same oid already exists' do
before do
create(:lfs_object, oid: 'oid')
diff --git a/spec/services/suggestions/apply_service_spec.rb b/spec/services/suggestions/apply_service_spec.rb
new file mode 100644
index 00000000000..3a483717756
--- /dev/null
+++ b/spec/services/suggestions/apply_service_spec.rb
@@ -0,0 +1,229 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Suggestions::ApplyService do
+ include ProjectForksHelper
+
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user, :commit_email) }
+
+ let(:position) do
+ Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 9,
+ diff_refs: merge_request.diff_refs)
+ end
+
+ let(:suggestion) do
+ create(:suggestion, note: diff_note,
+ from_content: " raise RuntimeError, \"System commands must be given as an array of strings\"\n",
+ to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
+ end
+
+ subject { described_class.new(user) }
+
+ context 'patch is appliable' do
+ let(:expected_content) do
+ <<-CONTENT.strip_heredoc
+ require 'fileutils'
+ require 'open3'
+
+ module Popen
+ extend self
+
+ def popen(cmd, path=nil)
+ unless cmd.is_a?(Array)
+ raise RuntimeError, 'Explosion'
+ # explosion?
+ end
+
+ path ||= Dir.pwd
+
+ vars = {
+ "PWD" => path
+ }
+
+ options = {
+ chdir: path
+ }
+
+ unless File.directory?(path)
+ FileUtils.mkdir_p(path)
+ end
+
+ @cmd_output = ""
+ @cmd_status = 0
+
+ Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
+ @cmd_output << stdout.read
+ @cmd_output << stderr.read
+ @cmd_status = wait_thr.value.exitstatus
+ end
+
+ return @cmd_output, @cmd_status
+ end
+ end
+ CONTENT
+ end
+
+ context 'non-fork project' do
+ let(:merge_request) do
+ create(:merge_request, source_project: project,
+ target_project: project)
+ end
+
+ let!(:diff_note) do
+ create(:diff_note_on_merge_request, noteable: merge_request,
+ position: position,
+ project: project)
+ end
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'updates the file with the new contents' do
+ subject.execute(suggestion)
+
+ blob = project.repository.blob_at_branch(merge_request.source_branch,
+ position.new_path)
+
+ expect(blob.data).to eq(expected_content)
+ end
+
+ it 'returns success status' do
+ result = subject.execute(suggestion)
+
+ expect(result[:status]).to eq(:success)
+ end
+
+ it 'updates suggestion applied and commit_id columns' do
+ expect { subject.execute(suggestion) }
+ .to change(suggestion, :applied)
+ .from(false).to(true)
+ .and change(suggestion, :commit_id)
+ .from(nil)
+ end
+
+ it 'created commit has users email and name' do
+ subject.execute(suggestion)
+
+ commit = project.repository.commit
+
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ expect(commit.author_name).to eq(user.name)
+ end
+ end
+
+ context 'fork-project' do
+ let(:project) { create(:project, :public, :repository) }
+
+ let(:forked_project) do
+ fork_project_with_submodules(project, user)
+ end
+
+ let(:merge_request) do
+ create(:merge_request,
+ source_branch: 'conflict-resolvable-fork', source_project: forked_project,
+ target_branch: 'conflict-start', target_project: project)
+ end
+
+ let!(:diff_note) do
+ create(:diff_note_on_merge_request, noteable: merge_request, position: position, project: project)
+ end
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'updates file in the source project' do
+ expect(Files::UpdateService).to receive(:new)
+ .with(merge_request.source_project, user, anything)
+ .and_call_original
+
+ subject.execute(suggestion)
+ end
+ end
+ end
+
+ context 'no permission' do
+ let(:merge_request) do
+ create(:merge_request, source_project: project,
+ target_project: project)
+ end
+
+ let(:diff_note) do
+ create(:diff_note_on_merge_request, noteable: merge_request,
+ position: position,
+ project: project)
+ end
+
+ context 'user cannot write in project repo' do
+ before do
+ project.add_reporter(user)
+ end
+
+ it 'returns error' do
+ result = subject.execute(suggestion)
+
+ expect(result).to eq(message: "You are not allowed to push into this branch",
+ status: :error)
+ end
+ end
+ end
+
+ context 'patch is not appliable' do
+ let(:merge_request) do
+ create(:merge_request, source_project: project,
+ target_project: project)
+ end
+
+ let(:diff_note) do
+ create(:diff_note_on_merge_request, noteable: merge_request,
+ position: position,
+ project: project)
+ end
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'suggestion was already applied' do
+ it 'returns success status' do
+ result = subject.execute(suggestion)
+
+ expect(result[:status]).to eq(:success)
+ end
+ end
+
+ context 'note is outdated' do
+ before do
+ allow(diff_note).to receive(:active?) { false }
+ end
+
+ it 'returns error message' do
+ result = subject.execute(suggestion)
+
+ expect(result).to eq(message: 'Suggestion is not appliable',
+ status: :error)
+ end
+ end
+
+ context 'suggestion was already applied' do
+ before do
+ suggestion.update!(applied: true, commit_id: 'sha')
+ end
+
+ it 'returns error message' do
+ result = subject.execute(suggestion)
+
+ expect(result).to eq(message: 'Suggestion is not appliable',
+ status: :error)
+ end
+ end
+ end
+end
diff --git a/spec/services/suggestions/create_service_spec.rb b/spec/services/suggestions/create_service_spec.rb
new file mode 100644
index 00000000000..f1142c88a69
--- /dev/null
+++ b/spec/services/suggestions/create_service_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Suggestions::CreateService do
+ let(:project_with_repo) { create(:project, :repository) }
+ let(:merge_request) do
+ create(:merge_request, source_project: project_with_repo,
+ target_project: project_with_repo)
+ end
+
+ let(:position) do
+ Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ diff_refs: merge_request.diff_refs)
+ end
+
+ let(:markdown) do
+ <<-MARKDOWN.strip_heredoc
+ ```suggestion
+ foo
+ bar
+ ```
+
+ ```
+ nothing
+ ```
+
+ ```suggestion
+ xpto
+ baz
+ ```
+
+ ```thing
+ this is not a suggestion, it's a thing
+ ```
+ MARKDOWN
+ end
+
+ subject { described_class.new(note) }
+
+ describe '#execute' do
+ context 'should not try to parse suggestions' do
+ context 'when not a diff note for merge requests' do
+ let(:note) do
+ create(:diff_note_on_commit, project: project_with_repo,
+ note: markdown)
+ end
+
+ it 'does not try to parse suggestions' do
+ expect(Banzai::SuggestionsParser).not_to receive(:parse)
+
+ subject.execute
+ end
+ end
+
+ context 'when diff note is not for text' do
+ let(:note) do
+ create(:diff_note_on_merge_request, project: project_with_repo,
+ noteable: merge_request,
+ position: position,
+ note: markdown)
+ end
+
+ it 'does not try to parse suggestions' do
+ allow(note).to receive(:on_text?) { false }
+
+ expect(Banzai::SuggestionsParser).not_to receive(:parse)
+
+ subject.execute
+ end
+ end
+ end
+
+ context 'should create suggestions' do
+ let(:note) do
+ create(:diff_note_on_merge_request, project: project_with_repo,
+ noteable: merge_request,
+ position: position,
+ note: markdown)
+ end
+
+ context 'single line suggestions' do
+ it 'persists suggestion records' do
+ expect { subject.execute }
+ .to change { note.suggestions.count }
+ .from(0)
+ .to(2)
+ end
+
+ it 'persists original from_content lines and suggested lines' do
+ subject.execute
+
+ suggestions = note.suggestions.order(:relative_order)
+
+ suggestion_1 = suggestions.first
+ suggestion_2 = suggestions.last
+
+ expect(suggestion_1).to have_attributes(from_content: " vars = {\n",
+ to_content: " foo\n bar\n")
+
+ expect(suggestion_2).to have_attributes(from_content: " vars = {\n",
+ to_content: " xpto\n baz\n")
+ end
+ end
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 3fedb9ed48c..fb3421b61d3 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -115,7 +115,7 @@ RSpec.configure do |config|
TestEnv.clean_test_path
end
- config.before(:example) do
+ config.before do
# Enable all features by default for testing
allow(Feature).to receive(:enabled?) { true }
@@ -136,11 +136,11 @@ RSpec.configure do |config|
RequestStore.clear!
end
- config.after(:example) do
+ config.after do
Fog.unmock! if Fog.mock?
end
- config.after(:example) do
+ config.after do
Gitlab::CurrentSettings.clear_in_memory_application_settings!
end
@@ -235,10 +235,6 @@ RSpec.configure do |config|
example.run if Gitlab::Database.mysql?
end
- config.around(:each, :rails5) do |example|
- example.run if Gitlab.rails5?
- end
-
# This makes sure the `ApplicationController#can?` method is stubbed with the
# original implementation for all view specs.
config.before(:each, type: :view) do
diff --git a/spec/support/carrierwave.rb b/spec/support/carrierwave.rb
index b4b016e408f..b376822d530 100644
--- a/spec/support/carrierwave.rb
+++ b/spec/support/carrierwave.rb
@@ -1,7 +1,7 @@
CarrierWave.root = File.expand_path('tmp/tests/public', Rails.root)
RSpec.configure do |config|
- config.after(:each) do
+ config.after do
FileUtils.rm_rf(CarrierWave.root)
end
end
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 5edc5de2a09..34b9efaaecd 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -23,7 +23,7 @@ RSpec.configure do |config|
DatabaseCleaner.clean_with(:deletion, cache_tables: false)
end
- config.before(:each) do
+ config.before do
DatabaseCleaner.strategy = :transaction
end
@@ -39,11 +39,11 @@ RSpec.configure do |config|
DatabaseCleaner.strategy = :deletion, { cache_tables: false }
end
- config.before(:each) do
+ config.before do
DatabaseCleaner.start
end
- config.append_after(:each) do
+ config.append_after do
DatabaseCleaner.clean
end
end
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index 922f3df144d..42a086d58d2 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -178,6 +178,16 @@ shared_examples 'discussion comments' do |resource_name|
let(:note_id) { find("#{comments_selector} .note:first-child", match: :first)['data-note-id'] }
let(:reply_id) { find("#{comments_selector} .note:last-child", match: :first)['data-note-id'] }
+ it 'can be replied to after resolving' do
+ click_button "Resolve discussion"
+ wait_for_requests
+
+ refresh
+ wait_for_requests
+
+ submit_reply('to reply or not reply')
+ end
+
it 'shows resolved discussion when toggled' do
submit_reply('a')
diff --git a/spec/support/helpers/email_helpers.rb b/spec/support/helpers/email_helpers.rb
index 1fb8252459f..ad6e1064499 100644
--- a/spec/support/helpers/email_helpers.rb
+++ b/spec/support/helpers/email_helpers.rb
@@ -34,4 +34,13 @@ module EmailHelpers
def find_email_for(user)
ActionMailer::Base.deliveries.find { |d| d.to.include?(user.notification_email) }
end
+
+ def have_referable_subject(referable, include_project: true, reply: false)
+ prefix = (include_project && referable.project ? "#{referable.project.name} | " : '').freeze
+ prefix = "Re: #{prefix}" if reply
+
+ suffix = "#{referable.title} (#{referable.to_reference})"
+
+ have_subject [prefix, suffix].compact.join
+ end
end
diff --git a/spec/support/helpers/fake_migration_classes.rb b/spec/support/helpers/fake_migration_classes.rb
index b0fc8422857..c7766df7a52 100644
--- a/spec/support/helpers/fake_migration_classes.rb
+++ b/spec/support/helpers/fake_migration_classes.rb
@@ -1,4 +1,4 @@
-class FakeRenameReservedPathMigrationV1 < ActiveRecord::Migration
+class FakeRenameReservedPathMigrationV1 < ActiveRecord::Migration[4.2]
include Gitlab::Database::RenameReservedPathsMigration::V1
def version
diff --git a/spec/support/helpers/test_request_helpers.rb b/spec/support/helpers/test_request_helpers.rb
index 187a0e07891..5a84d67bdfc 100644
--- a/spec/support/helpers/test_request_helpers.rb
+++ b/spec/support/helpers/test_request_helpers.rb
@@ -2,10 +2,6 @@
module TestRequestHelpers
def test_request(remote_ip: '127.0.0.1')
- if Gitlab.rails5?
- ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new)
- else
- ActionController::TestRequest.new(remote_ip: remote_ip)
- end
+ ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new)
end
end
diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
index 1478c6b5a47..62ae95df8c0 100644
--- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
+++ b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
@@ -86,7 +86,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user
end
it "blocks the user before #{record_class_name} migration begins" do
- expect(service).to receive("migrate_#{record_class_name.parameterize('_').pluralize}".to_sym) do
+ expect(service).to receive("migrate_#{record_class_name.parameterize(separator: '_').pluralize}".to_sym) do
expect(user.reload).to be_blocked
end
diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb
index 2e7c88bfc09..1d2a4856724 100644
--- a/spec/support/setup_builds_storage.rb
+++ b/spec/support/setup_builds_storage.rb
@@ -11,7 +11,7 @@ RSpec.configure do |config|
FileUtils.mkdir_p(builds_path)
end
- config.before(:each) do
+ config.before do
FileUtils.rm_rf(builds_path)
FileUtils.mkdir_p(builds_path)
end
diff --git a/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb b/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb
new file mode 100644
index 00000000000..b34948be670
--- /dev/null
+++ b/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb
@@ -0,0 +1,32 @@
+shared_examples 'set sort order from user preference' do
+ describe '#set_sort_order_from_user_preference' do
+ # There is no issuable_sorting_field defined in any CE controllers yet,
+ # however any other field present in user_preferences table can be used for testing.
+ let(:sorting_field) { :issue_notes_filter }
+ let(:sorting_param) { 'any' }
+
+ before do
+ allow(controller).to receive(:issuable_sorting_field).and_return(sorting_field)
+ end
+
+ context 'when database is in read-only mode' do
+ it 'it does not update user preference' do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+
+ expect_any_instance_of(UserPreference).not_to receive(:update_attribute).with(sorting_field, sorting_param)
+
+ get :index, namespace_id: project.namespace, project_id: project, sort: sorting_param
+ end
+ end
+
+ context 'when database is not in read-only mode' do
+ it 'updates user preference' do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(false)
+
+ expect_any_instance_of(UserPreference).to receive(:update_attribute).with(sorting_field, sorting_param)
+
+ get :index, namespace_id: project.namespace, project_id: project, sort: sorting_param
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb
index 66536e80db2..a38354060cf 100644
--- a/spec/support/shared_examples/notify_shared_examples.rb
+++ b/spec/support/shared_examples/notify_shared_examples.rb
@@ -1,5 +1,5 @@
shared_context 'gitlab email notification' do
- set(:project) { create(:project, :repository) }
+ set(:project) { create(:project, :repository, name: 'a-known-name') }
set(:recipient) { create(:user, email: 'recipient@example.com') }
let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name }
@@ -62,9 +62,11 @@ end
shared_examples 'an email with X-GitLab headers containing project details' do
it 'has X-GitLab-Project headers' do
aggregate_failures do
+ full_path_as_domain = "#{project.name}.#{project.namespace.path}"
is_expected.to have_header('X-GitLab-Project', /#{project.name}/)
is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/)
is_expected.to have_header('X-GitLab-Project-Path', /#{project.full_path}/)
+ is_expected.to have_header('List-Id', "#{project.full_path} <#{project.id}.#{full_path_as_domain}.#{Gitlab.config.gitlab.host}>")
end
end
end
diff --git a/spec/support/shared_examples/only_except_policy_examples.rb b/spec/support/shared_examples/only_except_policy_examples.rb
deleted file mode 100644
index 35240af1d74..00000000000
--- a/spec/support/shared_examples/only_except_policy_examples.rb
+++ /dev/null
@@ -1,167 +0,0 @@
-# frozen_string_literal: true
-
-shared_examples 'correct only except policy' do
- context 'when using simplified policy' do
- describe 'validations' do
- context 'when entry config value is valid' do
- context 'when config is a branch or tag name' do
- let(:config) { %w[master feature/branch] }
-
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- end
- end
-
- describe '#value' do
- it 'returns refs hash' do
- expect(entry.value).to eq(refs: config)
- end
- end
- end
-
- context 'when config is a regexp' do
- let(:config) { ['/^issue-.*$/'] }
-
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- end
- end
- end
-
- context 'when config is a special keyword' do
- let(:config) { %w[tags triggers branches] }
-
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- end
- end
- end
- end
-
- context 'when entry value is not valid' do
- let(:config) { [1] }
-
- describe '#errors' do
- it 'saves errors' do
- expect(entry.errors)
- .to include /policy config should be an array of strings or regexps/
- end
- end
- end
- end
- end
-
- context 'when using complex policy' do
- context 'when specifying refs policy' do
- let(:config) { { refs: ['master'] } }
-
- it 'is a correct configuraton' do
- expect(entry).to be_valid
- expect(entry.value).to eq(refs: %w[master])
- end
- end
-
- context 'when specifying kubernetes policy' do
- let(:config) { { kubernetes: 'active' } }
-
- it 'is a correct configuraton' do
- expect(entry).to be_valid
- expect(entry.value).to eq(kubernetes: 'active')
- end
- end
-
- context 'when specifying invalid kubernetes policy' do
- let(:config) { { kubernetes: 'something' } }
-
- it 'reports an error about invalid policy' do
- expect(entry.errors).to include /unknown value: something/
- end
- end
-
- context 'when specifying valid variables expressions policy' do
- let(:config) { { variables: ['$VAR == null'] } }
-
- it 'is a correct configuraton' do
- expect(entry).to be_valid
- expect(entry.value).to eq(config)
- end
- end
-
- context 'when specifying variables expressions in invalid format' do
- let(:config) { { variables: '$MY_VAR' } }
-
- it 'reports an error about invalid format' do
- expect(entry.errors).to include /should be an array of strings/
- end
- end
-
- context 'when specifying invalid variables expressions statement' do
- let(:config) { { variables: ['$MY_VAR =='] } }
-
- it 'reports an error about invalid statement' do
- expect(entry.errors).to include /invalid expression syntax/
- end
- end
-
- context 'when specifying invalid variables expressions token' do
- let(:config) { { variables: ['$MY_VAR == 123'] } }
-
- it 'reports an error about invalid expression' do
- expect(entry.errors).to include /invalid expression syntax/
- end
- end
-
- context 'when using invalid variables expressions regexp' do
- let(:config) { { variables: ['$MY_VAR =~ /some ( thing/'] } }
-
- it 'reports an error about invalid expression' do
- expect(entry.errors).to include /invalid expression syntax/
- end
- end
-
- context 'when specifying a valid changes policy' do
- let(:config) { { changes: %w[some/* paths/**/*.rb] } }
-
- it 'is a correct configuraton' do
- expect(entry).to be_valid
- expect(entry.value).to eq(config)
- end
- end
-
- context 'when changes policy is invalid' do
- let(:config) { { changes: [1, 2] } }
-
- it 'returns errors' do
- expect(entry.errors).to include /changes should be an array of strings/
- end
- end
-
- context 'when specifying unknown policy' do
- let(:config) { { refs: ['master'], invalid: :something } }
-
- it 'returns error about invalid key' do
- expect(entry.errors).to include /unknown keys: invalid/
- end
- end
-
- context 'when policy is empty' do
- let(:config) { {} }
-
- it 'is not a valid configuration' do
- expect(entry.errors).to include /can't be blank/
- end
- end
- end
-
- context 'when policy strategy does not match' do
- let(:config) { 'string strategy' }
-
- it 'returns information about errors' do
- expect(entry.errors)
- .to include /has to be either an array of conditions or a hash/
- end
- end
-end
diff --git a/spec/support/shared_examples/serializers/diff_file_entity_examples.rb b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb
index b8065886c42..1770308f789 100644
--- a/spec/support/shared_examples/serializers/diff_file_entity_examples.rb
+++ b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb
@@ -32,7 +32,7 @@ shared_examples 'diff file entity' do
it 'exposes correct attributes' do
expect(subject).to include(:too_large, :added_lines, :removed_lines,
:context_lines_path, :highlighted_diff_lines,
- :parallel_diff_lines)
+ :parallel_diff_lines, :empty)
end
it 'includes viewer' do
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 8c4360d4cf0..3b8f7f5fe7d 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -74,6 +74,7 @@ describe 'gitlab:app namespace rake task' do
it 'invokes restoration on match' do
allow(YAML).to receive(:load_file)
.and_return({ gitlab_version: gitlab_version })
+
expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
expect(Rake::Task['gitlab:backup:db:restore']).to receive(:invoke)
expect(Rake::Task['gitlab:backup:repo:restore']).to receive(:invoke)
diff --git a/spec/workers/repository_update_remote_mirror_worker_spec.rb b/spec/workers/repository_update_remote_mirror_worker_spec.rb
index 4f1ad2474f5..d73b0b53713 100644
--- a/spec/workers/repository_update_remote_mirror_worker_spec.rb
+++ b/spec/workers/repository_update_remote_mirror_worker_spec.rb
@@ -25,12 +25,19 @@ describe RepositoryUpdateRemoteMirrorWorker do
it 'sets status as failed when update remote mirror service executes with errors' do
error_message = 'fail!'
- expect_any_instance_of(Projects::UpdateRemoteMirrorService).to receive(:execute).with(remote_mirror).and_return(status: :error, message: error_message)
+ expect_next_instance_of(Projects::UpdateRemoteMirrorService) do |service|
+ expect(service).to receive(:execute).with(remote_mirror).and_return(status: :error, message: error_message)
+ end
+
+ # Mock the finder so that it returns an object we can set expectations on
+ expect_next_instance_of(RemoteMirrorFinder) do |finder|
+ expect(finder).to receive(:execute).and_return(remote_mirror)
+ end
+ expect(remote_mirror).to receive(:mark_as_failed).with(error_message)
+
expect do
subject.perform(remote_mirror.id, Time.now)
end.to raise_error(RepositoryUpdateRemoteMirrorWorker::UpdateError, error_message)
-
- expect(remote_mirror.reload.update_status).to eq('failed')
end
it 'does nothing if last_update_started_at is higher than the time the job was scheduled in' do