summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-05-30 10:33:43 +0100
committerPhil Hughes <me@iamphill.com>2018-05-30 10:33:43 +0100
commitc760965079fce0f020980987794ea61c86eb09ff (patch)
tree32bbac523c665a785eaaab85d9be305c02d8ce68 /spec
parent323e402e21a643da22714bdede3cc6e248ba2cf4 (diff)
parentfd79df64c5411308e67a62b4c02a07f5317ddec1 (diff)
downloadgitlab-ce-c760965079fce0f020980987794ea61c86eb09ff.tar.gz
Merge branch 'master' into ide-list-merge-requests
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb2
-rw-r--r--spec/controllers/profiles_controller_spec.rb13
-rw-r--r--spec/controllers/projects/boards_controller_spec.rb66
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb18
-rw-r--r--spec/controllers/projects/clusters/gcp_controller_spec.rb34
-rw-r--r--spec/controllers/projects/clusters_controller_spec.rb9
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb7
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb12
-rw-r--r--spec/controllers/projects/imports_controller_spec.rb18
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb23
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb2
-rw-r--r--spec/factories/application_settings.rb1
-rw-r--r--spec/features/admin/admin_abuse_reports_spec.rb2
-rw-r--r--spec/features/admin/admin_projects_spec.rb2
-rw-r--r--spec/features/admin/admin_requests_profiles_spec.rb4
-rw-r--r--spec/features/admin/admin_runners_spec.rb6
-rw-r--r--spec/features/admin/admin_users_spec.rb6
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb32
-rw-r--r--spec/features/boards/boards_spec.rb52
-rw-r--r--spec/features/boards/issue_ordering_spec.rb34
-rw-r--r--spec/features/boards/modal_filter_spec.rb22
-rw-r--r--spec/features/boards/new_issue_spec.rb2
-rw-r--r--spec/features/boards/sidebar_spec.rb26
-rw-r--r--spec/features/boards/sub_group_project_spec.rb2
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb2
-rw-r--r--spec/features/explore/new_menu_spec.rb6
-rw-r--r--spec/features/groups/group_settings_spec.rb21
-rw-r--r--spec/features/groups/members/search_members_spec.rb2
-rw-r--r--spec/features/groups/settings/group_badges_spec.rb16
-rw-r--r--spec/features/groups/share_lock_spec.rb31
-rw-r--r--spec/features/groups/user_browse_projects_group_page_spec.rb2
-rw-r--r--spec/features/groups_spec.rb6
-rw-r--r--spec/features/issues/filtered_search/filter_issues_spec.rb2
-rw-r--r--spec/features/issues/user_creates_branch_and_merge_request_spec.rb4
-rw-r--r--spec/features/labels_hierarchy_spec.rb24
-rw-r--r--spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb36
-rw-r--r--spec/features/merge_requests/user_squashes_merge_request_spec.rb124
-rw-r--r--spec/features/milestone_spec.rb2
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb191
-rw-r--r--spec/features/projects/issues/user_sorts_issues_spec.rb2
-rw-r--r--spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb4
-rw-r--r--spec/features/projects/merge_requests/user_views_diffs_spec.rb4
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb4
-rw-r--r--spec/features/projects/settings/project_badges_spec.rb16
-rw-r--r--spec/features/projects/user_sees_sidebar_spec.rb12
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb1
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb1
-rw-r--r--spec/features/projects/wiki/user_updates_wiki_page_spec.rb1
-rw-r--r--spec/features/projects/wiki/user_views_wiki_empty_spec.rb75
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb2
-rw-r--r--spec/features/protected_branches_spec.rb2
-rw-r--r--spec/features/signed_commits_spec.rb61
-rw-r--r--spec/features/triggers_spec.rb2
-rw-r--r--spec/features/uploads/user_uploads_avatar_to_group_spec.rb4
-rw-r--r--spec/features/users/login_spec.rb2
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb16
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v3/merge_requests.json5
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/merge_requests.json3
-rw-r--r--spec/helpers/issuables_helper_spec.rb8
-rw-r--r--spec/javascripts/badges/components/badge_list_spec.js2
-rw-r--r--spec/javascripts/badges/components/badge_settings_spec.js4
-rw-r--r--spec/javascripts/behaviors/secret_values_spec.js2
-rw-r--r--spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js6
-rw-r--r--spec/javascripts/blob/sketch/index_spec.js2
-rw-r--r--spec/javascripts/boards/board_list_spec.js4
-rw-r--r--spec/javascripts/boards/issue_card_spec.js40
-rw-r--r--spec/javascripts/fixtures/event_filter.html.haml2
-rw-r--r--spec/javascripts/fixtures/issue_sidebar_label.html.haml2
-rw-r--r--spec/javascripts/fixtures/linked_tabs.html.haml10
-rw-r--r--spec/javascripts/gl_dropdown_spec.js12
-rw-r--r--spec/javascripts/notes_spec.js4
-rw-r--r--spec/javascripts/pipelines/empty_state_spec.js2
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js103
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js92
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js88
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/helpers.js49
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/mock_data.js75
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/stores/actions_spec.js131
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/stores/getters_spec.js65
-rw-r--r--spec/javascripts/projects/gke_cluster_dropdowns/stores/mutations_spec.js87
-rw-r--r--spec/javascripts/projects_dropdown/components/search_spec.js1
-rw-r--r--spec/javascripts/sidebar/sidebar_move_issue_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js69
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js (renamed from spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_hidden_input_spec.js)8
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js52
-rw-r--r--spec/javascripts/vue_shared/components/dropdown/mock_data.js11
-rw-r--r--spec/lib/backup/repository_spec.rb34
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb2
-rw-r--r--spec/lib/gitlab/auth/user_access_denied_reason_spec.rb3
-rw-r--r--spec/lib/gitlab/database/count_spec.rb81
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb54
-rw-r--r--spec/lib/gitlab/git_access_spec.rb2
-rw-r--r--spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb35
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/attribute_cleaner_spec.rb29
-rw-r--r--spec/lib/gitlab/import_export/project.json2
-rw-r--r--spec/lib/gitlab/import_export/project.light.json2
-rw-r--r--spec/lib/gitlab/import_export/project_tree_restorer_spec.rb9
-rw-r--r--spec/lib/gitlab/import_export/relation_factory_spec.rb12
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab_spec.rb60
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb24
-rw-r--r--spec/migrations/fill_file_store_spec.rb43
-rw-r--r--spec/models/application_setting_spec.rb126
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/concerns/batch_destroy_dependent_associations_spec.rb60
-rw-r--r--spec/models/concerns/discussion_on_diff_spec.rb2
-rw-r--r--spec/models/diff_note_spec.rb62
-rw-r--r--spec/models/event_spec.rb13
-rw-r--r--spec/models/internal_id_spec.rb37
-rw-r--r--spec/models/merge_request_spec.rb59
-rw-r--r--spec/models/note_diff_file_spec.rb11
-rw-r--r--spec/requests/api/deploy_keys_spec.rb40
-rw-r--r--spec/requests/api/helpers_spec.rb2
-rw-r--r--spec/requests/api/internal_spec.rb12
-rw-r--r--spec/requests/api/jobs_spec.rb8
-rw-r--r--spec/requests/api/merge_requests_spec.rb20
-rw-r--r--spec/requests/api/runner_spec.rb17
-rw-r--r--spec/requests/api/settings_spec.rb26
-rw-r--r--spec/requests/api/v3/merge_requests_spec.rb20
-rw-r--r--spec/requests/api/version_spec.rb2
-rw-r--r--spec/requests/lfs_http_spec.rb2
-rw-r--r--spec/rubocop/cop/line_break_around_conditional_block_spec.rb12
-rw-r--r--spec/services/application_settings/update_service_spec.rb88
-rw-r--r--spec/services/check_gcp_project_billing_service_spec.rb32
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb60
-rw-r--r--spec/services/merge_requests/squash_service_spec.rb199
-rw-r--r--spec/services/notes/create_service_spec.rb82
-rw-r--r--spec/support/api/milestones_shared_examples.rb6
-rw-r--r--spec/support/helpers/board_helpers.rb2
-rw-r--r--spec/support/helpers/features/sorting_helpers.rb2
-rw-r--r--spec/support/helpers/mobile_helpers.rb4
-rw-r--r--spec/support/helpers/sorting_helper.rb2
-rw-r--r--spec/support/helpers/test_env.rb1
-rw-r--r--spec/support/shared_examples/features/protected_branches_access_control_ce.rb4
-rw-r--r--spec/uploaders/object_storage_spec.rb2
-rw-r--r--spec/views/admin/dashboard/index.html.haml_spec.rb7
-rw-r--r--spec/views/help/index.html.haml_spec.rb2
-rw-r--r--spec/workers/check_gcp_project_billing_worker_spec.rb116
-rw-r--r--spec/workers/create_note_diff_file_worker_spec.rb16
142 files changed, 2727 insertions, 846 deletions
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index 4770e187db6..dcb0faffbd4 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -17,7 +17,7 @@ describe Boards::IssuesController do
project.add_guest(guest)
end
- describe 'GET index' do
+ describe 'GET index', :request_store do
let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
context 'with invalid board id' do
diff --git a/spec/controllers/profiles_controller_spec.rb b/spec/controllers/profiles_controller_spec.rb
index c621eb69171..4530a301d4d 100644
--- a/spec/controllers/profiles_controller_spec.rb
+++ b/spec/controllers/profiles_controller_spec.rb
@@ -3,6 +3,19 @@ require('spec_helper')
describe ProfilesController, :request_store do
let(:user) { create(:user) }
+ describe 'POST update' do
+ it 'does not update password' do
+ sign_in(user)
+
+ expect do
+ post :update,
+ user: { password: 'hello12345', password_confirmation: 'hello12345' }
+ end.not_to change { user.reload.encrypted_password }
+
+ expect(response.status).to eq(302)
+ end
+ end
+
describe 'PUT update' do
it 'allows an email update from a user without an external email address' do
sign_in(user)
diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb
index 4d765229bde..509f19ed030 100644
--- a/spec/controllers/projects/boards_controller_spec.rb
+++ b/spec/controllers/projects/boards_controller_spec.rb
@@ -27,6 +27,20 @@ describe Projects::BoardsController do
expect(response).to render_template :index
expect(response.content_type).to eq 'text/html'
end
+
+ context 'with unauthorized user' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
+ allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
+ end
+
+ it 'returns a not found 404 response' do
+ list_boards
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(response.content_type).to eq 'text/html'
+ end
+ end
end
context 'when format is JSON' do
@@ -40,18 +54,19 @@ describe Projects::BoardsController do
expect(response).to match_response_schema('boards')
expect(parsed_response.length).to eq 2
end
- end
- context 'with unauthorized user' do
- before do
- allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
- allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
- end
+ context 'with unauthorized user' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
+ allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
+ end
- it 'returns a not found 404 response' do
- list_boards
+ it 'returns a not found 404 response' do
+ list_boards format: :json
- expect(response).to have_gitlab_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
+ expect(response.content_type).to eq 'application/json'
+ end
end
end
@@ -88,6 +103,20 @@ describe Projects::BoardsController do
expect(response).to render_template :show
expect(response.content_type).to eq 'text/html'
end
+
+ context 'with unauthorized user' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
+ allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
+ end
+
+ it 'returns a not found 404 response' do
+ read_board board: board
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(response.content_type).to eq 'text/html'
+ end
+ end
end
context 'when format is JSON' do
@@ -96,18 +125,19 @@ describe Projects::BoardsController do
expect(response).to match_response_schema('board')
end
- end
- context 'with unauthorized user' do
- before do
- allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
- allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
- end
+ context 'with unauthorized user' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
+ allow(Ability).to receive(:allowed?).with(user, :read_board, project).and_return(false)
+ end
- it 'returns a not found 404 response' do
- read_board board: board
+ it 'returns a not found 404 response' do
+ read_board board: board, format: :json
- expect(response).to have_gitlab_http_status(404)
+ expect(response).to have_gitlab_http_status(404)
+ expect(response.content_type).to eq 'application/json'
+ end
end
end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 16fb377b002..4860ea5dcce 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -146,6 +146,24 @@ describe Projects::BranchesController do
it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
end
+
+ it 'redirects to autodeploy setup page' do
+ result = { status: :success, branch: double(name: branch) }
+
+ create(:cluster, :provided_by_gcp, projects: [project])
+
+ expect_any_instance_of(CreateBranchService).to receive(:execute).and_return(result)
+ expect(SystemNoteService).to receive(:new_issue_branch).and_return(true)
+
+ post :create,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ branch_name: branch,
+ issue_iid: issue.iid
+
+ expect(response.location).to include(project_new_blob_path(project, branch))
+ expect(response).to have_gitlab_http_status(302)
+ end
end
context 'when create branch service fails' do
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb
index 715bb9f5e52..271ba37aed4 100644
--- a/spec/controllers/projects/clusters/gcp_controller_spec.rb
+++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb
@@ -77,8 +77,6 @@ describe Projects::Clusters::GcpController do
end
it 'has new object' do
- expect(controller).to receive(:authorize_google_project_billing)
-
go
expect(assigns(:cluster)).to be_an_instance_of(Clusters::Cluster)
@@ -137,33 +135,15 @@ describe Projects::Clusters::GcpController do
context 'when access token is valid' do
before do
stub_google_api_validate_token
- allow_any_instance_of(described_class).to receive(:authorize_google_project_billing)
- end
-
- context 'when google project billing is enabled' do
- before do
- redis_double = double.as_null_object
- allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double)
- allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true')
- end
-
- it 'creates a new cluster' do
- expect(ClusterProvisionWorker).to receive(:perform_async)
- expect { go }.to change { Clusters::Cluster.count }
- .and change { Clusters::Providers::Gcp.count }
- expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
- expect(project.clusters.first).to be_gcp
- expect(project.clusters.first).to be_kubernetes
- end
end
- context 'when google project billing is not enabled' do
- it 'renders the cluster form with an error' do
- go
-
- expect(response).to set_flash.now[:alert]
- expect(response).to render_template('new')
- end
+ it 'creates a new cluster' do
+ expect(ClusterProvisionWorker).to receive(:perform_async)
+ expect { go }.to change { Clusters::Cluster.count }
+ .and change { Clusters::Providers::Gcp.count }
+ expect(response).to redirect_to(project_cluster_path(project, project.clusters.first))
+ expect(project.clusters.first).to be_gcp
+ expect(project.clusters.first).to be_kubernetes
end
end
diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb
index 82b20e12850..380e50c8cac 100644
--- a/spec/controllers/projects/clusters_controller_spec.rb
+++ b/spec/controllers/projects/clusters_controller_spec.rb
@@ -2,7 +2,6 @@ require 'spec_helper'
describe Projects::ClustersController do
include AccessMatchersForController
- include GoogleApi::CloudPlatformHelpers
set(:project) { create(:project) }
@@ -333,7 +332,7 @@ describe Projects::ClustersController do
context 'when cluster is provided by GCP' do
context 'when cluster is created' do
- let!(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+ let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
it "destroys and redirects back to clusters list" do
expect { go }
@@ -347,7 +346,7 @@ describe Projects::ClustersController do
end
context 'when cluster is being created' do
- let!(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
+ let!(:cluster) { create(:cluster, :providing_by_gcp, :production_environment, projects: [project]) }
it "destroys and redirects back to clusters list" do
expect { go }
@@ -361,7 +360,7 @@ describe Projects::ClustersController do
end
context 'when cluster is provided by user' do
- let!(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
+ let!(:cluster) { create(:cluster, :provided_by_user, :production_environment, projects: [project]) }
it "destroys and redirects back to clusters list" do
expect { go }
@@ -376,7 +375,7 @@ describe Projects::ClustersController do
end
describe 'security' do
- set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+ set(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) }
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index ff9ab53d8c3..47d4942acbd 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -21,6 +21,13 @@ describe Projects::EnvironmentsController do
expect(response).to have_gitlab_http_status(:ok)
end
+
+ it 'expires etag cache to force reload environments list' do
+ expect_any_instance_of(Gitlab::EtagCaching::Store)
+ .to receive(:touch).with(project_environments_path(project, format: :json))
+
+ get :index, environment_params
+ end
end
context 'when requesting JSON response for folders' do
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index 5bfc3d31401..72f6af112b3 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -21,6 +21,18 @@ describe Projects::GroupLinksController do
end
end
+ context 'when project is not allowed to be shared with a group' do
+ before do
+ group.update_attributes(share_with_group_lock: false)
+ end
+
+ include_context 'link project to group'
+
+ it 'responds with status 404' do
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
context 'when user has access to group he want to link project to' do
before do
group.add_developer(user)
diff --git a/spec/controllers/projects/imports_controller_spec.rb b/spec/controllers/projects/imports_controller_spec.rb
index 7fb4c1b7425..011843baffc 100644
--- a/spec/controllers/projects/imports_controller_spec.rb
+++ b/spec/controllers/projects/imports_controller_spec.rb
@@ -2,16 +2,15 @@ require 'spec_helper'
describe Projects::ImportsController do
let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ sign_in(user)
+ project.add_master(user)
+ end
describe 'GET #show' do
context 'when repository does not exists' do
- let(:project) { create(:project) }
-
- before do
- sign_in(user)
- project.add_master(user)
- end
-
it 'renders template' do
get :show, namespace_id: project.namespace.to_param, project_id: project
@@ -28,11 +27,6 @@ describe Projects::ImportsController do
context 'when repository exists' do
let(:project) { create(:project_empty_repo, import_url: 'https://github.com/vim/vim.git') }
- before do
- sign_in(user)
- project.add_master(user)
- end
-
context 'when import is in progress' do
before do
project.update_attribute(:import_status, :started)
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index ca86b0bc737..106611b37c9 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -1,4 +1,4 @@
-require('spec_helper')
+require 'spec_helper'
describe Projects::IssuesController do
let(:project) { create(:project) }
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index d3042be9e8b..5efaaf2bb3a 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -275,6 +275,7 @@ describe Projects::MergeRequestsController do
namespace_id: project.namespace,
project_id: project,
id: merge_request.iid,
+ squash: false,
format: 'json'
}
end
@@ -315,8 +316,8 @@ describe Projects::MergeRequestsController do
end
context 'when the sha parameter matches the source SHA' do
- def merge_with_sha
- post :merge, base_params.merge(sha: merge_request.diff_head_sha)
+ def merge_with_sha(params = {})
+ post :merge, base_params.merge(sha: merge_request.diff_head_sha).merge(params)
end
it 'returns :success' do
@@ -331,6 +332,24 @@ describe Projects::MergeRequestsController do
merge_with_sha
end
+ context 'when squash is passed as 1' do
+ it 'updates the squash attribute on the MR to true' do
+ merge_request.update(squash: false)
+ merge_with_sha(squash: '1')
+
+ expect(merge_request.reload.squash).to be_truthy
+ end
+ end
+
+ context 'when squash is passed as 0' do
+ it 'updates the squash attribute on the MR to false' do
+ merge_request.update(squash: true)
+ merge_with_sha(squash: '0')
+
+ expect(merge_request.reload.squash).to be_falsey
+ end
+ end
+
context 'when the pipeline succeeds is passed' do
let!(:head_pipeline) do
create(:ci_empty_pipeline, project: project, sha: merge_request.diff_head_sha, ref: merge_request.source_branch, head_pipeline_of: merge_request)
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index 46b08a03b19..d84b31ad978 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -184,7 +184,7 @@ describe Projects::ProjectMembersController do
project.add_master(user)
end
- it 'cannot remove himself from the project' do
+ it 'cannot remove themselves from the project' do
delete :leave, namespace_id: project.namespace,
project_id: project
diff --git a/spec/factories/application_settings.rb b/spec/factories/application_settings.rb
index 3ecc90b6573..00c063c49f8 100644
--- a/spec/factories/application_settings.rb
+++ b/spec/factories/application_settings.rb
@@ -1,4 +1,5 @@
FactoryBot.define do
factory :application_setting do
+ default_projects_limit 42
end
end
diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb
index 766cd4b0090..d8fcdebfc6d 100644
--- a/spec/features/admin/admin_abuse_reports_spec.rb
+++ b/spec/features/admin/admin_abuse_reports_spec.rb
@@ -45,7 +45,7 @@ describe "Admin::AbuseReports", :js do
visit admin_abuse_reports_path
expect(page).to have_selector('.pagination')
- expect(page).to have_selector('.pagination .page', count: (report_count.to_f / AbuseReport.default_per_page).ceil)
+ expect(page).to have_selector('.pagination .js-pagination-page', count: (report_count.to_f / AbuseReport.default_per_page).ceil)
end
end
end
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 6d8350e99f1..328e8f25f89 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -34,7 +34,7 @@ describe "Admin::Projects" do
expect(page).to have_content(project.name)
expect(page).to have_content(archived_project.name)
- expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived')
+ expect(page).to have_xpath("//span[@class='badge badge-warning']", text: 'archived')
end
it 'renders only archived projects', :js do
diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb
index 380cd5d7703..2503fc9067d 100644
--- a/spec/features/admin/admin_requests_profiles_spec.rb
+++ b/spec/features/admin/admin_requests_profiles_spec.rb
@@ -33,12 +33,12 @@ describe 'Admin::RequestsProfilesController' do
visit admin_requests_profiles_path
- within('.panel', text: '/gitlab-org/gitlab-ce') do
+ within('.card', text: '/gitlab-org/gitlab-ce') do
expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile1)}']", text: time1.to_s(:long))
expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile2)}']", text: time2.to_s(:long))
end
- within('.panel', text: '/gitlab-com/infrastructure') do
+ within('.card', text: '/gitlab-com/infrastructure') do
expect(page).to have_selector("a[href='#{admin_requests_profile_path(profile3)}']", text: time3.to_s(:long))
end
end
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 3465ccfc423..c33014cbb31 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -68,7 +68,7 @@ describe "Admin Runners" do
visit admin_runners_path
within "#runner_#{runner.id}" do
- expect(page).to have_selector '.label', text: 'group'
+ expect(page).to have_selector '.badge', text: 'group'
expect(page).to have_text 'n/a'
end
end
@@ -81,7 +81,7 @@ describe "Admin Runners" do
visit admin_runners_path
within "#runner_#{runner.id}" do
- expect(page).to have_selector '.label', text: 'shared'
+ expect(page).to have_selector '.badge', text: 'shared'
expect(page).to have_text 'n/a'
end
end
@@ -95,7 +95,7 @@ describe "Admin Runners" do
visit admin_runners_path
within "#runner_#{runner.id}" do
- expect(page).to have_selector '.label', text: 'specific'
+ expect(page).to have_selector '.badge', text: 'specific'
expect(page).to have_text '1'
end
end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 8fc57f4b4c3..9e3221577c7 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -283,14 +283,14 @@ describe "Admin::Users" do
end
it "lists group projects" do
- within(:css, '.append-bottom-default + .panel') do
+ within(:css, '.append-bottom-default + .card') do
expect(page).to have_content 'Group projects'
expect(page).to have_link group.name, href: admin_group_path(group)
end
end
it 'allows navigation to the group details' do
- within(:css, '.append-bottom-default + .panel') do
+ within(:css, '.append-bottom-default + .card') do
click_link group.name
end
within(:css, 'h3.page-title') do
@@ -300,7 +300,7 @@ describe "Admin::Users" do
end
it 'shows the group access level' do
- within(:css, '.append-bottom-default + .panel') do
+ within(:css, '.append-bottom-default + .card') do
expect(page).to have_content 'Developer'
end
end
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index 18901a954cc..7a14a441088 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -81,7 +81,7 @@ describe 'Issue Boards add issue modal', :js do
expect(page).to have_content('2')
end
- expect(page).to have_selector('.card', count: 2)
+ expect(page).to have_selector('.board-card', count: 2)
end
end
@@ -89,7 +89,7 @@ describe 'Issue Boards add issue modal', :js do
page.within('.add-issues-modal') do
click_link 'Selected issues'
- expect(page).not_to have_selector('.card')
+ expect(page).not_to have_selector('.board-card')
end
end
@@ -122,7 +122,7 @@ describe 'Issue Boards add issue modal', :js do
wait_for_requests
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
@@ -133,7 +133,7 @@ describe 'Issue Boards add issue modal', :js do
wait_for_requests
- expect(page).not_to have_selector('.card')
+ expect(page).not_to have_selector('.board-card')
expect(page).not_to have_content("You haven't added any issues to your project yet")
end
end
@@ -142,7 +142,7 @@ describe 'Issue Boards add issue modal', :js do
context 'selecing issues' do
it 'selects single issue' do
page.within('.add-issues-modal') do
- first('.card .card-number').click
+ first('.board-card .board-card-number').click
page.within('.nav-links') do
expect(page).to have_content('Selected issues 1')
@@ -152,7 +152,7 @@ describe 'Issue Boards add issue modal', :js do
it 'changes button text' do
page.within('.add-issues-modal') do
- first('.card .card-number').click
+ first('.board-card .board-card-number').click
expect(first('.add-issues-footer .btn')).to have_content('Add 1 issue')
end
@@ -160,7 +160,7 @@ describe 'Issue Boards add issue modal', :js do
it 'changes button text with plural' do
page.within('.add-issues-modal') do
- all('.card .card-number').each do |el|
+ all('.board-card .board-card-number').each do |el|
el.click
end
@@ -170,11 +170,11 @@ describe 'Issue Boards add issue modal', :js do
it 'shows only selected issues on selected tab' do
page.within('.add-issues-modal') do
- first('.card .card-number').click
+ first('.board-card .board-card-number').click
click_link 'Selected issues'
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
@@ -200,7 +200,7 @@ describe 'Issue Boards add issue modal', :js do
it 'selects all that arent already selected' do
page.within('.add-issues-modal') do
- first('.card .card-number').click
+ first('.board-card .board-card-number').click
expect(page).to have_selector('.is-active', count: 1)
@@ -212,11 +212,11 @@ describe 'Issue Boards add issue modal', :js do
it 'unselects from selected tab' do
page.within('.add-issues-modal') do
- first('.card .card-number').click
+ first('.board-card .board-card-number').click
click_link 'Selected issues'
- first('.card .card-number').click
+ first('.board-card .board-card-number').click
expect(page).not_to have_selector('.is-active')
end
@@ -226,19 +226,19 @@ describe 'Issue Boards add issue modal', :js do
context 'adding issues' do
it 'adds to board' do
page.within('.add-issues-modal') do
- first('.card .card-number').click
+ first('.board-card .board-card-number').click
click_button 'Add 1 issue'
end
page.within(find('.board:nth-child(2)')) do
- expect(page).to have_selector('.card')
+ expect(page).to have_selector('.board-card')
end
end
it 'adds to second list' do
page.within('.add-issues-modal') do
- first('.card .card-number').click
+ first('.board-card .board-card-number').click
click_button planning.title
@@ -248,7 +248,7 @@ describe 'Issue Boards add issue modal', :js do
end
page.within(find('.board:nth-child(3)')) do
- expect(page).to have_selector('.card')
+ expect(page).to have_selector('.board-card')
end
end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 52ff47d57f9..e414345ac23 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -92,9 +92,9 @@ describe 'Issue Boards', :js do
wait_for_requests
expect(page).to have_selector('.board', count: 4)
- expect(find('.board:nth-child(2)')).to have_selector('.card')
- expect(find('.board:nth-child(3)')).to have_selector('.card')
- expect(find('.board:nth-child(4)')).to have_selector('.card')
+ expect(find('.board:nth-child(2)')).to have_selector('.board-card')
+ expect(find('.board:nth-child(3)')).to have_selector('.board-card')
+ expect(find('.board:nth-child(4)')).to have_selector('.board-card')
end
it 'shows description tooltip on list title' do
@@ -120,9 +120,9 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(4)')).to have_selector('.card', count: 1)
+ expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 0)
+ expect(find('.board:nth-child(3)')).to have_selector('.board-card', count: 0)
+ expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 1)
end
it 'search list' do
@@ -131,9 +131,9 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 1)
- expect(find('.board:nth-child(3)')).to have_selector('.card', count: 0)
- expect(find('.board:nth-child(4)')).to have_selector('.card', count: 0)
+ expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 1)
+ expect(find('.board:nth-child(3)')).to have_selector('.board-card', count: 0)
+ expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 0)
end
it 'allows user to delete board' do
@@ -171,21 +171,21 @@ describe 'Issue Boards', :js do
page.within(find('.board:nth-child(2)')) do
expect(page.find('.board-header')).to have_content('58')
- expect(page).to have_selector('.card', count: 20)
+ expect(page).to have_selector('.board-card', count: 20)
expect(page).to have_content('Showing 20 of 58 issues')
find('.board .board-list')
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
wait_for_requests
- expect(page).to have_selector('.card', count: 40)
+ expect(page).to have_selector('.board-card', count: 40)
expect(page).to have_content('Showing 40 of 58 issues')
find('.board .board-list')
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
wait_for_requests
- expect(page).to have_selector('.card', count: 58)
+ expect(page).to have_selector('.board-card', count: 58)
expect(page).to have_content('Showing all issues')
end
end
@@ -204,7 +204,7 @@ describe 'Issue Boards', :js do
wait_for_board_cards(4, 2)
expect(find('.board:nth-child(2)')).not_to have_content(issue9.title)
- expect(find('.board:nth-child(4)')).to have_selector('.card', count: 2)
+ expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 2)
expect(find('.board:nth-child(4)')).to have_content(issue9.title)
expect(find('.board:nth-child(4)')).not_to have_content(planning.title)
end
@@ -242,7 +242,7 @@ describe 'Issue Boards', :js do
wait_for_board_cards(4, 1)
expect(find('.board:nth-child(3)')).to have_content(issue6.title)
- expect(find('.board:nth-child(3)').all('.card').last).to have_content(development.title)
+ expect(find('.board:nth-child(3)').all('.board-card').last).to have_content(development.title)
end
it 'issue moves between lists' do
@@ -253,7 +253,7 @@ describe 'Issue Boards', :js do
wait_for_board_cards(4, 1)
expect(find('.board:nth-child(2)')).to have_content(issue7.title)
- expect(find('.board:nth-child(2)').all('.card').first).to have_content(planning.title)
+ expect(find('.board:nth-child(2)').all('.board-card').first).to have_content(planning.title)
end
it 'issue moves from closed' do
@@ -335,7 +335,7 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(page).to have_css('#js-add-list.open')
+ expect(page).to have_css('#js-add-list.show')
end
it 'creates new list from a new label' do
@@ -425,12 +425,12 @@ describe 'Issue Boards', :js do
page.within(find('.board:nth-child(2)')) do
expect(page.find('.board-header')).to have_content('1')
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
page.within(find('.board:nth-child(3)')) do
expect(page.find('.board-header')).to have_content('0')
- expect(page).to have_selector('.card', count: 0)
+ expect(page).to have_selector('.board-card', count: 0)
end
end
@@ -460,19 +460,19 @@ describe 'Issue Boards', :js do
page.within(find('.board:nth-child(2)')) do
expect(page.find('.board-header')).to have_content('51')
- expect(page).to have_selector('.card', count: 20)
+ expect(page).to have_selector('.board-card', count: 20)
expect(page).to have_content('Showing 20 of 51 issues')
find('.board .board-list')
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
- expect(page).to have_selector('.card', count: 40)
+ expect(page).to have_selector('.board-card', count: 40)
expect(page).to have_content('Showing 40 of 51 issues')
find('.board .board-list')
evaluate_script("document.querySelectorAll('.board .board-list')[1].scrollTop = document.querySelectorAll('.board .board-list')[1].scrollHeight")
- expect(page).to have_selector('.card', count: 51)
+ expect(page).to have_selector('.board-card', count: 51)
expect(page).to have_content('Showing all issues')
end
end
@@ -494,8 +494,8 @@ describe 'Issue Boards', :js do
it 'filters by clicking label button on issue' do
page.within(find('.board:nth-child(2)')) do
- expect(page).to have_selector('.card', count: 8)
- expect(find('.card', match: :first)).to have_content(bug.title)
+ expect(page).to have_selector('.board-card', count: 8)
+ expect(find('.board-card', match: :first)).to have_content(bug.title)
click_button(bug.title)
wait_for_requests
end
@@ -512,13 +512,13 @@ describe 'Issue Boards', :js do
it 'removes label filter by clicking label button on issue' do
page.within(find('.board:nth-child(2)')) do
- page.within(find('.card', match: :first)) do
+ page.within(find('.board-card', match: :first)) do
click_button(bug.title)
end
wait_for_requests
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
wait_for_requests
@@ -589,7 +589,7 @@ describe 'Issue Boards', :js do
def wait_for_board_cards(board_number, expected_cards)
page.within(find(".board:nth-child(#{board_number})")) do
expect(page.find('.board-header')).to have_content(expected_cards.to_s)
- expect(page).to have_selector('.card', count: expected_cards)
+ expect(page).to have_selector('.board-card', count: expected_cards)
end
end
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index 5abd02dbb48..193b1dfabbd 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -30,7 +30,7 @@ describe 'Issue Boards', :js do
it 'has un-ordered issue as last issue' do
page.within(find('.board:nth-child(2)')) do
- expect(all('.card').last).to have_content(issue4.title)
+ expect(all('.board-card').last).to have_content(issue4.title)
end
end
@@ -40,7 +40,7 @@ describe 'Issue Boards', :js do
wait_for_requests
page.within(find('.board:nth-child(2)')) do
- expect(first('.card')).to have_content(issue4.title)
+ expect(first('.board-card')).to have_content(issue4.title)
end
end
end
@@ -58,7 +58,7 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(first('.card')).to have_content(issue2.title)
+ expect(first('.board-card')).to have_content(issue2.title)
end
it 'moves from middle to bottom' do
@@ -66,7 +66,7 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(all('.card').last).to have_content(issue2.title)
+ expect(all('.board-card').last).to have_content(issue2.title)
end
it 'moves from top to bottom' do
@@ -74,7 +74,7 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(all('.card').last).to have_content(issue3.title)
+ expect(all('.board-card').last).to have_content(issue3.title)
end
it 'moves from bottom to top' do
@@ -82,7 +82,7 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(first('.card')).to have_content(issue1.title)
+ expect(first('.board-card')).to have_content(issue1.title)
end
it 'moves from top to middle' do
@@ -90,7 +90,7 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(first('.card')).to have_content(issue2.title)
+ expect(first('.board-card')).to have_content(issue2.title)
end
it 'moves from bottom to middle' do
@@ -98,7 +98,7 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(all('.card').last).to have_content(issue2.title)
+ expect(all('.board-card').last).to have_content(issue2.title)
end
end
@@ -121,11 +121,11 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 2)
- expect(all('.board')[2]).to have_selector('.card', count: 4)
+ expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 2)
+ expect(all('.board')[2]).to have_selector('.board-card', count: 4)
page.within(all('.board')[2]) do
- expect(first('.card')).to have_content(issue3.title)
+ expect(first('.board-card')).to have_content(issue3.title)
end
end
@@ -134,11 +134,11 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 2)
- expect(all('.board')[2]).to have_selector('.card', count: 4)
+ expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 2)
+ expect(all('.board')[2]).to have_selector('.board-card', count: 4)
page.within(all('.board')[2]) do
- expect(all('.card').last).to have_content(issue3.title)
+ expect(all('.board-card').last).to have_content(issue3.title)
end
end
@@ -147,11 +147,11 @@ describe 'Issue Boards', :js do
wait_for_requests
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 2)
- expect(all('.board')[2]).to have_selector('.card', count: 4)
+ expect(find('.board:nth-child(2)')).to have_selector('.board-card', count: 2)
+ expect(all('.board')[2]).to have_selector('.board-card', count: 4)
page.within(all('.board')[2]) do
- expect(all('.card')[1]).to have_content(issue3.title)
+ expect(all('.board-card')[1]).to have_content(issue3.title)
end
end
end
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index 5907bb0840f..be9c6a51c29 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -38,7 +38,7 @@ describe 'Issue Boards add issue modal filtering', :js do
page.within('.add-issues-modal') do
wait_for_requests
- expect(page).to have_selector('.card', count: 0)
+ expect(page).to have_selector('.board-card', count: 0)
click_button 'Cancel'
end
@@ -48,7 +48,7 @@ describe 'Issue Boards add issue modal filtering', :js do
page.within('.add-issues-modal') do
wait_for_requests
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
@@ -62,13 +62,13 @@ describe 'Issue Boards add issue modal filtering', :js do
page.within('.add-issues-modal') do
wait_for_requests
- expect(page).to have_selector('.card', count: 0)
+ expect(page).to have_selector('.board-card', count: 0)
find('.clear-search').click
wait_for_requests
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
@@ -90,7 +90,7 @@ describe 'Issue Boards add issue modal filtering', :js do
wait_for_requests
expect(page).to have_selector('.js-visual-token', text: user2.name)
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
end
@@ -113,7 +113,7 @@ describe 'Issue Boards add issue modal filtering', :js do
wait_for_requests
expect(page).to have_selector('.js-visual-token', text: 'none')
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
@@ -126,7 +126,7 @@ describe 'Issue Boards add issue modal filtering', :js do
wait_for_requests
expect(page).to have_selector('.js-visual-token', text: user2.name)
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
end
@@ -148,7 +148,7 @@ describe 'Issue Boards add issue modal filtering', :js do
wait_for_requests
expect(page).to have_selector('.js-visual-token', text: 'upcoming')
- expect(page).to have_selector('.card', count: 0)
+ expect(page).to have_selector('.board-card', count: 0)
end
end
@@ -161,7 +161,7 @@ describe 'Issue Boards add issue modal filtering', :js do
wait_for_requests
expect(page).to have_selector('.js-visual-token', text: milestone.name)
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
end
@@ -183,7 +183,7 @@ describe 'Issue Boards add issue modal filtering', :js do
wait_for_requests
expect(page).to have_selector('.js-visual-token', text: 'none')
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
@@ -196,7 +196,7 @@ describe 'Issue Boards add issue modal filtering', :js do
wait_for_requests
expect(page).to have_selector('.js-visual-token', text: label.title)
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
end
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index e880f0096c1..7a95f5cf871 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -64,7 +64,7 @@ describe 'Issue Boards new issue', :js do
expect(page).to have_content('1')
end
- page.within(first('.card')) do
+ page.within(first('.board-card')) do
issue = project.issues.find_by_title('bug')
expect(page).to have_content(issue.to_reference)
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 4d31123a699..a03aa681827 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -15,7 +15,7 @@ describe 'Issue Boards', :js do
let!(:issue2) { create(:labeled_issue, project: project, labels: [development, stretch], relative_position: 1) }
let(:board) { create(:board, project: project) }
let!(:list) { create(:list, board: board, label: development, position: 0) }
- let(:card) { find('.board:nth-child(2)').first('.card') }
+ let(:card) { find('.board:nth-child(2)').first('.board-card') }
around do |example|
Timecop.freeze { example.run }
@@ -75,7 +75,7 @@ describe 'Issue Boards', :js do
wait_for_requests
page.within(find('.board:nth-child(2)')) do
- expect(page).to have_selector('.card', count: 1)
+ expect(page).to have_selector('.board-card', count: 1)
end
end
@@ -86,11 +86,11 @@ describe 'Issue Boards', :js do
visit project_board_path(project, board)
wait_for_requests
- click_card(find('.board:nth-child(1)').first('.card'))
+ click_card(find('.board:nth-child(1)').first('.board-card'))
expect(find('.issue-boards-sidebar')).not_to have_button 'Remove from board'
- click_card(find('.board:nth-child(3)').first('.card'))
+ click_card(find('.board:nth-child(3)').first('.board-card'))
expect(find('.issue-boards-sidebar')).not_to have_button 'Remove from board'
end
@@ -117,7 +117,7 @@ describe 'Issue Boards', :js do
end
it 'removes the assignee' do
- card_two = find('.board:nth-child(2)').find('.card:nth-child(2)')
+ card_two = find('.board:nth-child(2)').find('.board-card:nth-child(2)')
click_card(card_two)
page.within('.assignee') do
@@ -171,7 +171,7 @@ describe 'Issue Boards', :js do
end
page.within(find('.board:nth-child(2)')) do
- find('.card:nth-child(2)').click
+ find('.board-card:nth-child(2)').click
end
page.within('.assignee') do
@@ -246,7 +246,7 @@ describe 'Issue Boards', :js do
wait_for_requests
page.within('.value') do
- expect(page).to have_selector('.label', count: 2)
+ expect(page).to have_selector('.badge', count: 2)
expect(page).to have_content(development.title)
expect(page).to have_content(stretch.title)
end
@@ -268,12 +268,12 @@ describe 'Issue Boards', :js do
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.label', count: 3)
+ expect(page).to have_selector('.badge', count: 3)
expect(page).to have_content(bug.title)
end
end
- expect(card).to have_selector('.label', count: 3)
+ expect(card).to have_selector('.badge', count: 3)
expect(card).to have_content(bug.title)
end
@@ -293,13 +293,13 @@ describe 'Issue Boards', :js do
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.label', count: 4)
+ expect(page).to have_selector('.badge', count: 4)
expect(page).to have_content(bug.title)
expect(page).to have_content(regression.title)
end
end
- expect(card).to have_selector('.label', count: 4)
+ expect(card).to have_selector('.badge', count: 4)
expect(card).to have_content(bug.title)
expect(card).to have_content(regression.title)
end
@@ -321,12 +321,12 @@ describe 'Issue Boards', :js do
find('.dropdown-menu-close-icon').click
page.within('.value') do
- expect(page).to have_selector('.label', count: 1)
+ expect(page).to have_selector('.badge', count: 1)
expect(page).not_to have_content(stretch.title)
end
end
- expect(card).to have_selector('.label', count: 1)
+ expect(card).to have_selector('.badge', count: 1)
expect(card).not_to have_content(stretch.title)
end
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
index 5fdb8044db2..271c610dcc8 100644
--- a/spec/features/boards/sub_group_project_spec.rb
+++ b/spec/features/boards/sub_group_project_spec.rb
@@ -20,7 +20,7 @@ describe 'Sub-group project issue boards', :js do
end
it 'creates new label from sidebar' do
- find('.card').click
+ find('.board-card').click
page.within '.labels' do
click_link 'Edit'
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index 94133c62b5c..5ed20b02a6e 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -246,7 +246,7 @@ feature 'Dashboard Todos' do
it 'is has the right number of pages' do
visit dashboard_todos_path
- expect(page).to have_selector('.gl-pagination .page', count: 2)
+ expect(page).to have_selector('.gl-pagination .js-pagination-page', count: 2)
end
describe 'mark all as done', :js do
diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb
index 8d5233d0c0f..2516db5422f 100644
--- a/spec/features/explore/new_menu_spec.rb
+++ b/spec/features/explore/new_menu_spec.rb
@@ -66,7 +66,7 @@ feature 'Top Plus Menu', :js do
page.within '.header-content' do
find('.header-new-dropdown-toggle').click
- expect(page).to have_selector('.header-new.dropdown.open', count: 1)
+ expect(page).to have_selector('.header-new.dropdown.show', count: 1)
find('.header-new-project-snippet a').click
end
@@ -88,7 +88,7 @@ feature 'Top Plus Menu', :js do
page.within '.header-content' do
find('.header-new-dropdown-toggle').click
- expect(page).to have_selector('.header-new.dropdown.open', count: 1)
+ expect(page).to have_selector('.header-new.dropdown.show', count: 1)
find('.header-new-group-project a').click
end
@@ -156,7 +156,7 @@ feature 'Top Plus Menu', :js do
def click_topmenuitem(item_name)
page.within '.header-content' do
find('.header-new-dropdown-toggle').click
- expect(page).to have_selector('.header-new.dropdown.open', count: 1)
+ expect(page).to have_selector('.header-new.dropdown.show', count: 1)
click_link item_name
end
end
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 1ce30015e81..bf329b0bb94 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -83,7 +83,7 @@ feature 'Edit group settings' do
attach_file(:group_avatar, Rails.root.join('spec', 'fixtures', 'banana_sample.gif'))
- expect { click_button 'Save group' }.to change { group.reload.avatar? }.to(true)
+ expect { save_group }.to change { group.reload.avatar? }.to(true)
end
it 'uploads new group avatar' do
@@ -97,10 +97,19 @@ feature 'Edit group settings' do
expect(page).not_to have_link('Remove avatar')
end
end
-end
-def update_path(new_group_path)
- visit edit_group_path(group)
- fill_in 'group_path', with: new_group_path
- click_button 'Save group'
+ def update_path(new_group_path)
+ visit edit_group_path(group)
+
+ page.within('.gs-advanced') do
+ fill_in 'group_path', with: new_group_path
+ click_button 'Change group path'
+ end
+ end
+
+ def save_group
+ page.within('.gs-general') do
+ click_button 'Save group'
+ end
+ end
end
diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb
index 31fbbcf562c..e7efdf7dfef 100644
--- a/spec/features/groups/members/search_members_spec.rb
+++ b/spec/features/groups/members/search_members_spec.rb
@@ -22,7 +22,7 @@ describe 'Search group member' do
find('.member-search-btn').click
end
- group_members_list = find(".panel .content-list")
+ group_members_list = find(".card .content-list")
expect(group_members_list).to have_content(member.name)
expect(group_members_list).not_to have_content(user.name)
end
diff --git a/spec/features/groups/settings/group_badges_spec.rb b/spec/features/groups/settings/group_badges_spec.rb
index 92217294446..a99da4a119b 100644
--- a/spec/features/groups/settings/group_badges_spec.rb
+++ b/spec/features/groups/settings/group_badges_spec.rb
@@ -21,7 +21,7 @@ feature 'Group Badges' do
page.within '.badge-settings' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
expect(rows[0]).to have_content badge_1.link_url
expect(rows[1]).to have_content badge_2.link_url
@@ -48,7 +48,7 @@ feature 'Group Badges' do
click_button 'Add badge'
wait_for_requests
- within '.panel-body' do
+ within '.card-body' do
expect(find('a')[:href]).to eq badge_link_url
expect(find('a img')[:src]).to eq badge_image_url
end
@@ -60,7 +60,7 @@ feature 'Group Badges' do
it 'form is shown when clicking edit button in list' do
page.within '.badge-settings' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
rows[1].find('[aria-label="Edit"]').click
@@ -74,7 +74,7 @@ feature 'Group Badges' do
it 'updates a badge when submitting the edit form' do
page.within '.badge-settings' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
rows[1].find('[aria-label="Edit"]').click
within 'form' do
@@ -85,7 +85,7 @@ feature 'Group Badges' do
wait_for_requests
end
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
expect(rows[1]).to have_content badge_link_url
end
@@ -99,7 +99,7 @@ feature 'Group Badges' do
it 'shows a modal when deleting a badge' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
click_delete_button(rows[1])
@@ -109,14 +109,14 @@ feature 'Group Badges' do
it 'deletes a badge when confirming the modal' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
click_delete_button(rows[1])
find('.modal .btn-danger').click
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 1
expect(rows[0]).to have_content badge_1.link_url
end
diff --git a/spec/features/groups/share_lock_spec.rb b/spec/features/groups/share_lock_spec.rb
index 8842d1391aa..cefbc15e068 100644
--- a/spec/features/groups/share_lock_spec.rb
+++ b/spec/features/groups/share_lock_spec.rb
@@ -15,9 +15,8 @@ feature 'Group share with group lock' do
context 'when enabling the parent group share with group lock' do
scenario 'the subgroup share with group lock becomes enabled' do
visit edit_group_path(root_group)
- check 'group_share_with_group_lock'
- click_on 'Save group'
+ enable_group_lock
expect(subgroup.reload.share_with_group_lock?).to be_truthy
end
@@ -26,16 +25,15 @@ feature 'Group share with group lock' do
context 'when disabling the parent group share with group lock (which was already enabled)' do
background do
visit edit_group_path(root_group)
- check 'group_share_with_group_lock'
- click_on 'Save group'
+
+ enable_group_lock
end
context 'and the subgroup share with group lock is enabled' do
scenario 'the subgroup share with group lock does not change' do
visit edit_group_path(root_group)
- uncheck 'group_share_with_group_lock'
- click_on 'Save group'
+ disable_group_lock
expect(subgroup.reload.share_with_group_lock?).to be_truthy
end
@@ -44,19 +42,32 @@ feature 'Group share with group lock' do
context 'but the subgroup share with group lock is disabled' do
background do
visit edit_group_path(subgroup)
- uncheck 'group_share_with_group_lock'
- click_on 'Save group'
+
+ disable_group_lock
end
scenario 'the subgroup share with group lock does not change' do
visit edit_group_path(root_group)
- uncheck 'group_share_with_group_lock'
- click_on 'Save group'
+ disable_group_lock
expect(subgroup.reload.share_with_group_lock?).to be_falsey
end
end
end
end
+
+ def enable_group_lock
+ page.within('.gs-permissions') do
+ check 'group_share_with_group_lock'
+ click_on 'Save group'
+ end
+ end
+
+ def disable_group_lock
+ page.within('.gs-permissions') do
+ uncheck 'group_share_with_group_lock'
+ click_on 'Save group'
+ end
+ end
end
diff --git a/spec/features/groups/user_browse_projects_group_page_spec.rb b/spec/features/groups/user_browse_projects_group_page_spec.rb
index e81c3180e78..916363c41dd 100644
--- a/spec/features/groups/user_browse_projects_group_page_spec.rb
+++ b/spec/features/groups/user_browse_projects_group_page_spec.rb
@@ -21,7 +21,7 @@ describe 'User browse group projects page' do
visit projects_group_path(group)
expect(page).to have_link project.name
- expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived')
+ expect(page).to have_xpath("//span[@class='badge badge-warning']", text: 'archived')
end
end
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index c1f3d94bc20..236768b5d7f 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -141,8 +141,10 @@ feature 'Group' do
end
it 'saves new settings' do
- fill_in 'group_name', with: new_name
- click_button 'Save group'
+ page.within('.gs-general') do
+ fill_in 'group_name', with: new_name
+ click_button 'Save group'
+ end
expect(page).to have_content 'successfully updated'
expect(find('#group_name').value).to eq(new_name)
diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb
index 08ba91a2682..483122ae463 100644
--- a/spec/features/issues/filtered_search/filter_issues_spec.rb
+++ b/spec/features/issues/filtered_search/filter_issues_spec.rb
@@ -248,7 +248,7 @@ describe 'Filter issues', :js do
context 'issue label clicked' do
it 'filters and displays in search bar' do
- find('.issues-list .issue .issue-main-info .issuable-info a .label', text: multiple_words_label.title).click
+ find('.issues-list .issue .issue-main-info .issuable-info a .badge', text: multiple_words_label.title).click
expect_issues_list_count(1)
expect_tokens([label_token("\"#{multiple_words_label.title}\"")])
diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
index 539d7e9ff01..3dfcbc2fcb8 100644
--- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
+++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb
@@ -139,8 +139,8 @@ describe 'User creates branch and merge request on issue page', :js do
end
it 'disables the create branch button' do
- expect(page).to have_css('.create-mr-dropdown-wrap .unavailable:not(.hide)')
- expect(page).to have_css('.create-mr-dropdown-wrap .available.hide', visible: false)
+ expect(page).to have_css('.create-mr-dropdown-wrap .unavailable:not(.hidden)')
+ expect(page).to have_css('.create-mr-dropdown-wrap .available.hidden', visible: false)
expect(page).to have_content /1 Related Merge Request/
end
end
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index ae41f611ddc..4700ada1aae 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -34,7 +34,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
- expect(page).to have_selector('span.label', text: label.title)
+ expect(page).to have_selector('span.badge', text: label.title)
end
end
@@ -45,7 +45,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
- expect(page).not_to have_selector('span.label', text: child_group_label.title)
+ expect(page).not_to have_selector('span.badge', text: child_group_label.title)
end
end
@@ -57,7 +57,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
if board
- expect(page).to have_selector('.card-title') do |card|
+ expect(page).to have_selector('.board-card-title') do |card|
expect(card).to have_selector('a', text: labeled_issue.title)
end
else
@@ -96,11 +96,11 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
if board
- expect(page).to have_selector('.card-title') do |card|
+ expect(page).to have_selector('.board-card-title') do |card|
expect(card).to have_selector('a', text: labeled_issue.title)
end
- expect(page).to have_selector('.card-title') do |card|
+ expect(page).to have_selector('.board-card-title') do |card|
expect(card).to have_selector('a', text: labeled_issue_2.title)
end
else
@@ -118,11 +118,11 @@ feature 'Labels Hierarchy', :js, :nested_groups do
select_label_on_dropdown(group_label_3.title)
if board
- expect(page).to have_selector('.card-title') do |card|
+ expect(page).to have_selector('.board-card-title') do |card|
expect(card).not_to have_selector('a', text: labeled_issue_2.title)
end
- expect(page).to have_selector('.card-title') do |card|
+ expect(page).to have_selector('.board-card-title') do |card|
expect(card).to have_selector('a', text: labeled_issue_3.title)
end
else
@@ -159,9 +159,9 @@ feature 'Labels Hierarchy', :js, :nested_groups do
find('.btn-create').click
expect(page.find('.issue-details h2.title')).to have_content('new created issue')
- expect(page).to have_selector('span.label', text: grandparent_group_label.title)
- expect(page).to have_selector('span.label', text: parent_group_label.title)
- expect(page).to have_selector('span.label', text: project_label_1.title)
+ expect(page).to have_selector('span.badge', text: grandparent_group_label.title)
+ expect(page).to have_selector('span.badge', text: parent_group_label.title)
+ expect(page).to have_selector('span.badge', text: project_label_1.title)
end
end
@@ -188,7 +188,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
- find('.card').click
+ find('.board-card').click
end
it_behaves_like 'assigning labels from sidebar'
@@ -204,7 +204,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
- find('.card').click
+ find('.board-card').click
end
it_behaves_like 'assigning labels from sidebar'
diff --git a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
index 42c279af117..ed6e29335d1 100644
--- a/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
+++ b/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb
@@ -4,6 +4,12 @@ describe 'Merge request > User selects branches for new MR', :js do
let(:project) { create(:project, :public, :repository) }
let(:user) { project.creator }
+ def select_source_branch(branch_name)
+ find('.js-source-branch', match: :first).click
+ find('.js-source-branch-dropdown .dropdown-input-field').native.send_keys branch_name
+ find('.js-source-branch-dropdown .dropdown-content a', text: branch_name, match: :first).click
+ end
+
before do
project.add_master(user)
sign_in(user)
@@ -43,8 +49,7 @@ describe 'Merge request > User selects branches for new MR', :js do
it 'generates a diff for an orphaned branch' do
visit project_new_merge_request_path(project)
- find('.js-source-branch', match: :first).click
- find('.js-source-branch-dropdown .dropdown-content a', text: 'orphaned-branch', match: :first).click
+ select_source_branch('orphaned-branch')
click_button "Compare branches"
click_link "Changes"
@@ -169,4 +174,31 @@ describe 'Merge request > User selects branches for new MR', :js do
end
end
end
+
+ context 'with special characters in branch names' do
+ it 'escapes quotes in branch names' do
+ special_branch_name = '"with-quotes"'
+ CreateBranchService.new(project, user)
+ .execute(special_branch_name, 'add-pdf-file')
+
+ visit project_new_merge_request_path(project)
+ select_source_branch(special_branch_name)
+
+ source_branch_input = find('[name="merge_request[source_branch]"]', visible: false)
+ expect(source_branch_input.value).to eq special_branch_name
+ end
+
+ it 'does not escape unicode in branch names' do
+ special_branch_name = 'ʕ•ᴥ•ʔ'
+ CreateBranchService.new(project, user)
+ .execute(special_branch_name, 'add-pdf-file')
+
+ visit project_new_merge_request_path(project)
+ select_source_branch(special_branch_name)
+
+ click_button "Compare branches"
+
+ expect(page).to have_button("Submit merge request")
+ end
+ end
end
diff --git a/spec/features/merge_requests/user_squashes_merge_request_spec.rb b/spec/features/merge_requests/user_squashes_merge_request_spec.rb
new file mode 100644
index 00000000000..6c952791591
--- /dev/null
+++ b/spec/features/merge_requests/user_squashes_merge_request_spec.rb
@@ -0,0 +1,124 @@
+require 'spec_helper'
+
+feature 'User squashes a merge request', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:source_branch) { 'csv' }
+
+ let!(:original_head) { project.repository.commit('master') }
+
+ shared_examples 'squash' do
+ it 'squashes the commits into a single commit, and adds a merge commit' do
+ expect(page).to have_content('Merged')
+
+ latest_master_commits = project.repository.commits_between(original_head.sha, 'master').map(&:raw)
+
+ squash_commit = an_object_having_attributes(sha: a_string_matching(/\h{40}/),
+ message: "Csv\n",
+ author_name: user.name,
+ committer_name: user.name)
+
+ merge_commit = an_object_having_attributes(sha: a_string_matching(/\h{40}/),
+ message: a_string_starting_with("Merge branch 'csv' into 'master'"),
+ author_name: user.name,
+ committer_name: user.name)
+
+ expect(project.repository).not_to be_merged_to_root_ref(source_branch)
+ expect(latest_master_commits).to match([squash_commit, merge_commit])
+ end
+ end
+
+ shared_examples 'no squash' do
+ it 'accepts the merge request without squashing' do
+ expect(page).to have_content('Merged')
+ expect(project.repository).to be_merged_to_root_ref(source_branch)
+ end
+ end
+
+ def accept_mr
+ expect(page).to have_button('Merge')
+
+ uncheck 'Remove source branch'
+ click_on 'Merge'
+ end
+
+ before do
+ # Prevent source branch from being removed so we can use be_merged_to_root_ref
+ # method to check if squash was performed or not
+ allow_any_instance_of(MergeRequest).to receive(:force_remove_source_branch?).and_return(false)
+ project.add_master(user)
+
+ sign_in user
+ end
+
+ context 'when the MR has only one commit' do
+ before do
+ merge_request = create(:merge_request, source_project: project, target_project: project, source_branch: 'master', target_branch: 'branch-merged')
+
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'does not show the squash checkbox' do
+ expect(page).not_to have_field('squash')
+ end
+ end
+
+ context 'when squash is enabled on merge request creation' do
+ before do
+ visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: source_branch })
+ check 'merge_request[squash]'
+ click_on 'Submit merge request'
+ wait_for_requests
+ end
+
+ it 'shows the squash checkbox as checked' do
+ expect(page).to have_checked_field('squash')
+ end
+
+ context 'when accepting with squash checked' do
+ before do
+ accept_mr
+ end
+
+ include_examples 'squash'
+ end
+
+ context 'when accepting and unchecking squash' do
+ before do
+ uncheck 'squash'
+ accept_mr
+ end
+
+ include_examples 'no squash'
+ end
+ end
+
+ context 'when squash is not enabled on merge request creation' do
+ before do
+ visit project_new_merge_request_path(project, merge_request: { target_branch: 'master', source_branch: source_branch })
+ click_on 'Submit merge request'
+ wait_for_requests
+ end
+
+ it 'shows the squash checkbox as unchecked' do
+ expect(page).to have_unchecked_field('squash')
+ end
+
+ context 'when accepting and checking squash' do
+ before do
+ check 'squash'
+ accept_mr
+ end
+
+ include_examples 'squash'
+ end
+
+ context 'when accepting with squash unchecked' do
+ before do
+ accept_mr
+ end
+
+ include_examples 'no squash'
+ end
+ end
+end
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index 6c51e4bbe26..b0db6870ddf 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -119,7 +119,7 @@ feature 'Milestone' do
find('.milestone-deprecation-message .js-popover-link').click
- expect(page).to have_selector('.milestone-deprecation-message .popover')
+ expect(page).to have_selector('.popover')
end
end
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index a8a627d8806..c85b82b2090 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -22,152 +22,123 @@ feature 'Gcp Cluster', :js do
.to receive(:expires_at_in_session).and_return(1.hour.since.to_i.to_s)
end
- context 'when user has a GCP project with billing enabled' do
+ context 'when user does not have a cluster and visits cluster index page' do
before do
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(true)
- end
-
- context 'when user does not have a cluster and visits cluster index page' do
- before do
- visit project_clusters_path(project)
-
- click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
- end
-
- context 'when user filled form with valid parameters' do
- before do
- allow_any_instance_of(GoogleApi::CloudPlatform::Client)
- .to receive(:projects_zones_clusters_create) do
- OpenStruct.new(
- self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
- status: 'RUNNING'
- )
- end
-
- allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
-
- fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
- fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Create Kubernetes cluster'
- end
+ visit project_clusters_path(project)
- it 'user sees a cluster details page and creation status' do
- expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+ click_link 'Add Kubernetes cluster'
+ click_link 'Create on Google Kubernetes Engine'
+ end
- Clusters::Cluster.last.provider.make_created!
+ context 'when user filled form with valid parameters' do
+ subject { click_button 'Create Kubernetes cluster' }
- expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
+ before do
+ allow_any_instance_of(GoogleApi::CloudPlatform::Client)
+ .to receive(:projects_zones_clusters_create) do
+ OpenStruct.new(
+ self_link: 'projects/gcp-project-12345/zones/us-central1-a/operations/ope-123',
+ status: 'RUNNING'
+ )
end
- it 'user sees a error if something worng during creation' do
- expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+ allow(WaitForClusterCreationWorker).to receive(:perform_in).and_return(nil)
- Clusters::Cluster.last.provider.make_errored!('Something wrong!')
+ execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
+ sleep 2 # wait for ajax
+ execute_script('document.querySelector(".js-gcp-project-id-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gcp-zone-dropdown input").setAttribute("type", "text")')
+ execute_script('document.querySelector(".js-gcp-machine-type-dropdown input").setAttribute("type", "text")')
- expect(page).to have_content('Something wrong!')
- end
+ fill_in 'cluster[name]', with: 'dev-cluster'
+ fill_in 'cluster[provider_gcp_attributes][gcp_project_id]', with: 'gcp-project-123'
+ fill_in 'cluster[provider_gcp_attributes][zone]', with: 'us-central1-a'
+ fill_in 'cluster[provider_gcp_attributes][machine_type]', with: 'n1-standard-2'
end
- context 'when user filled form with invalid parameters' do
- before do
- click_button 'Create Kubernetes cluster'
- end
-
- it 'user sees a validation error' do
- expect(page).to have_css('#error_explanation')
- end
+ it 'users sees a form with the GCP token' do
+ expect(page).to have_selector(:css, 'form[data-token="token"]')
end
- end
- context 'when user does have a cluster and visits cluster page' do
- let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
+ it 'user sees a cluster details page and creation status' do
+ subject
- before do
- visit project_cluster_path(project, cluster)
- end
+ expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
+
+ Clusters::Cluster.last.provider.make_created!
- it 'user sees a cluster details page' do
- expect(page).to have_button('Save changes')
- expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
+ expect(page).to have_content('Kubernetes cluster was successfully created on Google Kubernetes Engine')
end
- context 'when user disables the cluster' do
- before do
- page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click
- page.within('#cluster-integration') { click_button 'Save changes' }
- end
+ it 'user sees a error if something wrong during creation' do
+ subject
- it 'user sees the successful message' do
- expect(page).to have_content('Kubernetes cluster was successfully updated.')
- end
- end
+ expect(page).to have_content('Kubernetes cluster is being created on Google Kubernetes Engine...')
- context 'when user changes cluster parameters' do
- before do
- fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
- page.within('#js-cluster-details') { click_button 'Save changes' }
- end
+ Clusters::Cluster.last.provider.make_errored!('Something wrong!')
- it 'user sees the successful message' do
- expect(page).to have_content('Kubernetes cluster was successfully updated.')
- expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace')
- end
+ expect(page).to have_content('Something wrong!')
end
+ end
- context 'when user destroy the cluster' do
- before do
- page.accept_confirm do
- click_link 'Remove integration'
- end
- end
+ context 'when user filled form with invalid parameters' do
+ before do
+ execute_script('document.querySelector(".js-gke-cluster-creation-submit").removeAttribute("disabled")')
+ click_button 'Create Kubernetes cluster'
+ end
- it 'user sees creation form with the successful message' do
- expect(page).to have_content('Kubernetes cluster integration was successfully removed.')
- expect(page).to have_link('Add Kubernetes cluster')
- end
+ it 'user sees a validation error' do
+ expect(page).to have_css('#error_explanation')
end
end
end
- context 'when user does not have a GCP project with billing enabled' do
- before do
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(false)
-
- visit project_clusters_path(project)
-
- click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
+ context 'when user does have a cluster and visits cluster page' do
+ let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
- fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
- fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Create Kubernetes cluster'
+ before do
+ visit project_cluster_path(project, cluster)
end
- it 'user sees form with error' do
- expect(page).to have_content('Please enable billing for one of your projects to be able to create a Kubernetes cluster, then try again.')
+ it 'user sees a cluster details page' do
+ expect(page).to have_button('Save changes')
+ expect(page.find(:css, '.cluster-name').value).to eq(cluster.name)
end
- end
- context 'when gcp billing status is not in redis' do
- before do
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(nil)
+ context 'when user disables the cluster' do
+ before do
+ page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click
+ page.within('#cluster-integration') { click_button 'Save changes' }
+ end
- visit project_clusters_path(project)
+ it 'user sees the successful message' do
+ expect(page).to have_content('Kubernetes cluster was successfully updated.')
+ end
+ end
- click_link 'Add Kubernetes cluster'
- click_link 'Create on Google Kubernetes Engine'
+ context 'when user changes cluster parameters' do
+ before do
+ fill_in 'cluster_platform_kubernetes_attributes_namespace', with: 'my-namespace'
+ page.within('#js-cluster-details') { click_button 'Save changes' }
+ end
- fill_in 'cluster_provider_gcp_attributes_gcp_project_id', with: 'gcp-project-123'
- fill_in 'cluster_name', with: 'dev-cluster'
- click_button 'Create Kubernetes cluster'
+ it 'user sees the successful message' do
+ expect(page).to have_content('Kubernetes cluster was successfully updated.')
+ expect(cluster.reload.platform_kubernetes.namespace).to eq('my-namespace')
+ end
end
- it 'user sees form with error' do
- expect(page).to have_content('We could not verify that one of your projects on GCP has billing enabled. Please try again.')
+ context 'when user destroy the cluster' do
+ before do
+ page.accept_confirm do
+ click_link 'Remove integration'
+ end
+ end
+
+ it 'user sees creation form with the successful message' do
+ expect(page).to have_content('Kubernetes cluster integration was successfully removed.')
+ expect(page).to have_link('Add Kubernetes cluster')
+ end
end
end
end
diff --git a/spec/features/projects/issues/user_sorts_issues_spec.rb b/spec/features/projects/issues/user_sorts_issues_spec.rb
index c3d63000dac..db5936a30cb 100644
--- a/spec/features/projects/issues/user_sorts_issues_spec.rb
+++ b/spec/features/projects/issues/user_sorts_issues_spec.rb
@@ -18,7 +18,7 @@ describe "User sorts issues" do
it "sorts by popularity" do
find("button.dropdown-toggle").click
- page.within(".content ul.dropdown-menu.dropdown-menu-align-right li") do
+ page.within(".content ul.dropdown-menu.dropdown-menu-right li") do
click_link("Popularity")
end
diff --git a/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb b/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
index d8d9f7e2a8c..305658f1b5d 100644
--- a/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
+++ b/spec/features/projects/merge_requests/user_sorts_merge_requests_spec.rb
@@ -18,7 +18,7 @@ describe 'User sorts merge requests' do
it 'keeps the sort option' do
find('button.dropdown-toggle').click
- page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
+ page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Last updated')
end
@@ -43,7 +43,7 @@ describe 'User sorts merge requests' do
it 'sorts by popularity' do
find('button.dropdown-toggle').click
- page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
+ page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Popularity')
end
diff --git a/spec/features/projects/merge_requests/user_views_diffs_spec.rb b/spec/features/projects/merge_requests/user_views_diffs_spec.rb
index 295eb02b625..d36aafdbc54 100644
--- a/spec/features/projects/merge_requests/user_views_diffs_spec.rb
+++ b/spec/features/projects/merge_requests/user_views_diffs_spec.rb
@@ -26,6 +26,10 @@ describe 'User views diffs', :js do
expect(page).to have_css('#inline-diff-btn', count: 1)
end
+ it 'hides loading spinner after load' do
+ expect(page).not_to have_selector('.mr-loading-status .loading', visible: true)
+ end
+
context 'when in the inline view' do
include_examples 'unfold diffs'
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index a29c21f6fef..af2a9567a47 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -165,7 +165,7 @@ describe 'Pipeline', :js do
end
it 'shows Pipeline tab as active' do
- expect(page).to have_css('.js-pipeline-tab-link.active')
+ expect(page).to have_css('.js-pipeline-tab-link .active')
end
context 'without permission to access builds' do
@@ -271,7 +271,7 @@ describe 'Pipeline', :js do
end
it 'shows Jobs tab as active' do
- expect(page).to have_css('li.js-builds-tab-link.active')
+ expect(page).to have_css('li.js-builds-tab-link .active')
end
end
diff --git a/spec/features/projects/settings/project_badges_spec.rb b/spec/features/projects/settings/project_badges_spec.rb
index cc3551a4c21..4893bef8884 100644
--- a/spec/features/projects/settings/project_badges_spec.rb
+++ b/spec/features/projects/settings/project_badges_spec.rb
@@ -22,7 +22,7 @@ feature 'Project Badges' do
page.within '.badge-settings' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
expect(rows[0]).to have_content group_badge.link_url
expect(rows[1]).to have_content project_badge.link_url
@@ -49,7 +49,7 @@ feature 'Project Badges' do
click_button 'Add badge'
wait_for_requests
- within '.panel-body' do
+ within '.card-body' do
expect(find('a')[:href]).to eq badge_link_url
expect(find('a img')[:src]).to eq badge_image_url
end
@@ -61,7 +61,7 @@ feature 'Project Badges' do
it 'form is shown when clicking edit button in list' do
page.within '.badge-settings' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
rows[1].find('[aria-label="Edit"]').click
@@ -75,7 +75,7 @@ feature 'Project Badges' do
it 'updates a badge when submitting the edit form' do
page.within '.badge-settings' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
rows[1].find('[aria-label="Edit"]').click
within 'form' do
@@ -86,7 +86,7 @@ feature 'Project Badges' do
wait_for_requests
end
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
expect(rows[1]).to have_content badge_link_url
end
@@ -100,7 +100,7 @@ feature 'Project Badges' do
it 'shows a modal when deleting a badge' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
click_delete_button(rows[1])
@@ -110,14 +110,14 @@ feature 'Project Badges' do
it 'deletes a badge when confirming the modal' do
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 2
click_delete_button(rows[1])
find('.modal .btn-danger').click
wait_for_requests
- rows = all('.panel-body > div')
+ rows = all('.card-body > div')
expect(rows.length).to eq 1
expect(rows[0]).to have_content group_badge.link_url
end
diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb
index 8a9255db9e8..ee5734a9bf1 100644
--- a/spec/features/projects/user_sees_sidebar_spec.rb
+++ b/spec/features/projects/user_sees_sidebar_spec.rb
@@ -44,6 +44,18 @@ describe 'Projects > User sees sidebar' do
expect(page).not_to have_content 'Repository'
expect(page).not_to have_content 'CI / CD'
expect(page).not_to have_content 'Merge Requests'
+ expect(page).not_to have_content 'Operations'
+ end
+ end
+
+ it 'shows build tab if builds are public' do
+ project.public_builds = true
+ project.save
+
+ visit project_path(project)
+
+ within('.nav-sidebar') do
+ expect(page).to have_content 'CI / CD'
end
end
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index e473739a6aa..bbdd98a7623 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -19,6 +19,7 @@ feature 'Projects > Wiki > User previews markdown changes', :js do
visit project_path(project)
find('.shortcuts-wiki').click
+ click_link "Create your first page"
end
context "while creating a new wiki page" do
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 9989e1ffda7..706894f4b32 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -8,6 +8,7 @@ describe "User creates wiki page" do
sign_in(user)
visit(project_wikis_path(project))
+ click_link "Create your first page"
end
context "when wiki is empty" do
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index e019e3ce5a5..272dac127dd 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -11,6 +11,7 @@ describe 'User updates wiki page' do
context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
+ click_link "Create your first page"
end
context 'in a user namespace' do
diff --git a/spec/features/projects/wiki/user_views_wiki_empty_spec.rb b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
new file mode 100644
index 00000000000..83ffbb4a94e
--- /dev/null
+++ b/spec/features/projects/wiki/user_views_wiki_empty_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe 'User views empty wiki' do
+ let(:user) { create(:user) }
+
+ shared_examples 'empty wiki and accessible issues' do
+ it 'show "issue tracker" message' do
+ visit(project_wikis_path(project))
+
+ element = page.find('.row.empty-state')
+
+ expect(element).to have_content('This project has no wiki pages')
+ expect(element).to have_link("issue tracker", href: project_issues_path(project))
+ expect(element).to have_link("Suggest wiki improvement", href: new_project_issue_path(project))
+ end
+ end
+
+ shared_examples 'empty wiki and non-accessible issues' do
+ it 'does not show "issue tracker" message' do
+ visit(project_wikis_path(project))
+
+ element = page.find('.row.empty-state')
+
+ expect(element).to have_content('This project has no wiki pages')
+ expect(element).to have_no_link('Suggest wiki improvement')
+ end
+ end
+
+ context 'when user is logged out and issue tracker is public' do
+ let(:project) { create(:project, :public, :wiki_repo) }
+
+ it_behaves_like 'empty wiki and accessible issues'
+ end
+
+ context 'when user is logged in and not a member' do
+ let(:project) { create(:project, :public, :wiki_repo) }
+
+ before do
+ sign_in(user)
+ end
+
+ it_behaves_like 'empty wiki and accessible issues'
+ end
+
+ context 'when issue tracker is private' do
+ let(:project) { create(:project, :public, :wiki_repo, :issues_private) }
+
+ it_behaves_like 'empty wiki and non-accessible issues'
+ end
+
+ context 'when issue tracker is disabled' do
+ let(:project) { create(:project, :public, :wiki_repo, :issues_disabled) }
+
+ it_behaves_like 'empty wiki and non-accessible issues'
+ end
+
+ context 'when user is logged in and a memeber' do
+ let(:project) { create(:project, :public, :wiki_repo) }
+
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ it 'show "create first page" message' do
+ visit(project_wikis_path(project))
+
+ element = page.find('.row.empty-state')
+
+ element.click_link 'Create your first page'
+
+ expect(page).to have_button('Create page')
+ end
+ end
+end
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index 6661714222a..1de7d9a56a8 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -18,6 +18,7 @@ describe 'User views a wiki page' do
context 'when wiki is empty' do
before do
visit(project_wikis_path(project))
+ click_link "Create your first page"
click_on('New page')
@@ -140,6 +141,7 @@ describe 'User views a wiki page' do
visit(project_path(project))
find('.shortcuts-wiki').click
+ click_link "Create your first page"
expect(page).to have_content('Home · Create Page')
end
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index 43cabd3b9f2..0c28a853b54 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -68,8 +68,10 @@ feature 'Protected Branches', :js do
within form do
find(".js-allowed-to-merge").click
+ wait_for_requests
click_link 'No one'
find(".js-allowed-to-push").click
+ wait_for_requests
click_link 'Developers + Masters'
end
diff --git a/spec/features/signed_commits_spec.rb b/spec/features/signed_commits_spec.rb
index 6088b831c14..db141ef7096 100644
--- a/spec/features/signed_commits_spec.rb
+++ b/spec/features/signed_commits_spec.rb
@@ -96,10 +96,11 @@ describe 'GPG signed commits', :js do
within(find('.commit', text: 'signed commit by bette cartwright')) do
click_on 'Unverified'
- within '.popover' do
- expect(page).to have_content 'This commit was signed with an unverified signature.'
- expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}"
- end
+ end
+
+ within '.popover' do
+ expect(page).to have_content 'This commit was signed with an unverified signature.'
+ expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}"
end
end
@@ -110,12 +111,13 @@ describe 'GPG signed commits', :js do
within(find('.commit', text: 'signed and authored commit by bette cartwright, different email')) do
click_on 'Unverified'
- within '.popover' do
- expect(page).to have_content 'This commit was signed with a verified signature, but the committer email is not verified to belong to the same user.'
- expect(page).to have_content 'Bette Cartwright'
- expect(page).to have_content '@bette.cartwright'
- expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}"
- end
+ end
+
+ within '.popover' do
+ expect(page).to have_content 'This commit was signed with a verified signature, but the committer email is not verified to belong to the same user.'
+ expect(page).to have_content 'Bette Cartwright'
+ expect(page).to have_content '@bette.cartwright'
+ expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}"
end
end
@@ -126,12 +128,13 @@ describe 'GPG signed commits', :js do
within(find('.commit', text: 'signed commit by bette cartwright')) do
click_on 'Unverified'
- within '.popover' do
- expect(page).to have_content "This commit was signed with a different user's verified signature."
- expect(page).to have_content 'Bette Cartwright'
- expect(page).to have_content '@bette.cartwright'
- expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}"
- end
+ end
+
+ within '.popover' do
+ expect(page).to have_content "This commit was signed with a different user's verified signature."
+ expect(page).to have_content 'Bette Cartwright'
+ expect(page).to have_content '@bette.cartwright'
+ expect(page).to have_content "GPG Key ID: #{GpgHelpers::User2.primary_keyid}"
end
end
@@ -142,12 +145,13 @@ describe 'GPG signed commits', :js do
within(find('.commit', text: 'signed and authored commit by nannie bernhard')) do
click_on 'Verified'
- within '.popover' do
- expect(page).to have_content 'This commit was signed with a verified signature and the committer email is verified to belong to the same user.'
- expect(page).to have_content 'Nannie Bernhard'
- expect(page).to have_content '@nannie.bernhard'
- expect(page).to have_content "GPG Key ID: #{GpgHelpers::User1.primary_keyid}"
- end
+ end
+
+ within '.popover' do
+ expect(page).to have_content 'This commit was signed with a verified signature and the committer email is verified to belong to the same user.'
+ expect(page).to have_content 'Nannie Bernhard'
+ expect(page).to have_content '@nannie.bernhard'
+ expect(page).to have_content "GPG Key ID: #{GpgHelpers::User1.primary_keyid}"
end
end
@@ -167,12 +171,13 @@ describe 'GPG signed commits', :js do
within(find('.commit', text: 'signed and authored commit by nannie bernhard')) do
click_on 'Verified'
- within '.popover' do
- expect(page).to have_content 'This commit was signed with a verified signature and the committer email is verified to belong to the same user.'
- expect(page).to have_content 'Nannie Bernhard'
- expect(page).to have_content 'nannie.bernhard@example.com'
- expect(page).to have_content "GPG Key ID: #{GpgHelpers::User1.primary_keyid}"
- end
+ end
+
+ within '.popover' do
+ expect(page).to have_content 'This commit was signed with a verified signature and the committer email is verified to belong to the same user.'
+ expect(page).to have_content 'Nannie Bernhard'
+ expect(page).to have_content 'nannie.bernhard@example.com'
+ expect(page).to have_content "GPG Key ID: #{GpgHelpers::User1.primary_keyid}"
end
end
end
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 19784120108..6be2606fd0d 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -23,7 +23,7 @@ feature 'Triggers', :js do
click_button 'Add trigger'
# See if input has error due to empty value
- expect(page.find('form.gl-show-field-errors .gl-field-error')['style']).to eq 'display: block;'
+ expect(page.find('form.gl-show-field-errors .gl-field-error')).to be_visible
end
scenario 'adds new trigger with description' do
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index e8884bc1a00..c8db82a562f 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -14,7 +14,9 @@ feature 'User uploads avatar to group' do
visible: false
)
- click_button 'Save group'
+ page.within('.gs-general') do
+ click_button 'Save group'
+ end
visit group_path(group)
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 6f968a2c590..1f8d31a5c88 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -394,7 +394,7 @@ feature 'Login' do
end
def ensure_one_active_tab
- expect(page).to have_selector('ul.new-session-tabs > li.active', count: 1)
+ expect(page).to have_selector('ul.new-session-tabs > li > a.active', count: 1)
end
def ensure_one_active_pane
diff --git a/spec/features/users/user_browses_projects_on_user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 7bede0b0d48..5478e38ce70 100644
--- a/spec/features/users/user_browses_projects_on_user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -26,18 +26,23 @@ describe 'Users > User browses projects on user page', :js do
end
end
+ it 'hides loading spinner after load', :js do
+ visit user_path(user)
+ click_nav_link('Personal projects')
+
+ wait_for_requests
+
+ expect(page).not_to have_selector('.loading-status .loading', visible: true)
+ end
+
it 'paginates projects', :js do
project = create(:project, namespace: user.namespace, updated_at: 2.minutes.since)
project2 = create(:project, namespace: user.namespace, updated_at: 1.minute.since)
allow(Project).to receive(:default_per_page).and_return(1)
sign_in(user)
-
visit user_path(user)
-
- page.within('.user-profile-nav') do
- click_link('Personal projects')
- end
+ click_nav_link('Personal projects')
wait_for_requests
@@ -92,7 +97,6 @@ describe 'Users > User browses projects on user page', :js do
click_nav_link('Personal projects')
expect(title).to start_with(user.name)
-
expect(page).to have_content(private_project.name)
expect(page).to have_content(public_project.name)
expect(page).to have_content(internal_project.name)
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index 233102c4314..7be8c9e3e67 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -112,7 +112,8 @@
"rebase_commit_sha": { "type": ["string", "null"] },
"rebase_in_progress": { "type": "boolean" },
"can_push_to_source_branch": { "type": "boolean" },
- "rebase_path": { "type": ["string", "null"] }
+ "rebase_path": { "type": ["string", "null"] },
+ "squash": { "type": "boolean" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v3/merge_requests.json b/spec/fixtures/api/schemas/public_api/v3/merge_requests.json
index b5c74bcc26e..9af7a8c9f4f 100644
--- a/spec/fixtures/api/schemas/public_api/v3/merge_requests.json
+++ b/spec/fixtures/api/schemas/public_api/v3/merge_requests.json
@@ -73,7 +73,8 @@
"should_remove_source_branch": { "type": ["boolean", "null"] },
"force_remove_source_branch": { "type": ["boolean", "null"] },
"web_url": { "type": "uri" },
- "subscribed": { "type": ["boolean"] }
+ "subscribed": { "type": ["boolean"] },
+ "squash": { "type": "boolean" }
},
"required": [
"id", "iid", "project_id", "title", "description",
@@ -83,7 +84,7 @@
"labels", "work_in_progress", "milestone", "merge_when_build_succeeds",
"merge_status", "sha", "merge_commit_sha", "user_notes_count",
"should_remove_source_branch", "force_remove_source_branch",
- "web_url", "subscribed"
+ "web_url", "subscribed", "squash"
],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
index 0dc2eabec5d..f97461ce9cc 100644
--- a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
+++ b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
@@ -75,6 +75,7 @@
"force_remove_source_branch": { "type": ["boolean", "null"] },
"discussion_locked": { "type": ["boolean", "null"] },
"web_url": { "type": "uri" },
+ "squash": { "type": "boolean" },
"time_stats": {
"time_estimate": { "type": "integer" },
"total_time_spent": { "type": "integer" },
@@ -91,7 +92,7 @@
"labels", "work_in_progress", "milestone", "merge_when_pipeline_succeeds",
"merge_status", "sha", "merge_commit_sha", "user_notes_count",
"should_remove_source_branch", "force_remove_source_branch",
- "web_url"
+ "web_url", "squash"
],
"additionalProperties": false
}
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 7b59fde999d..cddb49b320f 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -45,22 +45,22 @@ describe IssuablesHelper do
it 'returns "Open" when state is :opened' do
expect(helper.issuables_state_counter_text(:issues, :opened, true))
- .to eq('<span>Open</span> <span class="badge">42</span>')
+ .to eq('<span>Open</span> <span class="badge badge-pill">42</span>')
end
it 'returns "Closed" when state is :closed' do
expect(helper.issuables_state_counter_text(:issues, :closed, true))
- .to eq('<span>Closed</span> <span class="badge">42</span>')
+ .to eq('<span>Closed</span> <span class="badge badge-pill">42</span>')
end
it 'returns "Merged" when state is :merged' do
expect(helper.issuables_state_counter_text(:merge_requests, :merged, true))
- .to eq('<span>Merged</span> <span class="badge">42</span>')
+ .to eq('<span>Merged</span> <span class="badge badge-pill">42</span>')
end
it 'returns "All" when state is :all' do
expect(helper.issuables_state_counter_text(:merge_requests, :all, true))
- .to eq('<span>All</span> <span class="badge">42</span>')
+ .to eq('<span>All</span> <span class="badge badge-pill">42</span>')
end
end
end
diff --git a/spec/javascripts/badges/components/badge_list_spec.js b/spec/javascripts/badges/components/badge_list_spec.js
index 9439c578973..02e59ae0843 100644
--- a/spec/javascripts/badges/components/badge_list_spec.js
+++ b/spec/javascripts/badges/components/badge_list_spec.js
@@ -33,7 +33,7 @@ describe('BadgeList component', () => {
});
it('renders a header with the badge count', () => {
- const header = vm.$el.querySelector('.panel-heading');
+ const header = vm.$el.querySelector('.card-header');
expect(header).toHaveText(new RegExp(`Your badges\\s+${numberOfDummyBadges}`));
});
diff --git a/spec/javascripts/badges/components/badge_settings_spec.js b/spec/javascripts/badges/components/badge_settings_spec.js
index 3db02982ad4..59367c85125 100644
--- a/spec/javascripts/badges/components/badge_settings_spec.js
+++ b/spec/javascripts/badges/components/badge_settings_spec.js
@@ -60,7 +60,7 @@ describe('BadgeSettings component', () => {
});
it('displays badge list', () => {
- const badgeListElement = vm.$el.querySelector('.panel');
+ const badgeListElement = vm.$el.querySelector('.card');
expect(badgeListElement).not.toBe(null);
expect(badgeListElement).toBeVisible();
expect(badgeListElement).toContainText('Your badges');
@@ -87,7 +87,7 @@ describe('BadgeSettings component', () => {
});
it('displays no badge list', () => {
- const badgeListElement = vm.$el.querySelector('.panel');
+ const badgeListElement = vm.$el.querySelector('.card');
expect(badgeListElement).toBeHidden();
});
});
diff --git a/spec/javascripts/behaviors/secret_values_spec.js b/spec/javascripts/behaviors/secret_values_spec.js
index 38d9bba6868..95122fcf30f 100644
--- a/spec/javascripts/behaviors/secret_values_spec.js
+++ b/spec/javascripts/behaviors/secret_values_spec.js
@@ -9,7 +9,7 @@ function generateValueMarkup(
<div class="${placeholderClass}">
***
</div>
- <div class="hide ${valueClass}">
+ <div class="hidden ${valueClass}">
${secret}
</div>
`;
diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js
index aa87956109f..cb0f2ba686d 100644
--- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js
+++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_spec.js
@@ -257,9 +257,9 @@ describe('BalsamiqViewer', () => {
name = 'name';
resource = 'resource';
template = `
- <div class="panel panel-default">
- <div class="panel-heading">name</div>
- <div class="panel-body">
+ <div class="card">
+ <div class="card-header">name</div>
+ <div class="card-body">
<img class="img-thumbnail" src="data:image/png;base64,image"/>
</div>
</div>
diff --git a/spec/javascripts/blob/sketch/index_spec.js b/spec/javascripts/blob/sketch/index_spec.js
index 79f40559817..e062a068a92 100644
--- a/spec/javascripts/blob/sketch/index_spec.js
+++ b/spec/javascripts/blob/sketch/index_spec.js
@@ -82,7 +82,7 @@ describe('Sketch viewer', () => {
const img = document.querySelector('#js-sketch-viewer img');
expect(img).not.toBeNull();
- expect(img.classList.contains('img-responsive')).toBeTruthy();
+ expect(img.classList.contains('img-fluid')).toBeTruthy();
});
it('renders link to image', () => {
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index 03df6c06691..c06b2f60813 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -83,13 +83,13 @@ describe('Board list component', () => {
it('renders issues', () => {
expect(
- component.$el.querySelectorAll('.card').length,
+ component.$el.querySelectorAll('.board-card').length,
).toBe(1);
});
it('sets data attribute with issue id', () => {
expect(
- component.$el.querySelector('.card').getAttribute('data-issue-id'),
+ component.$el.querySelector('.board-card').getAttribute('data-issue-id'),
).toBe('1');
});
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index be1ea0b57b4..abeef272c68 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -70,19 +70,19 @@ describe('Issue card component', () => {
it('renders issue title', () => {
expect(
- component.$el.querySelector('.card-title').textContent,
+ component.$el.querySelector('.board-card-title').textContent,
).toContain(issue.title);
});
it('includes issue base in link', () => {
expect(
- component.$el.querySelector('.card-title a').getAttribute('href'),
+ component.$el.querySelector('.board-card-title a').getAttribute('href'),
).toContain('/test');
});
it('includes issue title on link', () => {
expect(
- component.$el.querySelector('.card-title a').getAttribute('title'),
+ component.$el.querySelector('.board-card-title a').getAttribute('title'),
).toBe(issue.title);
});
@@ -105,14 +105,14 @@ describe('Issue card component', () => {
it('renders issue ID with #', () => {
expect(
- component.$el.querySelector('.card-number').textContent,
+ component.$el.querySelector('.board-card-number').textContent,
).toContain(`#${issue.id}`);
});
describe('assignee', () => {
it('does not render assignee', () => {
expect(
- component.$el.querySelector('.card-assignee .avatar'),
+ component.$el.querySelector('.board-card-assignee .avatar'),
).toBeNull();
});
@@ -125,25 +125,25 @@ describe('Issue card component', () => {
it('renders assignee', () => {
expect(
- component.$el.querySelector('.card-assignee .avatar'),
+ component.$el.querySelector('.board-card-assignee .avatar'),
).not.toBeNull();
});
it('sets title', () => {
expect(
- component.$el.querySelector('.card-assignee img').getAttribute('data-original-title'),
+ component.$el.querySelector('.board-card-assignee img').getAttribute('data-original-title'),
).toContain(`Assigned to ${user.name}`);
});
it('sets users path', () => {
expect(
- component.$el.querySelector('.card-assignee a').getAttribute('href'),
+ component.$el.querySelector('.board-card-assignee a').getAttribute('href'),
).toBe('/test');
});
it('renders avatar', () => {
expect(
- component.$el.querySelector('.card-assignee img'),
+ component.$el.querySelector('.board-card-assignee img'),
).not.toBeNull();
});
});
@@ -161,10 +161,10 @@ describe('Issue card component', () => {
it('displays defaults avatar if users avatar is null', () => {
expect(
- component.$el.querySelector('.card-assignee img'),
+ component.$el.querySelector('.board-card-assignee img'),
).not.toBeNull();
expect(
- component.$el.querySelector('.card-assignee img').getAttribute('src'),
+ component.$el.querySelector('.board-card-assignee img').getAttribute('src'),
).toBe('default_avatar');
});
});
@@ -197,7 +197,7 @@ describe('Issue card component', () => {
});
it('renders all four assignees', () => {
- expect(component.$el.querySelectorAll('.card-assignee .avatar').length).toEqual(4);
+ expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(4);
});
describe('more than four assignees', () => {
@@ -213,11 +213,11 @@ describe('Issue card component', () => {
});
it('renders more avatar counter', () => {
- expect(component.$el.querySelector('.card-assignee .avatar-counter').innerText).toEqual('+2');
+ expect(component.$el.querySelector('.board-card-assignee .avatar-counter').innerText).toEqual('+2');
});
it('renders three assignees', () => {
- expect(component.$el.querySelectorAll('.card-assignee .avatar').length).toEqual(3);
+ expect(component.$el.querySelectorAll('.board-card-assignee .avatar').length).toEqual(3);
});
it('renders 99+ avatar counter', (done) => {
@@ -232,7 +232,7 @@ describe('Issue card component', () => {
}
Vue.nextTick(() => {
- expect(component.$el.querySelector('.card-assignee .avatar-counter').innerText).toEqual('99+');
+ expect(component.$el.querySelector('.board-card-assignee .avatar-counter').innerText).toEqual('99+');
done();
});
});
@@ -248,13 +248,13 @@ describe('Issue card component', () => {
it('renders list label', () => {
expect(
- component.$el.querySelectorAll('.label').length,
+ component.$el.querySelectorAll('.badge').length,
).toBe(2);
});
it('renders label', () => {
const nodes = [];
- component.$el.querySelectorAll('.label').forEach((label) => {
+ component.$el.querySelectorAll('.badge').forEach((label) => {
nodes.push(label.title);
});
@@ -265,13 +265,13 @@ describe('Issue card component', () => {
it('sets label description as title', () => {
expect(
- component.$el.querySelector('.label').getAttribute('title'),
+ component.$el.querySelector('.badge').getAttribute('title'),
).toContain(label1.description);
});
it('sets background color of button', () => {
const nodes = [];
- component.$el.querySelectorAll('.label').forEach((label) => {
+ component.$el.querySelectorAll('.badge').forEach((label) => {
nodes.push(label.style.backgroundColor);
});
@@ -288,7 +288,7 @@ describe('Issue card component', () => {
Vue.nextTick()
.then(() => {
expect(
- component.$el.querySelectorAll('.label').length,
+ component.$el.querySelectorAll('.badge').length,
).toBe(2);
expect(
component.$el.textContent,
diff --git a/spec/javascripts/fixtures/event_filter.html.haml b/spec/javascripts/fixtures/event_filter.html.haml
index 5477c6075f0..aa7af61c7eb 100644
--- a/spec/javascripts/fixtures/event_filter.html.haml
+++ b/spec/javascripts/fixtures/event_filter.html.haml
@@ -1,4 +1,4 @@
-%ul.nav-links.event-filter.scrolling-tabs
+%ul.nav-links.event-filter.scrolling-tabs.nav.nav-tabs
%li.active
%a.event-filter-link{ id: "all_event_filter", title: "Filter by all", href: "/dashboard/activity"}
%span
diff --git a/spec/javascripts/fixtures/issue_sidebar_label.html.haml b/spec/javascripts/fixtures/issue_sidebar_label.html.haml
index 397bdc85c67..06ce248dc9c 100644
--- a/spec/javascripts/fixtures/issue_sidebar_label.html.haml
+++ b/spec/javascripts/fixtures/issue_sidebar_label.html.haml
@@ -1,7 +1,7 @@
.block.labels
.sidebar-collapsed-icon.js-sidebar-labels-tooltip
.title.hide-collapsed
- %a.edit-link.pull-right{ href: "#" }
+ %a.edit-link.float-right{ href: "#" }
Edit
.selectbox.hide-collapsed{ style: "display: none;" }
.dropdown
diff --git a/spec/javascripts/fixtures/linked_tabs.html.haml b/spec/javascripts/fixtures/linked_tabs.html.haml
index c38fe8b1f25..632606e0536 100644
--- a/spec/javascripts/fixtures/linked_tabs.html.haml
+++ b/spec/javascripts/fixtures/linked_tabs.html.haml
@@ -1,9 +1,9 @@
-%ul.nav-links.new-session-tabs.linked-tabs
- %li
- %a{ href: 'foo/bar/1', data: { target: 'div#tab1', action: 'tab1', toggle: 'tab' } }
+%ul.nav.nav-tabs.new-session-tabs.linked-tabs
+ %li.nav-item
+ %a.nav-link{ href: 'foo/bar/1', data: { target: 'div#tab1', action: 'tab1', toggle: 'tab' } }
Tab 1
- %li
- %a{ href: 'foo/bar/1/context', data: { target: 'div#tab2', action: 'tab2', toggle: 'tab' } }
+ %li.nav-item
+ %a.nav-link{ href: 'foo/bar/1/context', data: { target: 'div#tab2', action: 'tab2', toggle: 'tab' } }
Tab 2
.tab-content
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index 7f9c4811fba..175f386b60e 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -70,9 +70,9 @@ describe('glDropdown', function describeDropdown() {
it('should open on click', () => {
initDropDown.call(this, false);
- expect(this.dropdownContainerElement).not.toHaveClass('open');
+ expect(this.dropdownContainerElement).not.toHaveClass('show');
this.dropdownButtonElement.click();
- expect(this.dropdownContainerElement).toHaveClass('open');
+ expect(this.dropdownContainerElement).toHaveClass('show');
});
it('escapes HTML as text', () => {
@@ -134,12 +134,12 @@ describe('glDropdown', function describeDropdown() {
});
it('should click the selected item on ENTER keypress', () => {
- expect(this.dropdownContainerElement).toHaveClass('open');
+ expect(this.dropdownContainerElement).toHaveClass('show');
const randomIndex = Math.floor(Math.random() * (this.projectsData.length - 1)) + 0;
navigateWithKeys('down', randomIndex, () => {
const visitUrl = spyOnDependency(GLDropdown, 'visitUrl').and.stub();
navigateWithKeys('enter', null, () => {
- expect(this.dropdownContainerElement).not.toHaveClass('open');
+ expect(this.dropdownContainerElement).not.toHaveClass('show');
const link = $(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement);
expect(link).toHaveClass('is-active');
const linkedLocation = link.attr('href');
@@ -149,13 +149,13 @@ describe('glDropdown', function describeDropdown() {
});
it('should close on ESC keypress', () => {
- expect(this.dropdownContainerElement).toHaveClass('open');
+ expect(this.dropdownContainerElement).toHaveClass('show');
this.dropdownContainerElement.trigger({
type: 'keyup',
which: ARROW_KEYS.ESC,
keyCode: ARROW_KEYS.ESC
});
- expect(this.dropdownContainerElement).not.toHaveClass('open');
+ expect(this.dropdownContainerElement).not.toHaveClass('show');
});
});
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 0952356c2f4..648fb3e9bd3 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -974,7 +974,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
).toBeFalsy();
expect(
$tempNoteHeader
- .find('.hidden-xs')
+ .find('.d-none.d-sm-block')
.text()
.trim(),
).toEqual(currentUserFullname);
@@ -1020,7 +1020,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
const $tempNoteHeader = $tempNote.find('.note-header');
expect(
$tempNoteHeader
- .find('.hidden-xs')
+ .find('.d-none.d-sm-block')
.text()
.trim(),
).toEqual('Foo &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;');
diff --git a/spec/javascripts/pipelines/empty_state_spec.js b/spec/javascripts/pipelines/empty_state_spec.js
index 71f77e5f42e..1e41a7bfe7c 100644
--- a/spec/javascripts/pipelines/empty_state_spec.js
+++ b/spec/javascripts/pipelines/empty_state_spec.js
@@ -29,7 +29,7 @@ describe('Pipelines Empty State', () => {
expect(
component.$el.querySelector('p').innerHTML.trim().replace(/\n+\s+/m, ' ').replace(/\s\s+/g, ' '),
- ).toContain('Continous Integration can help catch bugs by running your tests automatically,');
+ ).toContain('Continuous Integration can help catch bugs by running your tests automatically,');
expect(
component.$el.querySelector('p').innerHTML.trim().replace(/\n+\s+/m, ' ').replace(/\s\s+/g, ' '),
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js
new file mode 100644
index 00000000000..21805ef0b28
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown_spec.js
@@ -0,0 +1,103 @@
+import Vue from 'vue';
+import GkeMachineTypeDropdown from '~/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue';
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import {
+ SET_PROJECT,
+ SET_PROJECT_BILLING_STATUS,
+ SET_ZONE,
+ SET_MACHINE_TYPES,
+} from '~/projects/gke_cluster_dropdowns/store/mutation_types';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import {
+ selectedZoneMock,
+ selectedProjectMock,
+ selectedMachineTypeMock,
+ gapiMachineTypesResponseMock,
+} from '../mock_data';
+
+const componentConfig = {
+ fieldId: 'cluster_provider_gcp_attributes_gcp_machine_type',
+ fieldName: 'cluster[provider_gcp_attributes][gcp_machine_type]',
+};
+
+const LABELS = {
+ LOADING: 'Fetching machine types',
+ DISABLED_NO_PROJECT: 'Select project and zone to choose machine type',
+ DISABLED_NO_ZONE: 'Select zone to choose machine type',
+ DEFAULT: 'Select machine type',
+};
+
+const createComponent = (store, props = componentConfig) => {
+ const Component = Vue.extend(GkeMachineTypeDropdown);
+
+ return mountComponentWithStore(Component, {
+ el: null,
+ props,
+ store,
+ });
+};
+
+describe('GkeMachineTypeDropdown', () => {
+ let vm;
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ vm = createComponent(store);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('shows various toggle text depending on state', () => {
+ it('returns disabled state toggle text when no project and zone are selected', () => {
+ expect(vm.toggleText).toBe(LABELS.DISABLED_NO_PROJECT);
+ });
+
+ it('returns disabled state toggle text when no zone is selected', () => {
+ vm.$store.commit(SET_PROJECT, selectedProjectMock);
+ vm.$store.commit(SET_PROJECT_BILLING_STATUS, true);
+
+ expect(vm.toggleText).toBe(LABELS.DISABLED_NO_ZONE);
+ });
+
+ it('returns loading toggle text', () => {
+ vm.isLoading = true;
+
+ expect(vm.toggleText).toBe(LABELS.LOADING);
+ });
+
+ it('returns default toggle text', () => {
+ expect(vm.toggleText).toBe(LABELS.DISABLED_NO_PROJECT);
+
+ vm.$store.commit(SET_PROJECT, selectedProjectMock);
+ vm.$store.commit(SET_PROJECT_BILLING_STATUS, true);
+ vm.$store.commit(SET_ZONE, selectedZoneMock);
+
+ expect(vm.toggleText).toBe(LABELS.DEFAULT);
+ });
+
+ it('returns machine type name if machine type selected', () => {
+ vm.setItem(selectedMachineTypeMock);
+
+ expect(vm.toggleText).toBe(selectedMachineTypeMock);
+ });
+ });
+
+ describe('form input', () => {
+ it('reflects new value when dropdown item is clicked', done => {
+ expect(vm.$el.querySelector('input').value).toBe('');
+ vm.$store.commit(SET_MACHINE_TYPES, gapiMachineTypesResponseMock.items);
+
+ return vm.$nextTick().then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm.$nextTick().then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedMachineTypeMock);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js
new file mode 100644
index 00000000000..d4fcb2dc8ff
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown_spec.js
@@ -0,0 +1,92 @@
+import Vue from 'vue';
+import GkeProjectIdDropdown from '~/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue';
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import { SET_PROJECTS } from '~/projects/gke_cluster_dropdowns/store/mutation_types';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { emptyProjectMock, selectedProjectMock } from '../mock_data';
+
+const componentConfig = {
+ docsUrl: 'https://console.cloud.google.com/home/dashboard',
+ fieldId: 'cluster_provider_gcp_attributes_gcp_project_id',
+ fieldName: 'cluster[provider_gcp_attributes][gcp_project_id]',
+};
+
+const LABELS = {
+ LOADING: 'Fetching projects',
+ VALIDATING_PROJECT_BILLING: 'Validating project billing status',
+ DEFAULT: 'Select project',
+ EMPTY: 'No projects found',
+};
+
+const createComponent = (store, props = componentConfig) => {
+ const Component = Vue.extend(GkeProjectIdDropdown);
+
+ return mountComponentWithStore(Component, {
+ el: null,
+ props,
+ store,
+ });
+};
+
+describe('GkeProjectIdDropdown', () => {
+ let vm;
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ vm = createComponent(store);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('toggleText', () => {
+ it('returns loading toggle text', () => {
+ expect(vm.toggleText).toBe(LABELS.LOADING);
+ });
+
+ it('returns project billing validation text', () => {
+ vm.setIsValidatingProjectBilling(true);
+ expect(vm.toggleText).toBe(LABELS.VALIDATING_PROJECT_BILLING);
+ });
+
+ it('returns default toggle text', done =>
+ vm.$nextTick().then(() => {
+ vm.setItem(emptyProjectMock);
+
+ expect(vm.toggleText).toBe(LABELS.DEFAULT);
+ done();
+ }));
+
+ it('returns project name if project selected', done =>
+ vm.$nextTick().then(() => {
+ expect(vm.toggleText).toBe(selectedProjectMock.name);
+ done();
+ }));
+
+ it('returns empty toggle text', done =>
+ vm.$nextTick().then(() => {
+ vm.$store.commit(SET_PROJECTS, null);
+ vm.setItem(emptyProjectMock);
+
+ expect(vm.toggleText).toBe(LABELS.EMPTY);
+ done();
+ }));
+ });
+
+ describe('selectItem', () => {
+ it('reflects new value when dropdown item is clicked', done => {
+ expect(vm.$el.querySelector('input').value).toBe('');
+
+ return vm.$nextTick().then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm.$nextTick().then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedProjectMock.projectId);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js
new file mode 100644
index 00000000000..89a4a7ea2ce
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown_spec.js
@@ -0,0 +1,88 @@
+import Vue from 'vue';
+import GkeZoneDropdown from '~/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue';
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import {
+ SET_PROJECT,
+ SET_ZONES,
+ SET_PROJECT_BILLING_STATUS,
+} from '~/projects/gke_cluster_dropdowns/store/mutation_types';
+import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
+import { selectedZoneMock, selectedProjectMock, gapiZonesResponseMock } from '../mock_data';
+
+const componentConfig = {
+ fieldId: 'cluster_provider_gcp_attributes_gcp_zone',
+ fieldName: 'cluster[provider_gcp_attributes][gcp_zone]',
+};
+
+const LABELS = {
+ LOADING: 'Fetching zones',
+ DISABLED: 'Select project to choose zone',
+ DEFAULT: 'Select zone',
+};
+
+const createComponent = (store, props = componentConfig) => {
+ const Component = Vue.extend(GkeZoneDropdown);
+
+ return mountComponentWithStore(Component, {
+ el: null,
+ props,
+ store,
+ });
+};
+
+describe('GkeZoneDropdown', () => {
+ let vm;
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ vm = createComponent(store);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('toggleText', () => {
+ it('returns disabled state toggle text', () => {
+ expect(vm.toggleText).toBe(LABELS.DISABLED);
+ });
+
+ it('returns loading toggle text', () => {
+ vm.isLoading = true;
+
+ expect(vm.toggleText).toBe(LABELS.LOADING);
+ });
+
+ it('returns default toggle text', () => {
+ expect(vm.toggleText).toBe(LABELS.DISABLED);
+
+ vm.$store.commit(SET_PROJECT, selectedProjectMock);
+ vm.$store.commit(SET_PROJECT_BILLING_STATUS, true);
+
+ expect(vm.toggleText).toBe(LABELS.DEFAULT);
+ });
+
+ it('returns project name if project selected', () => {
+ vm.setItem(selectedZoneMock);
+
+ expect(vm.toggleText).toBe(selectedZoneMock);
+ });
+ });
+
+ describe('selectItem', () => {
+ it('reflects new value when dropdown item is clicked', done => {
+ expect(vm.$el.querySelector('input').value).toBe('');
+ vm.$store.commit(SET_ZONES, gapiZonesResponseMock.items);
+
+ return vm.$nextTick().then(() => {
+ vm.$el.querySelector('.dropdown-content button').click();
+
+ return vm.$nextTick().then(() => {
+ expect(vm.$el.querySelector('input').value).toBe(selectedZoneMock);
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/helpers.js b/spec/javascripts/projects/gke_cluster_dropdowns/helpers.js
new file mode 100644
index 00000000000..6df511e9157
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/helpers.js
@@ -0,0 +1,49 @@
+import {
+ gapiProjectsResponseMock,
+ gapiZonesResponseMock,
+ gapiMachineTypesResponseMock,
+} from './mock_data';
+
+// eslint-disable-next-line import/prefer-default-export
+export const gapi = () => ({
+ client: {
+ cloudbilling: {
+ projects: {
+ getBillingInfo: () =>
+ new Promise(resolve => {
+ resolve({
+ result: { billingEnabled: true },
+ });
+ }),
+ },
+ },
+ cloudresourcemanager: {
+ projects: {
+ list: () =>
+ new Promise(resolve => {
+ resolve({
+ result: { ...gapiProjectsResponseMock },
+ });
+ }),
+ },
+ },
+ compute: {
+ zones: {
+ list: () =>
+ new Promise(resolve => {
+ resolve({
+ result: { ...gapiZonesResponseMock },
+ });
+ }),
+ },
+ machineTypes: {
+ list: () =>
+ new Promise(resolve => {
+ resolve({
+ result: { ...gapiMachineTypesResponseMock },
+ });
+ }),
+ },
+ },
+ },
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/mock_data.js b/spec/javascripts/projects/gke_cluster_dropdowns/mock_data.js
new file mode 100644
index 00000000000..d9f5dbc636f
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/mock_data.js
@@ -0,0 +1,75 @@
+export const emptyProjectMock = {
+ projectId: '',
+ name: '',
+};
+
+export const selectedProjectMock = {
+ projectId: 'gcp-project-123',
+ name: 'gcp-project',
+};
+
+export const selectedZoneMock = 'us-central1-a';
+
+export const selectedMachineTypeMock = 'n1-standard-2';
+
+export const gapiProjectsResponseMock = {
+ projects: [
+ {
+ projectNumber: '1234',
+ projectId: 'gcp-project-123',
+ lifecycleState: 'ACTIVE',
+ name: 'gcp-project',
+ createTime: '2017-12-16T01:48:29.129Z',
+ parent: {
+ type: 'organization',
+ id: '12345',
+ },
+ },
+ ],
+};
+
+export const gapiZonesResponseMock = {
+ kind: 'compute#zoneList',
+ id: 'projects/gitlab-internal-153318/zones',
+ items: [
+ {
+ kind: 'compute#zone',
+ id: '2000',
+ creationTimestamp: '1969-12-31T16:00:00.000-08:00',
+ name: 'us-central1-a',
+ description: 'us-central1-a',
+ status: 'UP',
+ region:
+ 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/regions/us-central1',
+ selfLink:
+ 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a',
+ availableCpuPlatforms: ['Intel Skylake', 'Intel Broadwell', 'Intel Sandy Bridge'],
+ },
+ ],
+ selfLink: 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones',
+};
+
+export const gapiMachineTypesResponseMock = {
+ kind: 'compute#machineTypeList',
+ id: 'projects/gitlab-internal-153318/zones/us-central1-a/machineTypes',
+ items: [
+ {
+ kind: 'compute#machineType',
+ id: '3002',
+ creationTimestamp: '1969-12-31T16:00:00.000-08:00',
+ name: 'n1-standard-2',
+ description: '2 vCPUs, 7.5 GB RAM',
+ guestCpus: 2,
+ memoryMb: 7680,
+ imageSpaceGb: 10,
+ maximumPersistentDisks: 64,
+ maximumPersistentDisksSizeGb: '65536',
+ zone: 'us-central1-a',
+ selfLink:
+ 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a/machineTypes/n1-standard-2',
+ isSharedCpu: false,
+ },
+ ],
+ selfLink:
+ 'https://www.googleapis.com/compute/v1/projects/gitlab-internal-153318/zones/us-central1-a/machineTypes',
+};
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/stores/actions_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/stores/actions_spec.js
new file mode 100644
index 00000000000..9d892b8185b
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/stores/actions_spec.js
@@ -0,0 +1,131 @@
+import testAction from 'spec/helpers/vuex_action_helper';
+import * as actions from '~/projects/gke_cluster_dropdowns/store/actions';
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import { gapi } from '../helpers';
+import { selectedProjectMock, selectedZoneMock, selectedMachineTypeMock } from '../mock_data';
+
+describe('GCP Cluster Dropdown Store Actions', () => {
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ });
+
+ describe('setProject', () => {
+ it('should set project', done => {
+ testAction(
+ actions.setProject,
+ selectedProjectMock,
+ { selectedProject: {} },
+ [{ type: 'SET_PROJECT', payload: selectedProjectMock }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setZone', () => {
+ it('should set zone', done => {
+ testAction(
+ actions.setZone,
+ selectedZoneMock,
+ { selectedZone: '' },
+ [{ type: 'SET_ZONE', payload: selectedZoneMock }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setMachineType', () => {
+ it('should set machine type', done => {
+ testAction(
+ actions.setMachineType,
+ selectedMachineTypeMock,
+ { selectedMachineType: '' },
+ [{ type: 'SET_MACHINE_TYPE', payload: selectedMachineTypeMock }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('setIsValidatingProjectBilling', () => {
+ it('should set machine type', done => {
+ testAction(
+ actions.setIsValidatingProjectBilling,
+ true,
+ { isValidatingProjectBilling: null },
+ [{ type: 'SET_IS_VALIDATING_PROJECT_BILLING', payload: true }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('async fetch methods', () => {
+ window.gapi = gapi();
+
+ describe('fetchProjects', () => {
+ it('fetches projects from Google API', done => {
+ store
+ .dispatch('fetchProjects')
+ .then(() => {
+ expect(store.state.projects[0].projectId).toEqual(selectedProjectMock.projectId);
+ expect(store.state.projects[0].name).toEqual(selectedProjectMock.name);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('validateProjectBilling', () => {
+ it('checks project billing status from Google API', done => {
+ testAction(
+ actions.validateProjectBilling,
+ true,
+ {
+ selectedProject: selectedProjectMock,
+ selectedZone: '',
+ selectedMachineType: '',
+ projectHasBillingEnabled: null,
+ },
+ [
+ { type: 'SET_ZONE', payload: '' },
+ { type: 'SET_MACHINE_TYPE', payload: '' },
+ { type: 'SET_PROJECT_BILLING_STATUS', payload: true },
+ ],
+ [{ type: 'setIsValidatingProjectBilling', payload: false }],
+ done,
+ );
+ });
+ });
+
+ describe('fetchZones', () => {
+ it('fetches zones from Google API', done => {
+ store
+ .dispatch('fetchZones')
+ .then(() => {
+ expect(store.state.zones[0].name).toEqual(selectedZoneMock);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('fetchMachineTypes', () => {
+ it('fetches machine types from Google API', done => {
+ store
+ .dispatch('fetchMachineTypes')
+ .then(() => {
+ expect(store.state.machineTypes[0].name).toEqual(selectedMachineTypeMock);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/stores/getters_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/stores/getters_spec.js
new file mode 100644
index 00000000000..6f89158f807
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/stores/getters_spec.js
@@ -0,0 +1,65 @@
+import * as getters from '~/projects/gke_cluster_dropdowns/store/getters';
+import { selectedProjectMock, selectedZoneMock, selectedMachineTypeMock } from '../mock_data';
+
+describe('GCP Cluster Dropdown Store Getters', () => {
+ let state;
+
+ describe('valid states', () => {
+ beforeEach(() => {
+ state = {
+ selectedProject: selectedProjectMock,
+ selectedZone: selectedZoneMock,
+ selectedMachineType: selectedMachineTypeMock,
+ };
+ });
+
+ describe('hasProject', () => {
+ it('should return true when project is selected', () => {
+ expect(getters.hasProject(state)).toEqual(true);
+ });
+ });
+
+ describe('hasZone', () => {
+ it('should return true when zone is selected', () => {
+ expect(getters.hasZone(state)).toEqual(true);
+ });
+ });
+
+ describe('hasMachineType', () => {
+ it('should return true when machine type is selected', () => {
+ expect(getters.hasMachineType(state)).toEqual(true);
+ });
+ });
+ });
+
+ describe('invalid states', () => {
+ beforeEach(() => {
+ state = {
+ selectedProject: {
+ projectId: '',
+ name: '',
+ },
+ selectedZone: '',
+ selectedMachineType: '',
+ };
+ });
+
+ describe('hasProject', () => {
+ it('should return false when project is not selected', () => {
+ expect(getters.hasProject(state)).toEqual(false);
+ });
+ });
+
+ describe('hasZone', () => {
+ it('should return false when zone is not selected', () => {
+ expect(getters.hasZone(state)).toEqual(false);
+ });
+ });
+
+ describe('hasMachineType', () => {
+ it('should return false when machine type is not selected', () => {
+ expect(getters.hasMachineType(state)).toEqual(false);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/projects/gke_cluster_dropdowns/stores/mutations_spec.js b/spec/javascripts/projects/gke_cluster_dropdowns/stores/mutations_spec.js
new file mode 100644
index 00000000000..7f8c4f314e4
--- /dev/null
+++ b/spec/javascripts/projects/gke_cluster_dropdowns/stores/mutations_spec.js
@@ -0,0 +1,87 @@
+import { createStore } from '~/projects/gke_cluster_dropdowns/store';
+import * as types from '~/projects/gke_cluster_dropdowns/store/mutation_types';
+import {
+ selectedProjectMock,
+ selectedZoneMock,
+ selectedMachineTypeMock,
+ gapiProjectsResponseMock,
+ gapiZonesResponseMock,
+ gapiMachineTypesResponseMock,
+} from '../mock_data';
+
+describe('GCP Cluster Dropdown Store Mutations', () => {
+ let store;
+
+ beforeEach(() => {
+ store = createStore();
+ });
+
+ describe('SET_PROJECT', () => {
+ it('should set GCP project as selectedProject', () => {
+ const projectToSelect = gapiProjectsResponseMock.projects[0];
+
+ store.commit(types.SET_PROJECT, projectToSelect);
+
+ expect(store.state.selectedProject.projectId).toEqual(selectedProjectMock.projectId);
+ expect(store.state.selectedProject.name).toEqual(selectedProjectMock.name);
+ });
+ });
+
+ describe('SET_PROJECT_BILLING_STATUS', () => {
+ it('should set project billing status', () => {
+ store.commit(types.SET_PROJECT_BILLING_STATUS, true);
+
+ expect(store.state.projectHasBillingEnabled).toBeTruthy();
+ });
+ });
+
+ describe('SET_ZONE', () => {
+ it('should set GCP zone as selectedZone', () => {
+ const zoneToSelect = gapiZonesResponseMock.items[0].name;
+
+ store.commit(types.SET_ZONE, zoneToSelect);
+
+ expect(store.state.selectedZone).toEqual(selectedZoneMock);
+ });
+ });
+
+ describe('SET_MACHINE_TYPE', () => {
+ it('should set GCP machine type as selectedMachineType', () => {
+ const machineTypeToSelect = gapiMachineTypesResponseMock.items[0].name;
+
+ store.commit(types.SET_MACHINE_TYPE, machineTypeToSelect);
+
+ expect(store.state.selectedMachineType).toEqual(selectedMachineTypeMock);
+ });
+ });
+
+ describe('SET_PROJECTS', () => {
+ it('should set Google API Projects response as projects', () => {
+ expect(store.state.projects.length).toEqual(0);
+
+ store.commit(types.SET_PROJECTS, gapiProjectsResponseMock.projects);
+
+ expect(store.state.projects.length).toEqual(gapiProjectsResponseMock.projects.length);
+ });
+ });
+
+ describe('SET_ZONES', () => {
+ it('should set Google API Zones response as zones', () => {
+ expect(store.state.zones.length).toEqual(0);
+
+ store.commit(types.SET_ZONES, gapiZonesResponseMock.items);
+
+ expect(store.state.zones.length).toEqual(gapiZonesResponseMock.items.length);
+ });
+ });
+
+ describe('SET_MACHINE_TYPES', () => {
+ it('should set Google API Machine Types response as machineTypes', () => {
+ expect(store.state.machineTypes.length).toEqual(0);
+
+ store.commit(types.SET_MACHINE_TYPES, gapiMachineTypesResponseMock.items);
+
+ expect(store.state.machineTypes.length).toEqual(gapiMachineTypesResponseMock.items.length);
+ });
+ });
+});
diff --git a/spec/javascripts/projects_dropdown/components/search_spec.js b/spec/javascripts/projects_dropdown/components/search_spec.js
index 601264258c2..427f5024e3a 100644
--- a/spec/javascripts/projects_dropdown/components/search_spec.js
+++ b/spec/javascripts/projects_dropdown/components/search_spec.js
@@ -92,7 +92,6 @@ describe('SearchComponent', () => {
const inputEl = vm.$el.querySelector('input.form-control');
expect(vm.$el.classList.contains('search-input-container')).toBeTruthy();
- expect(vm.$el.classList.contains('hidden-xs')).toBeTruthy();
expect(inputEl).not.toBe(null);
expect(inputEl.getAttribute('placeholder')).toBe('Search your projects');
expect(vm.$el.querySelector('.search-icon')).toBeDefined();
diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
index 00847df4b60..8f35b9ca437 100644
--- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js
+++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
@@ -14,7 +14,9 @@ describe('SidebarMoveIssue', function () {
this.$content = $(`
<div class="dropdown">
<div class="js-toggle"></div>
- <div class="dropdown-content"></div>
+ <div class="dropdown-menu">
+ <div class="dropdown-content"></div>
+ </div>
<div class="js-confirm-button"></div>
</div>
`);
diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
new file mode 100644
index 00000000000..ba897f4660d
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_button_spec.js
@@ -0,0 +1,69 @@
+import Vue from 'vue';
+
+import dropdownButtonComponent from '~/vue_shared/components/dropdown/dropdown_button.vue';
+
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const defaultLabel = 'Select';
+const customLabel = 'Select project';
+
+const createComponent = config => {
+ const Component = Vue.extend(dropdownButtonComponent);
+
+ return mountComponent(Component, config);
+};
+
+describe('DropdownButtonComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('dropdownToggleText', () => {
+ it('returns default toggle text', () => {
+ expect(vm.toggleText).toBe(defaultLabel);
+ });
+
+ it('returns custom toggle text when provided via props', () => {
+ const vmEmptyLabels = createComponent({ toggleText: customLabel });
+
+ expect(vmEmptyLabels.toggleText).toBe(customLabel);
+ vmEmptyLabels.$destroy();
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component container element of type `button`', () => {
+ expect(vm.$el.nodeName).toBe('BUTTON');
+ });
+
+ it('renders component container element with required data attributes', () => {
+ expect(vm.$el.dataset.abilityName).toBe(vm.abilityName);
+ expect(vm.$el.dataset.fieldName).toBe(vm.fieldName);
+ expect(vm.$el.dataset.issueUpdate).toBe(vm.updatePath);
+ expect(vm.$el.dataset.labels).toBe(vm.labelsPath);
+ expect(vm.$el.dataset.namespacePath).toBe(vm.namespace);
+ expect(vm.$el.dataset.showAny).not.toBeDefined();
+ });
+
+ it('renders dropdown toggle text element', () => {
+ const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text');
+ expect(dropdownToggleTextEl).not.toBeNull();
+ expect(dropdownToggleTextEl.innerText.trim()).toBe(defaultLabel);
+ });
+
+ it('renders dropdown button icon', () => {
+ const dropdownIconEl = vm.$el.querySelector('.dropdown-toggle-icon i.fa');
+
+ expect(dropdownIconEl).not.toBeNull();
+ expect(dropdownIconEl.classList.contains('fa-chevron-down')).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_hidden_input_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js
index 88733922a59..445ab0cb40e 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_hidden_input_spec.js
+++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_hidden_input_spec.js
@@ -1,17 +1,17 @@
import Vue from 'vue';
-import dropdownHiddenInputComponent from '~/vue_shared/components/sidebar/labels_select/dropdown_hidden_input.vue';
+import dropdownHiddenInputComponent from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockLabels } from './mock_data';
-const createComponent = (name = 'label_id[]', label = mockLabels[0]) => {
+const createComponent = (name = 'label_id[]', value = mockLabels[0].id) => {
const Component = Vue.extend(dropdownHiddenInputComponent);
return mountComponent(Component, {
name,
- label,
+ value,
});
};
@@ -31,7 +31,7 @@ describe('DropdownHiddenInputComponent', () => {
expect(vm.$el.nodeName).toBe('INPUT');
expect(vm.$el.getAttribute('type')).toBe('hidden');
expect(vm.$el.getAttribute('name')).toBe(vm.name);
- expect(vm.$el.getAttribute('value')).toBe(`${vm.label.id}`);
+ expect(vm.$el.getAttribute('value')).toBe(`${vm.value}`);
});
});
});
diff --git a/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js b/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js
new file mode 100644
index 00000000000..551520721e5
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/dropdown/dropdown_search_input_spec.js
@@ -0,0 +1,52 @@
+import Vue from 'vue';
+
+import dropdownSearchInputComponent from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
+
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const componentConfig = {
+ placeholderText: 'Search something',
+};
+
+const createComponent = (config = componentConfig) => {
+ const Component = Vue.extend(dropdownSearchInputComponent);
+
+ return mountComponent(Component, config);
+};
+
+describe('DropdownSearchInputComponent', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('template', () => {
+ it('renders input element with type `search`', () => {
+ const inputEl = vm.$el.querySelector('input.dropdown-input-field');
+
+ expect(inputEl).not.toBeNull();
+ expect(inputEl.getAttribute('type')).toBe('search');
+ });
+
+ it('renders search icon element', () => {
+ expect(vm.$el.querySelector('.fa-search.dropdown-input-search')).not.toBeNull();
+ });
+
+ it('renders clear search icon element', () => {
+ expect(
+ vm.$el.querySelector('.fa-times.dropdown-input-clear.js-dropdown-input-clear'),
+ ).not.toBeNull();
+ });
+
+ it('displays custom placeholder text', () => {
+ const inputEl = vm.$el.querySelector('input.dropdown-input-field');
+
+ expect(inputEl.getAttribute('placeholder')).toBe(componentConfig.placeholderText);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/dropdown/mock_data.js b/spec/javascripts/vue_shared/components/dropdown/mock_data.js
new file mode 100644
index 00000000000..b09d42da401
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/dropdown/mock_data.js
@@ -0,0 +1,11 @@
+export const mockLabels = [
+ {
+ id: 26,
+ title: 'Foo Label',
+ description: 'Foobar',
+ color: '#BADA55',
+ text_color: '#FFFFFF',
+ },
+];
+
+export default mockLabels;
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index a44243ac82d..023bedaaebb 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -71,6 +71,40 @@ describe Backup::Repository do
end
end
+ describe '#delete_all_repositories', :seed_helper do
+ shared_examples('delete_all_repositories') do
+ before do
+ allow(FileUtils).to receive(:mkdir_p).and_call_original
+ allow(FileUtils).to receive(:mv).and_call_original
+ end
+
+ after(:all) do
+ ensure_seeds
+ end
+
+ it 'removes all repositories' do
+ # Sanity check: there should be something for us to delete
+ expect(list_repositories).to include(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH))
+
+ subject.delete_all_repositories('default', Gitlab.config.repositories.storages['default'])
+
+ expect(list_repositories).to be_empty
+ end
+
+ def list_repositories
+ Dir[SEED_STORAGE_PATH + '/*.git']
+ end
+ end
+
+ context 'with gitaly' do
+ it_behaves_like 'delete_all_repositories'
+ end
+
+ context 'without gitaly', :skip_gitaly_mock do
+ it_behaves_like 'delete_all_repositories'
+ end
+ end
+
describe '#empty_repo?' do
context 'for a wiki' do
let(:wiki) { create(:project_wiki) }
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 392905076dc..b30f3661e70 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -59,7 +59,7 @@ describe Banzai::Filter::LabelReferenceFilter do
describe 'label span element' do
it 'includes default classes' do
doc = reference_filter("Label #{reference}")
- expect(doc.css('a span').first.attr('class')).to eq 'label color-label has-tooltip'
+ expect(doc.css('a span').first.attr('class')).to eq 'badge color-label has-tooltip'
end
it 'includes a style attribute' do
diff --git a/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb b/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb
index fa209bed74e..002ce776be9 100644
--- a/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb
+++ b/spec/lib/gitlab/auth/user_access_denied_reason_spec.rb
@@ -22,7 +22,8 @@ describe Gitlab::Auth::UserAccessDeniedReason do
enforce_terms
end
- it { is_expected.to match /You must accept the Terms of Service/ }
+ it { is_expected.to match /must accept the Terms of Service/ }
+ it { is_expected.to include(user.username) }
end
context 'when the user is internal' do
diff --git a/spec/lib/gitlab/database/count_spec.rb b/spec/lib/gitlab/database/count_spec.rb
index 9d9caaabe16..407d9470785 100644
--- a/spec/lib/gitlab/database/count_spec.rb
+++ b/spec/lib/gitlab/database/count_spec.rb
@@ -3,59 +3,68 @@ require 'spec_helper'
describe Gitlab::Database::Count do
before do
create_list(:project, 3)
+ create(:identity)
end
- describe '.execute_estimate_if_updated_recently', :postgresql do
- context 'when reltuples have not been updated' do
- before do
- expect(described_class).to receive(:reltuples_updated_recently?).and_return(false)
- end
+ let(:models) { [Project, Identity] }
- it 'returns nil' do
- expect(described_class.execute_estimate_if_updated_recently(Project)).to be nil
- end
- end
+ describe '.approximate_counts' do
+ context 'with MySQL' do
+ context 'when reltuples have not been updated' do
+ it 'counts all models the normal way' do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
- context 'when reltuples have been updated' do
- before do
- ActiveRecord::Base.connection.execute('ANALYZE projects')
- end
+ expect(Project).to receive(:count).and_call_original
+ expect(Identity).to receive(:count).and_call_original
- it 'calls postgresql_estimate_query' do
- expect(described_class).to receive(:postgresql_estimate_query).with(Project).and_call_original
- expect(described_class.execute_estimate_if_updated_recently(Project)).to eq(3)
+ expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
+ end
end
end
- end
- describe '.approximate_count' do
- context 'when reltuples have not been updated' do
- it 'counts all projects the normal way' do
- allow(described_class).to receive(:reltuples_updated_recently?).and_return(false)
+ context 'with PostgreSQL', :postgresql do
+ describe 'when reltuples have not been updated' do
+ it 'counts all models the normal way' do
+ expect(described_class).to receive(:reltuples_from_recently_updated).with(%w(projects identities)).and_return({})
- expect(Project).to receive(:count).and_call_original
- expect(described_class.approximate_count(Project)).to eq(3)
+ expect(Project).to receive(:count).and_call_original
+ expect(Identity).to receive(:count).and_call_original
+ expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
+ end
end
- end
- context 'no permission' do
- it 'falls back to standard query' do
- allow(described_class).to receive(:reltuples_updated_recently?).and_raise(PG::InsufficientPrivilege)
+ describe 'no permission' do
+ it 'falls back to standard query' do
+ allow(described_class).to receive(:postgresql_estimate_query).and_raise(PG::InsufficientPrivilege)
- expect(Project).to receive(:count).and_call_original
- expect(described_class.approximate_count(Project)).to eq(3)
+ expect(Project).to receive(:count).and_call_original
+ expect(Identity).to receive(:count).and_call_original
+ expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
+ end
end
- end
- describe 'when reltuples have been updated', :postgresql do
- before do
- ActiveRecord::Base.connection.execute('ANALYZE projects')
+ describe 'when some reltuples have been updated' do
+ it 'counts projects in the fast way' do
+ expect(described_class).to receive(:reltuples_from_recently_updated).with(%w(projects identities)).and_return({ 'projects' => 3 })
+
+ expect(Project).not_to receive(:count).and_call_original
+ expect(Identity).to receive(:count).and_call_original
+ expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
+ end
end
- it 'counts all projects in the fast way' do
- expect(described_class).to receive(:postgresql_estimate_query).with(Project).and_call_original
+ describe 'when all reltuples have been updated' do
+ before do
+ ActiveRecord::Base.connection.execute('ANALYZE projects')
+ ActiveRecord::Base.connection.execute('ANALYZE identities')
+ end
+
+ it 'counts models with the standard way' do
+ expect(Project).not_to receive(:count)
+ expect(Identity).not_to receive(:count)
- expect(described_class.approximate_count(Project)).to eq(3)
+ expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
+ end
end
end
end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 0c2e18c268a..0588fe935c3 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -468,4 +468,58 @@ describe Gitlab::Diff::File do
end
end
end
+
+ describe '#diff_hunk' do
+ let(:raw_diff) do
+ <<EOS
+@@ -6,12 +6,18 @@ module Popen
+
+ def popen(cmd, path=nil)
+ unless cmd.is_a?(Array)
+- raise "System commands must be given as an array of strings"
++ raise RuntimeError, "System commands must be given as an array of strings"
+ end
+
+ path ||= Dir.pwd
+- vars = { "PWD" => path }
+- options = { chdir: path }
++
++ vars = {
++ "PWD" => path
++ }
++
++ options = {
++ chdir: path
++ }
+
+ unless File.directory?(path)
+ FileUtils.mkdir_p(path)
+@@ -19,6 +25,7 @@ module Popen
+
+ @cmd_output = ""
+ @cmd_status = 0
++
+ Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
+ @cmd_output << stdout.read
+ @cmd_output << stderr.read
+EOS
+ end
+
+ it 'returns raw diff up to given line index' do
+ allow(diff_file).to receive(:raw_diff) { raw_diff }
+ diff_line = instance_double(Gitlab::Diff::Line, index: 5)
+
+ diff_hunk = <<EOS
+@@ -6,12 +6,18 @@ module Popen
+
+ def popen(cmd, path=nil)
+ unless cmd.is_a?(Array)
+- raise "System commands must be given as an array of strings"
++ raise RuntimeError, "System commands must be given as an array of strings"
+ end
+EOS
+
+ expect(diff_file.diff_hunk(diff_line)).to eq(diff_hunk)
+ end
+ end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 317a932d5a6..dfffea7797f 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -1055,7 +1055,7 @@ describe Gitlab::GitAccess do
it 'blocks access when the user did not accept terms', :aggregate_failures do
actions.each do |action|
- expect { action.call }.to raise_unauthorized(/You must accept the Terms of Service in order to perform this action/)
+ expect { action.call }.to raise_unauthorized(/must accept the Terms of Service in order to perform this action/)
end
end
diff --git a/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
new file mode 100644
index 00000000000..f47b9dd3498
--- /dev/null
+++ b/spec/lib/gitlab/grape_logging/loggers/queue_duration_logger_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::GrapeLogging::Loggers::QueueDurationLogger do
+ subject { described_class.new }
+
+ describe ".parameters" do
+ let(:start_time) { Time.new(2018, 01, 01) }
+
+ describe 'when no proxy time is available' do
+ let(:mock_request) { OpenStruct.new(env: {}) }
+
+ it 'returns an empty hash' do
+ expect(subject.parameters(mock_request, nil)).to eq({})
+ end
+ end
+
+ describe 'when a proxy time is available' do
+ let(:mock_request) do
+ OpenStruct.new(
+ env: {
+ 'HTTP_GITLAB_WORKHORSE_PROXY_START' => (start_time - 1.hour).to_i * (10**9)
+ }
+ )
+ end
+
+ it 'returns the correct duration in ms' do
+ Timecop.freeze(start_time) do
+ subject.before
+
+ expect(subject.parameters(mock_request, nil)).to eq( { 'queue_duration': 1.hour.to_f * 1000 })
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 8b46b04b8b5..fb5fd300dbb 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -35,6 +35,7 @@ notes:
- todos
- events
- system_note_metadata
+- note_diff_file
label_links:
- target
- label
diff --git a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
index cd5a1b2982b..536cc359d39 100644
--- a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
@@ -15,7 +15,10 @@ describe Gitlab::ImportExport::AttributeCleaner do
'project_id' => 99,
'user_id' => 99,
'random_id_in_the_middle' => 99,
- 'notid' => 99
+ 'notid' => 99,
+ 'import_source' => 'whatever',
+ 'import_type' => 'whatever',
+ 'non_existent_attr' => 'whatever'
}
end
@@ -28,10 +31,30 @@ describe Gitlab::ImportExport::AttributeCleaner do
}
end
+ let(:excluded_keys) { %w[import_source import_type] }
+
+ subject { described_class.clean(relation_hash: unsafe_hash, relation_class: relation_class, excluded_keys: excluded_keys) }
+
+ before do
+ allow(relation_class).to receive(:attribute_method?).and_return(true)
+ allow(relation_class).to receive(:attribute_method?).with('non_existent_attr').and_return(false)
+ end
+
it 'removes unwanted attributes from the hash' do
- # allow(relation_class).to receive(:attribute_method?).and_return(true)
+ expect(subject).to eq(post_safe_hash)
+ end
+
+ it 'removes attributes not present in relation_class' do
+ expect(subject.keys).not_to include 'non_existent_attr'
+ end
+
+ it 'removes excluded keys from the hash' do
+ expect(subject.keys).not_to include excluded_keys
+ end
+
+ it 'does not remove excluded key if not listed' do
parsed_hash = described_class.clean(relation_hash: unsafe_hash, relation_class: relation_class)
- expect(parsed_hash).to eq(post_safe_hash)
+ expect(parsed_hash.keys).to eq post_safe_hash.keys + excluded_keys
end
end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 4f64f2bd6b4..1b7fa11cb3c 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -1,5 +1,7 @@
{
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
+ "import_type": "gitlab_project",
+ "creator_id": 123,
"visibility_level": 10,
"archived": false,
"labels": [
diff --git a/spec/lib/gitlab/import_export/project.light.json b/spec/lib/gitlab/import_export/project.light.json
index 5dbf0ed289b..c13cf4a0507 100644
--- a/spec/lib/gitlab/import_export/project.light.json
+++ b/spec/lib/gitlab/import_export/project.light.json
@@ -1,5 +1,7 @@
{
"description": "Nisi et repellendus ut enim quo accusamus vel magnam.",
+ "import_type": "gitlab_project",
+ "creator_id": 123,
"visibility_level": 10,
"archived": false,
"milestones": [
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 13a8c9adcee..68ddc947e02 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -23,6 +23,10 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
allow_any_instance_of(Gitlab::Git::Repository).to receive(:create_branch)
project_tree_restorer = described_class.new(user: @user, shared: @shared, project: @project)
+
+ expect(Gitlab::ImportExport::RelationFactory).to receive(:create).with(hash_including(excluded_keys: ['whatever'])).and_call_original.at_least(:once)
+ allow(project_tree_restorer).to receive(:excluded_keys_for_relation).and_return(['whatever'])
+
@restored_project_json = project_tree_restorer.restore
end
end
@@ -248,6 +252,11 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do
expect(labels.where(type: "ProjectLabel").count).to eq(results.fetch(:first_issue_labels, 0))
expect(labels.where(type: "ProjectLabel").where.not(group_id: nil).count).to eq(0)
end
+
+ it 'does not set params that are excluded from import_export settings' do
+ expect(project.import_type).to be_nil
+ expect(project.creator_id).not_to eq 123
+ end
end
shared_examples 'restores group correctly' do |**results|
diff --git a/spec/lib/gitlab/import_export/relation_factory_spec.rb b/spec/lib/gitlab/import_export/relation_factory_spec.rb
index 5c61a5a2044..5f0dfd64b15 100644
--- a/spec/lib/gitlab/import_export/relation_factory_spec.rb
+++ b/spec/lib/gitlab/import_export/relation_factory_spec.rb
@@ -4,12 +4,14 @@ describe Gitlab::ImportExport::RelationFactory do
let(:project) { create(:project) }
let(:members_mapper) { double('members_mapper').as_null_object }
let(:user) { create(:admin) }
+ let(:excluded_keys) { [] }
let(:created_object) do
described_class.create(relation_sym: relation_sym,
relation_hash: relation_hash,
members_mapper: members_mapper,
user: user,
- project: project)
+ project: project,
+ excluded_keys: excluded_keys)
end
context 'hook object' do
@@ -67,6 +69,14 @@ describe Gitlab::ImportExport::RelationFactory do
expect(created_object.service_id).not_to eq(service_id)
end
end
+
+ context 'excluded attributes' do
+ let(:excluded_keys) { %w[url] }
+
+ it 'are removed from the imported object' do
+ expect(created_object.url).to be_nil
+ end
+ end
end
# Mocks an ActiveRecordish object with the dodgy columns
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 62da967cf96..3d5271cd030 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -165,6 +165,7 @@ MergeRequest:
- approvals_before_merge
- rebase_commit_sha
- time_estimate
+- squash
- last_edited_at
- last_edited_by_id
- head_pipeline_id
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index da146e24893..d63f448883b 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -8,6 +8,66 @@ describe Gitlab do
expect(described_class.root).to eq(Pathname.new(File.expand_path('../..', __dir__)))
end
end
+ describe '.revision' do
+ let(:cmd) { %W[#{described_class.config.git.bin_path} log --pretty=format:%h -n 1] }
+
+ around do |example|
+ described_class.instance_variable_set(:@_revision, nil)
+ example.run
+ described_class.instance_variable_set(:@_revision, nil)
+ end
+
+ context 'when a REVISION file exists' do
+ before do
+ expect(File).to receive(:exist?)
+ .with(described_class.root.join('REVISION'))
+ .and_return(true)
+ end
+
+ it 'returns the actual Git revision' do
+ expect(File).to receive(:read)
+ .with(described_class.root.join('REVISION'))
+ .and_return("abc123\n")
+
+ expect(described_class.revision).to eq('abc123')
+ end
+
+ it 'memoizes the revision' do
+ expect(File).to receive(:read)
+ .once
+ .with(described_class.root.join('REVISION'))
+ .and_return("abc123\n")
+
+ 2.times { described_class.revision }
+ end
+ end
+
+ context 'when no REVISION file exist' do
+ context 'when the Git command succeeds' do
+ before do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with(cmd)
+ .and_return(Gitlab::Popen::Result.new(cmd, 'abc123', '', double(success?: true)))
+ end
+
+ it 'returns the actual Git revision' do
+ expect(described_class.revision).to eq('abc123')
+ end
+ end
+
+ context 'when the Git command fails' do
+ before do
+ expect(Gitlab::Popen).to receive(:popen_with_detail)
+ .with(cmd)
+ .and_return(Gitlab::Popen::Result.new(cmd, '', 'fatal: Not a git repository', double('Process::Status', success?: false)))
+ end
+
+ it 'returns "Unknown"' do
+ expect(described_class.revision).to eq('Unknown')
+ end
+ end
+ end
+ end
describe '.com?' do
it 'is true when on GitLab.com' do
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index db9d9158b29..27cb3198e5b 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -50,30 +50,6 @@ describe GoogleApi::CloudPlatform::Client do
end
end
- describe '#projects_list' do
- subject { client.projects_list }
- let(:projects) { double }
-
- before do
- allow_any_instance_of(Google::Apis::CloudresourcemanagerV1::CloudResourceManagerService)
- .to receive(:fetch_all).and_return(projects)
- end
-
- it { is_expected.to eq(projects) }
- end
-
- describe '#projects_get_billing_info' do
- subject { client.projects_get_billing_info('project') }
- let(:billing_info) { double }
-
- before do
- allow_any_instance_of(Google::Apis::CloudbillingV1::CloudbillingService)
- .to receive(:get_project_billing_info).and_return(billing_info)
- end
-
- it { is_expected.to eq(billing_info) }
- end
-
describe '#projects_zones_clusters_get' do
subject { client.projects_zones_clusters_get(spy, spy, spy) }
let(:gke_cluster) { double }
diff --git a/spec/migrations/fill_file_store_spec.rb b/spec/migrations/fill_file_store_spec.rb
new file mode 100644
index 00000000000..5ff7aa56ce2
--- /dev/null
+++ b/spec/migrations/fill_file_store_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180424151928_fill_file_store')
+
+describe FillFileStore, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:builds) { table(:ci_builds) }
+ let(:job_artifacts) { table(:ci_job_artifacts) }
+ let(:lfs_objects) { table(:lfs_objects) }
+ let(:uploads) { table(:uploads) }
+
+ before do
+ namespaces.create!(id: 123, name: 'gitlab1', path: 'gitlab1')
+ projects.create!(id: 123, name: 'gitlab1', path: 'gitlab1', namespace_id: 123)
+ builds.create!(id: 1)
+
+ ##
+ # Create rows that have nullfied `file_store` column
+ job_artifacts.create!(project_id: 123, job_id: 1, file_type: 1, file_store: nil)
+ lfs_objects.create!(oid: 123, size: 10, file: 'file_name', file_store: nil)
+ uploads.create!(size: 10, path: 'path', uploader: 'uploader', mount_point: 'file_name', store: nil)
+ end
+
+ it 'correctly migrates nullified file_store/store column' do
+ expect(job_artifacts.where(file_store: nil).count).to eq(1)
+ expect(lfs_objects.where(file_store: nil).count).to eq(1)
+ expect(uploads.where(store: nil).count).to eq(1)
+
+ expect(job_artifacts.where(file_store: 1).count).to eq(0)
+ expect(lfs_objects.where(file_store: 1).count).to eq(0)
+ expect(uploads.where(store: 1).count).to eq(0)
+
+ migrate!
+
+ expect(job_artifacts.where(file_store: nil).count).to eq(0)
+ expect(lfs_objects.where(file_store: nil).count).to eq(0)
+ expect(uploads.where(store: nil).count).to eq(0)
+
+ expect(job_artifacts.where(file_store: 1).count).to eq(1)
+ expect(lfs_objects.where(file_store: 1).count).to eq(1)
+ expect(uploads.where(store: 1).count).to eq(1)
+ end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 7e47043a1cb..f8f07205623 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -391,68 +391,6 @@ describe ApplicationSetting do
end
describe 'performance bar settings' do
- describe 'performance_bar_allowed_group_id=' do
- context 'with a blank path' do
- before do
- setting.performance_bar_allowed_group_id = create(:group).full_path
- end
-
- it 'persists nil for a "" path and clears allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_allowed_group_id = ''
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- end
- end
-
- context 'with an invalid path' do
- it 'does not persist an invalid group path' do
- setting.performance_bar_allowed_group_id = 'foo'
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- end
- end
-
- context 'with a path to an existing group' do
- let(:group) { create(:group) }
-
- it 'persists a valid group path and clears allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_allowed_group_id = group.full_path
-
- expect(setting.performance_bar_allowed_group_id).to eq(group.id)
- end
-
- context 'when the given path is the same' do
- context 'with a blank path' do
- before do
- setting.performance_bar_allowed_group_id = nil
- end
-
- it 'clears the cached allowed user IDs' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_allowed_group_id = ''
- end
- end
-
- context 'with a valid path' do
- before do
- setting.performance_bar_allowed_group_id = group.full_path
- end
-
- it 'clears the cached allowed user IDs' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_allowed_group_id = group.full_path
- end
- end
- end
- end
- end
-
describe 'performance_bar_allowed_group' do
context 'with no performance_bar_allowed_group_id saved' do
it 'returns nil' do
@@ -464,11 +402,11 @@ describe ApplicationSetting do
let(:group) { create(:group) }
before do
- setting.performance_bar_allowed_group_id = group.full_path
+ setting.update!(performance_bar_allowed_group_id: group.id)
end
it 'returns the group' do
- expect(setting.performance_bar_allowed_group).to eq(group)
+ expect(setting.reload.performance_bar_allowed_group).to eq(group)
end
end
end
@@ -478,67 +416,11 @@ describe ApplicationSetting do
let(:group) { create(:group) }
before do
- setting.performance_bar_allowed_group_id = group.full_path
+ setting.update!(performance_bar_allowed_group_id: group.id)
end
it 'returns true' do
- expect(setting.performance_bar_enabled).to be_truthy
- end
- end
- end
-
- describe 'performance_bar_enabled=' do
- context 'when the performance bar is enabled' do
- let(:group) { create(:group) }
-
- before do
- setting.performance_bar_allowed_group_id = group.full_path
- end
-
- context 'when passing true' do
- it 'does not clear allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_enabled = true
-
- expect(setting.performance_bar_allowed_group_id).to eq(group.id)
- expect(setting.performance_bar_enabled).to be_truthy
- end
- end
-
- context 'when passing false' do
- it 'disables the performance bar and clears allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_enabled = false
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- expect(setting.performance_bar_enabled).to be_falsey
- end
- end
- end
-
- context 'when the performance bar is disabled' do
- context 'when passing true' do
- it 'does nothing and does not clear allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_enabled = true
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- expect(setting.performance_bar_enabled).to be_falsey
- end
- end
-
- context 'when passing false' do
- it 'does nothing and does not clear allowed user IDs cache' do
- expect(Gitlab::PerformanceBar).not_to receive(:expire_allowed_user_ids_cache)
-
- setting.performance_bar_enabled = false
-
- expect(setting.performance_bar_allowed_group_id).to be_nil
- expect(setting.performance_bar_enabled).to be_falsey
- end
+ expect(setting.reload.performance_bar_enabled).to be_truthy
end
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 96173889ccd..77179028ede 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1546,7 +1546,7 @@ describe Ci::Build do
{ key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
- { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
+ { key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true },
{ key: 'CI_JOB_NAME', value: 'test', public: true },
{ key: 'CI_JOB_STAGE', value: 'test', public: true },
{ key: 'CI_COMMIT_SHA', value: build.sha, public: true },
diff --git a/spec/models/concerns/batch_destroy_dependent_associations_spec.rb b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
new file mode 100644
index 00000000000..c16b245bea8
--- /dev/null
+++ b/spec/models/concerns/batch_destroy_dependent_associations_spec.rb
@@ -0,0 +1,60 @@
+require 'spec_helper'
+
+describe BatchDestroyDependentAssociations do
+ class TestProject < ActiveRecord::Base
+ self.table_name = 'projects'
+
+ has_many :builds, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
+ has_many :pages_domains
+ has_many :todos
+
+ include BatchDestroyDependentAssociations
+ end
+
+ describe '#dependent_associations_to_destroy' do
+ set(:project) { TestProject.new }
+
+ it 'returns the right associations' do
+ expect(project.dependent_associations_to_destroy.map(&:name)).to match_array([:builds])
+ end
+ end
+
+ describe '#destroy_dependent_associations_in_batches' do
+ set(:project) { create(:project) }
+ set(:build) { create(:ci_build, project: project) }
+ set(:notification_setting) { create(:notification_setting, project: project) }
+ let!(:todos) { create(:todo, project: project) }
+
+ it 'destroys multiple builds' do
+ create(:ci_build, project: project)
+
+ expect(Ci::Build.count).to eq(2)
+
+ project.destroy_dependent_associations_in_batches
+
+ expect(Ci::Build.count).to eq(0)
+ end
+
+ it 'destroys builds in batches' do
+ expect(project).to receive_message_chain(:builds, :find_each).and_yield(build)
+ expect(build).to receive(:destroy).and_call_original
+
+ project.destroy_dependent_associations_in_batches
+
+ expect(Ci::Build.count).to eq(0)
+ expect(Todo.count).to eq(1)
+ expect(User.count).to be > 0
+ expect(NotificationSetting.count).to eq(User.count)
+ end
+
+ it 'excludes associations' do
+ project.destroy_dependent_associations_in_batches(exclude: [:builds])
+
+ expect(Ci::Build.count).to eq(1)
+ expect(Todo.count).to eq(1)
+ expect(User.count).to be > 0
+ expect(NotificationSetting.count).to eq(User.count)
+ end
+ end
+end
diff --git a/spec/models/concerns/discussion_on_diff_spec.rb b/spec/models/concerns/discussion_on_diff_spec.rb
index 30572ce9332..8cd129dc851 100644
--- a/spec/models/concerns/discussion_on_diff_spec.rb
+++ b/spec/models/concerns/discussion_on_diff_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe DiscussionOnDiff do
- subject { create(:diff_note_on_merge_request).to_discussion }
+ subject { create(:diff_note_on_merge_request, line_number: 18).to_discussion }
describe "#truncated_diff_lines" do
let(:truncated_lines) { subject.truncated_diff_lines }
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index fb51c0172ab..8624f0daa4d 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -84,7 +84,47 @@ describe DiffNote do
end
end
- describe "#diff_file" do
+ describe '#create_diff_file callback' do
+ let(:noteable) { create(:merge_request) }
+ let(:project) { noteable.project }
+
+ context 'merge request' do
+ let!(:diff_note) { create(:diff_note_on_merge_request, project: project, noteable: noteable) }
+
+ it 'creates a diff note file' do
+ expect(diff_note.reload.note_diff_file).to be_present
+ end
+
+ it 'does not create diff note file if it is a reply' do
+ expect { create(:diff_note_on_merge_request, noteable: noteable, in_reply_to: diff_note) }
+ .not_to change(NoteDiffFile, :count)
+ end
+ end
+
+ context 'commit' do
+ let!(:diff_note) { create(:diff_note_on_commit, project: project) }
+
+ it 'creates a diff note file' do
+ expect(diff_note.reload.note_diff_file).to be_present
+ end
+
+ it 'does not create diff note file if it is a reply' do
+ expect { create(:diff_note_on_commit, in_reply_to: diff_note) }
+ .not_to change(NoteDiffFile, :count)
+ end
+ end
+ end
+
+ describe '#diff_file', :clean_gitlab_redis_shared_state do
+ context 'when note_diff_file association exists' do
+ it 'returns persisted diff file data' do
+ diff_file = subject.diff_file
+
+ expect(diff_file.diff.to_hash.with_indifferent_access)
+ .to include(subject.note_diff_file.to_hash)
+ end
+ end
+
context 'when the discussion was created in the diff' do
it 'returns correct diff file' do
diff_file = subject.diff_file
@@ -115,6 +155,26 @@ describe DiffNote do
expect(diff_file.diff_refs).to eq(position.diff_refs)
end
end
+
+ context 'note diff file creation enqueuing' do
+ it 'enqueues CreateNoteDiffFileWorker if it is the first note of a discussion' do
+ subject.note_diff_file.destroy!
+
+ expect(CreateNoteDiffFileWorker).to receive(:perform_async).with(subject.id)
+
+ subject.reload.diff_file
+ end
+
+ it 'does not enqueues CreateNoteDiffFileWorker if not first note of a discussion' do
+ mr = create(:merge_request)
+ diff_note = create(:diff_note_on_merge_request, project: mr.project, noteable: mr)
+ reply_diff_note = create(:diff_note_on_merge_request, in_reply_to: diff_note)
+
+ expect(CreateNoteDiffFileWorker).not_to receive(:perform_async).with(reply_diff_note.id)
+
+ reply_diff_note.reload.diff_file
+ end
+ end
end
describe "#diff_line" do
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 8ea92410022..c1eac4fa489 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -50,6 +50,19 @@ describe Event do
end
end
+ describe '#set_last_repository_updated_at' do
+ it 'only updates once every Event::REPOSITORY_UPDATED_AT_INTERVAL minutes' do
+ last_known_timestamp = (Event::REPOSITORY_UPDATED_AT_INTERVAL - 1.minute).ago
+ project.update(last_repository_updated_at: last_known_timestamp)
+ project.reload # a reload removes fractions of seconds
+
+ expect do
+ create_push_event(project, project.owner)
+ project.reload
+ end.not_to change { project.last_repository_updated_at }
+ end
+ end
+
describe 'after_create :track_user_interacted_projects' do
let(:event) { build(:push_event, project: project, author: project.owner) }
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 8ef91e8fab5..581fd0293cc 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -5,7 +5,7 @@ describe InternalId do
let(:usage) { :issues }
let(:issue) { build(:issue, project: project) }
let(:scope) { { project: project } }
- let(:init) { ->(s) { s.project.issues.maximum(:iid) } }
+ let(:init) { ->(s) { s.project.issues.size } }
context 'validations' do
it { is_expected.to validate_presence_of(:usage) }
@@ -39,29 +39,6 @@ describe InternalId do
end
end
- context 'with an InternalId record present and existing issues with a higher internal id' do
- # This can happen if the old NonatomicInternalId is still in use
- before do
- issues = Array.new(rand(1..10)).map { create(:issue, project: project) }
-
- issue = issues.last
- issue.iid = issues.map { |i| i.iid }.max + 1
- issue.save
- end
-
- let(:maximum_iid) { project.issues.map { |i| i.iid }.max }
-
- it 'updates last_value to the maximum internal id present' do
- subject
-
- expect(described_class.find_by(project: project, usage: described_class.usages[usage.to_s]).last_value).to eq(maximum_iid + 1)
- end
-
- it 'returns next internal id correctly' do
- expect(subject).to eq(maximum_iid + 1)
- end
- end
-
context 'with concurrent inserts on table' do
it 'looks up the record if it was created concurrently' do
args = { **scope, usage: described_class.usages[usage.to_s] }
@@ -104,8 +81,7 @@ describe InternalId do
describe '#increment_and_save!' do
let(:id) { create(:internal_id) }
- let(:maximum_iid) { nil }
- subject { id.increment_and_save!(maximum_iid) }
+ subject { id.increment_and_save! }
it 'returns incremented iid' do
value = id.last_value
@@ -126,14 +102,5 @@ describe InternalId do
expect(subject).to eq(1)
end
end
-
- context 'with maximum_iid given' do
- let(:id) { create(:internal_id, last_value: 1) }
- let(:maximum_iid) { id.last_value + 10 }
-
- it 'returns maximum_iid instead' do
- expect(subject).to eq(12)
- end
- end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 92e33a64d26..9ffa91fc265 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -14,6 +14,65 @@ describe MergeRequest do
it { is_expected.to have_many(:merge_request_diffs) }
end
+ describe '#squash_in_progress?' do
+ shared_examples 'checking whether a squash is in progress' do
+ let(:repo_path) { subject.source_project.repository.path }
+ let(:squash_path) { File.join(repo_path, "gitlab-worktree", "squash-#{subject.id}") }
+
+ before do
+ system(*%W(#{Gitlab.config.git.bin_path} -C #{repo_path} worktree add --detach #{squash_path} master))
+ end
+
+ it 'returns true when there is a current squash directory' do
+ expect(subject.squash_in_progress?).to be_truthy
+ end
+
+ it 'returns false when there is no squash directory' do
+ FileUtils.rm_rf(squash_path)
+
+ expect(subject.squash_in_progress?).to be_falsey
+ end
+
+ it 'returns false when the squash directory has expired' do
+ time = 20.minutes.ago.to_time
+ File.utime(time, time, squash_path)
+
+ expect(subject.squash_in_progress?).to be_falsey
+ end
+
+ it 'returns false when the source project has been removed' do
+ allow(subject).to receive(:source_project).and_return(nil)
+
+ expect(subject.squash_in_progress?).to be_falsey
+ end
+ end
+
+ context 'when Gitaly squash_in_progress is enabled' do
+ it_behaves_like 'checking whether a squash is in progress'
+ end
+
+ context 'when Gitaly squash_in_progress is disabled', :disable_gitaly do
+ it_behaves_like 'checking whether a squash is in progress'
+ end
+ end
+
+ describe '#squash?' do
+ let(:merge_request) { build(:merge_request, squash: squash) }
+ subject { merge_request.squash? }
+
+ context 'disabled in database' do
+ let(:squash) { false }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'enabled in database' do
+ let(:squash) { true }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
describe 'modules' do
subject { described_class }
diff --git a/spec/models/note_diff_file_spec.rb b/spec/models/note_diff_file_spec.rb
new file mode 100644
index 00000000000..591c1a89748
--- /dev/null
+++ b/spec/models/note_diff_file_spec.rb
@@ -0,0 +1,11 @@
+require 'rails_helper'
+
+describe NoteDiffFile do
+ describe 'associations' do
+ it { is_expected.to belong_to(:diff_note) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:diff_note) }
+ end
+end
diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb
index ae9c0e9c304..32fc704a79b 100644
--- a/spec/requests/api/deploy_keys_spec.rb
+++ b/spec/requests/api/deploy_keys_spec.rb
@@ -171,7 +171,7 @@ describe API::DeployKeys do
deploy_key
end
- it 'deletes existing key' do
+ it 'removes existing key from project' do
expect do
delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
@@ -179,6 +179,44 @@ describe API::DeployKeys do
end.to change { project.deploy_keys.count }.by(-1)
end
+ context 'when the deploy key is public' do
+ it 'does not delete the deploy key' do
+ expect do
+ delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
+
+ expect(response).to have_gitlab_http_status(204)
+ end.not_to change { DeployKey.count }
+ end
+ end
+
+ context 'when the deploy key is not public' do
+ let!(:deploy_key) { create(:deploy_key, public: false) }
+
+ context 'when the deploy key is only used by this project' do
+ it 'deletes the deploy key' do
+ expect do
+ delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
+
+ expect(response).to have_gitlab_http_status(204)
+ end.to change { DeployKey.count }.by(-1)
+ end
+ end
+
+ context 'when the deploy key is used by other projects' do
+ before do
+ create(:deploy_keys_project, project: project2, deploy_key: deploy_key)
+ end
+
+ it 'does not delete the deploy key' do
+ expect do
+ delete api("/projects/#{project.id}/deploy_keys/#{deploy_key.id}", admin)
+
+ expect(response).to have_gitlab_http_status(204)
+ end.not_to change { DeployKey.count }
+ end
+ end
+ end
+
it 'returns 404 Not Found with invalid ID' do
delete api("/projects/#{project.id}/deploy_keys/404", admin)
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index d3ab44c0d7e..d8a51f36dba 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -171,7 +171,7 @@ describe API::Helpers do
end
it 'returns a 403 when a user has not accepted the terms' do
- expect { current_user }.to raise_error /You must accept the Terms of Service/
+ expect { current_user }.to raise_error /must accept the Terms of Service/
end
it 'sets the current user when the user accepted the terms' do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index db8c5f963d6..5dc3ddd4b36 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -1,9 +1,9 @@
require 'spec_helper'
describe API::Internal do
- let(:user) { create(:user) }
+ set(:user) { create(:user) }
let(:key) { create(:key, user: user) }
- let(:project) { create(:project, :repository, :wiki_repo) }
+ set(:project) { create(:project, :repository, :wiki_repo) }
let(:secret_token) { Gitlab::Shell.secret_token }
let(:gl_repository) { "project-#{project.id}" }
let(:reference_counter) { double('ReferenceCounter') }
@@ -277,7 +277,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
+ expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(user).not_to have_an_activity_record
end
@@ -289,7 +289,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.wiki.repository.path_to_repo)
+ expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(user).to have_an_activity_record
end
@@ -301,7 +301,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("project-#{project.id}")
expect(json_response["gitaly"]).not_to be_nil
expect(json_response["gitaly"]["repository"]).not_to be_nil
@@ -320,7 +320,7 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
+ expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("project-#{project.id}")
expect(json_response["gitaly"]).not_to be_nil
expect(json_response["gitaly"]["repository"]).not_to be_nil
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 0a2963452e4..45082e644ca 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -13,7 +13,10 @@ describe API::Jobs do
ref: project.default_branch)
end
- let!(:job) { create(:ci_build, :success, pipeline: pipeline) }
+ let!(:job) do
+ create(:ci_build, :success, pipeline: pipeline,
+ artifacts_expire_at: 1.day.since)
+ end
let(:user) { create(:user) }
let(:api_user) { user }
@@ -43,6 +46,7 @@ describe API::Jobs do
it 'returns correct values' do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
+ expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
end
it 'returns pipeline data' do
@@ -128,6 +132,7 @@ describe API::Jobs do
it 'returns correct values' do
expect(json_response).not_to be_empty
expect(json_response.first['commit']['id']).to eq project.commit.id
+ expect(Time.parse(json_response.first['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
end
it 'returns pipeline data' do
@@ -201,6 +206,7 @@ describe API::Jobs do
expect(Time.parse(json_response['created_at'])).to be_like_time(job.created_at)
expect(Time.parse(json_response['started_at'])).to be_like_time(job.started_at)
expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at)
+ expect(Time.parse(json_response['artifacts_expire_at'])).to be_like_time(job.artifacts_expire_at)
expect(json_response['duration']).to eq(job.duration)
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 1eeeb4f1045..6b91e48ae6a 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -263,6 +263,7 @@ describe API::MergeRequests do
expect(json_response.first['sha']).to eq(merge_request_merged.diff_head_sha)
expect(json_response.first['merge_commit_sha']).not_to be_nil
expect(json_response.first['merge_commit_sha']).to eq(merge_request_merged.merge_commit_sha)
+ expect(json_response.first['squash']).to eq(merge_request_merged.squash)
end
it "returns an array of all merge_requests using simple mode" do
@@ -671,12 +672,14 @@ describe API::MergeRequests do
target_branch: 'master',
author: user,
labels: 'label, label2',
- milestone_id: milestone.id
+ milestone_id: milestone.id,
+ squash: true
expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('Test merge_request')
expect(json_response['labels']).to eq(%w(label label2))
expect(json_response['milestone']['id']).to eq(milestone.id)
+ expect(json_response['squash']).to be_truthy
expect(json_response['force_remove_source_branch']).to be_falsy
end
@@ -965,6 +968,14 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(200)
end
+ it "updates the MR's squash attribute" do
+ expect do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user), squash: true
+ end.to change { merge_request.reload.squash }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
it "enables merge when pipeline succeeds if the pipeline is active" do
allow_any_instance_of(MergeRequest).to receive(:head_pipeline).and_return(pipeline)
allow(pipeline).to receive(:active?).and_return(true)
@@ -1029,6 +1040,13 @@ describe API::MergeRequests do
expect(json_response['milestone']['id']).to eq(milestone.id)
end
+ it "updates squash and returns merge_request" do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), squash: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['squash']).to be_truthy
+ end
+
it "returns merge_request with renamed target_branch" do
put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), target_branch: "wiki"
expect(response).to have_gitlab_http_status(200)
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index efb9bddde44..6aadf839dbd 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -830,6 +830,21 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
end
+ context 'when job has already been finished' do
+ before do
+ job.trace.set('Job failed')
+ job.drop!(:script_failure)
+ end
+
+ it 'does not update job status and job trace' do
+ update_job(state: 'success', trace: 'BUILD TRACE UPDATED')
+
+ expect(response).to have_gitlab_http_status(403)
+ expect(job.trace.raw).to eq 'Job failed'
+ expect(job).to be_failed
+ end
+ end
+
def update_job(token = job.token, **params)
new_params = params.merge(token: token)
put api("/jobs/#{job.id}"), new_params
@@ -1210,7 +1225,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
before do
fog_connection.directories.get('artifacts').files.create(
- key: 'tmp/upload/12312300',
+ key: 'tmp/uploads/12312300',
body: 'content'
)
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 8b22d1e72f3..aead8978dd4 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -24,10 +24,15 @@ describe API::Settings, 'Settings' do
expect(json_response['ecdsa_key_restriction']).to eq(0)
expect(json_response['ed25519_key_restriction']).to eq(0)
expect(json_response['circuitbreaker_failure_count_threshold']).not_to be_nil
+ expect(json_response['performance_bar_allowed_group_id']).to be_nil
+ expect(json_response).not_to have_key('performance_bar_allowed_group_path')
+ expect(json_response).not_to have_key('performance_bar_enabled')
end
end
describe "PUT /application/settings" do
+ let(:group) { create(:group) }
+
context "custom repository storage type set in the config" do
before do
storages = { 'custom' => 'tmp/tests/custom_repositories' }
@@ -56,7 +61,8 @@ describe API::Settings, 'Settings' do
ed25519_key_restriction: 256,
circuitbreaker_check_interval: 2,
enforce_terms: true,
- terms: 'Hello world!'
+ terms: 'Hello world!',
+ performance_bar_allowed_group_path: group.full_path
expect(response).to have_gitlab_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
@@ -80,9 +86,27 @@ describe API::Settings, 'Settings' do
expect(json_response['circuitbreaker_check_interval']).to eq(2)
expect(json_response['enforce_terms']).to be(true)
expect(json_response['terms']).to eq('Hello world!')
+ expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
end
end
+ it "supports legacy performance_bar_allowed_group_id" do
+ put api("/application/settings", admin),
+ performance_bar_allowed_group_id: group.full_path
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['performance_bar_allowed_group_id']).to eq(group.id)
+ end
+
+ it "supports legacy performance_bar_enabled" do
+ put api("/application/settings", admin),
+ performance_bar_enabled: false,
+ performance_bar_allowed_group_id: group.full_path
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['performance_bar_allowed_group_id']).to be_nil
+ end
+
context "missing koding_url value when koding_enabled is true" do
it "returns a blank parameter error message" do
put api("/application/settings", admin), koding_enabled: true
diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb
index be70cb24dce..79a16fbd1b0 100644
--- a/spec/requests/api/v3/merge_requests_spec.rb
+++ b/spec/requests/api/v3/merge_requests_spec.rb
@@ -40,6 +40,7 @@ describe API::MergeRequests do
expect(json_response.first['sha']).to eq(merge_request_merged.diff_head_sha)
expect(json_response.first['merge_commit_sha']).not_to be_nil
expect(json_response.first['merge_commit_sha']).to eq(merge_request_merged.merge_commit_sha)
+ expect(json_response.first['squash']).to eq(merge_request_merged.squash)
end
it "returns an array of all merge_requests" do
@@ -241,13 +242,15 @@ describe API::MergeRequests do
author: user,
labels: 'label, label2',
milestone_id: milestone.id,
- remove_source_branch: true
+ remove_source_branch: true,
+ squash: true
expect(response).to have_gitlab_http_status(201)
expect(json_response['title']).to eq('Test merge_request')
expect(json_response['labels']).to eq(%w(label label2))
expect(json_response['milestone']['id']).to eq(milestone.id)
expect(json_response['force_remove_source_branch']).to be_truthy
+ expect(json_response['squash']).to be_truthy
end
it "returns 422 when source_branch equals target_branch" do
@@ -489,6 +492,14 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(200)
end
+ it "updates the MR's squash attribute" do
+ expect do
+ put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user), squash: true
+ end.to change { merge_request.reload.squash }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
it "enables merge when pipeline succeeds if the pipeline is active" do
allow_any_instance_of(MergeRequest).to receive(:head_pipeline).and_return(pipeline)
allow(pipeline).to receive(:active?).and_return(true)
@@ -529,6 +540,13 @@ describe API::MergeRequests do
expect(json_response['milestone']['id']).to eq(milestone.id)
end
+ it "updates squash and returns merge_request" do
+ put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), squash: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['squash']).to be_truthy
+ end
+
it "returns merge_request with renamed target_branch" do
put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user), target_branch: "wiki"
expect(response).to have_gitlab_http_status(200)
diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb
index 7bbf34422b8..38b618191fb 100644
--- a/spec/requests/api/version_spec.rb
+++ b/spec/requests/api/version_spec.rb
@@ -18,7 +18,7 @@ describe API::Version do
expect(response).to have_gitlab_http_status(200)
expect(json_response['version']).to eq(Gitlab::VERSION)
- expect(json_response['revision']).to eq(Gitlab::REVISION)
+ expect(json_response['revision']).to eq(Gitlab.revision)
end
end
end
diff --git a/spec/requests/lfs_http_spec.rb b/spec/requests/lfs_http_spec.rb
index f80abb06fca..79672fe1cc5 100644
--- a/spec/requests/lfs_http_spec.rb
+++ b/spec/requests/lfs_http_spec.rb
@@ -1089,7 +1089,7 @@ describe 'Git LFS API and storage' do
context 'with valid remote_id' do
before do
fog_connection.directories.get('lfs-objects').files.create(
- key: 'tmp/upload/12312300',
+ key: 'tmp/uploads/12312300',
body: 'content'
)
end
diff --git a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
index 7ddf9141fcd..03eeffe6483 100644
--- a/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
+++ b/spec/rubocop/cop/line_break_around_conditional_block_spec.rb
@@ -256,6 +256,18 @@ describe RuboCop::Cop::LineBreakAroundConditionalBlock do
expect(cop.offenses).to be_empty
end
+ it "doesn't flag violation for #{conditional} followed by a comment" do
+ source = <<~RUBY
+ #{conditional} condition
+ do_something
+ end
+ # a short comment
+ RUBY
+ inspect_source(source)
+
+ expect(cop.offenses).to be_empty
+ end
+
it "doesn't flag violation for #{conditional} followed by an end" do
source = <<~RUBY
class Foo
diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb
index fb07ecc6ae8..6337ee7d724 100644
--- a/spec/services/application_settings/update_service_spec.rb
+++ b/spec/services/application_settings/update_service_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe ApplicationSettings::UpdateService do
- let(:application_settings) { Gitlab::CurrentSettings.current_application_settings }
+ let(:application_settings) { create(:application_setting) }
let(:admin) { create(:user, :admin) }
let(:params) { {} }
@@ -54,4 +54,90 @@ describe ApplicationSettings::UpdateService do
end
end
end
+
+ describe 'performance bar settings' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:params_performance_bar_enabled,
+ :params_performance_bar_allowed_group_path,
+ :previous_performance_bar_allowed_group_id,
+ :expected_performance_bar_allowed_group_id) do
+ true | '' | nil | nil
+ true | '' | 42_000_000 | nil
+ true | nil | nil | nil
+ true | nil | 42_000_000 | nil
+ true | 'foo' | nil | nil
+ true | 'foo' | 42_000_000 | nil
+ true | 'group_a' | nil | 42_000_000
+ true | 'group_b' | 42_000_000 | 43_000_000
+ true | 'group_a' | 42_000_000 | 42_000_000
+ false | '' | nil | nil
+ false | '' | 42_000_000 | nil
+ false | nil | nil | nil
+ false | nil | 42_000_000 | nil
+ false | 'foo' | nil | nil
+ false | 'foo' | 42_000_000 | nil
+ false | 'group_a' | nil | nil
+ false | 'group_b' | 42_000_000 | nil
+ false | 'group_a' | 42_000_000 | nil
+ end
+
+ with_them do
+ let(:params) do
+ {
+ performance_bar_enabled: params_performance_bar_enabled,
+ performance_bar_allowed_group_path: params_performance_bar_allowed_group_path
+ }
+ end
+
+ before do
+ if previous_performance_bar_allowed_group_id == 42_000_000 || params_performance_bar_allowed_group_path == 'group_a'
+ create(:group, id: 42_000_000, path: 'group_a')
+ end
+
+ if expected_performance_bar_allowed_group_id == 43_000_000 || params_performance_bar_allowed_group_path == 'group_b'
+ create(:group, id: 43_000_000, path: 'group_b')
+ end
+
+ application_settings.update!(performance_bar_allowed_group_id: previous_performance_bar_allowed_group_id)
+ end
+
+ it 'sets performance_bar_allowed_group_id when present and performance_bar_enabled == true' do
+ expect(application_settings.performance_bar_allowed_group_id).to eq(previous_performance_bar_allowed_group_id)
+
+ if previous_performance_bar_allowed_group_id != expected_performance_bar_allowed_group_id
+ expect { subject.execute }
+ .to change(application_settings, :performance_bar_allowed_group_id)
+ .from(previous_performance_bar_allowed_group_id).to(expected_performance_bar_allowed_group_id)
+ else
+ expect { subject.execute }
+ .not_to change(application_settings, :performance_bar_allowed_group_id)
+ end
+ end
+ end
+
+ context 'when :performance_bar_allowed_group_path is not present' do
+ let(:group) { create(:group) }
+
+ before do
+ application_settings.update!(performance_bar_allowed_group_id: group.id)
+ end
+
+ it 'does not change the performance bar settings' do
+ expect { subject.execute }
+ .not_to change(application_settings, :performance_bar_allowed_group_id)
+ end
+ end
+
+ context 'when :performance_bar_enabled is not present' do
+ let(:group) { create(:group) }
+ let(:params) { { performance_bar_allowed_group_path: group.full_path } }
+
+ it 'implicitely defaults to true' do
+ expect { subject.execute }
+ .to change(application_settings, :performance_bar_allowed_group_id)
+ .from(nil).to(group.id)
+ end
+ end
+ end
end
diff --git a/spec/services/check_gcp_project_billing_service_spec.rb b/spec/services/check_gcp_project_billing_service_spec.rb
deleted file mode 100644
index 3e68d906e71..00000000000
--- a/spec/services/check_gcp_project_billing_service_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require 'spec_helper'
-
-describe CheckGcpProjectBillingService do
- include GoogleApi::CloudPlatformHelpers
-
- let(:service) { described_class.new }
- let(:project_id) { 'test-project-1234' }
-
- describe '#execute' do
- before do
- stub_cloud_platform_projects_list(project_id: project_id)
- end
-
- subject { service.execute('bogustoken') }
-
- context 'google account has a billing enabled gcp project' do
- before do
- stub_cloud_platform_projects_get_billing_info(project_id, true)
- end
-
- it { is_expected.to all(satisfy { |project| project.project_id == project_id }) }
- end
-
- context 'google account does not have a billing enabled gcp project' do
- before do
- stub_cloud_platform_projects_get_billing_info(project_id, false)
- end
-
- it { is_expected.to eq([]) }
- end
- end
-end
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index e8568bf8bb3..dc30a9bccc1 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -249,24 +249,58 @@ describe MergeRequests::MergeService do
expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
end
- context "when fast-forward merge is not allowed" do
+ context 'when squashing' do
before do
- allow_any_instance_of(Repository).to receive(:ancestor?).and_return(nil)
+ merge_request.update!(source_branch: 'master', target_branch: 'feature')
end
- %w(semi-linear ff).each do |merge_method|
- it "logs and saves error if merge is #{merge_method} only" do
- merge_method = 'rebase_merge' if merge_method == 'semi-linear'
- merge_request.project.update(merge_method: merge_method)
- error_message = 'Only fast-forward merge is allowed for your project. Please update your source branch'
- allow(service).to receive(:execute_hooks)
+ it 'logs and saves error if there is an error when squashing' do
+ error_message = 'Failed to squash. Should be done manually'
- service.execute(merge_request)
+ allow_any_instance_of(MergeRequests::SquashService).to receive(:squash).and_return(nil)
+ merge_request.update(squash: true)
+
+ service.execute(merge_request)
+
+ expect(merge_request).to be_open
+ expect(merge_request.merge_commit_sha).to be_nil
+ expect(merge_request.merge_error).to include(error_message)
+ expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
+ end
+
+ it 'logs and saves error if there is a squash in progress' do
+ error_message = 'another squash is already in progress'
+
+ allow_any_instance_of(MergeRequest).to receive(:squash_in_progress?).and_return(true)
+ merge_request.update(squash: true)
+
+ service.execute(merge_request)
- expect(merge_request).to be_open
- expect(merge_request.merge_commit_sha).to be_nil
- expect(merge_request.merge_error).to include(error_message)
- expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
+ expect(merge_request).to be_open
+ expect(merge_request.merge_commit_sha).to be_nil
+ expect(merge_request.merge_error).to include(error_message)
+ expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
+ end
+
+ context "when fast-forward merge is not allowed" do
+ before do
+ allow_any_instance_of(Repository).to receive(:ancestor?).and_return(nil)
+ end
+
+ %w(semi-linear ff).each do |merge_method|
+ it "logs and saves error if merge is #{merge_method} only" do
+ merge_method = 'rebase_merge' if merge_method == 'semi-linear'
+ merge_request.project.update(merge_method: merge_method)
+ error_message = 'Only fast-forward merge is allowed for your project. Please update your source branch'
+ allow(service).to receive(:execute_hooks)
+
+ service.execute(merge_request)
+
+ expect(merge_request).to be_open
+ expect(merge_request.merge_commit_sha).to be_nil
+ expect(merge_request.merge_error).to include(error_message)
+ expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
+ end
end
end
end
diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb
new file mode 100644
index 00000000000..bd884787425
--- /dev/null
+++ b/spec/services/merge_requests/squash_service_spec.rb
@@ -0,0 +1,199 @@
+require 'spec_helper'
+
+describe MergeRequests::SquashService do
+ let(:service) { described_class.new(project, user, {}) }
+ let(:user) { project.owner }
+ let(:project) { create(:project, :repository) }
+ let(:repository) { project.repository.raw }
+ let(:log_error) { "Failed to squash merge request #{merge_request.to_reference(full: true)}:" }
+ let(:squash_dir_path) do
+ File.join(Gitlab.config.shared.path, 'tmp/squash', repository.gl_repository, merge_request.id.to_s)
+ end
+ let(:merge_request_with_one_commit) do
+ create(:merge_request,
+ source_branch: 'feature', source_project: project,
+ target_branch: 'master', target_project: project)
+ end
+
+ let(:merge_request_with_only_new_files) do
+ create(:merge_request,
+ source_branch: 'video', source_project: project,
+ target_branch: 'master', target_project: project)
+ end
+
+ let(:merge_request_with_large_files) do
+ create(:merge_request,
+ source_branch: 'squash-large-files', source_project: project,
+ target_branch: 'master', target_project: project)
+ end
+
+ shared_examples 'the squash succeeds' do
+ it 'returns the squashed commit SHA' do
+ result = service.execute(merge_request)
+
+ expect(result).to match(status: :success, squash_sha: a_string_matching(/\h{40}/))
+ expect(result[:squash_sha]).not_to eq(merge_request.diff_head_sha)
+ end
+
+ it 'cleans up the temporary directory' do
+ service.execute(merge_request)
+
+ expect(File.exist?(squash_dir_path)).to be(false)
+ end
+
+ it 'does not keep the branch push event' do
+ expect { service.execute(merge_request) }.not_to change { Event.count }
+ end
+
+ context 'the squashed commit' do
+ let(:squash_sha) { service.execute(merge_request)[:squash_sha] }
+ let(:squash_commit) { project.repository.commit(squash_sha) }
+
+ it 'copies the author info and message from the merge request' do
+ expect(squash_commit.author_name).to eq(merge_request.author.name)
+ expect(squash_commit.author_email).to eq(merge_request.author.email)
+
+ # Commit messages have a trailing newline, but titles don't.
+ expect(squash_commit.message.chomp).to eq(merge_request.title)
+ end
+
+ it 'sets the current user as the committer' do
+ expect(squash_commit.committer_name).to eq(user.name.chomp('.'))
+ expect(squash_commit.committer_email).to eq(user.email)
+ end
+
+ it 'has the same diff as the merge request, but a different SHA' do
+ rugged = project.repository.rugged
+ mr_diff = rugged.diff(merge_request.diff_base_sha, merge_request.diff_head_sha)
+ squash_diff = rugged.diff(merge_request.diff_start_sha, squash_sha)
+
+ expect(squash_diff.patch.length).to eq(mr_diff.patch.length)
+ expect(squash_commit.sha).not_to eq(merge_request.diff_head_sha)
+ end
+ end
+ end
+
+ describe '#execute' do
+ context 'when there is only one commit in the merge request' do
+ it 'returns that commit SHA' do
+ result = service.execute(merge_request_with_one_commit)
+
+ expect(result).to match(status: :success, squash_sha: merge_request_with_one_commit.diff_head_sha)
+ end
+
+ it 'does not perform any git actions' do
+ expect(repository).not_to receive(:popen)
+
+ service.execute(merge_request_with_one_commit)
+ end
+ end
+
+ context 'when squashing only new files' do
+ let(:merge_request) { merge_request_with_only_new_files }
+
+ include_examples 'the squash succeeds'
+ end
+
+ context 'when squashing with files too large to display' do
+ let(:merge_request) { merge_request_with_large_files }
+
+ include_examples 'the squash succeeds'
+ end
+
+ context 'git errors' do
+ let(:merge_request) { merge_request_with_only_new_files }
+ let(:error) { 'A test error' }
+
+ context 'with gitaly enabled' do
+ before do
+ allow(repository.gitaly_operation_client).to receive(:user_squash)
+ .and_raise(Gitlab::Git::Repository::GitError, error)
+ end
+
+ it 'logs the stage and output' do
+ expect(service).to receive(:log_error).with(log_error)
+ expect(service).to receive(:log_error).with(error)
+
+ service.execute(merge_request)
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: a_string_including('squash'))
+ end
+ end
+
+ context 'with Gitaly disabled', :skip_gitaly_mock do
+ stages = {
+ 'add worktree for squash' => 'worktree',
+ 'configure sparse checkout' => 'config',
+ 'get files in diff' => 'diff --name-only',
+ 'check out target branch' => 'checkout',
+ 'apply patch' => 'diff --binary',
+ 'commit squashed changes' => 'commit',
+ 'get SHA of squashed commit' => 'rev-parse'
+ }
+
+ stages.each do |stage, command|
+ context "when the #{stage} stage fails" do
+ before do
+ git_command = a_collection_containing_exactly(
+ a_string_starting_with("#{Gitlab.config.git.bin_path} #{command}")
+ ).or(
+ a_collection_starting_with([Gitlab.config.git.bin_path] + command.split)
+ )
+
+ allow(repository).to receive(:popen).and_return(['', 0])
+ allow(repository).to receive(:popen).with(git_command, anything, anything, anything).and_return([error, 1])
+ end
+
+ it 'logs the stage and output' do
+ expect(service).to receive(:log_error).with(log_error)
+ expect(service).to receive(:log_error).with(error)
+
+ service.execute(merge_request)
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: a_string_including('squash'))
+ end
+
+ it 'cleans up the temporary directory' do
+ expect(File.exist?(squash_dir_path)).to be(false)
+
+ service.execute(merge_request)
+ end
+ end
+ end
+ end
+ end
+
+ context 'when any other exception is thrown' do
+ let(:merge_request) { merge_request_with_only_new_files }
+ let(:error) { 'A test error' }
+
+ before do
+ allow(merge_request).to receive(:commits_count).and_raise(error)
+ end
+
+ it 'logs the MR reference and exception' do
+ expect(service).to receive(:log_error).with(a_string_including("#{project.full_path}#{merge_request.to_reference}"))
+ expect(service).to receive(:log_error).with(error)
+
+ service.execute(merge_request)
+ end
+
+ it 'returns an error' do
+ expect(service.execute(merge_request)).to match(status: :error,
+ message: a_string_including('squash'))
+ end
+
+ it 'cleans up the temporary directory' do
+ service.execute(merge_request)
+
+ expect(File.exist?(squash_dir_path)).to be(false)
+ end
+ end
+ end
+end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index f5cff66de6d..2b2b983494f 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -57,6 +57,88 @@ describe Notes::CreateService do
end
end
+ context 'note diff file' 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(:line_number) { 14 }
+ let(:position) do
+ Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: line_number,
+ diff_refs: merge_request.diff_refs)
+ end
+ let(:previous_note) do
+ create(:diff_note_on_merge_request, noteable: merge_request, project: project_with_repo)
+ end
+
+ context 'when eligible to have a note diff file' do
+ let(:new_opts) do
+ opts.merge(in_reply_to_discussion_id: nil,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: position.to_h)
+ end
+
+ it 'note is associated with a note diff file' do
+ note = described_class.new(project_with_repo, user, new_opts).execute
+
+ expect(note).to be_persisted
+ expect(note.note_diff_file).to be_present
+ end
+ end
+
+ context 'when DiffNote is a reply' do
+ let(:new_opts) do
+ opts.merge(in_reply_to_discussion_id: previous_note.discussion_id,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: position.to_h)
+ end
+
+ it 'note is not associated with a note diff file' do
+ note = described_class.new(project_with_repo, user, new_opts).execute
+
+ expect(note).to be_persisted
+ expect(note.note_diff_file).to be_nil
+ end
+
+ context 'when DiffNote from an image' do
+ let(:image_position) do
+ Gitlab::Diff::Position.new(old_path: "files/images/6049019_460s.jpg",
+ new_path: "files/images/6049019_460s.jpg",
+ width: 100,
+ height: 100,
+ x: 1,
+ y: 100,
+ diff_refs: merge_request.diff_refs,
+ position_type: 'image')
+ end
+
+ let(:new_opts) do
+ opts.merge(in_reply_to_discussion_id: nil,
+ type: 'DiffNote',
+ noteable_type: 'MergeRequest',
+ noteable_id: merge_request.id,
+ position: image_position.to_h)
+ end
+
+ it 'note is not associated with a note diff file' do
+ note = described_class.new(project_with_repo, user, new_opts).execute
+
+ expect(note).to be_persisted
+ expect(note.note_diff_file).to be_nil
+ end
+ end
+ end
+ end
+
context 'note with commands' do
context 'as a user who can update the target' do
context '/close, /label, /assign & /milestone' do
diff --git a/spec/support/api/milestones_shared_examples.rb b/spec/support/api/milestones_shared_examples.rb
index 0388f110d71..a15189db35f 100644
--- a/spec/support/api/milestones_shared_examples.rb
+++ b/spec/support/api/milestones_shared_examples.rb
@@ -196,6 +196,12 @@ shared_examples_for 'group and project milestones' do |route_definition|
expect(json_response['state']).to eq('closed')
end
+
+ it 'updates milestone with only start date' do
+ put api(resource_route, user), start_date: Date.tomorrow
+
+ expect(response).to have_gitlab_http_status(200)
+ end
end
describe "GET #{route_definition}/:milestone_id/issues" do
diff --git a/spec/support/helpers/board_helpers.rb b/spec/support/helpers/board_helpers.rb
index 507d0432d7f..b85fde222ea 100644
--- a/spec/support/helpers/board_helpers.rb
+++ b/spec/support/helpers/board_helpers.rb
@@ -1,7 +1,7 @@
module BoardHelpers
def click_card(card)
within card do
- first('.card-number').click
+ first('.board-card-number').click
end
wait_for_sidebar
diff --git a/spec/support/helpers/features/sorting_helpers.rb b/spec/support/helpers/features/sorting_helpers.rb
index 50457b64745..ad0053ec5cf 100644
--- a/spec/support/helpers/features/sorting_helpers.rb
+++ b/spec/support/helpers/features/sorting_helpers.rb
@@ -15,7 +15,7 @@ module Spec
def sort_by(value)
find('button.dropdown-toggle').click
- page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
+ page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link(value)
end
end
diff --git a/spec/support/helpers/mobile_helpers.rb b/spec/support/helpers/mobile_helpers.rb
index 3b9eb84e824..9dc1f1de436 100644
--- a/spec/support/helpers/mobile_helpers.rb
+++ b/spec/support/helpers/mobile_helpers.rb
@@ -1,10 +1,10 @@
module MobileHelpers
def resize_screen_xs
- resize_window(767, 768)
+ resize_window(575, 768)
end
def resize_screen_sm
- resize_window(900, 768)
+ resize_window(767, 768)
end
def restore_window_size
diff --git a/spec/support/helpers/sorting_helper.rb b/spec/support/helpers/sorting_helper.rb
index 577518d726c..9496a94d8f4 100644
--- a/spec/support/helpers/sorting_helper.rb
+++ b/spec/support/helpers/sorting_helper.rb
@@ -11,7 +11,7 @@
module SortingHelper
def sorting_by(value)
find('button.dropdown-toggle').click
- page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
+ page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link value
end
end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 57aa07cf4fa..1fef50a52ec 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -47,6 +47,7 @@ module TestEnv
'v1.1.0' => 'b83d6e3',
'add-ipython-files' => '93ee732',
'add-pdf-file' => 'e774ebd',
+ 'squash-large-files' => '54cec52',
'add-pdf-text-binary' => '79faa7b',
'add_images_and_changes' => '010d106'
}.freeze
diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
index 17f319f49e9..5241c0fa6f1 100644
--- a/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
+++ b/spec/support/shared_examples/features/protected_branches_access_control_ce.rb
@@ -10,7 +10,7 @@ shared_examples "protected branches > access control > CE" do
unless allowed_to_push_button.text == access_type_name
allowed_to_push_button.click
- within(".dropdown.open .dropdown-menu") { click_on access_type_name }
+ within(".dropdown.show .dropdown-menu") { click_on access_type_name }
end
end
@@ -55,7 +55,7 @@ shared_examples "protected branches > access control > CE" do
unless allowed_to_merge_button.text == access_type_name
allowed_to_merge_button.click
- within(".dropdown.open .dropdown-menu") { click_on access_type_name }
+ within(".dropdown.show .dropdown-menu") { click_on access_type_name }
end
end
diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb
index e7277b337f6..4165a005063 100644
--- a/spec/uploaders/object_storage_spec.rb
+++ b/spec/uploaders/object_storage_spec.rb
@@ -618,7 +618,7 @@ describe ObjectStorage do
let!(:fog_file) do
fog_connection.directories.get('uploads').files.create(
- key: 'tmp/upload/test/123123',
+ key: 'tmp/uploads/test/123123',
body: 'content'
)
end
diff --git a/spec/views/admin/dashboard/index.html.haml_spec.rb b/spec/views/admin/dashboard/index.html.haml_spec.rb
index 099baacf019..0e8b7c82d3a 100644
--- a/spec/views/admin/dashboard/index.html.haml_spec.rb
+++ b/spec/views/admin/dashboard/index.html.haml_spec.rb
@@ -4,6 +4,11 @@ describe 'admin/dashboard/index.html.haml' do
include Devise::Test::ControllerHelpers
before do
+ counts = Admin::DashboardController::COUNTED_ITEMS.each_with_object({}) do |item, hash|
+ hash[item] = 100
+ end
+
+ assign(:counts, counts)
assign(:projects, create_list(:project, 1))
assign(:users, create_list(:user, 1))
assign(:groups, create_list(:group, 1))
@@ -22,6 +27,6 @@ describe 'admin/dashboard/index.html.haml' do
it "includes revision of GitLab" do
render
- expect(rendered).to have_content "#{Gitlab::VERSION} (#{Gitlab::REVISION})"
+ expect(rendered).to have_content "#{Gitlab::VERSION} (#{Gitlab.revision})"
end
end
diff --git a/spec/views/help/index.html.haml_spec.rb b/spec/views/help/index.html.haml_spec.rb
index 0a78606171d..836d452304c 100644
--- a/spec/views/help/index.html.haml_spec.rb
+++ b/spec/views/help/index.html.haml_spec.rb
@@ -39,7 +39,7 @@ describe 'help/index' do
def stub_version(version, revision)
stub_const('Gitlab::VERSION', version)
- stub_const('Gitlab::REVISION', revision)
+ allow(Gitlab).to receive(:revision).and_return(revision)
end
def stub_helpers
diff --git a/spec/workers/check_gcp_project_billing_worker_spec.rb b/spec/workers/check_gcp_project_billing_worker_spec.rb
deleted file mode 100644
index 526ecf75921..00000000000
--- a/spec/workers/check_gcp_project_billing_worker_spec.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-require 'spec_helper'
-
-describe CheckGcpProjectBillingWorker do
- describe '.perform' do
- let(:token) { 'bogustoken' }
-
- subject { described_class.new.perform('token_key') }
-
- before do
- allow(described_class).to receive(:get_billing_state)
- allow_any_instance_of(described_class).to receive(:update_billing_change_counter)
- end
-
- context 'when there is a token in redis' do
- before do
- allow(described_class).to receive(:get_session_token).and_return(token)
- end
-
- context 'when there is no lease' do
- before do
- allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return('randomuuid')
- end
-
- it 'calls the service' do
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
-
- subject
- end
-
- it 'stores billing status in redis' do
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
- expect(described_class).to receive(:set_billing_state).with(token, true)
-
- subject
- end
- end
-
- context 'when there is a lease' do
- before do
- allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return(false)
- end
-
- it 'does not call the service' do
- expect(CheckGcpProjectBillingService).not_to receive(:new)
-
- subject
- end
- end
- end
-
- context 'when there is no token in redis' do
- before do
- allow(described_class).to receive(:get_session_token).and_return(nil)
- end
-
- it 'does not call the service' do
- expect(CheckGcpProjectBillingService).not_to receive(:new)
-
- subject
- end
- end
- end
-
- describe 'billing change counter' do
- subject { described_class.new.perform('token_key') }
-
- before do
- allow(described_class).to receive(:get_session_token).and_return('bogustoken')
- allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return('randomuuid')
- allow(described_class).to receive(:set_billing_state)
- end
-
- context 'when previous state was false' do
- before do
- expect(described_class).to receive(:get_billing_state).and_return(false)
- end
-
- context 'when the current state is false' do
- before do
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([])
- end
-
- it 'increments the billing change counter' do
- expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
-
- subject
- end
- end
-
- context 'when the current state is true' do
- before do
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
- end
-
- it 'increments the billing change counter' do
- expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
-
- subject
- end
- end
- end
-
- context 'when previous state was true' do
- before do
- expect(described_class).to receive(:get_billing_state).and_return(true)
- expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
- end
-
- it 'increment the billing change counter' do
- expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
-
- subject
- end
- end
- end
-end
diff --git a/spec/workers/create_note_diff_file_worker_spec.rb b/spec/workers/create_note_diff_file_worker_spec.rb
new file mode 100644
index 00000000000..0ac946a1232
--- /dev/null
+++ b/spec/workers/create_note_diff_file_worker_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe CreateNoteDiffFileWorker do
+ describe '#perform' do
+ let(:diff_note) { create(:diff_note_on_merge_request) }
+
+ it 'creates diff file' do
+ diff_note.note_diff_file.destroy!
+
+ expect_any_instance_of(DiffNote).to receive(:create_diff_file)
+ .and_call_original
+
+ described_class.new.perform(diff_note.id)
+ end
+ end
+end