summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2018-04-25 10:38:06 +0100
committerPhil Hughes <me@iamphill.com>2018-04-25 10:38:06 +0100
commit7adbed74648574e35cfe79210ef67157cd44937e (patch)
treef87f83e80afe70fbb3951fd3ad9bf07d42c676e0 /spec
parentc45932bee018aa7d0256367fc4830f5b45bedbab (diff)
parentb36941bdaad03eeb5ab23235b77f1bfad0348f18 (diff)
downloadgitlab-ce-7adbed74648574e35cfe79210ef67157cd44937e.tar.gz
Merge branch 'master' into ide-workbench-bar
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb5
-rw-r--r--spec/controllers/import/gitlab_controller_spec.rb5
-rw-r--r--spec/controllers/ldap/omniauth_callbacks_controller_spec.rb58
-rw-r--r--spec/controllers/projects/forks_controller_spec.rb6
-rw-r--r--spec/factories/commits.rb2
-rw-r--r--spec/factories/deploy_tokens.rb8
-rw-r--r--spec/factories/gpg_key_subkeys.rb2
-rw-r--r--spec/factories/gpg_keys.rb2
-rw-r--r--spec/factories/gpg_signature.rb2
-rw-r--r--spec/factories/groups.rb8
-rw-r--r--spec/factories/namespaces.rb18
-rw-r--r--spec/factories/notes.rb2
-rw-r--r--spec/factories/projects.rb9
-rw-r--r--spec/fast_spec_helper.rb16
-rw-r--r--spec/features/labels_hierarchy_spec.rb14
-rw-r--r--spec/features/oauth_login_spec.rb49
-rw-r--r--spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb52
-rw-r--r--spec/features/projects/files/user_find_file_spec.rb66
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb2
-rw-r--r--spec/features/projects/new_project_spec.rb4
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb30
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb8
-rw-r--r--spec/features/projects/user_views_empty_project_spec.rb43
-rw-r--r--spec/helpers/gitlab_routing_helper_spec.rb15
-rw-r--r--spec/javascripts/.eslintrc1
-rw-r--r--spec/javascripts/activities_spec.js75
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js2
-rw-r--r--spec/javascripts/blob/blob_file_dropzone_spec.js2
-rw-r--r--spec/javascripts/comment_type_toggle_spec.js5
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/commits_spec.js12
-rw-r--r--spec/javascripts/droplab/hook_spec.js5
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js17
-rw-r--r--spec/javascripts/filtered_search/recent_searches_root_spec.js6
-rw-r--r--spec/javascripts/gl_dropdown_spec.js7
-rw-r--r--spec/javascripts/groups/components/app_spec.js5
-rw-r--r--spec/javascripts/groups/components/group_item_spec.js5
-rw-r--r--spec/javascripts/helpers/class_spec_helper_spec.js2
-rw-r--r--spec/javascripts/ide/components/file_finder/index_spec.js308
-rw-r--r--spec/javascripts/ide/components/file_finder/item_spec.js140
-rw-r--r--spec/javascripts/ide/components/ide_spec.js65
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js37
-rw-r--r--spec/javascripts/ide/stores/getters_spec.js20
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js8
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js8
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js25
-rw-r--r--spec/javascripts/issue_show/components/description_spec.js15
-rw-r--r--spec/javascripts/job_spec.js3
-rw-r--r--spec/javascripts/jobs/sidebar_details_block_spec.js2
-rw-r--r--spec/javascripts/lib/utils/csrf_token_spec.js2
-rw-r--r--spec/javascripts/lib/utils/image_utility_spec.js8
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js13
-rw-r--r--spec/javascripts/monitoring/monitoring_store_spec.js2
-rw-r--r--spec/javascripts/notes_spec.js7
-rw-r--r--spec/javascripts/pager_spec.js43
-rw-r--r--spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js5
-rw-r--r--spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js5
-rw-r--r--spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js2
-rw-r--r--spec/javascripts/pipelines/graph/action_component_spec.js47
-rw-r--r--spec/javascripts/pipelines/graph/dropdown_action_component_spec.js32
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js11
-rw-r--r--spec/javascripts/right_sidebar_spec.js4
-rw-r--r--spec/javascripts/search_autocomplete_spec.js4
-rw-r--r--spec/javascripts/settings_panels_spec.js4
-rw-r--r--spec/javascripts/shortcuts_dashboard_navigation_spec.js9
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js2
-rw-r--r--spec/javascripts/sidebar/sidebar_mediator_spec.js7
-rw-r--r--spec/javascripts/sidebar/sidebar_move_issue_spec.js2
-rw-r--r--spec/javascripts/sidebar/sidebar_store_spec.js2
-rw-r--r--spec/javascripts/test_bundle.js14
-rw-r--r--spec/javascripts/todos_spec.js5
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js2
-rw-r--r--spec/javascripts/u2f/register_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/deployment_spec.js5
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js17
-rw-r--r--spec/javascripts/vue_shared/components/ci_icon_spec.js149
-rw-r--r--spec/javascripts/vue_shared/components/clipboard_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/commit_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/expand_button_spec.js4
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js16
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js10
-rw-r--r--spec/lib/backup/files_spec.rb14
-rw-r--r--spec/lib/backup/repository_spec.rb12
-rw-r--r--spec/lib/gitlab/auth/ldap/user_spec.rb8
-rw-r--r--spec/lib/gitlab/auth/o_auth/identity_linker_spec.rb62
-rw-r--r--spec/lib/gitlab/auth/saml/identity_linker_spec.rb48
-rw-r--r--spec/lib/gitlab/bitbucket_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/play_spec.rb6
-rw-r--r--spec/lib/gitlab/git/committer_with_hooks_spec.rb (renamed from spec/lib/gitlab/wiki/committer_with_hooks_spec.rb)2
-rw-r--r--spec/lib/gitlab/gitlab_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/google_code_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml1
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml2
-rw-r--r--spec/lib/gitlab/legacy_github_import/project_creator_spec.rb2
-rw-r--r--spec/lib/gitlab/user_access_spec.rb12
-rw-r--r--spec/lib/gitlab_spec.rb10
-rw-r--r--spec/migrations/create_missing_namespace_for_internal_users_spec.rb42
-rw-r--r--spec/models/ci/build_spec.rb32
-rw-r--r--spec/models/concerns/avatarable_spec.rb16
-rw-r--r--spec/models/deploy_token_spec.rb19
-rw-r--r--spec/models/environment_spec.rb4
-rw-r--r--spec/models/project_ci_cd_setting_spec.rb24
-rw-r--r--spec/models/project_spec.rb97
-rw-r--r--spec/models/user_spec.rb8
-rw-r--r--spec/policies/group_policy_spec.rb27
-rw-r--r--spec/presenters/project_presenter_spec.rb13
-rw-r--r--spec/requests/api/projects_spec.rb3
-rw-r--r--spec/requests/api/users_spec.rb12
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb2
-rw-r--r--spec/services/groups/nested_create_service_spec.rb7
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb76
-rw-r--r--spec/spec_helper.rb59
-rw-r--r--spec/support/capybara.rb2
-rw-r--r--spec/support/controllers/githubish_import_controller_shared_examples.rb9
-rw-r--r--spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb33
-rw-r--r--spec/support/factory_bot.rb3
-rwxr-xr-xspec/support/generate-seed-repo-rb2
-rw-r--r--spec/support/gitlab-git-test.git/README.md2
-rw-r--r--spec/support/helpers/api_helpers.rb (renamed from spec/support/api_helpers.rb)0
-rw-r--r--spec/support/helpers/bare_repo_operations.rb (renamed from spec/support/bare_repo_operations.rb)0
-rw-r--r--spec/support/helpers/board_helpers.rb (renamed from spec/support/board_helpers.rb)0
-rw-r--r--spec/support/helpers/capybara_helpers.rb (renamed from spec/support/capybara_helpers.rb)4
-rw-r--r--spec/support/helpers/cookie_helper.rb (renamed from spec/support/cookie_helper.rb)0
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb (renamed from spec/support/cycle_analytics_helpers.rb)4
-rw-r--r--spec/support/helpers/database_connection_helpers.rb (renamed from spec/support/database_connection_helpers.rb)0
-rw-r--r--spec/support/helpers/devise_helpers.rb (renamed from spec/support/devise_helpers.rb)0
-rw-r--r--spec/support/helpers/drag_to_helper.rb (renamed from spec/support/drag_to_helper.rb)0
-rw-r--r--spec/support/helpers/dropzone_helper.rb (renamed from spec/support/dropzone_helper.rb)0
-rw-r--r--spec/support/helpers/email_helpers.rb (renamed from spec/support/email_helpers.rb)0
-rw-r--r--spec/support/helpers/fake_migration_classes.rb (renamed from spec/support/fake_migration_classes.rb)0
-rw-r--r--spec/support/helpers/fake_u2f_device.rb (renamed from spec/support/fake_u2f_device.rb)0
-rw-r--r--spec/support/helpers/filter_item_select_helper.rb (renamed from spec/support/filter_item_select_helper.rb)0
-rw-r--r--spec/support/helpers/filter_spec_helper.rb (renamed from spec/support/filter_spec_helper.rb)0
-rw-r--r--spec/support/helpers/filtered_search_helpers.rb (renamed from spec/support/filtered_search_helpers.rb)0
-rw-r--r--spec/support/helpers/fixture_helpers.rb (renamed from spec/support/fixture_helpers.rb)4
-rw-r--r--spec/support/helpers/git_http_helpers.rb (renamed from spec/support/git_http_helpers.rb)0
-rw-r--r--spec/support/helpers/gitlab_verify_helpers.rb25
-rw-r--r--spec/support/helpers/gpg_helpers.rb (renamed from spec/support/gpg_helpers.rb)0
-rw-r--r--spec/support/helpers/import_spec_helper.rb (renamed from spec/support/import_spec_helper.rb)0
-rw-r--r--spec/support/helpers/input_helper.rb (renamed from spec/support/input_helper.rb)0
-rw-r--r--spec/support/helpers/inspect_requests.rb (renamed from spec/support/inspect_requests.rb)0
-rw-r--r--spec/support/helpers/issue_helpers.rb (renamed from spec/support/issue_helpers.rb)0
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb (renamed from spec/support/javascript_fixtures_helpers.rb)0
-rw-r--r--spec/support/helpers/jira_service_helper.rb (renamed from spec/support/jira_service_helper.rb)0
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb (renamed from spec/support/kubernetes_helpers.rb)0
-rw-r--r--spec/support/helpers/ldap_helpers.rb (renamed from spec/support/ldap_helpers.rb)4
-rw-r--r--spec/support/helpers/live_debugger.rb (renamed from spec/support/live_debugger.rb)0
-rw-r--r--spec/support/helpers/login_helpers.rb (renamed from spec/support/login_helpers.rb)4
-rw-r--r--spec/support/helpers/markdown_feature.rb (renamed from spec/support/markdown_feature.rb)0
-rw-r--r--spec/support/helpers/merge_request_helpers.rb (renamed from spec/support/merge_request_helpers.rb)0
-rw-r--r--spec/support/helpers/migrations_helpers.rb (renamed from spec/support/migrations_helpers.rb)0
-rw-r--r--spec/support/helpers/mobile_helpers.rb (renamed from spec/support/mobile_helpers.rb)0
-rw-r--r--spec/support/helpers/project_forks_helper.rb (renamed from spec/support/project_forks_helper.rb)0
-rw-r--r--spec/support/helpers/prometheus_helpers.rb (renamed from spec/support/prometheus_helpers.rb)0
-rw-r--r--spec/support/helpers/query_recorder.rb38
-rw-r--r--spec/support/helpers/quick_actions_helpers.rb10
-rw-r--r--spec/support/helpers/rake_helpers.rb (renamed from spec/support/rake_helpers.rb)0
-rw-r--r--spec/support/helpers/reactive_caching_helpers.rb (renamed from spec/support/reactive_caching_helpers.rb)0
-rw-r--r--spec/support/helpers/redis_without_keys.rb (renamed from spec/support/redis_without_keys.rb)0
-rw-r--r--spec/support/helpers/reference_parser_helpers.rb (renamed from spec/support/reference_parser_helpers.rb)0
-rw-r--r--spec/support/helpers/repo_helpers.rb (renamed from spec/support/repo_helpers.rb)0
-rw-r--r--spec/support/helpers/search_helpers.rb (renamed from spec/support/search_helpers.rb)0
-rw-r--r--spec/support/helpers/seed_helper.rb (renamed from spec/support/seed_helper.rb)10
-rw-r--r--spec/support/helpers/seed_repo.rb (renamed from spec/support/seed_repo.rb)0
-rw-r--r--spec/support/helpers/select2_helper.rb (renamed from spec/support/select2_helper.rb)0
-rw-r--r--spec/support/helpers/selection_helper.rb (renamed from spec/support/selection_helper.rb)0
-rw-r--r--spec/support/helpers/sorting_helper.rb18
-rw-r--r--spec/support/helpers/stub_configuration.rb (renamed from spec/support/stub_configuration.rb)3
-rw-r--r--spec/support/helpers/stub_env.rb (renamed from spec/support/stub_env.rb)0
-rw-r--r--spec/support/helpers/stub_feature_flags.rb (renamed from spec/support/stub_feature_flags.rb)0
-rw-r--r--spec/support/helpers/stub_gitlab_calls.rb (renamed from spec/support/stub_gitlab_calls.rb)0
-rw-r--r--spec/support/helpers/stub_gitlab_data.rb (renamed from spec/support/stub_gitlab_data.rb)0
-rw-r--r--spec/support/helpers/stub_object_storage.rb (renamed from spec/support/stub_object_storage.rb)2
-rw-r--r--spec/support/helpers/test_env.rb (renamed from spec/support/test_env.rb)0
-rw-r--r--spec/support/helpers/upload_helpers.rb (renamed from spec/support/upload_helpers.rb)0
-rw-r--r--spec/support/helpers/user_activities_helpers.rb (renamed from spec/support/user_activities_helpers.rb)0
-rw-r--r--spec/support/helpers/wait_for_requests.rb (renamed from spec/support/wait_for_requests.rb)0
-rw-r--r--spec/support/helpers/workhorse_helpers.rb (renamed from spec/support/workhorse_helpers.rb)0
-rw-r--r--spec/support/issuables_list_metadata_shared_examples.rb46
-rw-r--r--spec/support/json_response.rb (renamed from spec/support/json_response_helpers.rb)4
-rw-r--r--spec/support/matchers/background_migrations_matchers.rb (renamed from spec/support/background_migrations_matchers.rb)0
-rw-r--r--spec/support/matchers/exceed_query_limit.rb (renamed from spec/support/query_recorder.rb)39
-rwxr-xr-xspec/support/prepare-gitlab-git-test-for-commit2
-rw-r--r--spec/support/routing_helpers.rb3
-rw-r--r--spec/support/rspec.rb12
-rw-r--r--spec/support/seed.rb7
-rw-r--r--spec/support/shared_contexts/json_response_shared_context.rb3
-rw-r--r--spec/support/shared_contexts/services_shared_context.rb (renamed from spec/support/services_shared_context.rb)0
-rw-r--r--spec/support/shared_examples/chat_slash_commands_shared_examples.rb (renamed from spec/support/chat_slash_commands_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/email_format_shared_examples.rb (renamed from spec/support/email_format_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/gitlab_verify.rb (renamed from spec/support/gitlab_verify.rb)26
-rw-r--r--spec/support/shared_examples/group_members_shared_example.rb (renamed from spec/support/group_members_shared_example.rb)0
-rw-r--r--spec/support/shared_examples/issuable_shared_examples.rb (renamed from spec/support/issuable_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/issuables_list_metadata_shared_examples.rb62
-rw-r--r--spec/support/shared_examples/issue_tracker_service_shared_example.rb (renamed from spec/support/issue_tracker_service_shared_example.rb)0
-rw-r--r--spec/support/shared_examples/ldap_shared_examples.rb (renamed from spec/support/ldap_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/legacy_path_redirect_shared_examples.rb (renamed from spec/support/legacy_path_redirect_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/malicious_regexp_shared_examples.rb (renamed from spec/support/malicious_regexp_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/mentionable_shared_examples.rb (renamed from spec/support/mentionable_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/milestone_tabs_examples.rb (renamed from spec/support/milestone_tabs_examples.rb)0
-rw-r--r--spec/support/shared_examples/notify_shared_examples.rb (renamed from spec/support/notify_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/reference_parser_shared_examples.rb (renamed from spec/support/reference_parser_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb (renamed from spec/support/slack_mattermost_notifications_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/snippet_visibility.rb (renamed from spec/support/snippet_visibility.rb)0
-rw-r--r--spec/support/shared_examples/snippets_shared_examples.rb (renamed from spec/support/snippets_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/taskable_shared_examples.rb (renamed from spec/support/taskable_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/time_tracking_shared_examples.rb (renamed from spec/support/time_tracking_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/unique_ip_check_shared_examples.rb (renamed from spec/support/unique_ip_check_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/update_invalid_issuable.rb (renamed from spec/support/update_invalid_issuable.rb)0
-rw-r--r--spec/support/shared_examples/updating_mentions_shared_examples.rb (renamed from spec/support/updating_mentions_shared_examples.rb)0
-rw-r--r--spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb (renamed from spec/views/projects/settings/ci_cd/_form.html.haml_spec.rb)2
-rw-r--r--spec/workers/issue_due_scheduler_worker_spec.rb4
212 files changed, 2031 insertions, 629 deletions
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index 2be46049aab..be49b92d23f 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -223,11 +223,12 @@ describe Import::BitbucketController do
end
context 'user has chosen an existing nested namespace and name for the project', :postgresql do
- let(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
let(:test_name) { 'test_name' }
before do
+ parent_namespace.add_owner(user)
nested_namespace.add_owner(user)
end
@@ -273,7 +274,7 @@ describe Import::BitbucketController do
context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
let(:test_name) { 'test_name' }
- let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ let!(:parent_namespace) { create(:group, name: 'foo') }
before do
parent_namespace.add_owner(user)
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index e958be077c2..742f4787126 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -196,10 +196,11 @@ describe Import::GitlabController do
end
context 'user has chosen an existing nested namespace for the project', :postgresql do
- let(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
before do
+ parent_namespace.add_owner(user)
nested_namespace.add_owner(user)
end
@@ -245,7 +246,7 @@ describe Import::GitlabController do
context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
let(:test_name) { 'test_name' }
- let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ let!(:parent_namespace) { create(:group, name: 'foo') }
before do
parent_namespace.add_owner(user)
diff --git a/spec/controllers/ldap/omniauth_callbacks_controller_spec.rb b/spec/controllers/ldap/omniauth_callbacks_controller_spec.rb
new file mode 100644
index 00000000000..87c10a86cdd
--- /dev/null
+++ b/spec/controllers/ldap/omniauth_callbacks_controller_spec.rb
@@ -0,0 +1,58 @@
+require 'spec_helper'
+
+describe Ldap::OmniauthCallbacksController do
+ include_context 'Ldap::OmniauthCallbacksController'
+
+ it 'allows sign in' do
+ post provider
+
+ expect(request.env['warden']).to be_authenticated
+ end
+
+ it 'respects remember me checkbox' do
+ expect do
+ post provider, remember_me: '1'
+ end.to change { user.reload.remember_created_at }.from(nil)
+ end
+
+ context 'with 2FA' do
+ let(:user) { create(:omniauth_user, :two_factor_via_otp, extern_uid: uid, provider: provider) }
+
+ it 'passes remember_me to the Devise view' do
+ post provider, remember_me: '1'
+
+ expect(assigns[:user].remember_me).to eq '1'
+ end
+ end
+
+ context 'access denied' do
+ let(:valid_login?) { false }
+
+ it 'warns the user' do
+ post provider
+
+ expect(flash[:alert]).to match(/Access denied for your LDAP account*/)
+ end
+
+ it "doesn't authenticate user" do
+ post provider
+
+ expect(request.env['warden']).not_to be_authenticated
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+
+ context 'sign up' do
+ let(:user) { double(email: 'new@example.com') }
+
+ before do
+ stub_omniauth_setting(block_auto_created_users: false)
+ end
+
+ it 'is allowed' do
+ post provider
+
+ expect(request.env['warden']).to be_authenticated
+ end
+ end
+end
diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb
index c4b32dc3a09..e20623c0ac1 100644
--- a/spec/controllers/projects/forks_controller_spec.rb
+++ b/spec/controllers/projects/forks_controller_spec.rb
@@ -4,7 +4,11 @@ describe Projects::ForksController do
let(:user) { create(:user) }
let(:project) { create(:project, :public, :repository) }
let(:forked_project) { Projects::ForkService.new(project, user).execute }
- let(:group) { create(:group, owner: forked_project.creator) }
+ let(:group) { create(:group) }
+
+ before do
+ group.add_owner(user)
+ end
describe 'GET index' do
def get_forks
diff --git a/spec/factories/commits.rb b/spec/factories/commits.rb
index d5d819d862a..818f7b046f6 100644
--- a/spec/factories/commits.rb
+++ b/spec/factories/commits.rb
@@ -1,4 +1,4 @@
-require_relative '../support/repo_helpers'
+require_relative '../support/helpers/repo_helpers'
FactoryBot.define do
factory :commit do
diff --git a/spec/factories/deploy_tokens.rb b/spec/factories/deploy_tokens.rb
index 5fea4a9d5a6..017e866e69c 100644
--- a/spec/factories/deploy_tokens.rb
+++ b/spec/factories/deploy_tokens.rb
@@ -10,5 +10,13 @@ FactoryBot.define do
trait :revoked do
revoked true
end
+
+ trait :gitlab_deploy_token do
+ name DeployToken::GITLAB_DEPLOY_TOKEN_NAME
+ end
+
+ trait :expired do
+ expires_at { Date.today - 1.month }
+ end
end
end
diff --git a/spec/factories/gpg_key_subkeys.rb b/spec/factories/gpg_key_subkeys.rb
index 57eaaee345f..6c7db5379a9 100644
--- a/spec/factories/gpg_key_subkeys.rb
+++ b/spec/factories/gpg_key_subkeys.rb
@@ -1,5 +1,3 @@
-require_relative '../support/gpg_helpers'
-
FactoryBot.define do
factory :gpg_key_subkey do
gpg_key
diff --git a/spec/factories/gpg_keys.rb b/spec/factories/gpg_keys.rb
index b8aabf74221..51b8ddc9934 100644
--- a/spec/factories/gpg_keys.rb
+++ b/spec/factories/gpg_keys.rb
@@ -1,4 +1,4 @@
-require_relative '../support/gpg_helpers'
+require_relative '../support/helpers/gpg_helpers'
FactoryBot.define do
factory :gpg_key do
diff --git a/spec/factories/gpg_signature.rb b/spec/factories/gpg_signature.rb
index 4620caff823..b89e6ffc4b3 100644
--- a/spec/factories/gpg_signature.rb
+++ b/spec/factories/gpg_signature.rb
@@ -1,5 +1,3 @@
-require_relative '../support/gpg_helpers'
-
FactoryBot.define do
factory :gpg_signature do
commit_sha { Digest::SHA1.hexdigest(SecureRandom.hex) }
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 8c531cf5909..3b354c0d96b 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -5,6 +5,14 @@ FactoryBot.define do
type 'Group'
owner nil
+ after(:create) do |group|
+ if group.owner
+ # We could remove this after we have proper constraint:
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/43292
+ raise "Don't set owner for groups, use `group.add_owner(user)` instead"
+ end
+ end
+
trait :public do
visibility_level Gitlab::VisibilityLevel::PUBLIC
end
diff --git a/spec/factories/namespaces.rb b/spec/factories/namespaces.rb
index f94b09cff15..6feafa5ece9 100644
--- a/spec/factories/namespaces.rb
+++ b/spec/factories/namespaces.rb
@@ -2,6 +2,22 @@ FactoryBot.define do
factory :namespace do
sequence(:name) { |n| "namespace#{n}" }
path { name.downcase.gsub(/\s/, '_') }
- owner
+
+ # This is a workaround to avoid the user creating another namespace via
+ # User#ensure_namespace_correct. We should try to remove it and then
+ # we could remove this workaround
+ association :owner, factory: :user, strategy: :build
+ before(:create) do |namespace|
+ owner = namespace.owner
+
+ if owner
+ # We're changing the username here because we want to keep our path,
+ # and User#ensure_namespace_correct would change the path based on
+ # username, so we're forced to do this otherwise we'll need to change
+ # a lot of existing tests.
+ owner.username = namespace.path
+ owner.namespace = namespace
+ end
+ end
end
end
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 857333f222d..40f3fa7d69b 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -1,4 +1,4 @@
-require_relative '../support/repo_helpers'
+require_relative '../support/helpers/repo_helpers'
include ActionDispatch::TestProcess
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 1761b6e2a3b..803498d3b19 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -1,4 +1,4 @@
-require_relative '../support/test_env'
+require_relative '../support/helpers/test_env'
FactoryBot.define do
# Project without repository
@@ -151,6 +151,13 @@ FactoryBot.define do
end
end
+ trait :stubbed_repository do
+ after(:build) do |project|
+ allow(project).to receive(:empty_repo?).and_return(false)
+ allow(project.repository).to receive(:empty?).and_return(false)
+ end
+ end
+
trait :wiki_repo do
after(:create) do |project|
raise 'Failed to create wiki repository!' unless project.create_wiki
diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb
new file mode 100644
index 00000000000..978113a08a4
--- /dev/null
+++ b/spec/fast_spec_helper.rb
@@ -0,0 +1,16 @@
+require 'bundler/setup'
+
+ENV['GITLAB_ENV'] = 'test'
+ENV['IN_MEMORY_APPLICATION_SETTINGS'] = 'true'
+
+unless Object.respond_to?(:require_dependency)
+ class Object
+ alias_method :require_dependency, :require
+ end
+end
+
+# Defines Settings and Gitlab.config which are at the center of the app
+require_relative '../config/settings'
+require_relative '../lib/gitlab' unless defined?(Gitlab.config)
+
+require_relative 'support/rspec'
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 3e05e7b7f38..ae41f611ddc 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -170,6 +170,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do
context 'on issue sidebar' do
before do
+ project_1.add_developer(user)
+
visit project_issue_path(project_1, issue)
end
@@ -180,6 +182,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do
let(:board) { create(:board, project: project_1) }
before do
+ project_1.add_developer(user)
+
visit project_board_path(project_1, board)
wait_for_requests
@@ -194,6 +198,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do
let(:board) { create(:board, group: parent) }
before do
+ parent.add_developer(user)
+
visit group_board_path(parent, board)
wait_for_requests
@@ -211,6 +217,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do
context 'on project issuable list' do
before do
+ project_1.add_developer(user)
+
visit project_issues_path(project_1)
end
@@ -237,6 +245,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do
let(:board) { create(:board, project: project_1) }
before do
+ project_1.add_developer(user)
+
visit project_board_path(project_1, board)
end
@@ -247,6 +257,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do
let(:board) { create(:board, group: parent) }
before do
+ parent.add_developer(user)
+
visit group_board_path(parent, board)
end
@@ -259,6 +271,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
let(:board) { create(:board, project: project_1) }
before do
+ project_1.add_developer(user)
visit project_board_path(project_1, board)
find('.js-new-board-list').click
wait_for_requests
@@ -281,6 +294,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
let(:board) { create(:board, group: parent) }
before do
+ parent.add_developer(user)
visit group_board_path(parent, board)
find('.js-new-board-list').click
wait_for_requests
diff --git a/spec/features/oauth_login_spec.rb b/spec/features/oauth_login_spec.rb
index a5e325ee2e3..013cdaa6479 100644
--- a/spec/features/oauth_login_spec.rb
+++ b/spec/features/oauth_login_spec.rb
@@ -28,35 +28,46 @@ feature 'OAuth Login', :js, :allow_forgery_protection do
OmniAuth.config.full_host = @omniauth_config_full_host
end
+ def login_with_provider(provider, enter_two_factor: false)
+ login_via(provider.to_s, user, uid, remember_me: remember_me)
+ enter_code(user.current_otp) if enter_two_factor
+ end
+
providers.each do |provider|
context "when the user logs in using the #{provider} provider" do
+ let(:uid) { 'my-uid' }
+ let(:remember_me) { false }
+ let(:user) { create(:omniauth_user, extern_uid: uid, provider: provider.to_s) }
+ let(:two_factor_user) { create(:omniauth_user, :two_factor, extern_uid: uid, provider: provider.to_s) }
+
+ before do
+ stub_omniauth_config(provider)
+ end
+
context 'when two-factor authentication is disabled' do
it 'logs the user in' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid')
+ login_with_provider(provider)
expect(current_path).to eq root_path
end
end
context 'when two-factor authentication is enabled' do
+ let(:user) { two_factor_user }
+
it 'logs the user in' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid')
+ login_with_provider(provider, enter_two_factor: true)
- enter_code(user.current_otp)
expect(current_path).to eq root_path
end
end
context 'when "remember me" is checked' do
+ let(:remember_me) { true }
+
context 'when two-factor authentication is disabled' do
it 'remembers the user after a browser restart' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid', remember_me: true)
+ login_with_provider(provider)
clear_browser_session
@@ -66,11 +77,10 @@ feature 'OAuth Login', :js, :allow_forgery_protection do
end
context 'when two-factor authentication is enabled' do
+ let(:user) { two_factor_user }
+
it 'remembers the user after a browser restart' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid', remember_me: true)
- enter_code(user.current_otp)
+ login_with_provider(provider, enter_two_factor: true)
clear_browser_session
@@ -83,9 +93,7 @@ feature 'OAuth Login', :js, :allow_forgery_protection do
context 'when "remember me" is not checked' do
context 'when two-factor authentication is disabled' do
it 'does not remember the user after a browser restart' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid', remember_me: false)
+ login_with_provider(provider)
clear_browser_session
@@ -95,11 +103,10 @@ feature 'OAuth Login', :js, :allow_forgery_protection do
end
context 'when two-factor authentication is enabled' do
+ let(:user) { two_factor_user }
+
it 'does not remember the user after a browser restart' do
- stub_omniauth_config(provider)
- user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: provider.to_s)
- login_via(provider.to_s, user, 'my-uid', remember_me: false)
- enter_code(user.current_otp)
+ login_with_provider(provider, enter_two_factor: true)
clear_browser_session
diff --git a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
new file mode 100644
index 00000000000..b7d063596c1
--- /dev/null
+++ b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+feature 'User creates blob in new project', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :empty_repo) }
+
+ shared_examples 'creating a file' do
+ before do
+ sign_in(user)
+ visit project_path(project)
+ end
+
+ it 'allows the user to add a new file' do
+ click_link 'New file'
+
+ find('#editor')
+ execute_script('ace.edit("editor").setValue("Hello world")')
+
+ fill_in(:file_name, with: 'dummy-file')
+
+ click_button('Commit changes')
+
+ expect(page).to have_content('The file has been successfully created')
+ end
+ end
+
+ describe 'as a master' do
+ before do
+ project.add_master(user)
+ end
+
+ it_behaves_like 'creating a file'
+ end
+
+ describe 'as an admin' do
+ let(:user) { create(:user, :admin) }
+
+ it_behaves_like 'creating a file'
+ end
+
+ describe 'as a developer' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ visit project_path(project)
+ end
+
+ it 'does not allow pushing to the default branch' do
+ expect(page).not_to have_content('New file')
+ end
+ end
+end
diff --git a/spec/features/projects/files/user_find_file_spec.rb b/spec/features/projects/files/user_find_file_spec.rb
new file mode 100644
index 00000000000..df405e70dd4
--- /dev/null
+++ b/spec/features/projects/files/user_find_file_spec.rb
@@ -0,0 +1,66 @@
+require 'spec_helper'
+
+describe 'User find project file' do
+ let(:user) { create :user }
+ let(:project) { create :project, :repository }
+
+ before do
+ sign_in(user)
+ project.add_master(user)
+
+ visit project_tree_path(project, project.repository.root_ref)
+ end
+
+ def active_main_tab
+ find('.sidebar-top-level-items > li.active')
+ end
+
+ def find_file(text)
+ fill_in 'file_find', with: text
+ end
+
+ it 'navigates to find file by shortcut', :js do
+ find('body').native.send_key('t')
+
+ expect(active_main_tab).to have_content('Repository')
+ expect(page).to have_selector('.file-finder-holder', count: 1)
+ end
+
+ it 'navigates to find file' do
+ click_link 'Find file'
+
+ expect(active_main_tab).to have_content('Repository')
+ expect(page).to have_selector('.file-finder-holder', count: 1)
+ end
+
+ it 'searches CHANGELOG file', :js do
+ click_link 'Find file'
+
+ find_file 'change'
+
+ expect(page).to have_content('CHANGELOG')
+ expect(page).not_to have_content('.gitignore')
+ expect(page).not_to have_content('VERSION')
+ end
+
+ it 'does not find file when search not exist file', :js do
+ click_link 'Find file'
+
+ find_file 'asdfghjklqwertyuizxcvbnm'
+
+ expect(page).not_to have_content('CHANGELOG')
+ expect(page).not_to have_content('.gitignore')
+ expect(page).not_to have_content('VERSION')
+ end
+
+ it 'searches file by partially matches', :js do
+ click_link 'Find file'
+
+ find_file 'git'
+
+ expect(page).to have_content('.gitignore')
+ expect(page).to have_content('.gitmodules')
+ expect(page).not_to have_content('CHANGELOG')
+ expect(page).not_to have_content('VERSION')
+ end
+end
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index 36ebbeadd4a..786ec327b92 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -26,7 +26,7 @@ describe 'User browses jobs' do
page.within('.nav-controls') do
ci_lint_tool_link = page.find_link('CI lint')
- expect(ci_lint_tool_link[:href]).to end_with(ci_lint_path)
+ expect(ci_lint_tool_link[:href]).to end_with(project_ci_lint_path(project))
end
end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index a5954fec54b..fee6287558e 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -64,7 +64,7 @@ feature 'New project' do
end
context 'with group namespace' do
- let(:group) { create(:group, :private, owner: user) }
+ let(:group) { create(:group, :private) }
before do
group.add_owner(user)
@@ -81,7 +81,7 @@ feature 'New project' do
end
context 'with subgroup namespace' do
- let(:group) { create(:group, owner: user) }
+ let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
before do
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index d9020333f28..e875a88a52b 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -8,6 +8,7 @@ describe "Projects > Settings > Pipelines settings" do
before do
sign_in(user)
project.add_role(user, role)
+ create(:project_auto_devops, project: project)
end
context 'for developer' do
@@ -27,10 +28,17 @@ describe "Projects > Settings > Pipelines settings" do
visit project_settings_ci_cd_path(project)
fill_in('Test coverage parsing', with: 'coverage_regex')
- click_on 'Save changes'
+
+ page.within '#js-general-pipeline-settings' do
+ click_on 'Save changes'
+ end
expect(page.status_code).to eq(200)
- expect(page).to have_button('Save changes', disabled: false)
+
+ page.within '#js-general-pipeline-settings' do
+ expect(page).to have_button('Save changes', disabled: false)
+ end
+
expect(page).to have_field('Test coverage parsing', with: 'coverage_regex')
end
@@ -38,10 +46,15 @@ describe "Projects > Settings > Pipelines settings" do
visit project_settings_ci_cd_path(project)
page.check('Auto-cancel redundant, pending pipelines')
- click_on 'Save changes'
+ page.within '#js-general-pipeline-settings' do
+ click_on 'Save changes'
+ end
expect(page.status_code).to eq(200)
- expect(page).to have_button('Save changes', disabled: false)
+
+ page.within '#js-general-pipeline-settings' do
+ expect(page).to have_button('Save changes', disabled: false)
+ end
checkbox = find_field('project_auto_cancel_pending_pipelines')
expect(checkbox).to be_checked
@@ -51,13 +64,16 @@ describe "Projects > Settings > Pipelines settings" do
it 'update auto devops settings' do
visit project_settings_ci_cd_path(project)
- fill_in('project_auto_devops_attributes_domain', with: 'test.com')
- page.choose('project_auto_devops_attributes_enabled_false')
- click_on 'Save changes'
+ page.within '#autodevops-settings' do
+ fill_in('project_auto_devops_attributes_domain', with: 'test.com')
+ page.choose('project_auto_devops_attributes_enabled_false')
+ click_on 'Save changes'
+ end
expect(page.status_code).to eq(200)
expect(project.auto_devops).to be_present
expect(project.auto_devops).not_to be_enabled
+ expect(project.auto_devops.domain).to eq('test.com')
end
end
end
diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
index a906fa20233..e44361fbe26 100644
--- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
+++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
@@ -65,7 +65,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'Auto DevOps button' do
it '"Enable Auto DevOps" button linked to settings page' do
page.within('.project-stats') do
- expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
end
@@ -75,7 +75,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
page.within('.project-stats') do
- expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
end
end
@@ -212,7 +212,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
describe 'Auto DevOps button' do
it '"Enable Auto DevOps" button linked to settings page' do
page.within('.project-stats') do
- expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
end
@@ -222,7 +222,7 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
page.within('.project-stats') do
- expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
end
end
diff --git a/spec/features/projects/user_views_empty_project_spec.rb b/spec/features/projects/user_views_empty_project_spec.rb
new file mode 100644
index 00000000000..7b982301ffc
--- /dev/null
+++ b/spec/features/projects/user_views_empty_project_spec.rb
@@ -0,0 +1,43 @@
+require 'spec_helper'
+
+describe 'User views an empty project' do
+ let(:project) { create(:project, :empty_repo) }
+ let(:user) { create(:user) }
+
+ shared_examples 'allowing push to default branch' do
+ before do
+ sign_in(user)
+ visit project_path(project)
+ end
+
+ it 'shows push-to-master instructions' do
+ expect(page).to have_content('git push -u origin master')
+ end
+ end
+
+ describe 'as a master' do
+ before do
+ project.add_master(user)
+ end
+
+ it_behaves_like 'allowing push to default branch'
+ end
+
+ describe 'as an admin' do
+ let(:user) { create(:user, :admin) }
+
+ it_behaves_like 'allowing push to default branch'
+ end
+
+ describe 'as a developer' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ visit project_path(project)
+ end
+
+ it 'does not show push-to-master instructions' do
+ expect(page).not_to have_content('git push -u origin master')
+ end
+ end
+end
diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb
index 6c4f7050ee0..143b28728a3 100644
--- a/spec/helpers/gitlab_routing_helper_spec.rb
+++ b/spec/helpers/gitlab_routing_helper_spec.rb
@@ -89,4 +89,19 @@ describe GitlabRoutingHelper do
expect(preview_markdown_path(project)).to eq("/#{project.full_path}/preview_markdown")
end
end
+
+ describe '#edit_milestone_path' do
+ it 'returns group milestone edit path when given entity parent is a Group' do
+ group = create(:group)
+ milestone = create(:milestone, group: group)
+
+ expect(edit_milestone_path(milestone)).to eq("/groups/#{group.path}/-/milestones/#{milestone.iid}/edit")
+ end
+
+ it 'returns project milestone edit path when given entity parent is not a Group' do
+ milestone = create(:milestone, group: nil)
+
+ expect(edit_milestone_path(milestone)).to eq("/#{milestone.project.full_path}/milestones/#{milestone.iid}/edit")
+ end
+ end
end
diff --git a/spec/javascripts/.eslintrc b/spec/javascripts/.eslintrc
index 3d922021978..9eb0e732572 100644
--- a/spec/javascripts/.eslintrc
+++ b/spec/javascripts/.eslintrc
@@ -18,6 +18,7 @@
"sandbox": false,
"setFixtures": false,
"setStyleFixtures": false,
+ "spyOnDependency": false,
"spyOnEvent": false,
"ClassSpecHelper": false
},
diff --git a/spec/javascripts/activities_spec.js b/spec/javascripts/activities_spec.js
index 909a1bf76bc..5dbdcd24296 100644
--- a/spec/javascripts/activities_spec.js
+++ b/spec/javascripts/activities_spec.js
@@ -3,24 +3,30 @@
import $ from 'jquery';
import 'vendor/jquery.endless-scroll';
import Activities from '~/activities';
+import Pager from '~/pager';
-(() => {
+describe('Activities', () => {
window.gon || (window.gon = {});
const fixtureTemplate = 'static/event_filter.html.raw';
const filters = [
{
id: 'all',
- }, {
+ },
+ {
id: 'push',
name: 'push events',
- }, {
+ },
+ {
id: 'merged',
name: 'merge events',
- }, {
+ },
+ {
id: 'comments',
- }, {
+ },
+ {
id: 'team',
- }];
+ },
+ ];
function getEventName(index) {
const filter = filters[index];
@@ -32,31 +38,34 @@ import Activities from '~/activities';
return `#${filter.id}_event_filter`;
}
- describe('Activities', () => {
- beforeEach(() => {
- loadFixtures(fixtureTemplate);
- new Activities();
- });
-
- for (let i = 0; i < filters.length; i += 1) {
- ((i) => {
- describe(`when selecting ${getEventName(i)}`, () => {
- beforeEach(() => {
- $(getSelector(i)).click();
- });
-
- for (let x = 0; x < filters.length; x += 1) {
- ((x) => {
- const shouldHighlight = i === x;
- const testName = shouldHighlight ? 'should highlight' : 'should not highlight';
-
- it(`${testName} ${getEventName(x)}`, () => {
- expect($(getSelector(x)).parent().hasClass('active')).toEqual(shouldHighlight);
- });
- })(x);
- }
- });
- })(i);
- }
+ beforeEach(() => {
+ loadFixtures(fixtureTemplate);
+ spyOn(Pager, 'init').and.stub();
+ new Activities();
});
-})();
+
+ for (let i = 0; i < filters.length; i += 1) {
+ (i => {
+ describe(`when selecting ${getEventName(i)}`, () => {
+ beforeEach(() => {
+ $(getSelector(i)).click();
+ });
+
+ for (let x = 0; x < filters.length; x += 1) {
+ (x => {
+ const shouldHighlight = i === x;
+ const testName = shouldHighlight ? 'should highlight' : 'should not highlight';
+
+ it(`${testName} ${getEventName(x)}`, () => {
+ expect(
+ $(getSelector(x))
+ .parent()
+ .hasClass('active'),
+ ).toEqual(shouldHighlight);
+ });
+ })(x);
+ }
+ });
+ })(i);
+ }
+});
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js
index c37c62c63dd..d03836d10f9 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js
+++ b/spec/javascripts/behaviors/quick_submit_spec.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import '~/behaviors/quick_submit';
-describe('Quick Submit behavior', () => {
+describe('Quick Submit behavior', function () {
const keydownEvent = (options = { keyCode: 13, metaKey: true }) => $.Event('keydown', options);
preloadFixtures('merge_requests/merge_request_with_task_list.html.raw');
diff --git a/spec/javascripts/blob/blob_file_dropzone_spec.js b/spec/javascripts/blob/blob_file_dropzone_spec.js
index 0b1de504435..346f795c3f5 100644
--- a/spec/javascripts/blob/blob_file_dropzone_spec.js
+++ b/spec/javascripts/blob/blob_file_dropzone_spec.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import BlobFileDropzone from '~/blob/blob_file_dropzone';
-describe('BlobFileDropzone', () => {
+describe('BlobFileDropzone', function () {
preloadFixtures('blob/show.html.raw');
beforeEach(() => {
diff --git a/spec/javascripts/comment_type_toggle_spec.js b/spec/javascripts/comment_type_toggle_spec.js
index dfd0810d52e..0ba709298c5 100644
--- a/spec/javascripts/comment_type_toggle_spec.js
+++ b/spec/javascripts/comment_type_toggle_spec.js
@@ -1,5 +1,4 @@
import CommentTypeToggle from '~/comment_type_toggle';
-import * as dropLabSrc from '~/droplab/drop_lab';
import InputSetter from '~/droplab/plugins/input_setter';
describe('CommentTypeToggle', function () {
@@ -59,14 +58,14 @@ describe('CommentTypeToggle', function () {
this.droplab = jasmine.createSpyObj('droplab', ['init']);
- spyOn(dropLabSrc, 'default').and.returnValue(this.droplab);
+ this.droplabConstructor = spyOnDependency(CommentTypeToggle, 'DropLab').and.returnValue(this.droplab);
spyOn(this.commentTypeToggle, 'setConfig').and.returnValue(this.config);
CommentTypeToggle.prototype.initDroplab.call(this.commentTypeToggle);
});
it('should instantiate a DropLab instance', function () {
- expect(dropLabSrc.default).toHaveBeenCalled();
+ expect(this.droplabConstructor).toHaveBeenCalled();
});
it('should set .droplab', function () {
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index 53820770f3f..819ed7896ca 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import pipelinesTable from '~/commit/pipelines/pipelines_table.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-describe('Pipelines table in Commits and Merge requests', () => {
+describe('Pipelines table in Commits and Merge requests', function () {
const jsonFixtureName = 'pipelines/pipelines.json';
let pipeline;
let PipelinesTable;
diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js
index 977298b9221..60d100e8544 100644
--- a/spec/javascripts/commits_spec.js
+++ b/spec/javascripts/commits_spec.js
@@ -3,6 +3,7 @@ import 'vendor/jquery.endless-scroll';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import CommitsList from '~/commits';
+import Pager from '~/pager';
describe('Commits List', () => {
let commitsList;
@@ -14,6 +15,7 @@ describe('Commits List', () => {
</form>
<ol id="commits-list"></ol>
`);
+ spyOn(Pager, 'init').and.stub();
commitsList = new CommitsList(25);
});
@@ -68,9 +70,10 @@ describe('Commits List', () => {
mock.restore();
});
- it('should save the last search string', (done) => {
+ it('should save the last search string', done => {
commitsList.searchField.val('GitLab');
- commitsList.filterResults()
+ commitsList
+ .filterResults()
.then(() => {
expect(ajaxSpy).toHaveBeenCalled();
expect(commitsList.lastSearch).toEqual('GitLab');
@@ -80,8 +83,9 @@ describe('Commits List', () => {
.catch(done.fail);
});
- it('should not make ajax call if the input does not change', (done) => {
- commitsList.filterResults()
+ it('should not make ajax call if the input does not change', done => {
+ commitsList
+ .filterResults()
.then(() => {
expect(ajaxSpy).not.toHaveBeenCalled();
expect(commitsList.lastSearch).toEqual('');
diff --git a/spec/javascripts/droplab/hook_spec.js b/spec/javascripts/droplab/hook_spec.js
index 3d39bd0812b..5eed1db2750 100644
--- a/spec/javascripts/droplab/hook_spec.js
+++ b/spec/javascripts/droplab/hook_spec.js
@@ -1,5 +1,4 @@
import Hook from '~/droplab/hook';
-import * as dropdownSrc from '~/droplab/drop_down';
describe('Hook', function () {
describe('class constructor', function () {
@@ -10,7 +9,7 @@ describe('Hook', function () {
this.config = {};
this.dropdown = {};
- spyOn(dropdownSrc, 'default').and.returnValue(this.dropdown);
+ this.dropdownConstructor = spyOnDependency(Hook, 'DropDown').and.returnValue(this.dropdown);
this.hook = new Hook(this.trigger, this.list, this.plugins, this.config);
});
@@ -24,7 +23,7 @@ describe('Hook', function () {
});
it('should call DropDown constructor', function () {
- expect(dropdownSrc.default).toHaveBeenCalledWith(this.list, this.config);
+ expect(this.dropdownConstructor).toHaveBeenCalledWith(this.list, this.config);
});
it('should set .type', function () {
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 95d02974bdc..8fcee36beb8 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -1,5 +1,3 @@
-import * as urlUtils from '~/lib/utils/url_utility';
-import * as recentSearchesStoreSrc from '~/filtered_search/stores/recent_searches_store';
import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
@@ -11,7 +9,7 @@ import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dro
import FilteredSearchManager from '~/filtered_search/filtered_search_manager';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
-describe('Filtered Search Manager', () => {
+describe('Filtered Search Manager', function () {
let input;
let manager;
let tokensContainer;
@@ -74,18 +72,19 @@ describe('Filtered Search Manager', () => {
describe('class constructor', () => {
const isLocalStorageAvailable = 'isLocalStorageAvailable';
+ let RecentSearchesStoreSpy;
beforeEach(() => {
spyOn(RecentSearchesService, 'isAvailable').and.returnValue(isLocalStorageAvailable);
- spyOn(recentSearchesStoreSrc, 'default');
spyOn(RecentSearchesRoot.prototype, 'render');
+ RecentSearchesStoreSpy = spyOnDependency(FilteredSearchManager, 'RecentSearchesStore');
});
it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => {
manager = new FilteredSearchManager({ page });
expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
- expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({
+ expect(RecentSearchesStoreSpy).toHaveBeenCalledWith({
isLocalStorageAvailable,
allowedKeys: FilteredSearchTokenKeys.getKeys(),
});
@@ -164,7 +163,7 @@ describe('Filtered Search Manager', () => {
it('should search with a single word', (done) => {
input.value = 'searchTerm';
- spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=searchTerm`);
done();
});
@@ -175,7 +174,7 @@ describe('Filtered Search Manager', () => {
it('should search with multiple words', (done) => {
input.value = 'awesome search terms';
- spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
done();
});
@@ -186,7 +185,7 @@ describe('Filtered Search Manager', () => {
it('should search with special characters', (done) => {
input.value = '~!@#$%^&*()_+{}:<>,.?/';
- spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
done();
});
@@ -200,7 +199,7 @@ describe('Filtered Search Manager', () => {
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
`);
- spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(FilteredSearchManager, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&label_name[]=bug`);
done();
});
diff --git a/spec/javascripts/filtered_search/recent_searches_root_spec.js b/spec/javascripts/filtered_search/recent_searches_root_spec.js
index d8ba6de5f45..1e6272bad0b 100644
--- a/spec/javascripts/filtered_search/recent_searches_root_spec.js
+++ b/spec/javascripts/filtered_search/recent_searches_root_spec.js
@@ -1,11 +1,11 @@
import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
-import * as vueSrc from 'vue';
describe('RecentSearchesRoot', () => {
describe('render', () => {
let recentSearchesRoot;
let data;
let template;
+ let VueSpy;
beforeEach(() => {
recentSearchesRoot = {
@@ -14,7 +14,7 @@ describe('RecentSearchesRoot', () => {
},
};
- spyOn(vueSrc, 'default').and.callFake((options) => {
+ VueSpy = spyOnDependency(RecentSearchesRoot, 'Vue').and.callFake((options) => {
data = options.data;
template = options.template;
});
@@ -23,7 +23,7 @@ describe('RecentSearchesRoot', () => {
});
it('should instantiate Vue', () => {
- expect(vueSrc.default).toHaveBeenCalled();
+ expect(VueSpy).toHaveBeenCalled();
expect(data()).toBe(recentSearchesRoot.store.state);
expect(template).toContain(':is-local-storage-available="isLocalStorageAvailable"');
});
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index 5393502196e..7f9c4811fba 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -1,9 +1,8 @@
/* eslint-disable comma-dangle, no-param-reassign, no-unused-expressions, max-len */
import $ from 'jquery';
-import '~/gl_dropdown';
+import GLDropdown from '~/gl_dropdown';
import '~/lib/utils/common_utils';
-import * as urlUtils from '~/lib/utils/url_utility';
describe('glDropdown', function describeDropdown() {
preloadFixtures('static/gl_dropdown.html.raw');
@@ -138,13 +137,13 @@ describe('glDropdown', function describeDropdown() {
expect(this.dropdownContainerElement).toHaveClass('open');
const randomIndex = Math.floor(Math.random() * (this.projectsData.length - 1)) + 0;
navigateWithKeys('down', randomIndex, () => {
- spyOn(urlUtils, 'visitUrl').and.stub();
+ const visitUrl = spyOnDependency(GLDropdown, 'visitUrl').and.stub();
navigateWithKeys('enter', null, () => {
expect(this.dropdownContainerElement).not.toHaveClass('open');
const link = $(`${ITEM_SELECTOR}:eq(${randomIndex}) a`, this.$dropdownMenuElement);
expect(link).toHaveClass('is-active');
const linkedLocation = link.attr('href');
- if (linkedLocation && linkedLocation !== '#') expect(urlUtils.visitUrl).toHaveBeenCalledWith(linkedLocation);
+ if (linkedLocation && linkedLocation !== '#') expect(visitUrl).toHaveBeenCalledWith(linkedLocation);
});
});
});
diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js
index d8428bd0e08..2b92c485f41 100644
--- a/spec/javascripts/groups/components/app_spec.js
+++ b/spec/javascripts/groups/components/app_spec.js
@@ -1,7 +1,6 @@
import $ from 'jquery';
import Vue from 'vue';
-import * as utils from '~/lib/utils/url_utility';
import appComponent from '~/groups/components/app.vue';
import groupFolderComponent from '~/groups/components/group_folder.vue';
import groupItemComponent from '~/groups/components/group_item.vue';
@@ -177,7 +176,7 @@ describe('AppComponent', () => {
it('should fetch groups for provided page details and update window state', (done) => {
spyOn(vm, 'fetchGroups').and.returnValue(returnServicePromise(mockGroups));
spyOn(vm, 'updateGroups').and.callThrough();
- spyOn(utils, 'mergeUrlParams').and.callThrough();
+ const mergeUrlParams = spyOnDependency(appComponent, 'mergeUrlParams').and.callThrough();
spyOn(window.history, 'replaceState');
spyOn($, 'scrollTo');
@@ -193,7 +192,7 @@ describe('AppComponent', () => {
setTimeout(() => {
expect(vm.isLoading).toBe(false);
expect($.scrollTo).toHaveBeenCalledWith(0);
- expect(utils.mergeUrlParams).toHaveBeenCalledWith({ page: 2 }, jasmine.any(String));
+ expect(mergeUrlParams).toHaveBeenCalledWith({ page: 2 }, jasmine.any(String));
expect(window.history.replaceState).toHaveBeenCalledWith({
page: jasmine.any(String),
}, jasmine.any(String), jasmine.any(String));
diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js
index e3c942597a3..49a139855c8 100644
--- a/spec/javascripts/groups/components/group_item_spec.js
+++ b/spec/javascripts/groups/components/group_item_spec.js
@@ -1,5 +1,4 @@
import Vue from 'vue';
-import * as urlUtils from '~/lib/utils/url_utility';
import groupItemComponent from '~/groups/components/group_item.vue';
import groupFolderComponent from '~/groups/components/group_folder.vue';
import eventHub from '~/groups/event_hub';
@@ -135,13 +134,13 @@ describe('GroupItemComponent', () => {
const group = Object.assign({}, mockParentGroupItem);
group.childrenCount = 0;
const newVm = createComponent(group);
- spyOn(urlUtils, 'visitUrl').and.stub();
+ const visitUrl = spyOnDependency(groupItemComponent, 'visitUrl').and.stub();
spyOn(eventHub, '$emit');
newVm.onClickRowGroup(event);
setTimeout(() => {
expect(eventHub.$emit).not.toHaveBeenCalled();
- expect(urlUtils.visitUrl).toHaveBeenCalledWith(newVm.group.relativePath);
+ expect(visitUrl).toHaveBeenCalledWith(newVm.group.relativePath);
done();
}, 0);
});
diff --git a/spec/javascripts/helpers/class_spec_helper_spec.js b/spec/javascripts/helpers/class_spec_helper_spec.js
index 1415ffb7eb3..fa104ae5bcd 100644
--- a/spec/javascripts/helpers/class_spec_helper_spec.js
+++ b/spec/javascripts/helpers/class_spec_helper_spec.js
@@ -2,7 +2,7 @@
import './class_spec_helper';
-describe('ClassSpecHelper', () => {
+describe('ClassSpecHelper', function () {
describe('itShouldBeAStaticMethod', () => {
beforeEach(() => {
class TestClass {
diff --git a/spec/javascripts/ide/components/file_finder/index_spec.js b/spec/javascripts/ide/components/file_finder/index_spec.js
new file mode 100644
index 00000000000..4f208e946d2
--- /dev/null
+++ b/spec/javascripts/ide/components/file_finder/index_spec.js
@@ -0,0 +1,308 @@
+import Vue from 'vue';
+import store from '~/ide/stores';
+import FindFileComponent from '~/ide/components/file_finder/index.vue';
+import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
+import router from '~/ide/ide_router';
+import { file, resetStore } from '../../helpers';
+import { mountComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+
+describe('IDE File finder item spec', () => {
+ const Component = Vue.extend(FindFileComponent);
+ let vm;
+
+ beforeEach(done => {
+ setFixtures('<div id="app"></div>');
+
+ vm = mountComponentWithStore(Component, {
+ store,
+ el: '#app',
+ props: {
+ index: 0,
+ },
+ });
+
+ setTimeout(done);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+
+ resetStore(vm.$store);
+ });
+
+ describe('with entries', () => {
+ beforeEach(done => {
+ Vue.set(vm.$store.state.entries, 'folder', {
+ ...file('folder'),
+ path: 'folder',
+ type: 'folder',
+ });
+
+ Vue.set(vm.$store.state.entries, 'index.js', {
+ ...file('index.js'),
+ path: 'index.js',
+ type: 'blob',
+ url: '/index.jsurl',
+ });
+
+ Vue.set(vm.$store.state.entries, 'component.js', {
+ ...file('component.js'),
+ path: 'component.js',
+ type: 'blob',
+ });
+
+ setTimeout(done);
+ });
+
+ it('renders list of blobs', () => {
+ expect(vm.$el.textContent).toContain('index.js');
+ expect(vm.$el.textContent).toContain('component.js');
+ expect(vm.$el.textContent).not.toContain('folder');
+ });
+
+ it('filters entries', done => {
+ vm.searchText = 'index';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.textContent).toContain('index.js');
+ expect(vm.$el.textContent).not.toContain('component.js');
+
+ done();
+ });
+ });
+
+ it('shows clear button when searchText is not empty', done => {
+ vm.searchText = 'index';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.dropdown-input-clear').classList).toContain('show');
+ expect(vm.$el.querySelector('.dropdown-input-search').classList).toContain('hidden');
+
+ done();
+ });
+ });
+
+ it('clear button resets searchText', done => {
+ vm.searchText = 'index';
+
+ vm
+ .$nextTick()
+ .then(() => {
+ vm.$el.querySelector('.dropdown-input-clear').click();
+ })
+ .then(vm.$nextTick)
+ .then(() => {
+ expect(vm.searchText).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('clear button focues search input', done => {
+ spyOn(vm.$refs.searchInput, 'focus');
+ vm.searchText = 'index';
+
+ vm
+ .$nextTick()
+ .then(() => {
+ vm.$el.querySelector('.dropdown-input-clear').click();
+ })
+ .then(vm.$nextTick)
+ .then(() => {
+ expect(vm.$refs.searchInput.focus).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ describe('listShowCount', () => {
+ it('returns 1 when no filtered entries exist', done => {
+ vm.searchText = 'testing 123';
+
+ vm.$nextTick(() => {
+ expect(vm.listShowCount).toBe(1);
+
+ done();
+ });
+ });
+
+ it('returns entries length when not filtered', () => {
+ expect(vm.listShowCount).toBe(2);
+ });
+ });
+
+ describe('listHeight', () => {
+ it('returns 55 when entries exist', () => {
+ expect(vm.listHeight).toBe(55);
+ });
+
+ it('returns 33 when entries dont exist', done => {
+ vm.searchText = 'testing 123';
+
+ vm.$nextTick(() => {
+ expect(vm.listHeight).toBe(33);
+
+ done();
+ });
+ });
+ });
+
+ describe('filteredBlobsLength', () => {
+ it('returns length of filtered blobs', done => {
+ vm.searchText = 'index';
+
+ vm.$nextTick(() => {
+ expect(vm.filteredBlobsLength).toBe(1);
+
+ done();
+ });
+ });
+ });
+
+ describe('watches', () => {
+ describe('searchText', () => {
+ it('resets focusedIndex when updated', done => {
+ vm.focusedIndex = 1;
+ vm.searchText = 'test';
+
+ vm.$nextTick(() => {
+ expect(vm.focusedIndex).toBe(0);
+
+ done();
+ });
+ });
+ });
+
+ describe('fileFindVisible', () => {
+ it('returns searchText when false', done => {
+ vm.searchText = 'test';
+ vm.$store.state.fileFindVisible = true;
+
+ vm
+ .$nextTick()
+ .then(() => {
+ vm.$store.state.fileFindVisible = false;
+ })
+ .then(vm.$nextTick)
+ .then(() => {
+ expect(vm.searchText).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('openFile', () => {
+ beforeEach(() => {
+ spyOn(router, 'push');
+ spyOn(vm, 'toggleFileFinder');
+ });
+
+ it('closes file finder', () => {
+ vm.openFile(vm.$store.state.entries['index.js']);
+
+ expect(vm.toggleFileFinder).toHaveBeenCalled();
+ });
+
+ it('pushes to router', () => {
+ vm.openFile(vm.$store.state.entries['index.js']);
+
+ expect(router.push).toHaveBeenCalledWith('/project/index.jsurl');
+ });
+ });
+
+ describe('onKeyup', () => {
+ it('opens file on enter key', done => {
+ const event = new CustomEvent('keyup');
+ event.keyCode = ENTER_KEY_CODE;
+
+ spyOn(vm, 'openFile');
+
+ vm.$refs.searchInput.dispatchEvent(event);
+
+ vm.$nextTick(() => {
+ expect(vm.openFile).toHaveBeenCalledWith(vm.$store.state.entries['index.js']);
+
+ done();
+ });
+ });
+
+ it('closes file finder on esc key', done => {
+ const event = new CustomEvent('keyup');
+ event.keyCode = ESC_KEY_CODE;
+
+ spyOn(vm, 'toggleFileFinder');
+
+ vm.$refs.searchInput.dispatchEvent(event);
+
+ vm.$nextTick(() => {
+ expect(vm.toggleFileFinder).toHaveBeenCalled();
+
+ done();
+ });
+ });
+ });
+
+ describe('onKeyDown', () => {
+ let el;
+
+ beforeEach(() => {
+ el = vm.$refs.searchInput;
+ });
+
+ describe('up key', () => {
+ const event = new CustomEvent('keydown');
+ event.keyCode = UP_KEY_CODE;
+
+ it('resets to last index when at top', () => {
+ el.dispatchEvent(event);
+
+ expect(vm.focusedIndex).toBe(1);
+ });
+
+ it('minus 1 from focusedIndex', () => {
+ vm.focusedIndex = 1;
+
+ el.dispatchEvent(event);
+
+ expect(vm.focusedIndex).toBe(0);
+ });
+ });
+
+ describe('down key', () => {
+ const event = new CustomEvent('keydown');
+ event.keyCode = DOWN_KEY_CODE;
+
+ it('resets to first index when at bottom', () => {
+ vm.focusedIndex = 1;
+ el.dispatchEvent(event);
+
+ expect(vm.focusedIndex).toBe(0);
+ });
+
+ it('adds 1 to focusedIndex', () => {
+ el.dispatchEvent(event);
+
+ expect(vm.focusedIndex).toBe(1);
+ });
+ });
+ });
+ });
+
+ describe('without entries', () => {
+ it('renders loading text when loading', done => {
+ store.state.loading = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.textContent).toContain('Loading...');
+
+ done();
+ });
+ });
+
+ it('renders no files text', () => {
+ expect(vm.$el.textContent).toContain('No files found.');
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/file_finder/item_spec.js b/spec/javascripts/ide/components/file_finder/item_spec.js
new file mode 100644
index 00000000000..0f1116c6912
--- /dev/null
+++ b/spec/javascripts/ide/components/file_finder/item_spec.js
@@ -0,0 +1,140 @@
+import Vue from 'vue';
+import ItemComponent from '~/ide/components/file_finder/item.vue';
+import { file } from '../../helpers';
+import createComponent from '../../../helpers/vue_mount_component_helper';
+
+describe('IDE File finder item spec', () => {
+ const Component = Vue.extend(ItemComponent);
+ let vm;
+ let localFile;
+
+ beforeEach(() => {
+ localFile = {
+ ...file(),
+ name: 'test file',
+ path: 'test/file',
+ };
+
+ vm = createComponent(Component, {
+ file: localFile,
+ focused: true,
+ searchText: '',
+ index: 0,
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders file name & path', () => {
+ expect(vm.$el.textContent).toContain('test file');
+ expect(vm.$el.textContent).toContain('test/file');
+ });
+
+ describe('focused', () => {
+ it('adds is-focused class', () => {
+ expect(vm.$el.classList).toContain('is-focused');
+ });
+
+ it('does not have is-focused class when not focused', done => {
+ vm.focused = false;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.classList).not.toContain('is-focused');
+
+ done();
+ });
+ });
+ });
+
+ describe('changed file icon', () => {
+ it('does not render when not a changed or temp file', () => {
+ expect(vm.$el.querySelector('.diff-changed-stats')).toBe(null);
+ });
+
+ it('renders when a changed file', done => {
+ vm.file.changed = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.diff-changed-stats')).not.toBe(null);
+
+ done();
+ });
+ });
+
+ it('renders when a temp file', done => {
+ vm.file.tempFile = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.diff-changed-stats')).not.toBe(null);
+
+ done();
+ });
+ });
+ });
+
+ it('emits event when clicked', () => {
+ spyOn(vm, '$emit');
+
+ vm.$el.click();
+
+ expect(vm.$emit).toHaveBeenCalledWith('click', vm.file);
+ });
+
+ describe('path', () => {
+ let el;
+
+ beforeEach(done => {
+ vm.searchText = 'file';
+
+ el = vm.$el.querySelector('.diff-changed-file-path');
+
+ vm.$nextTick(done);
+ });
+
+ it('highlights text', () => {
+ expect(el.querySelectorAll('.highlighted').length).toBe(4);
+ });
+
+ it('adds ellipsis to long text', done => {
+ vm.file.path = new Array(70)
+ .fill()
+ .map((_, i) => `${i}-`)
+ .join('');
+
+ vm.$nextTick(() => {
+ expect(el.textContent).toBe(`...${vm.file.path.substr(vm.file.path.length - 60)}`);
+ done();
+ });
+ });
+ });
+
+ describe('name', () => {
+ let el;
+
+ beforeEach(done => {
+ vm.searchText = 'file';
+
+ el = vm.$el.querySelector('.diff-changed-file-name');
+
+ vm.$nextTick(done);
+ });
+
+ it('highlights text', () => {
+ expect(el.querySelectorAll('.highlighted').length).toBe(4);
+ });
+
+ it('does not add ellipsis to long text', done => {
+ vm.file.name = new Array(70)
+ .fill()
+ .map((_, i) => `${i}-`)
+ .join('');
+
+ vm.$nextTick(() => {
+ expect(el.textContent).not.toBe(`...${vm.file.name.substr(vm.file.name.length - 60)}`);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/ide/components/ide_spec.js b/spec/javascripts/ide/components/ide_spec.js
index 2a49adc5740..6f580e1f7af 100644
--- a/spec/javascripts/ide/components/ide_spec.js
+++ b/spec/javascripts/ide/components/ide_spec.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import Mousetrap from 'mousetrap';
import store from '~/ide/stores';
import ide from '~/ide/components/ide.vue';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
@@ -43,4 +44,68 @@ describe('ide component', () => {
done();
});
});
+
+ describe('file finder', () => {
+ beforeEach(done => {
+ spyOn(vm, 'toggleFileFinder');
+
+ vm.$store.state.fileFindVisible = true;
+
+ vm.$nextTick(done);
+ });
+
+ it('calls toggleFileFinder on `t` key press', done => {
+ Mousetrap.trigger('t');
+
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.toggleFileFinder).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('calls toggleFileFinder on `command+p` key press', done => {
+ Mousetrap.trigger('command+p');
+
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.toggleFileFinder).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('calls toggleFileFinder on `ctrl+p` key press', done => {
+ Mousetrap.trigger('ctrl+p');
+
+ vm
+ .$nextTick()
+ .then(() => {
+ expect(vm.toggleFileFinder).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('always allows `command+p` to trigger toggleFileFinder', () => {
+ expect(
+ vm.mousetrapStopCallback(null, vm.$el.querySelector('.dropdown-input-field'), 'command+p'),
+ ).toBe(false);
+ });
+
+ it('always allows `ctrl+p` to trigger toggleFileFinder', () => {
+ expect(
+ vm.mousetrapStopCallback(null, vm.$el.querySelector('.dropdown-input-field'), 'ctrl+p'),
+ ).toBe(false);
+ });
+
+ it('onlys handles `t` when focused in input-field', () => {
+ expect(
+ vm.mousetrapStopCallback(null, vm.$el.querySelector('.dropdown-input-field'), 't'),
+ ).toBe(true);
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index 9f27b89b34a..2e773062524 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -1,5 +1,11 @@
-import * as urlUtils from '~/lib/utils/url_utility';
-import * as actions from '~/ide/stores/actions';
+import actions, {
+ stageAllChanges,
+ unstageAllChanges,
+ toggleFileFinder,
+ setCurrentBranchId,
+ setEmptyStateSvgs,
+ updateActivityBarView,
+} from '~/ide/stores/actions';
import store from '~/ide/stores';
import * as types from '~/ide/stores/mutation_types';
import router from '~/ide/ide_router';
@@ -17,12 +23,12 @@ describe('Multi-file store actions', () => {
describe('redirectToUrl', () => {
it('calls visitUrl', done => {
- spyOn(urlUtils, 'visitUrl');
+ const visitUrl = spyOnDependency(actions, 'visitUrl');
store
.dispatch('redirectToUrl', 'test')
.then(() => {
- expect(urlUtils.visitUrl).toHaveBeenCalledWith('test');
+ expect(visitUrl).toHaveBeenCalledWith('test');
done();
})
@@ -298,7 +304,7 @@ describe('Multi-file store actions', () => {
store.state.changedFiles.push(file(), file('new'));
testAction(
- actions.stageAllChanges,
+ stageAllChanges,
null,
store.state,
[
@@ -316,7 +322,7 @@ describe('Multi-file store actions', () => {
store.state.stagedFiles.push(file(), file('new'));
testAction(
- actions.unstageAllChanges,
+ unstageAllChanges,
null,
store.state,
[
@@ -344,7 +350,7 @@ describe('Multi-file store actions', () => {
describe('updateActivityBarView', () => {
it('commits UPDATE_ACTIVITY_BAR_VIEW', done => {
testAction(
- actions.updateActivityBarView,
+ updateActivityBarView,
'test',
{},
[{ type: 'UPDATE_ACTIVITY_BAR_VIEW', payload: 'test' }],
@@ -357,7 +363,7 @@ describe('Multi-file store actions', () => {
describe('setEmptyStateSvgs', () => {
it('commits setEmptyStateSvgs', done => {
testAction(
- actions.setEmptyStateSvgs,
+ setEmptyStateSvgs,
'svg',
{},
[{ type: 'SET_EMPTY_STATE_SVGS', payload: 'svg' }],
@@ -370,7 +376,7 @@ describe('Multi-file store actions', () => {
describe('setCurrentBranchId', () => {
it('commits setCurrentBranchId', done => {
testAction(
- actions.setCurrentBranchId,
+ setCurrentBranchId,
'branchId',
{},
[{ type: 'SET_CURRENT_BRANCH', payload: 'branchId' }],
@@ -379,4 +385,17 @@ describe('Multi-file store actions', () => {
);
});
});
+
+ describe('toggleFileFinder', () => {
+ it('commits TOGGLE_FILE_FINDER', done => {
+ testAction(
+ toggleFileFinder,
+ true,
+ null,
+ [{ type: 'TOGGLE_FILE_FINDER', payload: true }],
+ [],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/getters_spec.js b/spec/javascripts/ide/stores/getters_spec.js
index 212ae29254b..68e0cceef76 100644
--- a/spec/javascripts/ide/stores/getters_spec.js
+++ b/spec/javascripts/ide/stores/getters_spec.js
@@ -58,4 +58,24 @@ describe('IDE store getters', () => {
expect(getters.currentMergeRequest(localState)).toBeNull();
});
});
+
+ describe('allBlobs', () => {
+ beforeEach(() => {
+ Object.assign(localState.entries, {
+ index: { type: 'blob', name: 'index', lastOpenedAt: 0 },
+ app: { type: 'blob', name: 'blob', lastOpenedAt: 0 },
+ folder: { type: 'folder', name: 'folder', lastOpenedAt: 0 },
+ });
+ });
+
+ it('returns only blobs', () => {
+ expect(getters.allBlobs(localState).length).toBe(2);
+ });
+
+ it('returns list sorted by lastOpenedAt', () => {
+ localState.entries.app.lastOpenedAt = new Date().getTime();
+
+ expect(getters.allBlobs(localState)[0].name).toBe('blob');
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index 116967208e0..b2b4b85ca42 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -1,7 +1,7 @@
+import actions from '~/ide/stores/actions';
import store from '~/ide/stores';
import service from '~/ide/services';
import router from '~/ide/ide_router';
-import * as urlUtils from '~/lib/utils/url_utility';
import eventHub from '~/ide/eventhub';
import * as consts from '~/ide/stores/modules/commit/constants';
import { resetStore, file } from 'spec/ide/helpers';
@@ -307,8 +307,10 @@ describe('IDE commit module actions', () => {
});
describe('commitChanges', () => {
+ let visitUrl;
+
beforeEach(() => {
- spyOn(urlUtils, 'visitUrl');
+ visitUrl = spyOnDependency(actions, 'visitUrl');
document.body.innerHTML += '<div class="flash-container"></div>';
@@ -461,7 +463,7 @@ describe('IDE commit module actions', () => {
store
.dispatch('commit/commitChanges')
.then(() => {
- expect(urlUtils.visitUrl).toHaveBeenCalledWith(
+ expect(visitUrl).toHaveBeenCalledWith(
`webUrl/merge_requests/new?merge_request[source_branch]=${
store.getters['commit/newBranchName']
}&merge_request[target_branch]=master`,
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 26858dd1f10..6a7897a9e4d 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -108,4 +108,12 @@ describe('Multi-file store mutations', () => {
expect(localState.committedStateSvgPath).toBe('commited');
});
});
+
+ describe('TOGGLE_FILE_FINDER', () => {
+ it('updates fileFindVisible', () => {
+ mutations.TOGGLE_FILE_FINDER(localState, true);
+
+ expect(localState.fileFindVisible).toBe(true);
+ });
+ });
});
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index d5a87b5ce20..bf1f0c822fe 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -2,7 +2,6 @@ import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import '~/behaviors/markdown/render_gfm';
-import * as urlUtils from '~/lib/utils/url_utility';
import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub';
import setTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
@@ -174,7 +173,7 @@ describe('Issuable output', () => {
});
it('does not redirect if issue has not moved', (done) => {
- spyOn(urlUtils, 'visitUrl');
+ const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
data: {
@@ -187,16 +186,13 @@ describe('Issuable output', () => {
vm.updateIssuable();
setTimeout(() => {
- expect(
- urlUtils.visitUrl,
- ).not.toHaveBeenCalled();
-
+ expect(visitUrl).not.toHaveBeenCalled();
done();
});
});
it('redirects if returned web_url has changed', (done) => {
- spyOn(urlUtils, 'visitUrl');
+ const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
data: {
@@ -209,10 +205,7 @@ describe('Issuable output', () => {
vm.updateIssuable();
setTimeout(() => {
- expect(
- urlUtils.visitUrl,
- ).toHaveBeenCalledWith('/testing-issue-move');
-
+ expect(visitUrl).toHaveBeenCalledWith('/testing-issue-move');
done();
});
});
@@ -340,7 +333,7 @@ describe('Issuable output', () => {
describe('deleteIssuable', () => {
it('changes URL when deleted', (done) => {
- spyOn(urlUtils, 'visitUrl');
+ const visitUrl = spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
data: {
@@ -352,16 +345,13 @@ describe('Issuable output', () => {
vm.deleteIssuable();
setTimeout(() => {
- expect(
- urlUtils.visitUrl,
- ).toHaveBeenCalledWith('/test');
-
+ expect(visitUrl).toHaveBeenCalledWith('/test');
done();
});
});
it('stops polling when deleting', (done) => {
- spyOn(urlUtils, 'visitUrl');
+ spyOnDependency(issuableApp, 'visitUrl');
spyOn(vm.poll, 'stop').and.callThrough();
spyOn(vm.service, 'deleteIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
@@ -377,7 +367,6 @@ describe('Issuable output', () => {
expect(
vm.poll.stop,
).toHaveBeenCalledWith();
-
done();
});
});
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index d96151a8a3a..889c8545faa 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -1,7 +1,6 @@
import $ from 'jquery';
import Vue from 'vue';
-import descriptionComponent from '~/issue_show/components/description.vue';
-import * as taskList from '~/task_list';
+import Description from '~/issue_show/components/description.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Description component', () => {
@@ -17,7 +16,7 @@ describe('Description component', () => {
};
beforeEach(() => {
- DescriptionComponent = Vue.extend(descriptionComponent);
+ DescriptionComponent = Vue.extend(Description);
if (!document.querySelector('.issuable-meta')) {
const metaData = document.createElement('div');
@@ -82,18 +81,20 @@ describe('Description component', () => {
});
describe('TaskList', () => {
+ let TaskList;
+
beforeEach(() => {
vm = mountComponent(DescriptionComponent, Object.assign({}, props, {
issuableType: 'issuableType',
}));
- spyOn(taskList, 'default');
+ TaskList = spyOnDependency(Description, 'TaskList');
});
it('re-inits the TaskList when description changed', (done) => {
vm.descriptionHtml = 'changed';
setTimeout(() => {
- expect(taskList.default).toHaveBeenCalled();
+ expect(TaskList).toHaveBeenCalled();
done();
});
});
@@ -103,7 +104,7 @@ describe('Description component', () => {
vm.descriptionHtml = 'changed';
setTimeout(() => {
- expect(taskList.default).not.toHaveBeenCalled();
+ expect(TaskList).not.toHaveBeenCalled();
done();
});
});
@@ -112,7 +113,7 @@ describe('Description component', () => {
vm.descriptionHtml = 'changed';
setTimeout(() => {
- expect(taskList.default).toHaveBeenCalledWith({
+ expect(TaskList).toHaveBeenCalledWith({
dataType: 'issuableType',
fieldName: 'description',
selector: '.detail-page-description',
diff --git a/spec/javascripts/job_spec.js b/spec/javascripts/job_spec.js
index c6bbacf237a..da00b615c9b 100644
--- a/spec/javascripts/job_spec.js
+++ b/spec/javascripts/job_spec.js
@@ -2,7 +2,6 @@ import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
-import * as urlUtils from '~/lib/utils/url_utility';
import '~/lib/utils/datetime_utility';
import Job from '~/job';
import '~/breakpoints';
@@ -22,7 +21,7 @@ describe('Job', () => {
beforeEach(() => {
loadFixtures('builds/build-with-artifacts.html.raw');
- spyOn(urlUtils, 'visitUrl');
+ spyOnDependency(Job, 'visitUrl');
response = {};
diff --git a/spec/javascripts/jobs/sidebar_details_block_spec.js b/spec/javascripts/jobs/sidebar_details_block_spec.js
index 6b397c22fb9..9c4454252ce 100644
--- a/spec/javascripts/jobs/sidebar_details_block_spec.js
+++ b/spec/javascripts/jobs/sidebar_details_block_spec.js
@@ -102,7 +102,7 @@ describe('Sidebar details block', () => {
});
it('should render runner ID', () => {
- expect(trimWhitespace(vm.$el.querySelector('.js-job-runner'))).toEqual('Runner: #1');
+ expect(trimWhitespace(vm.$el.querySelector('.js-job-runner'))).toEqual('Runner: local ci runner (#1)');
});
it('should render timeout information', () => {
diff --git a/spec/javascripts/lib/utils/csrf_token_spec.js b/spec/javascripts/lib/utils/csrf_token_spec.js
index c484213df8e..81a39a97a84 100644
--- a/spec/javascripts/lib/utils/csrf_token_spec.js
+++ b/spec/javascripts/lib/utils/csrf_token_spec.js
@@ -1,6 +1,6 @@
import csrf from '~/lib/utils/csrf';
-describe('csrf', () => {
+describe('csrf', function () {
beforeEach(() => {
this.tokenKey = 'X-CSRF-Token';
this.token = 'pH1cvjnP9grx2oKlhWEDvUZnJ8x2eXsIs1qzyHkF3DugSG5yTxR76CWeEZRhML2D1IeVB7NEW0t5l/axE4iJpQ==';
diff --git a/spec/javascripts/lib/utils/image_utility_spec.js b/spec/javascripts/lib/utils/image_utility_spec.js
index 75addfcc833..a7eff419fba 100644
--- a/spec/javascripts/lib/utils/image_utility_spec.js
+++ b/spec/javascripts/lib/utils/image_utility_spec.js
@@ -1,4 +1,4 @@
-import * as imageUtility from '~/lib/utils/image_utility';
+import { isImageLoaded } from '~/lib/utils/image_utility';
describe('imageUtility', () => {
describe('isImageLoaded', () => {
@@ -8,7 +8,7 @@ describe('imageUtility', () => {
naturalHeight: 100,
};
- expect(imageUtility.isImageLoaded(element)).toEqual(false);
+ expect(isImageLoaded(element)).toEqual(false);
});
it('should return false when naturalHeight = 0', () => {
@@ -17,7 +17,7 @@ describe('imageUtility', () => {
naturalHeight: 0,
};
- expect(imageUtility.isImageLoaded(element)).toEqual(false);
+ expect(isImageLoaded(element)).toEqual(false);
});
it('should return true when image.complete and naturalHeight != 0', () => {
@@ -26,7 +26,7 @@ describe('imageUtility', () => {
naturalHeight: 100,
};
- expect(imageUtility.isImageLoaded(element)).toEqual(true);
+ expect(isImageLoaded(element)).toEqual(true);
});
});
});
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index 79c8cf0ba32..3dbd9756cd2 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -3,7 +3,6 @@
import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import * as urlUtils from '~/lib/utils/url_utility';
import MergeRequestTabs from '~/merge_request_tabs';
import '~/commit/pipelines/pipelines_bundle';
import '~/breakpoints';
@@ -356,7 +355,7 @@ import 'vendor/jquery.scrollTo';
describe('with note fragment hash', () => {
it('should expand and scroll to linked fragment hash #note_xxx', function (done) {
- spyOn(urlUtils, 'getLocationHash').and.returnValue(noteId);
+ spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue(noteId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
setTimeout(() => {
@@ -372,7 +371,7 @@ import 'vendor/jquery.scrollTo';
});
it('should gracefully ignore non-existant fragment hash', function (done) {
- spyOn(urlUtils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
+ spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
setTimeout(() => {
@@ -385,7 +384,7 @@ import 'vendor/jquery.scrollTo';
describe('with line number fragment hash', () => {
it('should gracefully ignore line number fragment hash', function () {
- spyOn(urlUtils, 'getLocationHash').and.returnValue(noteLineNumId);
+ spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue(noteLineNumId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
expect(noteLineNumId.length).toBeGreaterThan(0);
@@ -422,7 +421,7 @@ import 'vendor/jquery.scrollTo';
describe('with note fragment hash', () => {
it('should expand and scroll to linked fragment hash #note_xxx', function (done) {
- spyOn(urlUtils, 'getLocationHash').and.returnValue(noteId);
+ spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue(noteId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
@@ -439,7 +438,7 @@ import 'vendor/jquery.scrollTo';
});
it('should gracefully ignore non-existant fragment hash', function (done) {
- spyOn(urlUtils, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
+ spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue('note_something-that-does-not-exist');
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
setTimeout(() => {
@@ -451,7 +450,7 @@ import 'vendor/jquery.scrollTo';
describe('with line number fragment hash', () => {
it('should gracefully ignore line number fragment hash', function () {
- spyOn(urlUtils, 'getLocationHash').and.returnValue(noteLineNumId);
+ spyOnDependency(MergeRequestTabs, 'getLocationHash').and.returnValue(noteLineNumId);
this.class.loadDiff('/foo/bar/merge_requests/1/diffs');
expect(noteLineNumId.length).toBeGreaterThan(0);
diff --git a/spec/javascripts/monitoring/monitoring_store_spec.js b/spec/javascripts/monitoring/monitoring_store_spec.js
index 88aa7659275..08d54946787 100644
--- a/spec/javascripts/monitoring/monitoring_store_spec.js
+++ b/spec/javascripts/monitoring/monitoring_store_spec.js
@@ -1,7 +1,7 @@
import MonitoringStore from '~/monitoring/stores/monitoring_store';
import MonitoringMock, { deploymentData } from './mock_data';
-describe('MonitoringStore', () => {
+describe('MonitoringStore', function () {
this.store = new MonitoringStore();
this.store.storeMetrics(MonitoringMock.data);
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index ec56ab0e2f0..0952356c2f4 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -3,7 +3,6 @@ import $ from 'jquery';
import _ from 'underscore';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import * as urlUtils from '~/lib/utils/url_utility';
import 'autosize';
import '~/gl_form';
import '~/lib/utils/text_utility';
@@ -222,7 +221,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
});
it('sets target when hash matches', () => {
- spyOn(urlUtils, 'getLocationHash').and.returnValue(hash);
+ spyOnDependency(Notes, 'getLocationHash').and.returnValue(hash);
Notes.updateNoteTargetSelector($note);
@@ -231,7 +230,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
});
it('unsets target when hash does not match', () => {
- spyOn(urlUtils, 'getLocationHash').and.returnValue('note_doesnotexist');
+ spyOnDependency(Notes, 'getLocationHash').and.returnValue('note_doesnotexist');
Notes.updateNoteTargetSelector($note);
@@ -239,7 +238,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
});
it('unsets target when there is not a hash fragment anymore', () => {
- spyOn(urlUtils, 'getLocationHash').and.returnValue(null);
+ spyOnDependency(Notes, 'getLocationHash').and.returnValue(null);
Notes.updateNoteTargetSelector($note);
diff --git a/spec/javascripts/pager_spec.js b/spec/javascripts/pager_spec.js
index b09494f0b77..04f2e7ef4f9 100644
--- a/spec/javascripts/pager_spec.js
+++ b/spec/javascripts/pager_spec.js
@@ -1,15 +1,25 @@
-/* global fixture */
+import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import * as utils from '~/lib/utils/url_utility';
import Pager from '~/pager';
describe('pager', () => {
+ let axiosMock;
+
+ beforeEach(() => {
+ axiosMock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ axiosMock.restore();
+ });
+
describe('init', () => {
const originalHref = window.location.href;
beforeEach(() => {
setFixtures('<div class="content_list"></div><div class="loading"></div>');
+ spyOn($.fn, 'endlessScroll').and.stub();
});
afterEach(() => {
@@ -25,7 +35,7 @@ describe('pager', () => {
it('should use current url if data-href attribute not provided', () => {
const href = `${gl.TEST_HOST}/some_list`;
- spyOn(utils, 'removeParams').and.returnValue(href);
+ spyOnDependency(Pager, 'removeParams').and.returnValue(href);
Pager.init();
expect(Pager.url).toBe(href);
});
@@ -39,42 +49,37 @@ describe('pager', () => {
it('keeps extra query parameters from url', () => {
window.history.replaceState({}, null, '?filter=test&offset=100');
const href = `${gl.TEST_HOST}/some_list?filter=test`;
- spyOn(utils, 'removeParams').and.returnValue(href);
+ const removeParams = spyOnDependency(Pager, 'removeParams').and.returnValue(href);
Pager.init();
- expect(utils.removeParams).toHaveBeenCalledWith(['limit', 'offset']);
+ expect(removeParams).toHaveBeenCalledWith(['limit', 'offset']);
expect(Pager.url).toEqual(href);
});
});
describe('getOld', () => {
const urlRegex = /(.*)some_list(.*)$/;
- let mock;
function mockSuccess() {
- mock.onGet(urlRegex).reply(200, {
+ axiosMock.onGet(urlRegex).reply(200, {
count: 0,
html: '',
});
}
function mockError() {
- mock.onGet(urlRegex).networkError();
+ axiosMock.onGet(urlRegex).networkError();
}
beforeEach(() => {
- setFixtures('<div class="content_list" data-href="/some_list"></div><div class="loading"></div>');
+ setFixtures(
+ '<div class="content_list" data-href="/some_list"></div><div class="loading"></div>',
+ );
spyOn(axios, 'get').and.callThrough();
- mock = new MockAdapter(axios);
-
Pager.init();
});
- afterEach(() => {
- mock.restore();
- });
-
- it('shows loader while loading next page', (done) => {
+ it('shows loader while loading next page', done => {
mockSuccess();
spyOn(Pager.loading, 'show');
@@ -87,7 +92,7 @@ describe('pager', () => {
});
});
- it('hides loader on success', (done) => {
+ it('hides loader on success', done => {
mockSuccess();
spyOn(Pager.loading, 'hide');
@@ -100,7 +105,7 @@ describe('pager', () => {
});
});
- it('hides loader on error', (done) => {
+ it('hides loader on error', done => {
mockError();
spyOn(Pager.loading, 'hide');
@@ -113,7 +118,7 @@ describe('pager', () => {
});
});
- it('sends request to url with offset and limit params', (done) => {
+ it('sends request to url with offset and limit params', done => {
Pager.offset = 100;
Pager.limit = 20;
Pager.getOld();
diff --git a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
index a6fe9fb65e9..b69e5f9a3a0 100644
--- a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
+++ b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
@@ -2,7 +2,6 @@ import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import stopJobsModal from '~/pages/admin/jobs/index/components/stop_jobs_modal.vue';
-import * as urlUtility from '~/lib/utils/url_utility';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
@@ -24,7 +23,7 @@ describe('stop_jobs_modal.vue', () => {
describe('onSubmit', () => {
it('stops jobs and redirects to overview page', (done) => {
const responseURL = `${gl.TEST_HOST}/stop_jobs_modal.vue/jobs`;
- const redirectSpy = spyOn(urlUtility, 'redirectTo');
+ const redirectSpy = spyOnDependency(stopJobsModal, 'redirectTo');
spyOn(axios, 'post').and.callFake((url) => {
expect(url).toBe(props.url);
return Promise.resolve({
@@ -44,7 +43,7 @@ describe('stop_jobs_modal.vue', () => {
it('displays error if stopping jobs failed', (done) => {
const dummyError = new Error('stopping jobs failed');
- const redirectSpy = spyOn(urlUtility, 'redirectTo');
+ const redirectSpy = spyOnDependency(stopJobsModal, 'redirectTo');
spyOn(axios, 'post').and.callFake((url) => {
expect(url).toBe(props.url);
return Promise.reject(dummyError);
diff --git a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
index 6074e06fcec..94401beb5c9 100644
--- a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
+++ b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
@@ -3,7 +3,6 @@ import Vue from 'vue';
import axios from '~/lib/utils/axios_utils';
import deleteMilestoneModal from '~/pages/milestones/shared/components/delete_milestone_modal.vue';
import eventHub from '~/pages/milestones/shared/event_hub';
-import * as urlUtility from '~/lib/utils/url_utility';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
@@ -40,7 +39,7 @@ describe('delete_milestone_modal.vue', () => {
},
});
});
- const redirectSpy = spyOn(urlUtility, 'redirectTo');
+ const redirectSpy = spyOnDependency(deleteMilestoneModal, 'redirectTo');
vm.onSubmit()
.then(() => {
@@ -60,7 +59,7 @@ describe('delete_milestone_modal.vue', () => {
eventHub.$emit.calls.reset();
return Promise.reject(dummyError);
});
- const redirectSpy = spyOn(urlUtility, 'redirectTo');
+ const redirectSpy = spyOnDependency(deleteMilestoneModal, 'redirectTo');
vm.onSubmit()
.catch((error) => {
diff --git a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
index f95a7cef18a..fb7d2763b49 100644
--- a/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
+++ b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
@@ -6,7 +6,7 @@ const PipelineSchedulesCalloutComponent = Vue.extend(PipelineSchedulesCallout);
const cookieKey = 'pipeline_schedules_callout_dismissed';
const docsUrl = 'help/ci/scheduled_pipelines';
-describe('Pipeline Schedule Callout', () => {
+describe('Pipeline Schedule Callout', function () {
beforeEach(() => {
setFixtures(`
<div id='pipeline-schedules-callout' data-docs-url=${docsUrl}></div>
diff --git a/spec/javascripts/pipelines/graph/action_component_spec.js b/spec/javascripts/pipelines/graph/action_component_spec.js
index 581209f215d..3de10392472 100644
--- a/spec/javascripts/pipelines/graph/action_component_spec.js
+++ b/spec/javascripts/pipelines/graph/action_component_spec.js
@@ -6,7 +6,7 @@ import mountComponent from '../../helpers/vue_mount_component_helper';
describe('pipeline graph action component', () => {
let component;
- beforeEach((done) => {
+ beforeEach(done => {
const ActionComponent = Vue.extend(actionComponent);
component = mountComponent(ActionComponent, {
tooltipText: 'bar',
@@ -22,7 +22,7 @@ describe('pipeline graph action component', () => {
});
it('should emit an event with the provided link', () => {
- eventHub.$on('graphAction', (link) => {
+ eventHub.$on('graphAction', link => {
expect(link).toEqual('foo');
});
});
@@ -31,7 +31,7 @@ describe('pipeline graph action component', () => {
expect(component.$el.getAttribute('data-original-title')).toEqual('bar');
});
- it('should update bootstrap tooltip when title changes', (done) => {
+ it('should update bootstrap tooltip when title changes', done => {
component.tooltipText = 'changed';
setTimeout(() => {
@@ -44,4 +44,45 @@ describe('pipeline graph action component', () => {
expect(component.$el.querySelector('.ci-action-icon-wrapper')).toBeDefined();
expect(component.$el.querySelector('svg')).toBeDefined();
});
+
+ it('disables the button when clicked', done => {
+ component.$el.click();
+
+ component.$nextTick(() => {
+ expect(component.$el.getAttribute('disabled')).toEqual('disabled');
+ done();
+ });
+ });
+
+ it('re-enabled the button when `requestFinishedFor` matches `linkRequested`', done => {
+ component.$el.click();
+
+ component
+ .$nextTick()
+ .then(() => {
+ expect(component.$el.getAttribute('disabled')).toEqual('disabled');
+ component.requestFinishedFor = 'foo';
+ })
+ .then(() => {
+ expect(component.$el.getAttribute('disabled')).toBeNull();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not re-enable the button when `requestFinishedFor` does not matches `linkRequested`', done => {
+ component.$el.click();
+
+ component
+ .$nextTick()
+ .then(() => {
+ expect(component.$el.getAttribute('disabled')).toEqual('disabled');
+ component.requestFinishedFor = 'bar';
+ })
+ .then(() => {
+ expect(component.$el.getAttribute('disabled')).toEqual('disabled');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
});
diff --git a/spec/javascripts/pipelines/graph/dropdown_action_component_spec.js b/spec/javascripts/pipelines/graph/dropdown_action_component_spec.js
deleted file mode 100644
index ba721bc53c6..00000000000
--- a/spec/javascripts/pipelines/graph/dropdown_action_component_spec.js
+++ /dev/null
@@ -1,32 +0,0 @@
-import Vue from 'vue';
-import dropdownActionComponent from '~/pipelines/components/graph/dropdown_action_component.vue';
-
-describe('action component', () => {
- let component;
-
- beforeEach((done) => {
- const DropdownActionComponent = Vue.extend(dropdownActionComponent);
- component = new DropdownActionComponent({
- propsData: {
- tooltipText: 'bar',
- link: 'foo',
- actionMethod: 'post',
- actionIcon: 'cancel',
- },
- }).$mount();
-
- Vue.nextTick(done);
- });
-
- it('should render a link', () => {
- expect(component.$el.getAttribute('href')).toEqual('foo');
- });
-
- it('should render the provided title as a bootstrap tooltip', () => {
- expect(component.$el.getAttribute('data-original-title')).toEqual('bar');
- });
-
- it('should render an svg', () => {
- expect(component.$el.querySelector('svg')).toBeDefined();
- });
-});
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index c9677ae209a..073dae56c25 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -93,17 +93,6 @@ describe('pipeline graph job component', () => {
});
});
- describe('dropdown', () => {
- it('should render the dropdown action icon', () => {
- component = mountComponent(JobComponent, {
- job: mockJob,
- isDropdown: true,
- });
-
- expect(component.$el.querySelector('a.ci-action-icon-wrapper')).toBeDefined();
- });
- });
-
it('should render provided class name', () => {
component = mountComponent(JobComponent, {
job: mockJob,
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 80770a61011..e264b16335f 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -9,8 +9,6 @@ import Sidebar from '~/right_sidebar';
(function() {
var $aside, $icon, $labelsIcon, $page, $toggle, assertSidebarState;
- this.sidebar = null;
-
$aside = null;
$toggle = null;
@@ -43,7 +41,7 @@ import Sidebar from '~/right_sidebar';
beforeEach(function() {
loadFixtures(fixtureName);
mock = new MockAdapter(axios);
- this.sidebar = new Sidebar();
+ new Sidebar(); // eslint-disable-line no-new
$aside = $('.right-sidebar');
$page = $('.layout-page');
$icon = $aside.find('i');
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index 1a27955983d..4f515f98a7e 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -4,7 +4,6 @@ import $ from 'jquery';
import '~/gl_dropdown';
import SearchAutocomplete from '~/search_autocomplete';
import '~/lib/utils/common_utils';
-import * as urlUtils from '~/lib/utils/url_utility';
describe('Search autocomplete dropdown', () => {
var assertLinks,
@@ -129,9 +128,6 @@ describe('Search autocomplete dropdown', () => {
beforeEach(function() {
loadFixtures('static/search_autocomplete.html.raw');
- // Prevent turbolinks from triggering within gl_dropdown
- spyOn(urlUtils, 'visitUrl').and.returnValue(true);
-
window.gon = {};
window.gon.current_user_id = userId;
window.gon.current_username = userName;
diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js
index d433f8c3e07..4fba36bd4de 100644
--- a/spec/javascripts/settings_panels_spec.js
+++ b/spec/javascripts/settings_panels_spec.js
@@ -13,9 +13,9 @@ describe('Settings Panels', () => {
});
it('should expand linked hash fragment panel', () => {
- location.hash = '#js-general-pipeline-settings';
+ location.hash = '#autodevops-settings';
- const pipelineSettingsPanel = document.querySelector('#js-general-pipeline-settings');
+ const pipelineSettingsPanel = document.querySelector('#autodevops-settings');
// Our test environment automatically expands everything so we need to clear that out first
pipelineSettingsPanel.classList.remove('expanded');
diff --git a/spec/javascripts/shortcuts_dashboard_navigation_spec.js b/spec/javascripts/shortcuts_dashboard_navigation_spec.js
index 888b49004bf..7cb201e01d8 100644
--- a/spec/javascripts/shortcuts_dashboard_navigation_spec.js
+++ b/spec/javascripts/shortcuts_dashboard_navigation_spec.js
@@ -1,24 +1,23 @@
import findAndFollowLink from '~/shortcuts_dashboard_navigation';
-import * as urlUtility from '~/lib/utils/url_utility';
describe('findAndFollowLink', () => {
it('visits a link when the selector exists', () => {
const href = '/some/path';
- const locationSpy = spyOn(urlUtility, 'visitUrl');
+ const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl');
setFixtures(`<a class="my-shortcut" href="${href}">link</a>`);
findAndFollowLink('.my-shortcut');
- expect(locationSpy).toHaveBeenCalledWith(href);
+ expect(visitUrl).toHaveBeenCalledWith(href);
});
it('does not throw an exception when the selector does not exist', () => {
- const locationSpy = spyOn(urlUtility, 'visitUrl');
+ const visitUrl = spyOnDependency(findAndFollowLink, 'visitUrl');
// this should not throw an exception
findAndFollowLink('.this-selector-does-not-exist');
- expect(locationSpy).not.toHaveBeenCalled();
+ expect(visitUrl).not.toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index b0d714cbefb..d73608ed0ed 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -4,7 +4,7 @@ import ShortcutsIssuable from '~/shortcuts_issuable';
initCopyAsGFM();
-describe('ShortcutsIssuable', () => {
+describe('ShortcutsIssuable', function () {
const fixtureName = 'merge_requests/diff_comment.html.raw';
preloadFixtures(fixtureName);
beforeEach(() => {
diff --git a/spec/javascripts/sidebar/sidebar_mediator_spec.js b/spec/javascripts/sidebar/sidebar_mediator_spec.js
index afa18cc127e..da950258a94 100644
--- a/spec/javascripts/sidebar/sidebar_mediator_spec.js
+++ b/spec/javascripts/sidebar/sidebar_mediator_spec.js
@@ -1,12 +1,11 @@
import _ from 'underscore';
import Vue from 'vue';
-import * as urlUtils from '~/lib/utils/url_utility';
import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarStore from '~/sidebar/stores/sidebar_store';
import SidebarService from '~/sidebar/services/sidebar_service';
import Mock from './mock_data';
-describe('Sidebar mediator', () => {
+describe('Sidebar mediator', function() {
beforeEach(() => {
Vue.http.interceptors.push(Mock.sidebarMockInterceptor);
this.mediator = new SidebarMediator(Mock.mediator);
@@ -87,12 +86,12 @@ describe('Sidebar mediator', () => {
const moveToProjectId = 7;
this.mediator.store.setMoveToProjectId(moveToProjectId);
spyOn(this.mediator.service, 'moveIssue').and.callThrough();
- spyOn(urlUtils, 'visitUrl');
+ const visitUrl = spyOnDependency(SidebarMediator, 'visitUrl');
this.mediator.moveIssue()
.then(() => {
expect(this.mediator.service.moveIssue).toHaveBeenCalledWith(moveToProjectId);
- expect(urlUtils.visitUrl).toHaveBeenCalledWith('/root/some-project/issues/5');
+ expect(visitUrl).toHaveBeenCalledWith('/root/some-project/issues/5');
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
index d8e636cbdf0..a3fb965fbab 100644
--- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js
+++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
@@ -7,7 +7,7 @@ import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarMoveIssue from '~/sidebar/lib/sidebar_move_issue';
import Mock from './mock_data';
-describe('SidebarMoveIssue', () => {
+describe('SidebarMoveIssue', function () {
beforeEach(() => {
Vue.http.interceptors.push(Mock.sidebarMockInterceptor);
this.mediator = new SidebarMediator(Mock.mediator);
diff --git a/spec/javascripts/sidebar/sidebar_store_spec.js b/spec/javascripts/sidebar/sidebar_store_spec.js
index 3591f96ff87..08b112a54ba 100644
--- a/spec/javascripts/sidebar/sidebar_store_spec.js
+++ b/spec/javascripts/sidebar/sidebar_store_spec.js
@@ -31,7 +31,7 @@ const PARTICIPANT_LIST = [
{ ...PARTICIPANT, id: 3 },
];
-describe('Sidebar store', () => {
+describe('Sidebar store', function () {
beforeEach(() => {
this.store = new SidebarStore({
currentUser: {
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 14bff05e537..bcd15f5eae2 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -1,4 +1,5 @@
-/* eslint-disable jasmine/no-global-setup */
+/* eslint-disable jasmine/no-global-setup, jasmine/no-unsafe-spy, no-underscore-dangle */
+
import $ from 'jquery';
import 'vendor/jasmine-jquery';
import '~/commons';
@@ -55,6 +56,17 @@ window.addEventListener('unhandledrejection', event => {
console.error(event.reason.stack || event.reason);
});
+// Add global function to spy on a module's dependencies via rewire
+window.spyOnDependency = (module, name) => {
+ const dependency = module.__GetDependency__(name);
+ const spy = jasmine.createSpy(name, dependency);
+ module.__Rewire__(name, spy);
+ return spy;
+};
+
+// Reset any rewired modules after each test (see babel-plugin-rewire)
+afterEach(__rewire_reset_all__); // eslint-disable-line
+
// HACK: Chrome 59 disconnects if there are too many synchronous tests in a row
// because it appears to lock up the thread that communicates to Karma's socket
// This async beforeEach gets called on every spec and releases the JS thread long
diff --git a/spec/javascripts/todos_spec.js b/spec/javascripts/todos_spec.js
index 898bbb3819b..e74f4bdef7e 100644
--- a/spec/javascripts/todos_spec.js
+++ b/spec/javascripts/todos_spec.js
@@ -1,5 +1,4 @@
import $ from 'jquery';
-import * as urlUtils from '~/lib/utils/url_utility';
import Todos from '~/pages/dashboard/todos/index/todos';
import '~/lib/utils/common_utils';
@@ -18,7 +17,7 @@ describe('Todos', () => {
it('opens the todo url', (done) => {
const todoLink = todoItem.dataset.url;
- spyOn(urlUtils, 'visitUrl').and.callFake((url) => {
+ spyOnDependency(Todos, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(todoLink);
done();
});
@@ -33,7 +32,7 @@ describe('Todos', () => {
beforeEach(() => {
metakeyEvent = $.Event('click', { keyCode: 91, ctrlKey: true });
- visitUrlSpy = spyOn(urlUtils, 'visitUrl').and.callFake(() => {});
+ visitUrlSpy = spyOnDependency(Todos, 'visitUrl').and.callFake(() => {});
windowOpenSpy = spyOn(window, 'open').and.callFake(() => {});
});
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index 39c47a5c06d..d84b13b07c4 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -3,7 +3,7 @@ import U2FAuthenticate from '~/u2f/authenticate';
import 'vendor/u2f';
import MockU2FDevice from './mock_u2f_device';
-describe('U2FAuthenticate', () => {
+describe('U2FAuthenticate', function () {
preloadFixtures('u2f/authenticate.html.raw');
beforeEach((done) => {
diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js
index 136b4cad737..d9383314891 100644
--- a/spec/javascripts/u2f/register_spec.js
+++ b/spec/javascripts/u2f/register_spec.js
@@ -3,7 +3,7 @@ import U2FRegister from '~/u2f/register';
import 'vendor/u2f';
import MockU2FDevice from './mock_u2f_device';
-describe('U2FRegister', () => {
+describe('U2FRegister', function () {
preloadFixtures('u2f/register.html.raw');
beforeEach((done) => {
diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
index ff8d54c029f..c82ba61a5b1 100644
--- a/spec/javascripts/vue_mr_widget/components/deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
@@ -1,5 +1,4 @@
import Vue from 'vue';
-import * as urlUtils from '~/lib/utils/url_utility';
import deploymentComponent from '~/vue_merge_request_widget/components/deployment.vue';
import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
import { getTimeago } from '~/lib/utils/datetime_utility';
@@ -117,13 +116,13 @@ describe('Deployment component', () => {
it('should show a confirm dialog and call service.stopEnvironment when confirmed', (done) => {
spyOn(window, 'confirm').and.returnValue(true);
spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(true));
- spyOn(urlUtils, 'visitUrl').and.returnValue(true);
+ const visitUrl = spyOnDependency(deploymentComponent, 'visitUrl').and.returnValue(true);
vm = mockStopEnvironment();
expect(window.confirm).toHaveBeenCalled();
expect(MRWidgetService.stopEnvironment).toHaveBeenCalledWith(deploymentMockData.stop_url);
setTimeout(() => {
- expect(urlUtils.visitUrl).toHaveBeenCalledWith(url);
+ expect(visitUrl).toHaveBeenCalledWith(url);
done();
}, 333);
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 300b7882d03..81c16593eb4 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -1,7 +1,6 @@
import Vue from 'vue';
import ReadyToMerge from '~/vue_merge_request_widget/components/states/ready_to_merge.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-import * as simplePoll from '~/lib/utils/simple_poll';
const commitMessage = 'This is the commit message';
const commitMessageWithDescription = 'This is the commit message description';
@@ -355,9 +354,9 @@ describe('ReadyToMerge', () => {
describe('initiateMergePolling', () => {
it('should call simplePoll', () => {
- spyOn(simplePoll, 'default');
+ const simplePoll = spyOnDependency(ReadyToMerge, 'simplePoll');
vm.initiateMergePolling();
- expect(simplePoll.default).toHaveBeenCalled();
+ expect(simplePoll).toHaveBeenCalled();
});
});
@@ -457,11 +456,11 @@ describe('ReadyToMerge', () => {
describe('initiateRemoveSourceBranchPolling', () => {
it('should emit event and call simplePoll', () => {
spyOn(eventHub, '$emit');
- spyOn(simplePoll, 'default');
+ const simplePoll = spyOnDependency(ReadyToMerge, 'simplePoll');
vm.initiateRemoveSourceBranchPolling();
expect(eventHub.$emit).toHaveBeenCalledWith('SetBranchRemoveFlag', [true]);
- expect(simplePoll.default).toHaveBeenCalled();
+ expect(simplePoll).toHaveBeenCalled();
});
});
@@ -524,18 +523,20 @@ describe('ReadyToMerge', () => {
});
describe('when user can merge and can delete branch', () => {
+ let customVm;
+
beforeEach(() => {
- this.customVm = createComponent({
+ customVm = createComponent({
mr: { canRemoveSourceBranch: true },
});
});
it('isRemoveSourceBranchButtonDisabled should be false', () => {
- expect(this.customVm.isRemoveSourceBranchButtonDisabled).toBe(false);
+ expect(customVm.isRemoveSourceBranchButtonDisabled).toBe(false);
});
it('should be enabled in rendered output', () => {
- const checkboxElement = this.customVm.$el.querySelector('#remove-source-branch-input');
+ const checkboxElement = customVm.$el.querySelector('#remove-source-branch-input');
expect(checkboxElement).not.toBeNull();
});
});
diff --git a/spec/javascripts/vue_shared/components/ci_icon_spec.js b/spec/javascripts/vue_shared/components/ci_icon_spec.js
index d8664408595..423bc746a22 100644
--- a/spec/javascripts/vue_shared/components/ci_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/ci_icon_spec.js
@@ -1,139 +1,122 @@
import Vue from 'vue';
import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('CI Icon component', () => {
- let CiIcon;
- beforeEach(() => {
- CiIcon = Vue.extend(ciIcon);
+ const Component = Vue.extend(ciIcon);
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
});
it('should render a span element with an svg', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_success',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_success',
},
- }).$mount();
+ });
- expect(component.$el.tagName).toEqual('SPAN');
- expect(component.$el.querySelector('span > svg')).toBeDefined();
+ expect(vm.$el.tagName).toEqual('SPAN');
+ expect(vm.$el.querySelector('span > svg')).toBeDefined();
});
it('should render a success status', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_success',
- group: 'success',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_success',
+ group: 'success',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-success')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-success')).toEqual(true);
});
it('should render a failed status', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_failed',
- group: 'failed',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_failed',
+ group: 'failed',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-failed')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-failed')).toEqual(true);
});
it('should render success with warnings status', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_warning',
- group: 'warning',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_warning',
+ group: 'warning',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-warning')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-warning')).toEqual(true);
});
it('should render pending status', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_pending',
- group: 'pending',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_pending',
+ group: 'pending',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-pending')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-pending')).toEqual(true);
});
it('should render running status', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_running',
- group: 'running',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_running',
+ group: 'running',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-running')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-running')).toEqual(true);
});
it('should render created status', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_created',
- group: 'created',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_created',
+ group: 'created',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-created')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-created')).toEqual(true);
});
it('should render skipped status', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_skipped',
- group: 'skipped',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_skipped',
+ group: 'skipped',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-skipped')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-skipped')).toEqual(true);
});
it('should render canceled status', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_canceled',
- group: 'canceled',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_canceled',
+ group: 'canceled',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-canceled')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-canceled')).toEqual(true);
});
it('should render status for manual action', () => {
- const component = new CiIcon({
- propsData: {
- status: {
- icon: 'icon_status_manual',
- group: 'manual',
- },
+ vm = mountComponent(Component, {
+ status: {
+ icon: 'icon_status_manual',
+ group: 'manual',
},
- }).$mount();
+ });
- expect(component.$el.classList.contains('ci-status-icon-manual')).toEqual(true);
+ expect(vm.$el.classList.contains('ci-status-icon-manual')).toEqual(true);
});
});
diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
index f598b1afa74..97f0fbb04db 100644
--- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js
+++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
@@ -3,10 +3,10 @@ import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('clipboard button', () => {
+ const Component = Vue.extend(clipboardButton);
let vm;
beforeEach(() => {
- const Component = Vue.extend(clipboardButton);
vm = mountComponent(Component, {
text: 'copy me',
title: 'Copy this value into Clipboard!',
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index ed66361bfc3..7189e8cfcfa 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -55,7 +55,6 @@ describe('Commit component', () => {
path: '/jschatz1',
username: 'jschatz1',
},
- commitIconSvg: '<svg></svg>',
};
component = mountComponent(CommitComponent, props);
@@ -82,8 +81,10 @@ describe('Commit component', () => {
expect(component.$el.querySelector('.commit-sha').textContent).toContain(props.shortSha);
});
- it('should render the given commitIconSvg', () => {
- expect(component.$el.querySelector('.js-commit-icon').children).toContain('svg');
+ it('should render icon for commit', () => {
+ expect(
+ component.$el.querySelector('.js-commit-icon use').getAttribute('xlink:href'),
+ ).toContain('commit');
});
describe('Given commit title and author props', () => {
diff --git a/spec/javascripts/vue_shared/components/expand_button_spec.js b/spec/javascripts/vue_shared/components/expand_button_spec.js
index f19589d3b75..af9693c48fd 100644
--- a/spec/javascripts/vue_shared/components/expand_button_spec.js
+++ b/spec/javascripts/vue_shared/components/expand_button_spec.js
@@ -3,10 +3,10 @@ import expandButton from '~/vue_shared/components/expand_button.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('expand button', () => {
+ const Component = Vue.extend(expandButton);
let vm;
beforeEach(() => {
- const Component = Vue.extend(expandButton);
vm = mountComponent(Component, {
slots: {
expanded: '<p>Expanded!</p>',
@@ -22,7 +22,7 @@ describe('expand button', () => {
expect(vm.$el.textContent.trim()).toEqual('...');
});
- it('hides expander on click', (done) => {
+ it('hides expander on click', done => {
vm.$el.querySelector('button').click();
vm.$nextTick(() => {
expect(vm.$el.querySelector('button').getAttribute('style')).toEqual('display: none;');
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js
index 6fe95153204..e8685ab48be 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/base_spec.js
@@ -73,6 +73,22 @@ describe('BaseComponent', () => {
expect(vm.$emit).toHaveBeenCalledWith('onLabelClick', mockLabels[0]);
});
});
+
+ describe('handleCollapsedValueClick', () => {
+ it('emits toggleCollapse event on component', () => {
+ spyOn(vm, '$emit');
+ vm.handleCollapsedValueClick();
+ expect(vm.$emit).toHaveBeenCalledWith('toggleCollapse');
+ });
+ });
+
+ describe('handleDropdownHidden', () => {
+ it('emits onDropdownClose event on component', () => {
+ spyOn(vm, '$emit');
+ vm.handleDropdownHidden();
+ expect(vm.$emit).toHaveBeenCalledWith('onDropdownClose');
+ });
+ });
});
describe('mounted', () => {
diff --git a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
index 39040670a87..da74595bcdc 100644
--- a/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed_spec.js
@@ -56,6 +56,16 @@ describe('DropdownValueCollapsedComponent', () => {
});
});
+ describe('methods', () => {
+ describe('handleClick', () => {
+ it('emits onValueClick event on component', () => {
+ spyOn(vm, '$emit');
+ vm.handleClick();
+ expect(vm.$emit).toHaveBeenCalledWith('onValueClick');
+ });
+ });
+ });
+
describe('template', () => {
it('renders component container element with tooltip`', () => {
expect(vm.$el.dataset.placement).toBe('left');
diff --git a/spec/lib/backup/files_spec.rb b/spec/lib/backup/files_spec.rb
index 14d055cbcc1..99872211a4e 100644
--- a/spec/lib/backup/files_spec.rb
+++ b/spec/lib/backup/files_spec.rb
@@ -62,5 +62,19 @@ describe Backup::Files do
subject.restore
end
end
+
+ describe 'folders that are a mountpoint' do
+ before do
+ allow(FileUtils).to receive(:mv).and_raise(Errno::EBUSY)
+ allow(subject).to receive(:run_pipeline!).and_return(true)
+ end
+
+ it 'shows error message' do
+ expect(subject).to receive(:resource_busy_error).with("/var/gitlab-registry")
+ .and_call_original
+
+ expect { subject.restore }.to raise_error(/is a mountpoint/)
+ end
+ end
end
end
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index e4c1c9bafc0..b3777be312b 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -81,6 +81,18 @@ describe Backup::Repository do
subject.restore
end
end
+
+ describe 'folder that is a mountpoint' do
+ before do
+ allow(FileUtils).to receive(:mv).and_raise(Errno::EBUSY)
+ end
+
+ it 'shows error message' do
+ expect(subject).to receive(:resource_busy_error).and_call_original
+
+ expect { subject.restore }.to raise_error(/is a mountpoint/)
+ end
+ end
end
describe '#empty_repo?' do
diff --git a/spec/lib/gitlab/auth/ldap/user_spec.rb b/spec/lib/gitlab/auth/ldap/user_spec.rb
index cab2169593a..653c19942ea 100644
--- a/spec/lib/gitlab/auth/ldap/user_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/user_spec.rb
@@ -25,20 +25,20 @@ describe Gitlab::Auth::LDAP::User do
OmniAuth::AuthHash.new(uid: 'uid=John Smith,ou=People,dc=example,dc=com', provider: 'ldapmain', info: info_upper_case)
end
- describe '#changed?' do
+ describe '#should_save?' do
it "marks existing ldap user as changed" do
create(:omniauth_user, extern_uid: 'uid=John Smith,ou=People,dc=example,dc=com', provider: 'ldapmain')
- expect(ldap_user.changed?).to be_truthy
+ expect(ldap_user.should_save?).to be_truthy
end
it "marks existing non-ldap user if the email matches as changed" do
create(:user, email: 'john@example.com')
- expect(ldap_user.changed?).to be_truthy
+ expect(ldap_user.should_save?).to be_truthy
end
it "does not mark existing ldap user as changed" do
create(:omniauth_user, email: 'john@example.com', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', provider: 'ldapmain')
- expect(ldap_user.changed?).to be_falsey
+ expect(ldap_user.should_save?).to be_falsey
end
end
diff --git a/spec/lib/gitlab/auth/o_auth/identity_linker_spec.rb b/spec/lib/gitlab/auth/o_auth/identity_linker_spec.rb
new file mode 100644
index 00000000000..528f1b4ec57
--- /dev/null
+++ b/spec/lib/gitlab/auth/o_auth/identity_linker_spec.rb
@@ -0,0 +1,62 @@
+require 'spec_helper'
+
+describe Gitlab::Auth::OAuth::IdentityLinker do
+ let(:user) { create(:user) }
+ let(:provider) { 'twitter' }
+ let(:uid) { user.email }
+ let(:oauth) { { 'provider' => provider, 'uid' => uid } }
+
+ subject { described_class.new(user, oauth) }
+
+ context 'linked identity exists' do
+ let!(:identity) { user.identities.create!(provider: provider, extern_uid: uid) }
+
+ it "doesn't create new identity" do
+ expect { subject.link }.not_to change { Identity.count }
+ end
+
+ it "sets #changed? to false" do
+ subject.link
+
+ expect(subject).not_to be_changed
+ end
+ end
+
+ context 'identity already linked to different user' do
+ let!(:identity) { create(:identity, provider: provider, extern_uid: uid) }
+
+ it "#changed? returns false" do
+ subject.link
+
+ expect(subject).not_to be_changed
+ end
+
+ it 'exposes error message' do
+ expect(subject.error_message).to eq 'Extern uid has already been taken'
+ end
+ end
+
+ context 'identity needs to be created' do
+ it 'creates linked identity' do
+ expect { subject.link }.to change { user.identities.count }
+ end
+
+ it 'sets identity provider' do
+ subject.link
+
+ expect(user.identities.last.provider).to eq provider
+ end
+
+ it 'sets identity extern_uid' do
+ subject.link
+
+ expect(user.identities.last.extern_uid).to eq uid
+ end
+
+ it 'sets #changed? to true' do
+ subject.link
+
+ expect(subject).to be_changed
+ end
+ end
+end
diff --git a/spec/lib/gitlab/auth/saml/identity_linker_spec.rb b/spec/lib/gitlab/auth/saml/identity_linker_spec.rb
new file mode 100644
index 00000000000..f3305d574cc
--- /dev/null
+++ b/spec/lib/gitlab/auth/saml/identity_linker_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Gitlab::Auth::Saml::IdentityLinker do
+ let(:user) { create(:user) }
+ let(:provider) { 'saml' }
+ let(:uid) { user.email }
+ let(:oauth) { { 'provider' => provider, 'uid' => uid } }
+
+ subject { described_class.new(user, oauth) }
+
+ context 'linked identity exists' do
+ let!(:identity) { user.identities.create!(provider: provider, extern_uid: uid) }
+
+ it "doesn't create new identity" do
+ expect { subject.link }.not_to change { Identity.count }
+ end
+
+ it "sets #changed? to false" do
+ subject.link
+
+ expect(subject).not_to be_changed
+ end
+ end
+
+ context 'identity needs to be created' do
+ it 'creates linked identity' do
+ expect { subject.link }.to change { user.identities.count }
+ end
+
+ it 'sets identity provider' do
+ subject.link
+
+ expect(user.identities.last.provider).to eq provider
+ end
+
+ it 'sets identity extern_uid' do
+ subject.link
+
+ expect(user.identities.last.extern_uid).to eq uid
+ end
+
+ it 'sets #changed? to true' do
+ subject.link
+
+ expect(subject).to be_changed
+ end
+ end
+end
diff --git a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
index ae5b31dc12d..c3f528dd6fc 100644
--- a/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/project_creator_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::BitbucketImport::ProjectCreator do
has_wiki?: false)
end
- let(:namespace) { create(:group, owner: user) }
+ let(:namespace) { create(:group) }
let(:token) { "asdasd12345" }
let(:secret) { "sekrettt" }
let(:access_params) { { bitbucket_access_token: token, bitbucket_access_token_secret: secret } }
diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb
index f128c1d4ca4..e2bb378f663 100644
--- a/spec/lib/gitlab/ci/status/build/play_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/play_spec.rb
@@ -2,8 +2,8 @@ require 'spec_helper'
describe Gitlab::Ci::Status::Build::Play do
let(:user) { create(:user) }
- let(:project) { build.project }
- let(:build) { create(:ci_build, :manual) }
+ let(:project) { create(:project, :stubbed_repository) }
+ let(:build) { create(:ci_build, :manual, project: project) }
let(:status) { Gitlab::Ci::Status::Core.new(build, user) }
subject { described_class.new(status) }
@@ -46,6 +46,8 @@ describe Gitlab::Ci::Status::Build::Play do
context 'when user can not push to the branch' do
before do
build.project.add_developer(user)
+ create(:protected_branch, :masters_can_push,
+ name: build.ref, project: project)
end
it { is_expected.not_to have_action }
diff --git a/spec/lib/gitlab/wiki/committer_with_hooks_spec.rb b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
index 830fb8a8598..267056b96e6 100644
--- a/spec/lib/gitlab/wiki/committer_with_hooks_spec.rb
+++ b/spec/lib/gitlab/git/committer_with_hooks_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Wiki::CommitterWithHooks, seed_helper: true do
+describe Gitlab::Git::CommitterWithHooks, seed_helper: true do
shared_examples 'calling wiki hooks' do
let(:project) { create(:project) }
let(:user) { project.owner }
diff --git a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
index 82548c7fd31..5ea086e4abd 100644
--- a/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/project_creator_spec.rb
@@ -12,7 +12,7 @@ describe Gitlab::GitlabImport::ProjectCreator do
owner: { name: "john" }
}.with_indifferent_access
end
- let(:namespace) { create(:group, owner: user) }
+ let(:namespace) { create(:group) }
let(:token) { "asdffg" }
let(:access_params) { { gitlab_access_token: token } }
diff --git a/spec/lib/gitlab/google_code_import/project_creator_spec.rb b/spec/lib/gitlab/google_code_import/project_creator_spec.rb
index 8d5b60d50de..24cd518c77b 100644
--- a/spec/lib/gitlab/google_code_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/google_code_import/project_creator_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::GoogleCodeImport::ProjectCreator do
"repositoryUrls" => ["https://vim.googlecode.com/git/"]
)
end
- let(:namespace) { create(:group, owner: user) }
+ let(:namespace) { create(:group) }
before do
namespace.add_owner(user)
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 897a5984782..e7f20f81fe0 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -286,6 +286,7 @@ project:
- internal_ids
- project_deploy_tokens
- deploy_tokens
+- ci_cd_settings
award_emoji:
- awardable
- user
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 05790bb5fe1..31141807cb2 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -546,3 +546,5 @@ Badge:
- created_at
- updated_at
- type
+ProjectCiCdSetting:
+- group_runners_enabled
diff --git a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
index 737c9a624e0..eb1b13704ea 100644
--- a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::LegacyGithubImport::ProjectCreator do
let(:user) { create(:user) }
- let(:namespace) { create(:group, owner: user) }
+ let(:namespace) { create(:group) }
let(:repo) do
OpenStruct.new(
diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb
index 40c8286b1b9..97b6069f64d 100644
--- a/spec/lib/gitlab/user_access_spec.rb
+++ b/spec/lib/gitlab/user_access_spec.rb
@@ -32,6 +32,12 @@ describe Gitlab::UserAccess do
let(:empty_project) { create(:project_empty_repo) }
let(:project_access) { described_class.new(user, project: empty_project) }
+ it 'returns true for admins' do
+ user.update!(admin: true)
+
+ expect(access.can_push_to_branch?('master')).to be_truthy
+ end
+
it 'returns true if user is master' do
empty_project.add_master(user)
@@ -71,6 +77,12 @@ describe Gitlab::UserAccess do
let(:branch) { create :protected_branch, project: project, name: "test" }
let(:not_existing_branch) { create :protected_branch, :developers_can_merge, project: project }
+ it 'returns true for admins' do
+ user.update!(admin: true)
+
+ expect(access.can_push_to_branch?(branch.name)).to be_truthy
+ end
+
it 'returns true if user is a master' do
project.add_master(user)
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index bd443a5d9e7..da146e24893 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -1,6 +1,14 @@
-require 'rails_helper'
+require 'fast_spec_helper'
+
+require_dependency 'gitlab'
describe Gitlab do
+ describe '.root' do
+ it 'returns the root path of the app' do
+ expect(described_class.root).to eq(Pathname.new(File.expand_path('../..', __dir__)))
+ end
+ end
+
describe '.com?' do
it 'is true when on GitLab.com' do
stub_config_setting(url: 'https://gitlab.com')
diff --git a/spec/migrations/create_missing_namespace_for_internal_users_spec.rb b/spec/migrations/create_missing_namespace_for_internal_users_spec.rb
new file mode 100644
index 00000000000..ac3a4b1f68f
--- /dev/null
+++ b/spec/migrations/create_missing_namespace_for_internal_users_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20180413022611_create_missing_namespace_for_internal_users.rb')
+
+describe CreateMissingNamespaceForInternalUsers, :migration do
+ let(:users) { table(:users) }
+ let(:namespaces) { table(:namespaces) }
+ let(:routes) { table(:routes) }
+
+ internal_user_types = [:ghost]
+ internal_user_types << :support_bot if ActiveRecord::Base.connection.column_exists?(:users, :support_bot)
+
+ internal_user_types.each do |attr|
+ context "for #{attr} user" do
+ let(:internal_user) do
+ users.create!(email: 'test@example.com', projects_limit: 100, username: 'test', attr => true)
+ end
+
+ it 'creates the missing namespace' do
+ expect(namespaces.find_by(owner_id: internal_user.id)).to be_nil
+
+ migrate!
+
+ namespace = Namespace.find_by(type: nil, owner_id: internal_user.id)
+ route = namespace.route
+
+ expect(namespace.path).to eq(route.path)
+ expect(namespace.name).to eq(route.name)
+ end
+
+ it 'sets notification email' do
+ users.update(internal_user.id, notification_email: nil)
+
+ expect(users.find(internal_user.id).notification_email).to be_nil
+
+ migrate!
+
+ user = users.find(internal_user.id)
+ expect(user.notification_email).to eq(user.email)
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index fcdc31c8984..3158e006720 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2035,6 +2035,34 @@ describe Ci::Build do
expect(build).not_to be_persisted
end
end
+
+ context 'for deploy tokens' do
+ let(:deploy_token) { create(:deploy_token, :gitlab_deploy_token) }
+
+ let(:deploy_token_variables) do
+ [
+ { key: 'CI_DEPLOY_USER', value: deploy_token.name, public: true },
+ { key: 'CI_DEPLOY_PASSWORD', value: deploy_token.token, public: false }
+ ]
+ end
+
+ context 'when gitlab-deploy-token exists' do
+ before do
+ project.deploy_tokens << deploy_token
+ end
+
+ it 'should include deploy token variables' do
+ is_expected.to include(*deploy_token_variables)
+ end
+ end
+
+ context 'when gitlab-deploy-token does not exist' do
+ it 'should not include deploy token variables' do
+ expect(subject.find { |v| v[:key] == 'CI_DEPLOY_USER'}).to be_nil
+ expect(subject.find { |v| v[:key] == 'CI_DEPLOY_PASSWORD'}).to be_nil
+ end
+ end
+ end
end
describe '#scoped_variables' do
@@ -2083,7 +2111,9 @@ describe Ci::Build do
CI_REGISTRY_USER
CI_REGISTRY_PASSWORD
CI_REPOSITORY_URL
- CI_ENVIRONMENT_URL]
+ CI_ENVIRONMENT_URL
+ CI_DEPLOY_USER
+ CI_DEPLOY_PASSWORD]
build.scoped_variables.map { |env| env[:key] }.tap do |names|
expect(names).not_to include(*keys)
diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb
index 3696e6f62fd..9faf21bfbbd 100644
--- a/spec/models/concerns/avatarable_spec.rb
+++ b/spec/models/concerns/avatarable_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Avatarable do
- set(:project) { create(:project, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
+ let(:project) { create(:project, :with_avatar) }
let(:gitlab_host) { "https://gitlab.example.com" }
let(:relative_url_root) { "/gitlab" }
@@ -37,11 +37,23 @@ describe Avatarable do
project.visibility_level = visibility_level
end
- let(:avatar_path) { (avatar_path_prefix + [project.avatar.url]).join }
+ let(:avatar_path) { (avatar_path_prefix + [project.avatar.local_url]).join }
it 'returns the expected avatar path' do
expect(project.avatar_path(only_path: only_path)).to eq(avatar_path)
end
+
+ context "when avatar is stored remotely" do
+ before do
+ stub_uploads_object_storage(AvatarUploader)
+
+ project.avatar.migrate!(ObjectStorage::Store::REMOTE)
+ end
+
+ it 'returns the expected avatar path' do
+ expect(project.avatar_url(only_path: only_path)).to eq(avatar_path)
+ end
+ end
end
end
end
diff --git a/spec/models/deploy_token_spec.rb b/spec/models/deploy_token_spec.rb
index 780b200e837..f8d51a95833 100644
--- a/spec/models/deploy_token_spec.rb
+++ b/spec/models/deploy_token_spec.rb
@@ -142,4 +142,23 @@ describe DeployToken do
end
end
end
+
+ describe '.gitlab_deploy_token' do
+ let(:project) { create(:project ) }
+
+ subject { project.deploy_tokens.gitlab_deploy_token }
+
+ context 'with a gitlab deploy token associated' do
+ it 'should return the gitlab deploy token' do
+ deploy_token = create(:deploy_token, :gitlab_deploy_token, projects: [project])
+ is_expected.to eq(deploy_token)
+ end
+ end
+
+ context 'with no gitlab deploy token associated' do
+ it 'should return nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 56161bfcc28..25d6597084c 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Environment do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :stubbed_repository) }
subject(:environment) { create(:environment, project: project) }
it { is_expected.to belong_to(:project) }
@@ -201,7 +201,7 @@ describe Environment do
end
describe '#stop_with_action!' do
- let(:user) { create(:admin) }
+ let(:user) { create(:user) }
subject { environment.stop_with_action!(user) }
diff --git a/spec/models/project_ci_cd_setting_spec.rb b/spec/models/project_ci_cd_setting_spec.rb
new file mode 100644
index 00000000000..4aa62028169
--- /dev/null
+++ b/spec/models/project_ci_cd_setting_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectCiCdSetting do
+ describe '.available?' do
+ before do
+ described_class.reset_column_information
+ end
+
+ it 'returns true' do
+ expect(described_class).to be_available
+ end
+
+ it 'memoizes the schema version' do
+ expect(ActiveRecord::Migrator)
+ .to receive(:current_version)
+ .and_call_original
+ .once
+
+ 2.times { described_class.available? }
+ end
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 2675c2f52c1..127eb998abe 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -93,6 +93,15 @@ describe Project do
end
end
+ context 'when creating a new project' do
+ it 'automatically creates a CI/CD settings row' do
+ project = create(:project)
+
+ expect(project.ci_cd_settings).to be_an_instance_of(ProjectCiCdSetting)
+ expect(project.ci_cd_settings).to be_persisted
+ end
+ end
+
describe '#members & #requesters' do
let(:project) { create(:project, :public, :access_requestable) }
let(:requester) { create(:user) }
@@ -325,7 +334,7 @@ describe Project do
let(:owner) { create(:user, name: 'Gitlab') }
let(:namespace) { create(:namespace, path: 'sample-namespace', owner: owner) }
let(:project) { create(:project, path: 'sample-project', namespace: namespace) }
- let(:group) { create(:group, name: 'Group', path: 'sample-group', owner: owner) }
+ let(:group) { create(:group, name: 'Group', path: 'sample-group') }
context 'when nil argument' do
it 'returns nil' do
@@ -1483,52 +1492,6 @@ describe Project do
end
end
- describe '#user_can_push_to_empty_repo?' do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
-
- it 'returns false when default_branch_protection is in full protection and user is developer' do
- project.add_developer(user)
- stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL)
-
- expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
- end
-
- it 'returns false when default_branch_protection only lets devs merge and user is dev' do
- project.add_developer(user)
- stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
-
- expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
- end
-
- it 'returns true when default_branch_protection lets devs push and user is developer' do
- project.add_developer(user)
- stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
-
- expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
- end
-
- it 'returns true when default_branch_protection is unprotected and user is developer' do
- project.add_developer(user)
- stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
-
- expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
- end
-
- it 'returns true when user is master' do
- project.add_master(user)
-
- expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
- end
-
- it 'returns false when the repo is not empty' do
- project.add_master(user)
- expect(project).to receive(:empty_repo?).and_return(false)
-
- expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
- end
- end
-
describe '#container_registry_url' do
let(:project) { create(:project) }
@@ -3585,4 +3548,44 @@ describe Project do
it { is_expected.not_to be_valid }
end
end
+
+ describe '#gitlab_deploy_token' do
+ let(:project) { create(:project) }
+
+ subject { project.gitlab_deploy_token }
+
+ context 'when there is a gitlab deploy token associated' do
+ let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }
+
+ it { is_expected.to eq(deploy_token) }
+ end
+
+ context 'when there is no a gitlab deploy token associated' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'when there is a gitlab deploy token associated but is has been revoked' do
+ let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :revoked, projects: [project]) }
+ it { is_expected.to be_nil }
+ end
+
+ context 'when there is a gitlab deploy token associated but it is expired' do
+ let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :expired, projects: [project]) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when there is a deploy token associated with a different name' do
+ let!(:deploy_token) { create(:deploy_token, projects: [project]) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when there is a deploy token associated to a different project' do
+ let(:project_2) { create(:project) }
+ let!(:deploy_token) { create(:deploy_token, projects: [project_2]) }
+
+ it { is_expected.to be_nil }
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 35db7616efb..3f2eb58f009 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1164,8 +1164,12 @@ describe User do
end
context 'with a group route matching the given path' do
+ let!(:group) { create(:group, path: 'group_path') }
+
context 'when the group namespace has an owner_id (legacy data)' do
- let!(:group) { create(:group, path: 'group_path', owner: user) }
+ before do
+ group.update!(owner_id: user.id)
+ end
it 'returns nil' do
expect(described_class.find_by_full_path('group_path')).to eq(nil)
@@ -1173,8 +1177,6 @@ describe User do
end
context 'when the group namespace does not have an owner_id' do
- let!(:group) { create(:group, path: 'group_path') }
-
it 'returns nil' do
expect(described_class.find_by_full_path('group_path')).to eq(nil)
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index b4d25e06d9a..9b5c290b9f9 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -7,9 +7,9 @@ describe GroupPolicy do
let(:master) { create(:user) }
let(:owner) { create(:user) }
let(:admin) { create(:admin) }
- let(:group) { create(:group) }
+ let(:group) { create(:group, :private) }
- let(:guest_permissions) { [:read_group, :upload_file, :read_namespace] }
+ let(:guest_permissions) { [:read_label, :read_group, :upload_file, :read_namespace] }
let(:reporter_permissions) { [:admin_label] }
@@ -50,6 +50,7 @@ describe GroupPolicy do
end
context 'with no user' do
+ let(:group) { create(:group, :public) }
let(:current_user) { nil }
it do
@@ -63,6 +64,28 @@ describe GroupPolicy do
end
end
+ context 'has projects' do
+ let(:current_user) { create(:user) }
+ let(:project) { create(:project, namespace: group) }
+
+ before do
+ project.add_developer(current_user)
+ end
+
+ it do
+ expect_allowed(:read_group, :read_label)
+ end
+
+ context 'in subgroups', :nested_groups do
+ let(:subgroup) { create(:group, :private, parent: group) }
+ let(:project) { create(:project, namespace: subgroup) }
+
+ it do
+ expect_allowed(:read_group, :read_label)
+ end
+ end
+ end
+
context 'guests' do
let(:current_user) { guest }
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index 55962f345d4..830d2ee3b20 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -208,6 +208,17 @@ describe ProjectPresenter do
it 'returns nil if user cannot push' do
expect(presenter.new_file_anchor_data).to be_nil
end
+
+ context 'when the project is empty' do
+ let(:project) { create(:project, :empty_repo) }
+
+ # Since we protect the default branch for empty repos
+ it 'is empty for a developer' do
+ project.add_developer(user)
+
+ expect(presenter.new_file_anchor_data).to be_nil
+ end
+ end
end
describe '#readme_anchor_data' do
@@ -321,7 +332,7 @@ describe ProjectPresenter do
expect(presenter.autodevops_anchor_data).to eq(OpenStruct.new(enabled: false,
label: 'Enable Auto DevOps',
- link: presenter.project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings')))
+ link: presenter.project_settings_ci_cd_path(project, anchor: 'autodevops-settings')))
end
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 17272cb00e5..85a571b8f0e 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -685,7 +685,8 @@ describe API::Projects do
issues_enabled: false,
merge_requests_enabled: false,
wiki_enabled: false,
- request_access_enabled: true
+ request_access_enabled: true,
+ jobs_enabled: true
})
post api("/projects/user/#{user.id}", admin), project
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index f406d2ffb22..e8196980a8c 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -212,6 +212,18 @@ describe API::Users do
expect(json_response.last['id']).to eq(user.id)
end
+ it 'returns users with 2fa enabled' do
+ admin
+ user
+ user_with_2fa = create(:user, :two_factor_via_otp)
+
+ get api('/users', admin), { two_factor: 'enabled' }
+
+ expect(response).to match_response_schema('public_api/v4/user/admins')
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['id']).to eq(user_with_2fa.id)
+ end
+
it 'returns 400 when provided incorrect sort params' do
get api('/users', admin), { order_by: 'magic', sort: 'asc' }
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
index 6ce75c65c8c..f1acfc48468 100644
--- a/spec/services/ci/retry_pipeline_service_spec.rb
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -235,6 +235,8 @@ describe Ci::RetryPipelineService, '#execute' do
context 'when user is not allowed to trigger manual action' do
before do
project.add_developer(user)
+ create(:protected_branch, :masters_can_push,
+ name: pipeline.ref, project: project)
end
context 'when there is a failed manual action present' do
diff --git a/spec/services/groups/nested_create_service_spec.rb b/spec/services/groups/nested_create_service_spec.rb
index 6491fb34777..86fdd43c1e5 100644
--- a/spec/services/groups/nested_create_service_spec.rb
+++ b/spec/services/groups/nested_create_service_spec.rb
@@ -59,8 +59,11 @@ describe Groups::NestedCreateService do
describe "#execute" do
it 'returns the group if it already existed' do
- parent = create(:group, path: 'a-group', owner: user)
- child = create(:group, path: 'a-sub-group', parent: parent, owner: user)
+ parent = create(:group, path: 'a-group')
+ child = create(:group, path: 'a-sub-group', parent: parent)
+
+ parent.add_owner(user)
+ child.add_owner(user)
expect(service.execute).to eq(child)
end
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index f793f55e51b..bd835a1fca6 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -306,6 +306,23 @@ describe QuickActions::InterpretService do
end
end
+ shared_examples 'copy_metadata command' do
+ it 'fetches issue or merge request and copies labels and milestone if content contains /copy_metadata reference' do
+ source_issuable # populate the issue
+ todo_label # populate this label
+ inreview_label # populate this label
+ _, updates = service.execute(content, issuable)
+
+ expect(updates[:add_label_ids]).to match_array([inreview_label.id, todo_label.id])
+
+ if source_issuable.milestone
+ expect(updates[:milestone_id]).to eq(source_issuable.milestone.id)
+ else
+ expect(updates).not_to have_key(:milestone_id)
+ end
+ end
+ end
+
shared_examples 'shrug command' do
it 'appends ¯\_(ツ)_/¯ to the comment' do
new_content, _ = service.execute(content, issuable)
@@ -757,6 +774,65 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
+ context '/copy_metadata command' do
+ let(:todo_label) { create(:label, project: project, title: 'To Do') }
+ let(:inreview_label) { create(:label, project: project, title: 'In Review') }
+
+ it_behaves_like 'empty command' do
+ let(:content) { '/copy_metadata' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'copy_metadata command' do
+ let(:source_issuable) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) }
+
+ let(:content) { "/copy_metadata #{source_issuable.to_reference}" }
+ let(:issuable) { issue }
+ end
+
+ context 'when the parent issuable has a milestone' do
+ it_behaves_like 'copy_metadata command' do
+ let(:source_issuable) { create(:labeled_issue, project: project, labels: [todo_label, inreview_label], milestone: milestone) }
+
+ let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" }
+ let(:issuable) { issue }
+ end
+ end
+
+ context 'when more than one issuable is passed' do
+ it_behaves_like 'copy_metadata command' do
+ let(:source_issuable) { create(:labeled_issue, project: project, labels: [inreview_label, todo_label]) }
+ let(:other_label) { create(:label, project: project, title: 'Other') }
+ let(:other_source_issuable) { create(:labeled_issue, project: project, labels: [other_label]) }
+
+ let(:content) { "/copy_metadata #{source_issuable.to_reference} #{other_source_issuable.to_reference}" }
+ let(:issuable) { issue }
+ end
+ end
+
+ context 'cross project references' do
+ it_behaves_like 'empty command' do
+ let(:other_project) { create(:project, :public) }
+ let(:source_issuable) { create(:labeled_issue, project: other_project, labels: [todo_label, inreview_label]) }
+ let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'empty command' do
+ let(:content) { "/copy_metadata imaginary#1234" }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'empty command' do
+ let(:other_project) { create(:project, :private) }
+ let(:source_issuable) { create(:issue, project: other_project) }
+
+ let(:content) { "/copy_metadata #{source_issuable.to_reference(project)}" }
+ let(:issuable) { issue }
+ end
+ end
+ end
+
context '/duplicate command' do
it_behaves_like 'duplicate command' do
let(:issue_duplicate) { create(:issue, project: project) }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 83664bae046..53045815a6a 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -32,42 +32,19 @@ require 'rainbow/ext/string'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
+# Requires helpers, and shared contexts/examples first since they're used in other support files
+Dir[Rails.root.join("spec/support/helpers/*.rb")].each { |f| require f }
+Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f }
+Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f }
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.use_instantiated_fixtures = false
- config.mock_with :rspec
config.verbose_retry = true
config.display_try_failure_messages = true
- config.include Devise::Test::ControllerHelpers, type: :controller
- config.include Devise::Test::ControllerHelpers, type: :view
- config.include Devise::Test::IntegrationHelpers, type: :feature
- config.include Warden::Test::Helpers, type: :request
- config.include LoginHelpers, type: :feature
- config.include SearchHelpers, type: :feature
- config.include CookieHelper, :js
- config.include InputHelper, :js
- config.include SelectionHelper, :js
- config.include InspectRequests, :js
- config.include WaitForRequests, :js
- config.include LiveDebugger, :js
- config.include StubConfiguration
- config.include EmailHelpers, :mailer, type: :mailer
- config.include TestEnv
- config.include ActiveJob::TestHelper
- config.include ActiveSupport::Testing::TimeHelpers
- config.include StubGitlabCalls
- config.include StubGitlabData
- config.include ApiHelpers, :api
- config.include Gitlab::Routing, type: :routing
- config.include MigrationsHelpers, :migration
- config.include StubFeatureFlags
- config.include StubENV
- config.include ExpectOffense
-
config.infer_spec_type_from_file_location!
config.define_derived_metadata(file_path: %r{/spec/}) do |metadata|
@@ -82,7 +59,33 @@ RSpec.configure do |config|
metadata[:type] = match[1].singularize.to_sym if match
end
- config.raise_errors_for_deprecations!
+ config.include ActiveJob::TestHelper
+ config.include ActiveSupport::Testing::TimeHelpers
+ config.include CycleAnalyticsHelpers
+ config.include ExpectOffense
+ config.include FactoryBot::Syntax::Methods
+ config.include FixtureHelpers
+ config.include GitlabRoutingHelper
+ config.include StubFeatureFlags
+ config.include StubGitlabCalls
+ config.include StubGitlabData
+ config.include TestEnv
+ config.include Devise::Test::ControllerHelpers, type: :controller
+ config.include Devise::Test::IntegrationHelpers, type: :feature
+ config.include LoginHelpers, type: :feature
+ config.include SearchHelpers, type: :feature
+ config.include EmailHelpers, :mailer, type: :mailer
+ config.include Warden::Test::Helpers, type: :request
+ config.include Gitlab::Routing, type: :routing
+ config.include Devise::Test::ControllerHelpers, type: :view
+ config.include ApiHelpers, :api
+ config.include CookieHelper, :js
+ config.include InputHelper, :js
+ config.include SelectionHelper, :js
+ config.include InspectRequests, :js
+ config.include WaitForRequests, :js
+ config.include LiveDebugger, :js
+ config.include MigrationsHelpers, :migration
if ENV['CI']
# This includes the first try, i.e. tests will be run 4 times before failing.
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 9ddcc5f2fbf..c0ceb0f6605 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -60,6 +60,8 @@ Capybara::Screenshot.register_driver(:chrome) do |driver, path|
end
RSpec.configure do |config|
+ config.include CapybaraHelpers, type: :feature
+
config.before(:context, :js) do
next if $capybara_server_already_started
diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb
index 3321f920666..368439aa5b0 100644
--- a/spec/support/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb
@@ -56,7 +56,7 @@ shared_examples 'a GitHub-ish import controller: GET status' do
end
it "assigns variables" do
- project = create(:project, import_type: provider, creator_id: user.id)
+ project = create(:project, import_type: provider, namespace: user.namespace)
stub_client(repos: [repo, org_repo], orgs: [org], org_repos: [org_repo])
get :status
@@ -69,7 +69,7 @@ shared_examples 'a GitHub-ish import controller: GET status' do
end
it "does not show already added project" do
- project = create(:project, import_type: provider, creator_id: user.id, import_source: 'asd/vim')
+ project = create(:project, import_type: provider, namespace: user.namespace, import_source: 'asd/vim')
stub_client(repos: [repo], orgs: [])
get :status
@@ -257,11 +257,12 @@ shared_examples 'a GitHub-ish import controller: POST create' do
end
context 'user has chosen an existing nested namespace and name for the project', :postgresql do
- let(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ let(:parent_namespace) { create(:group, name: 'foo') }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
let(:test_name) { 'test_name' }
before do
+ parent_namespace.add_owner(user)
nested_namespace.add_owner(user)
end
@@ -307,7 +308,7 @@ shared_examples 'a GitHub-ish import controller: POST create' do
context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
let(:test_name) { 'test_name' }
- let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ let!(:parent_namespace) { create(:group, name: 'foo') }
before do
parent_namespace.add_owner(user)
diff --git a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
new file mode 100644
index 00000000000..72912ffb89d
--- /dev/null
+++ b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+shared_context 'Ldap::OmniauthCallbacksController' do
+ include LoginHelpers
+ include LdapHelpers
+
+ let(:uid) { 'my-uid' }
+ let(:provider) { 'ldapmain' }
+ let(:valid_login?) { true }
+ let(:user) { create(:omniauth_user, extern_uid: uid, provider: provider) }
+ let(:ldap_server_config) do
+ { main: ldap_config_defaults(:main) }
+ end
+
+ def ldap_config_defaults(key, hash = {})
+ {
+ provider_name: "ldap#{key}",
+ attributes: {},
+ encryption: 'plain'
+ }.merge(hash)
+ end
+
+ before do
+ stub_ldap_setting(enabled: true, servers: ldap_server_config)
+ described_class.define_providers!
+ Rails.application.reload_routes!
+
+ mock_auth_hash(provider.to_s, uid, user.email)
+ stub_omniauth_provider(provider, context: request)
+
+ allow(Gitlab::Auth::LDAP::Access).to receive(:allowed?).and_return(valid_login?)
+ end
+end
diff --git a/spec/support/factory_bot.rb b/spec/support/factory_bot.rb
deleted file mode 100644
index c7890e49c66..00000000000
--- a/spec/support/factory_bot.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-RSpec.configure do |config|
- config.include FactoryBot::Syntax::Methods
-end
diff --git a/spec/support/generate-seed-repo-rb b/spec/support/generate-seed-repo-rb
index 876b3b8242d..44b3de23b99 100755
--- a/spec/support/generate-seed-repo-rb
+++ b/spec/support/generate-seed-repo-rb
@@ -8,7 +8,7 @@
#
# Usage:
#
-# ./spec/support/generate-seed-repo-rb > spec/support/seed_repo.rb
+# ./spec/support/generate-seed-repo-rb > spec/support/helpers/seed_repo.rb
#
#
diff --git a/spec/support/gitlab-git-test.git/README.md b/spec/support/gitlab-git-test.git/README.md
index f072cd421be..f757e613ee6 100644
--- a/spec/support/gitlab-git-test.git/README.md
+++ b/spec/support/gitlab-git-test.git/README.md
@@ -12,5 +12,5 @@ inflate the size of the gitlab-ce repository.
- make changes in your local clone of gitlab-git-test
- run `git push` which will push to your local source `gitlab-ce/spec/support/gitlab-git-test.git`
- in gitlab-ce: run `spec/support/prepare-gitlab-git-test-for-commit`
-- in gitlab-ce: `git add spec/support/seed_repo.rb spec/support/gitlab-git-test.git`
+- in gitlab-ce: `git add spec/support/helpers/seed_repo.rb spec/support/gitlab-git-test.git`
- commit your changes in gitlab-ce
diff --git a/spec/support/api_helpers.rb b/spec/support/helpers/api_helpers.rb
index ac0c7a9b493..ac0c7a9b493 100644
--- a/spec/support/api_helpers.rb
+++ b/spec/support/helpers/api_helpers.rb
diff --git a/spec/support/bare_repo_operations.rb b/spec/support/helpers/bare_repo_operations.rb
index 3f4a4243cb6..3f4a4243cb6 100644
--- a/spec/support/bare_repo_operations.rb
+++ b/spec/support/helpers/bare_repo_operations.rb
diff --git a/spec/support/board_helpers.rb b/spec/support/helpers/board_helpers.rb
index 507d0432d7f..507d0432d7f 100644
--- a/spec/support/board_helpers.rb
+++ b/spec/support/helpers/board_helpers.rb
diff --git a/spec/support/capybara_helpers.rb b/spec/support/helpers/capybara_helpers.rb
index 868233416bf..bcc2df44708 100644
--- a/spec/support/capybara_helpers.rb
+++ b/spec/support/helpers/capybara_helpers.rb
@@ -41,7 +41,3 @@ module CapybaraHelpers
page.driver.browser.manage.delete_cookie('_gitlab_session')
end
end
-
-RSpec.configure do |config|
- config.include CapybaraHelpers, type: :feature
-end
diff --git a/spec/support/cookie_helper.rb b/spec/support/helpers/cookie_helper.rb
index 5ff7b0b68c9..5ff7b0b68c9 100644
--- a/spec/support/cookie_helper.rb
+++ b/spec/support/helpers/cookie_helper.rb
diff --git a/spec/support/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index 73cc64c0b74..55359d36597 100644
--- a/spec/support/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -135,7 +135,3 @@ module CycleAnalyticsHelpers
end
end
end
-
-RSpec.configure do |config|
- config.include CycleAnalyticsHelpers
-end
diff --git a/spec/support/database_connection_helpers.rb b/spec/support/helpers/database_connection_helpers.rb
index 763329499f0..763329499f0 100644
--- a/spec/support/database_connection_helpers.rb
+++ b/spec/support/helpers/database_connection_helpers.rb
diff --git a/spec/support/devise_helpers.rb b/spec/support/helpers/devise_helpers.rb
index 66874e10f38..66874e10f38 100644
--- a/spec/support/devise_helpers.rb
+++ b/spec/support/helpers/devise_helpers.rb
diff --git a/spec/support/drag_to_helper.rb b/spec/support/helpers/drag_to_helper.rb
index ae149631ed9..ae149631ed9 100644
--- a/spec/support/drag_to_helper.rb
+++ b/spec/support/helpers/drag_to_helper.rb
diff --git a/spec/support/dropzone_helper.rb b/spec/support/helpers/dropzone_helper.rb
index fe72d320fcf..fe72d320fcf 100644
--- a/spec/support/dropzone_helper.rb
+++ b/spec/support/helpers/dropzone_helper.rb
diff --git a/spec/support/email_helpers.rb b/spec/support/helpers/email_helpers.rb
index 1fb8252459f..1fb8252459f 100644
--- a/spec/support/email_helpers.rb
+++ b/spec/support/helpers/email_helpers.rb
diff --git a/spec/support/fake_migration_classes.rb b/spec/support/helpers/fake_migration_classes.rb
index b0fc8422857..b0fc8422857 100644
--- a/spec/support/fake_migration_classes.rb
+++ b/spec/support/helpers/fake_migration_classes.rb
diff --git a/spec/support/fake_u2f_device.rb b/spec/support/helpers/fake_u2f_device.rb
index a7605cd483a..a7605cd483a 100644
--- a/spec/support/fake_u2f_device.rb
+++ b/spec/support/helpers/fake_u2f_device.rb
diff --git a/spec/support/filter_item_select_helper.rb b/spec/support/helpers/filter_item_select_helper.rb
index 519e84d359e..519e84d359e 100644
--- a/spec/support/filter_item_select_helper.rb
+++ b/spec/support/helpers/filter_item_select_helper.rb
diff --git a/spec/support/filter_spec_helper.rb b/spec/support/helpers/filter_spec_helper.rb
index 721d359c2ee..721d359c2ee 100644
--- a/spec/support/filter_spec_helper.rb
+++ b/spec/support/helpers/filter_spec_helper.rb
diff --git a/spec/support/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb
index 5f42ff77fb2..5f42ff77fb2 100644
--- a/spec/support/filtered_search_helpers.rb
+++ b/spec/support/helpers/filtered_search_helpers.rb
diff --git a/spec/support/fixture_helpers.rb b/spec/support/helpers/fixture_helpers.rb
index 8854382dc6b..611d19f36a0 100644
--- a/spec/support/fixture_helpers.rb
+++ b/spec/support/helpers/fixture_helpers.rb
@@ -9,7 +9,3 @@ module FixtureHelpers
File.expand_path(Rails.root.join(dir, 'spec', 'fixtures', filename))
end
end
-
-RSpec.configure do |config|
- config.include FixtureHelpers
-end
diff --git a/spec/support/git_http_helpers.rb b/spec/support/helpers/git_http_helpers.rb
index b8289e6c5f1..b8289e6c5f1 100644
--- a/spec/support/git_http_helpers.rb
+++ b/spec/support/helpers/git_http_helpers.rb
diff --git a/spec/support/helpers/gitlab_verify_helpers.rb b/spec/support/helpers/gitlab_verify_helpers.rb
new file mode 100644
index 00000000000..5df4bf24ec2
--- /dev/null
+++ b/spec/support/helpers/gitlab_verify_helpers.rb
@@ -0,0 +1,25 @@
+module GitlabVerifyHelpers
+ def collect_ranges(args = {})
+ verifier = described_class.new(args.merge(batch_size: 1))
+
+ collect_results(verifier).map { |range, _| range }
+ end
+
+ def collect_failures
+ verifier = described_class.new(batch_size: 1)
+
+ out = {}
+
+ collect_results(verifier).map { |_, failures| out.merge!(failures) }
+
+ out
+ end
+
+ def collect_results(verifier)
+ out = []
+
+ verifier.run_batches { |*args| out << args }
+
+ out
+ end
+end
diff --git a/spec/support/gpg_helpers.rb b/spec/support/helpers/gpg_helpers.rb
index 3f7279a50e0..3f7279a50e0 100644
--- a/spec/support/gpg_helpers.rb
+++ b/spec/support/helpers/gpg_helpers.rb
diff --git a/spec/support/import_spec_helper.rb b/spec/support/helpers/import_spec_helper.rb
index d4eced724fa..d4eced724fa 100644
--- a/spec/support/import_spec_helper.rb
+++ b/spec/support/helpers/import_spec_helper.rb
diff --git a/spec/support/input_helper.rb b/spec/support/helpers/input_helper.rb
index acbb42274ec..acbb42274ec 100644
--- a/spec/support/input_helper.rb
+++ b/spec/support/helpers/input_helper.rb
diff --git a/spec/support/inspect_requests.rb b/spec/support/helpers/inspect_requests.rb
index 88ddc5c7f6c..88ddc5c7f6c 100644
--- a/spec/support/inspect_requests.rb
+++ b/spec/support/helpers/inspect_requests.rb
diff --git a/spec/support/issue_helpers.rb b/spec/support/helpers/issue_helpers.rb
index ffd72515f37..ffd72515f37 100644
--- a/spec/support/issue_helpers.rb
+++ b/spec/support/helpers/issue_helpers.rb
diff --git a/spec/support/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index 2197bc9d853..2197bc9d853 100644
--- a/spec/support/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
diff --git a/spec/support/jira_service_helper.rb b/spec/support/helpers/jira_service_helper.rb
index 88a7aeba461..88a7aeba461 100644
--- a/spec/support/jira_service_helper.rb
+++ b/spec/support/helpers/jira_service_helper.rb
diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index e46b61b6461..e46b61b6461 100644
--- a/spec/support/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
diff --git a/spec/support/ldap_helpers.rb b/spec/support/helpers/ldap_helpers.rb
index 0e87b3d359d..b90bbc4b106 100644
--- a/spec/support/ldap_helpers.rb
+++ b/spec/support/helpers/ldap_helpers.rb
@@ -18,6 +18,10 @@ module LdapHelpers
allow_any_instance_of(::Gitlab::Auth::LDAP::Config).to receive_messages(messages)
end
+ def stub_ldap_setting(messages)
+ allow(Gitlab.config.ldap).to receive_messages(to_settings(messages))
+ end
+
# Stub an LDAP person search and provide the return entry. Specify `nil` for
# `entry` to simulate when an LDAP person is not found
#
diff --git a/spec/support/live_debugger.rb b/spec/support/helpers/live_debugger.rb
index 911eb48a8ca..911eb48a8ca 100644
--- a/spec/support/live_debugger.rb
+++ b/spec/support/helpers/live_debugger.rb
diff --git a/spec/support/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index db34090e971..72e5c2d66dd 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -112,7 +112,7 @@ module LoginHelpers
}
}
})
- Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:saml]
+ Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym]
end
def mock_saml_config
@@ -129,7 +129,7 @@ module LoginHelpers
env = env_from_context(context)
set_devise_mapping(context: context)
- env['omniauth.auth'] = OmniAuth.config.mock_auth[provider]
+ env['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym]
end
def stub_omniauth_saml_config(messages)
diff --git a/spec/support/markdown_feature.rb b/spec/support/helpers/markdown_feature.rb
index 39e94ad53de..39e94ad53de 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/helpers/markdown_feature.rb
diff --git a/spec/support/merge_request_helpers.rb b/spec/support/helpers/merge_request_helpers.rb
index 772adff4626..772adff4626 100644
--- a/spec/support/merge_request_helpers.rb
+++ b/spec/support/helpers/merge_request_helpers.rb
diff --git a/spec/support/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb
index 5d6f662e8fe..5d6f662e8fe 100644
--- a/spec/support/migrations_helpers.rb
+++ b/spec/support/helpers/migrations_helpers.rb
diff --git a/spec/support/mobile_helpers.rb b/spec/support/helpers/mobile_helpers.rb
index 3b9eb84e824..3b9eb84e824 100644
--- a/spec/support/mobile_helpers.rb
+++ b/spec/support/helpers/mobile_helpers.rb
diff --git a/spec/support/project_forks_helper.rb b/spec/support/helpers/project_forks_helper.rb
index 2c501a2a27c..2c501a2a27c 100644
--- a/spec/support/project_forks_helper.rb
+++ b/spec/support/helpers/project_forks_helper.rb
diff --git a/spec/support/prometheus_helpers.rb b/spec/support/helpers/prometheus_helpers.rb
index 4212be2cc88..4212be2cc88 100644
--- a/spec/support/prometheus_helpers.rb
+++ b/spec/support/helpers/prometheus_helpers.rb
diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb
new file mode 100644
index 00000000000..28536bbef5e
--- /dev/null
+++ b/spec/support/helpers/query_recorder.rb
@@ -0,0 +1,38 @@
+module ActiveRecord
+ class QueryRecorder
+ attr_reader :log, :cached
+
+ def initialize(&block)
+ @log = []
+ @cached = []
+ ActiveSupport::Notifications.subscribed(method(:callback), 'sql.active_record', &block)
+ end
+
+ def show_backtrace(values)
+ Rails.logger.debug("QueryRecorder SQL: #{values[:sql]}")
+ caller.each { |line| Rails.logger.debug(" --> #{line}") }
+ end
+
+ def callback(name, start, finish, message_id, values)
+ show_backtrace(values) if ENV['QUERY_RECORDER_DEBUG']
+
+ if values[:name]&.include?("CACHE")
+ @cached << values[:sql]
+ elsif !values[:name]&.include?("SCHEMA")
+ @log << values[:sql]
+ end
+ end
+
+ def count
+ @log.count
+ end
+
+ def cached_count
+ @cached.count
+ end
+
+ def log_message
+ @log.join("\n\n")
+ end
+ end
+end
diff --git a/spec/support/helpers/quick_actions_helpers.rb b/spec/support/helpers/quick_actions_helpers.rb
new file mode 100644
index 00000000000..361190aa352
--- /dev/null
+++ b/spec/support/helpers/quick_actions_helpers.rb
@@ -0,0 +1,10 @@
+module QuickActionsHelpers
+ def write_note(text)
+ Sidekiq::Testing.fake! do
+ page.within('.js-main-target-form') do
+ fill_in 'note[note]', with: text
+ find('.js-comment-submit-button').click
+ end
+ end
+ end
+end
diff --git a/spec/support/rake_helpers.rb b/spec/support/helpers/rake_helpers.rb
index 86bfeed107c..86bfeed107c 100644
--- a/spec/support/rake_helpers.rb
+++ b/spec/support/helpers/rake_helpers.rb
diff --git a/spec/support/reactive_caching_helpers.rb b/spec/support/helpers/reactive_caching_helpers.rb
index e22dd974c6a..e22dd974c6a 100644
--- a/spec/support/reactive_caching_helpers.rb
+++ b/spec/support/helpers/reactive_caching_helpers.rb
diff --git a/spec/support/redis_without_keys.rb b/spec/support/helpers/redis_without_keys.rb
index 6220167dee6..6220167dee6 100644
--- a/spec/support/redis_without_keys.rb
+++ b/spec/support/helpers/redis_without_keys.rb
diff --git a/spec/support/reference_parser_helpers.rb b/spec/support/helpers/reference_parser_helpers.rb
index c01897ed1a1..c01897ed1a1 100644
--- a/spec/support/reference_parser_helpers.rb
+++ b/spec/support/helpers/reference_parser_helpers.rb
diff --git a/spec/support/repo_helpers.rb b/spec/support/helpers/repo_helpers.rb
index 3c6956cf5e0..3c6956cf5e0 100644
--- a/spec/support/repo_helpers.rb
+++ b/spec/support/helpers/repo_helpers.rb
diff --git a/spec/support/search_helpers.rb b/spec/support/helpers/search_helpers.rb
index abbbb636d66..abbbb636d66 100644
--- a/spec/support/search_helpers.rb
+++ b/spec/support/helpers/search_helpers.rb
diff --git a/spec/support/seed_helper.rb b/spec/support/helpers/seed_helper.rb
index 11ef1fc477f..8fd107260cc 100644
--- a/spec/support/seed_helper.rb
+++ b/spec/support/helpers/seed_helper.rb
@@ -9,7 +9,7 @@ TEST_MUTABLE_REPO_PATH = 'mutable-repo.git'.freeze
TEST_BROKEN_REPO_PATH = 'broken-repo.git'.freeze
module SeedHelper
- GITLAB_GIT_TEST_REPO_URL = File.expand_path('../gitlab-git-test.git', __FILE__).freeze
+ GITLAB_GIT_TEST_REPO_URL = File.expand_path('../gitlab-git-test.git', __dir__).freeze
def ensure_seeds
if File.exist?(SEED_STORAGE_PATH)
@@ -108,11 +108,3 @@ bla/bla.txt
{ 'GIT_TEMPLATE_DIR' => '' }
end
end
-
-RSpec.configure do |config|
- config.include SeedHelper, :seed_helper
-
- config.before(:all, :seed_helper) do
- ensure_seeds
- end
-end
diff --git a/spec/support/seed_repo.rb b/spec/support/helpers/seed_repo.rb
index b4868e82cd7..b4868e82cd7 100644
--- a/spec/support/seed_repo.rb
+++ b/spec/support/helpers/seed_repo.rb
diff --git a/spec/support/select2_helper.rb b/spec/support/helpers/select2_helper.rb
index 90618ba5b19..90618ba5b19 100644
--- a/spec/support/select2_helper.rb
+++ b/spec/support/helpers/select2_helper.rb
diff --git a/spec/support/selection_helper.rb b/spec/support/helpers/selection_helper.rb
index b4725b137b2..b4725b137b2 100644
--- a/spec/support/selection_helper.rb
+++ b/spec/support/helpers/selection_helper.rb
diff --git a/spec/support/helpers/sorting_helper.rb b/spec/support/helpers/sorting_helper.rb
new file mode 100644
index 00000000000..577518d726c
--- /dev/null
+++ b/spec/support/helpers/sorting_helper.rb
@@ -0,0 +1,18 @@
+# Helper allows you to sort items
+#
+# Params
+# value - value for sorting
+#
+# Usage:
+# include SortingHelper
+#
+# sorting_by('Oldest updated')
+#
+module SortingHelper
+ def sorting_by(value)
+ find('button.dropdown-toggle').click
+ page.within('.content ul.dropdown-menu.dropdown-menu-align-right li') do
+ click_link value
+ end
+ end
+end
diff --git a/spec/support/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index a75a3eaefcb..1823099dd9c 100644
--- a/spec/support/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -1,3 +1,6 @@
+require 'active_support/core_ext/hash/transform_values'
+require 'active_support/hash_with_indifferent_access'
+
module StubConfiguration
def stub_application_setting(messages)
add_predicates(messages)
diff --git a/spec/support/stub_env.rb b/spec/support/helpers/stub_env.rb
index 36b90fc68d6..36b90fc68d6 100644
--- a/spec/support/stub_env.rb
+++ b/spec/support/helpers/stub_env.rb
diff --git a/spec/support/stub_feature_flags.rb b/spec/support/helpers/stub_feature_flags.rb
index b96338bf548..b96338bf548 100644
--- a/spec/support/stub_feature_flags.rb
+++ b/spec/support/helpers/stub_feature_flags.rb
diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb
index c1618f5086c..c1618f5086c 100644
--- a/spec/support/stub_gitlab_calls.rb
+++ b/spec/support/helpers/stub_gitlab_calls.rb
diff --git a/spec/support/stub_gitlab_data.rb b/spec/support/helpers/stub_gitlab_data.rb
index fa402f35b95..fa402f35b95 100644
--- a/spec/support/stub_gitlab_data.rb
+++ b/spec/support/helpers/stub_gitlab_data.rb
diff --git a/spec/support/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index 6e88641da42..19d744b959a 100644
--- a/spec/support/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -1,4 +1,4 @@
-module StubConfiguration
+module StubObjectStorage
def stub_object_storage_uploader(
config:,
uploader:,
diff --git a/spec/support/test_env.rb b/spec/support/helpers/test_env.rb
index d87f265cdf0..d87f265cdf0 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/helpers/test_env.rb
diff --git a/spec/support/upload_helpers.rb b/spec/support/helpers/upload_helpers.rb
index 5eead80c935..5eead80c935 100644
--- a/spec/support/upload_helpers.rb
+++ b/spec/support/helpers/upload_helpers.rb
diff --git a/spec/support/user_activities_helpers.rb b/spec/support/helpers/user_activities_helpers.rb
index 44feb104644..44feb104644 100644
--- a/spec/support/user_activities_helpers.rb
+++ b/spec/support/helpers/user_activities_helpers.rb
diff --git a/spec/support/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb
index fda0e29f983..fda0e29f983 100644
--- a/spec/support/wait_for_requests.rb
+++ b/spec/support/helpers/wait_for_requests.rb
diff --git a/spec/support/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb
index ef1f9f68671..ef1f9f68671 100644
--- a/spec/support/workhorse_helpers.rb
+++ b/spec/support/helpers/workhorse_helpers.rb
diff --git a/spec/support/issuables_list_metadata_shared_examples.rb b/spec/support/issuables_list_metadata_shared_examples.rb
deleted file mode 100644
index e61983c60b4..00000000000
--- a/spec/support/issuables_list_metadata_shared_examples.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-shared_examples 'issuables list meta-data' do |issuable_type, action = nil|
- before do
- @issuable_ids = []
-
- %w[fix improve/awesome].each do |source_branch|
- issuable =
- if issuable_type == :issue
- create(issuable_type, project: project, author: project.creator)
- else
- create(issuable_type, source_project: project, source_branch: source_branch, author: project.creator)
- end
-
- @issuable_ids << issuable.id
- end
- end
-
- it "creates indexed meta-data object for issuable notes and votes count" do
- if action
- get action, author_id: project.creator.id
- else
- get :index, namespace_id: project.namespace, project_id: project
- end
-
- meta_data = assigns(:issuable_meta_data)
-
- aggregate_failures do
- expect(meta_data.keys).to match_array(@issuable_ids)
- expect(meta_data.values).to all(be_kind_of(Issuable::IssuableMeta))
- end
- end
-
- describe "when given empty collection" do
- let(:project2) { create(:project, :public) }
-
- it "doesn't execute any queries with false conditions" do
- get_action =
- if action
- proc { get action, author_id: project.creator.id }
- else
- proc { get :index, namespace_id: project2.namespace, project_id: project2 }
- end
-
- expect(&get_action).not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
- end
- end
-end
diff --git a/spec/support/json_response_helpers.rb b/spec/support/json_response.rb
index aa235529c56..210b0e6d867 100644
--- a/spec/support/json_response_helpers.rb
+++ b/spec/support/json_response.rb
@@ -1,7 +1,3 @@
-shared_context 'JSON response' do
- let(:json_response) { JSON.parse(response.body) }
-end
-
RSpec.configure do |config|
config.include_context 'JSON response'
config.include_context 'JSON response', type: :request
diff --git a/spec/support/background_migrations_matchers.rb b/spec/support/matchers/background_migrations_matchers.rb
index f4127efc6ae..f4127efc6ae 100644
--- a/spec/support/background_migrations_matchers.rb
+++ b/spec/support/matchers/background_migrations_matchers.rb
diff --git a/spec/support/query_recorder.rb b/spec/support/matchers/exceed_query_limit.rb
index 8cf8f45a8b2..88d22a3ddd9 100644
--- a/spec/support/query_recorder.rb
+++ b/spec/support/matchers/exceed_query_limit.rb
@@ -1,42 +1,3 @@
-module ActiveRecord
- class QueryRecorder
- attr_reader :log, :cached
-
- def initialize(&block)
- @log = []
- @cached = []
- ActiveSupport::Notifications.subscribed(method(:callback), 'sql.active_record', &block)
- end
-
- def show_backtrace(values)
- Rails.logger.debug("QueryRecorder SQL: #{values[:sql]}")
- caller.each { |line| Rails.logger.debug(" --> #{line}") }
- end
-
- def callback(name, start, finish, message_id, values)
- show_backtrace(values) if ENV['QUERY_RECORDER_DEBUG']
-
- if values[:name]&.include?("CACHE")
- @cached << values[:sql]
- elsif !values[:name]&.include?("SCHEMA")
- @log << values[:sql]
- end
- end
-
- def count
- @log.count
- end
-
- def cached_count
- @cached.count
- end
-
- def log_message
- @log.join("\n\n")
- end
- end
-end
-
RSpec::Matchers.define :exceed_query_limit do |expected|
supports_block_expectations
diff --git a/spec/support/prepare-gitlab-git-test-for-commit b/spec/support/prepare-gitlab-git-test-for-commit
index 3047786a599..d08e3ba5481 100755
--- a/spec/support/prepare-gitlab-git-test-for-commit
+++ b/spec/support/prepare-gitlab-git-test-for-commit
@@ -1,7 +1,7 @@
#!/usr/bin/env ruby
abort unless [
- system('spec/support/generate-seed-repo-rb', out: 'spec/support/seed_repo.rb'),
+ system('spec/support/generate-seed-repo-rb', out: 'spec/support/helpers/seed_repo.rb'),
system('spec/support/unpack-gitlab-git-test')
].all?
diff --git a/spec/support/routing_helpers.rb b/spec/support/routing_helpers.rb
deleted file mode 100644
index af1f4760804..00000000000
--- a/spec/support/routing_helpers.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-RSpec.configure do |config|
- config.include GitlabRoutingHelper
-end
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
new file mode 100644
index 00000000000..dffab22d8b5
--- /dev/null
+++ b/spec/support/rspec.rb
@@ -0,0 +1,12 @@
+require_relative "helpers/stub_configuration"
+require_relative "helpers/stub_object_storage"
+require_relative "helpers/stub_env"
+
+RSpec.configure do |config|
+ config.mock_with :rspec
+ config.raise_errors_for_deprecations!
+
+ config.include StubConfiguration
+ config.include StubObjectStorage
+ config.include StubENV
+end
diff --git a/spec/support/seed.rb b/spec/support/seed.rb
new file mode 100644
index 00000000000..bea2e9c3044
--- /dev/null
+++ b/spec/support/seed.rb
@@ -0,0 +1,7 @@
+RSpec.configure do |config|
+ config.include SeedHelper, :seed_helper
+
+ config.before(:all, :seed_helper) do
+ ensure_seeds
+ end
+end
diff --git a/spec/support/shared_contexts/json_response_shared_context.rb b/spec/support/shared_contexts/json_response_shared_context.rb
new file mode 100644
index 00000000000..df5fc288089
--- /dev/null
+++ b/spec/support/shared_contexts/json_response_shared_context.rb
@@ -0,0 +1,3 @@
+shared_context 'JSON response' do
+ let(:json_response) { JSON.parse(response.body) }
+end
diff --git a/spec/support/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb
index 23f9b46ae0c..23f9b46ae0c 100644
--- a/spec/support/services_shared_context.rb
+++ b/spec/support/shared_contexts/services_shared_context.rb
diff --git a/spec/support/chat_slash_commands_shared_examples.rb b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb
index dc97a39f051..dc97a39f051 100644
--- a/spec/support/chat_slash_commands_shared_examples.rb
+++ b/spec/support/shared_examples/chat_slash_commands_shared_examples.rb
diff --git a/spec/support/email_format_shared_examples.rb b/spec/support/shared_examples/email_format_shared_examples.rb
index b924a208e71..b924a208e71 100644
--- a/spec/support/email_format_shared_examples.rb
+++ b/spec/support/shared_examples/email_format_shared_examples.rb
diff --git a/spec/support/gitlab_verify.rb b/spec/support/shared_examples/gitlab_verify.rb
index 13e2e37624d..560913ca92f 100644
--- a/spec/support/gitlab_verify.rb
+++ b/spec/support/shared_examples/gitlab_verify.rb
@@ -17,29 +17,3 @@ RSpec.shared_examples 'Gitlab::Verify::BatchVerifier subclass' do
end
end
end
-
-module GitlabVerifyHelpers
- def collect_ranges(args = {})
- verifier = described_class.new(args.merge(batch_size: 1))
-
- collect_results(verifier).map { |range, _| range }
- end
-
- def collect_failures
- verifier = described_class.new(batch_size: 1)
-
- out = {}
-
- collect_results(verifier).map { |_, failures| out.merge!(failures) }
-
- out
- end
-
- def collect_results(verifier)
- out = []
-
- verifier.run_batches { |*args| out << args }
-
- out
- end
-end
diff --git a/spec/support/group_members_shared_example.rb b/spec/support/shared_examples/group_members_shared_example.rb
index 547c83c7955..547c83c7955 100644
--- a/spec/support/group_members_shared_example.rb
+++ b/spec/support/shared_examples/group_members_shared_example.rb
diff --git a/spec/support/issuable_shared_examples.rb b/spec/support/shared_examples/issuable_shared_examples.rb
index 42f3b4db23c..42f3b4db23c 100644
--- a/spec/support/issuable_shared_examples.rb
+++ b/spec/support/shared_examples/issuable_shared_examples.rb
diff --git a/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb b/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb
new file mode 100644
index 00000000000..f4bc6f8efa5
--- /dev/null
+++ b/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb
@@ -0,0 +1,62 @@
+shared_examples 'issuables list meta-data' do |issuable_type, action = nil|
+ include ProjectForksHelper
+
+ def get_action(action, project)
+ if action
+ get action, author_id: project.creator.id
+ else
+ get :index, namespace_id: project.namespace, project_id: project
+ end
+ end
+
+ def create_issuable(issuable_type, project, source_branch:)
+ if issuable_type == :issue
+ create(issuable_type, project: project, author: project.creator)
+ else
+ create(issuable_type, source_project: project, source_branch: source_branch, author: project.creator)
+ end
+ end
+
+ before do
+ @issuable_ids = %w[fix improve/awesome].map do |source_branch|
+ create_issuable(issuable_type, project, source_branch: source_branch).id
+ end
+ end
+
+ it "creates indexed meta-data object for issuable notes and votes count" do
+ get_action(action, project)
+
+ meta_data = assigns(:issuable_meta_data)
+
+ aggregate_failures do
+ expect(meta_data.keys).to match_array(@issuable_ids)
+ expect(meta_data.values).to all(be_kind_of(Issuable::IssuableMeta))
+ end
+ end
+
+ it "avoids N+1 queries" do
+ control = ActiveRecord::QueryRecorder.new { get_action(action, project) }
+ issuable = create_issuable(issuable_type, project, source_branch: 'csv')
+
+ if issuable_type == :merge_request
+ issuable.update!(source_project: fork_project(project))
+ end
+
+ expect { get_action(action, project) }.not_to exceed_query_limit(control.count)
+ end
+
+ describe "when given empty collection" do
+ let(:project2) { create(:project, :public) }
+
+ it "doesn't execute any queries with false conditions" do
+ get_empty =
+ if action
+ proc { get action, author_id: project.creator.id }
+ else
+ proc { get :index, namespace_id: project2.namespace, project_id: project2 }
+ end
+
+ expect(&get_empty).not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
+ end
+ end
+end
diff --git a/spec/support/issue_tracker_service_shared_example.rb b/spec/support/shared_examples/issue_tracker_service_shared_example.rb
index a6ab03cb808..a6ab03cb808 100644
--- a/spec/support/issue_tracker_service_shared_example.rb
+++ b/spec/support/shared_examples/issue_tracker_service_shared_example.rb
diff --git a/spec/support/ldap_shared_examples.rb b/spec/support/shared_examples/ldap_shared_examples.rb
index 52c34e78965..52c34e78965 100644
--- a/spec/support/ldap_shared_examples.rb
+++ b/spec/support/shared_examples/ldap_shared_examples.rb
diff --git a/spec/support/legacy_path_redirect_shared_examples.rb b/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb
index f300bdd48b1..f300bdd48b1 100644
--- a/spec/support/legacy_path_redirect_shared_examples.rb
+++ b/spec/support/shared_examples/legacy_path_redirect_shared_examples.rb
diff --git a/spec/support/malicious_regexp_shared_examples.rb b/spec/support/shared_examples/malicious_regexp_shared_examples.rb
index ac5d22298bb..ac5d22298bb 100644
--- a/spec/support/malicious_regexp_shared_examples.rb
+++ b/spec/support/shared_examples/malicious_regexp_shared_examples.rb
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/shared_examples/mentionable_shared_examples.rb
index 1685decbe94..1685decbe94 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/shared_examples/mentionable_shared_examples.rb
diff --git a/spec/support/milestone_tabs_examples.rb b/spec/support/shared_examples/milestone_tabs_examples.rb
index 70b499198bf..70b499198bf 100644
--- a/spec/support/milestone_tabs_examples.rb
+++ b/spec/support/shared_examples/milestone_tabs_examples.rb
diff --git a/spec/support/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb
index e2c23607406..e2c23607406 100644
--- a/spec/support/notify_shared_examples.rb
+++ b/spec/support/shared_examples/notify_shared_examples.rb
diff --git a/spec/support/reference_parser_shared_examples.rb b/spec/support/shared_examples/reference_parser_shared_examples.rb
index baf8bcc04b8..baf8bcc04b8 100644
--- a/spec/support/reference_parser_shared_examples.rb
+++ b/spec/support/shared_examples/reference_parser_shared_examples.rb
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
index 07bc3a51fd8..07bc3a51fd8 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/shared_examples/slack_mattermost_notifications_shared_examples.rb
diff --git a/spec/support/snippet_visibility.rb b/spec/support/shared_examples/snippet_visibility.rb
index 3a7c69b7877..3a7c69b7877 100644
--- a/spec/support/snippet_visibility.rb
+++ b/spec/support/shared_examples/snippet_visibility.rb
diff --git a/spec/support/snippets_shared_examples.rb b/spec/support/shared_examples/snippets_shared_examples.rb
index 85f0facd5c3..85f0facd5c3 100644
--- a/spec/support/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/snippets_shared_examples.rb
diff --git a/spec/support/taskable_shared_examples.rb b/spec/support/shared_examples/taskable_shared_examples.rb
index 4056ff06b84..4056ff06b84 100644
--- a/spec/support/taskable_shared_examples.rb
+++ b/spec/support/shared_examples/taskable_shared_examples.rb
diff --git a/spec/support/time_tracking_shared_examples.rb b/spec/support/shared_examples/time_tracking_shared_examples.rb
index 909d4e2ee8d..909d4e2ee8d 100644
--- a/spec/support/time_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/time_tracking_shared_examples.rb
diff --git a/spec/support/unique_ip_check_shared_examples.rb b/spec/support/shared_examples/unique_ip_check_shared_examples.rb
index e5c8ac6a004..e5c8ac6a004 100644
--- a/spec/support/unique_ip_check_shared_examples.rb
+++ b/spec/support/shared_examples/unique_ip_check_shared_examples.rb
diff --git a/spec/support/update_invalid_issuable.rb b/spec/support/shared_examples/update_invalid_issuable.rb
index 1490287681b..1490287681b 100644
--- a/spec/support/update_invalid_issuable.rb
+++ b/spec/support/shared_examples/update_invalid_issuable.rb
diff --git a/spec/support/updating_mentions_shared_examples.rb b/spec/support/shared_examples/updating_mentions_shared_examples.rb
index 5e3f19ba19e..5e3f19ba19e 100644
--- a/spec/support/updating_mentions_shared_examples.rb
+++ b/spec/support/shared_examples/updating_mentions_shared_examples.rb
diff --git a/spec/views/projects/settings/ci_cd/_form.html.haml_spec.rb b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb
index be9a4d9c57c..d15391911c1 100644
--- a/spec/views/projects/settings/ci_cd/_form.html.haml_spec.rb
+++ b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'projects/settings/ci_cd/_form' do
+describe 'projects/settings/ci_cd/_autodevops_form' do
let(:project) { create(:project, :repository) }
before do
diff --git a/spec/workers/issue_due_scheduler_worker_spec.rb b/spec/workers/issue_due_scheduler_worker_spec.rb
index 7b60835fd26..2710267d384 100644
--- a/spec/workers/issue_due_scheduler_worker_spec.rb
+++ b/spec/workers/issue_due_scheduler_worker_spec.rb
@@ -14,7 +14,9 @@ describe IssueDueSchedulerWorker do
create(:issue, :closed, project: project_closed_issue, due_date: Date.tomorrow)
create(:issue, :opened, project: project_issue_due_another_day, due_date: Date.today)
- expect(MailScheduler::IssueDueWorker).to receive(:bulk_perform_async).with([[project1.id], [project2.id]])
+ expect(MailScheduler::IssueDueWorker).to receive(:bulk_perform_async) do |args|
+ expect(args).to match_array([[project1.id], [project2.id]])
+ end
described_class.new.perform
end