From 41fe97390ceddf945f3d967b8fdb3de4c66b7dea Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Fri, 18 Mar 2022 20:02:30 +0000 Subject: Add latest changes from gitlab-org/gitlab@14-9-stable-ee --- spec/commands/sidekiq_cluster/cli_spec.rb | 216 ++-- spec/components/pajamas/component_spec.rb | 26 + spec/components/pajamas/toggle_component_spec.rb | 107 ++ .../admin/application_settings_controller_spec.rb | 12 + spec/controllers/admin/clusters_controller_spec.rb | 44 + .../admin/runner_projects_controller_spec.rb | 43 +- spec/controllers/admin/runners_controller_spec.rb | 2 +- spec/controllers/admin/topics_controller_spec.rb | 16 + spec/controllers/application_controller_spec.rb | 18 + spec/controllers/autocomplete_controller_spec.rb | 2 +- spec/controllers/boards/lists_controller_spec.rb | 4 +- .../concerns/product_analytics_tracking_spec.rb | 171 +++ .../akismet_mark_as_spam_action_spec.rb | 22 +- .../html_format_actions_support_spec.rb | 2 +- .../json_format_actions_support_spec.rb | 2 +- .../captcha_check/rest_api_actions_support_spec.rb | 86 ++ spec/controllers/confirmations_controller_spec.rb | 2 +- spec/controllers/dashboard_controller_spec.rb | 55 +- spec/controllers/graphql_controller_spec.rb | 39 +- .../controllers/groups/clusters_controller_spec.rb | 44 + ...endency_proxy_for_containers_controller_spec.rb | 22 +- .../groups/group_members_controller_spec.rb | 118 +- .../controllers/groups/releases_controller_spec.rb | 34 +- spec/controllers/groups/runners_controller_spec.rb | 38 +- .../jira_connect/events_controller_spec.rb | 54 +- spec/controllers/passwords_controller_spec.rb | 2 +- spec/controllers/projects/blob_controller_spec.rb | 27 +- .../projects/ci/pipeline_editor_controller_spec.rb | 12 - .../projects/ci/secure_files_controller_spec.rb | 49 + .../projects/clusters_controller_spec.rb | 44 + .../projects/environments_controller_spec.rb | 27 +- .../projects/error_tracking_controller_spec.rb | 22 +- spec/controllers/projects/forks_controller_spec.rb | 9 - .../projects/incidents_controller_spec.rb | 1 + .../controllers/projects/issues_controller_spec.rb | 62 +- .../merge_requests/diffs_controller_spec.rb | 40 +- .../projects/merge_requests_controller_spec.rb | 29 +- .../projects/pipelines_controller_spec.rb | 35 +- .../projects/project_members_controller_spec.rb | 224 ---- .../projects/releases_controller_spec.rb | 162 +++ .../projects/runners_controller_spec.rb | 2 +- .../serverless/functions_controller_spec.rb | 23 + .../projects/services_controller_spec.rb | 11 +- .../projects/tags/releases_controller_spec.rb | 4 +- spec/controllers/projects/tags_controller_spec.rb | 2 +- spec/controllers/projects_controller_spec.rb | 71 +- spec/controllers/search_controller_spec.rb | 30 +- spec/controllers/sessions_controller_spec.rb | 6 +- spec/controllers/snippets_controller_spec.rb | 4 +- spec/db/schema_spec.rb | 2 + spec/experiments/application_experiment_spec.rb | 77 +- .../analytics/cycle_analytics/aggregations.rb | 17 + spec/factories/ci/reports/security/evidence.rb | 60 + spec/factories/ci/reports/security/findings.rb | 49 +- .../issue_customer_relations_contacts.rb | 2 +- spec/factories/groups.rb | 9 + spec/factories/integrations.rb | 63 +- spec/factories/merge_requests.rb | 4 + spec/factories/project_hooks.rb | 4 + spec/factories/projects.rb | 17 +- .../projects/build_artifacts_size_refreshes.rb | 27 + spec/factories/releases.rb | 4 +- spec/factories/sequences.rb | 1 + spec/factories/usage_data.rb | 28 +- spec/factories/users.rb | 8 +- spec/factories/users/saved_replies.rb | 10 + spec/fast_spec_helper.rb | 3 + spec/features/admin/admin_appearance_spec.rb | 2 +- .../admin/admin_broadcast_messages_spec.rb | 27 +- spec/features/admin/admin_deploy_keys_spec.rb | 6 +- spec/features/admin/admin_groups_spec.rb | 2 +- spec/features/admin/admin_hook_logs_spec.rb | 2 +- spec/features/admin/admin_hooks_spec.rb | 10 +- spec/features/admin/admin_mode/login_spec.rb | 14 +- spec/features/admin/admin_mode/logout_spec.rb | 4 +- spec/features/admin/admin_projects_spec.rb | 34 +- spec/features/admin/admin_runners_spec.rb | 10 +- .../admin/admin_sees_background_migrations_spec.rb | 8 +- spec/features/admin/admin_settings_spec.rb | 67 +- spec/features/admin/admin_users_spec.rb | 2 +- .../admin/admin_uses_repository_checks_spec.rb | 2 +- spec/features/admin/clusters/eks_spec.rb | 6 +- spec/features/admin/users/user_spec.rb | 10 +- spec/features/admin/users/users_spec.rb | 12 +- spec/features/boards/board_filters_spec.rb | 2 - spec/features/boards/boards_spec.rb | 90 +- .../features/callouts/registration_enabled_spec.rb | 31 +- spec/features/clusters/cluster_detail_page_spec.rb | 14 + spec/features/commits_spec.rb | 56 +- spec/features/dashboard/group_spec.rb | 2 +- spec/features/dashboard/issuables_counter_spec.rb | 80 +- spec/features/dashboard/milestones_spec.rb | 8 +- spec/features/dashboard/projects_spec.rb | 6 +- .../dashboard/todos/todos_filtering_spec.rb | 2 +- spec/features/dashboard/todos/todos_spec.rb | 4 +- .../dashboard/user_filters_projects_spec.rb | 10 +- spec/features/expand_collapse_diffs_spec.rb | 2 + spec/features/explore/topics_spec.rb | 4 +- spec/features/file_uploads/user_avatar_spec.rb | 2 +- spec/features/global_search_spec.rb | 4 + spec/features/groups/clusters/eks_spec.rb | 6 +- spec/features/groups/clusters/user_spec.rb | 12 +- spec/features/groups/container_registry_spec.rb | 2 + spec/features/groups/group_settings_spec.rb | 50 +- spec/features/groups/issues_spec.rb | 16 + spec/features/groups/labels/create_spec.rb | 2 +- spec/features/groups/labels/edit_spec.rb | 2 +- spec/features/groups/labels/sort_labels_spec.rb | 4 +- spec/features/groups/members/leave_group_spec.rb | 8 +- spec/features/groups/members/manage_groups_spec.rb | 97 +- .../features/groups/members/manage_members_spec.rb | 84 +- spec/features/groups/navbar_spec.rb | 25 + spec/features/groups/settings/ci_cd_spec.rb | 18 + spec/features/groups/settings/repository_spec.rb | 6 +- .../settings/user_searches_in_settings_spec.rb | 2 +- spec/features/groups_spec.rb | 77 +- spec/features/incidents/incident_details_spec.rb | 41 +- spec/features/incidents/incidents_list_spec.rb | 23 + .../features/incidents/user_views_incident_spec.rb | 8 +- spec/features/invites_spec.rb | 30 +- .../filtered_search/dropdown_assignee_spec.rb | 73 +- .../issues/filtered_search/filter_issues_spec.rb | 10 + spec/features/issues/form_spec.rb | 115 +- spec/features/issues/gfm_autocomplete_spec.rb | 22 + spec/features/issues/incident_issue_spec.rb | 2 +- spec/features/issues/issue_detail_spec.rb | 4 +- spec/features/issues/issue_header_spec.rb | 8 +- spec/features/issues/issue_sidebar_spec.rb | 7 + spec/features/issues/move_spec.rb | 2 +- .../issues/spam_akismet_issue_creation_spec.rb | 178 +++ spec/features/issues/spam_issues_spec.rb | 188 ---- .../user_creates_branch_and_merge_request_spec.rb | 4 +- spec/features/issues/user_creates_issue_spec.rb | 4 +- spec/features/issues/user_sorts_issues_spec.rb | 8 +- spec/features/issues/user_views_issue_spec.rb | 2 +- spec/features/jira_connect/subscriptions_spec.rb | 11 +- spec/features/labels_hierarchy_spec.rb | 58 +- spec/features/markdown/copy_as_gfm_spec.rb | 9 +- spec/features/markdown/keyboard_shortcuts_spec.rb | 8 + spec/features/markdown/sandboxed_mermaid_spec.rb | 2 +- .../user_edits_assignees_sidebar_spec.rb | 174 ++- ...lves_diff_notes_and_discussions_resolve_spec.rb | 8 +- .../user_sees_avatar_on_diff_notes_spec.rb | 1 + spec/features/merge_request/user_sees_diff_spec.rb | 6 +- .../user_sees_merge_request_pipelines_spec.rb | 18 +- .../merge_request/user_sees_merge_widget_spec.rb | 5 +- .../user_sees_mini_pipeline_graph_spec.rb | 2 +- .../merge_request/user_sees_pipelines_spec.rb | 5 +- .../user_sees_suggest_pipeline_spec.rb | 3 - .../user_lists_merge_requests_spec.rb | 2 +- .../milestones/user_views_milestones_spec.rb | 4 +- spec/features/oauth_login_spec.rb | 16 +- spec/features/password_reset_spec.rb | 8 +- spec/features/profiles/account_spec.rb | 8 +- spec/features/profiles/chat_names_spec.rb | 4 +- spec/features/profiles/password_spec.rb | 12 +- .../user_visits_profile_preferences_page_spec.rb | 4 +- spec/features/profiles/user_visits_profile_spec.rb | 51 +- spec/features/projects/artifacts/file_spec.rb | 2 +- spec/features/projects/artifacts/raw_spec.rb | 2 +- .../artifacts/user_browses_artifacts_spec.rb | 2 +- spec/features/projects/blobs/blob_show_spec.rb | 618 +---------- spec/features/projects/blobs/edit_spec.rb | 2 +- .../user_views_pipeline_editor_button_spec.rb | 6 +- spec/features/projects/ci/editor_spec.rb | 10 +- spec/features/projects/ci/secure_files_spec.rb | 19 + spec/features/projects/cluster_agents_spec.rb | 2 +- spec/features/projects/clusters/eks_spec.rb | 8 +- spec/features/projects/clusters/gcp_spec.rb | 26 +- spec/features/projects/clusters/user_spec.rb | 12 +- spec/features/projects/clusters_spec.rb | 36 +- .../projects/commits/multi_view_diff_spec.rb | 85 ++ spec/features/projects/container_registry_spec.rb | 2 + .../environments/environment_metrics_spec.rb | 3 +- .../projects/environments/environments_spec.rb | 86 +- .../project_owner_creates_license_file_spec.rb | 20 +- ...to_create_license_file_in_empty_project_spec.rb | 8 +- .../projects/files/user_browses_files_spec.rb | 18 +- .../projects/files/user_browses_lfs_files_spec.rb | 19 +- .../projects/files/user_creates_directory_spec.rb | 6 +- .../projects/files/user_creates_files_spec.rb | 10 +- .../projects/files/user_deletes_files_spec.rb | 5 +- .../projects/files/user_edits_files_spec.rb | 119 +- .../projects/files/user_replaces_files_spec.rb | 11 +- spec/features/projects/fork_spec.rb | 195 ---- .../user_activates_issue_tracker_spec.rb | 6 +- .../user_activates_jetbrains_teamcity_ci_spec.rb | 2 +- .../integrations/user_activates_jira_spec.rb | 6 +- .../user_activates_slack_slash_command_spec.rb | 4 +- .../projects/jobs/user_browses_job_spec.rb | 7 +- .../projects/jobs/user_browses_jobs_spec.rb | 2 +- ...user_triggers_manual_job_with_variables_spec.rb | 2 +- spec/features/projects/jobs_spec.rb | 18 +- spec/features/projects/labels/sort_labels_spec.rb | 4 +- .../projects/members/group_members_spec.rb | 1 - .../features/projects/members/invite_group_spec.rb | 105 +- .../projects/members/member_leaves_project_spec.rb | 4 +- .../projects/members/user_requests_access_spec.rb | 6 +- spec/features/projects/navbar_spec.rb | 13 + spec/features/projects/new_project_spec.rb | 72 +- .../projects/pages/user_adds_domain_spec.rb | 2 +- .../pages/user_edits_lets_encrypt_settings_spec.rb | 4 +- spec/features/projects/pipeline_schedules_spec.rb | 12 +- spec/features/projects/pipelines/pipeline_spec.rb | 112 +- spec/features/projects/pipelines/pipelines_spec.rb | 130 +-- .../releases/user_views_edit_release_spec.rb | 2 +- spec/features/projects/remote_mirror_spec.rb | 2 +- .../projects/settings/registry_settings_spec.rb | 39 +- .../user_interacts_with_deploy_keys_spec.rb | 4 +- .../settings/user_manages_project_members_spec.rb | 23 - .../settings/user_renames_a_project_spec.rb | 6 +- .../settings/user_transfers_a_project_spec.rb | 6 +- .../projects/settings/webhooks_settings_spec.rb | 4 +- spec/features/projects/show/redirects_spec.rb | 8 +- .../show/user_interacts_with_stars_spec.rb | 2 +- .../show/user_sees_git_instructions_spec.rb | 2 +- .../features/projects/tags/user_views_tags_spec.rb | 2 +- spec/features/projects/tracings_spec.rb | 11 +- .../features/projects/user_creates_project_spec.rb | 6 +- spec/features/projects/wikis_spec.rb | 2 +- .../projects/blobs/balsamiq_spec.rb | 18 + .../blobs/blob_line_permalink_updater_spec.rb | 103 ++ .../projects/blobs/blob_show_spec.rb | 1154 ++++++++++++++++++++ .../projects/blobs/edit_spec.rb | 213 ++++ .../projects/blobs/shortcuts_blob_spec.rb | 45 + .../user_creates_new_blob_in_new_project_spec.rb | 63 ++ .../user_follows_pipeline_suggest_nudge_spec.rb | 80 ++ .../user_views_pipeline_editor_button_spec.rb | 46 + .../projects/files/editing_a_file_spec.rb | 34 + .../projects/files/find_file_keyboard_spec.rb | 42 + .../project_owner_creates_license_file_spec.rb | 72 ++ .../projects/files/user_browses_files_spec.rb | 377 +++++++ .../projects/files/user_browses_lfs_files_spec.rb | 86 ++ .../projects/files/user_deletes_files_spec.rb | 74 ++ .../projects/files/user_edits_files_spec.rb | 226 ++++ .../projects/files/user_replaces_files_spec.rb | 93 ++ .../features/search/user_searches_for_code_spec.rb | 125 ++- .../search/user_searches_for_issues_spec.rb | 2 +- .../user_searches_for_merge_requests_spec.rb | 2 +- .../search/user_searches_for_milestones_spec.rb | 4 +- .../search/user_searches_for_wiki_pages_spec.rb | 2 +- spec/features/static_site_editor_spec.rb | 11 +- spec/features/tags/developer_creates_tag_spec.rb | 8 +- spec/features/tags/developer_deletes_tag_spec.rb | 6 +- spec/features/tags/developer_updates_tag_spec.rb | 8 +- spec/features/tags/developer_views_tags_spec.rb | 20 +- spec/features/triggers_spec.rb | 2 + spec/features/unsubscribe_links_spec.rb | 8 +- spec/features/users/active_sessions_spec.rb | 6 +- spec/features/users/login_spec.rb | 71 +- spec/features/users/logout_spec.rb | 4 +- spec/features/users/show_spec.rb | 2 +- spec/features/users/signup_spec.rb | 16 +- spec/features/users/terms_spec.rb | 10 +- spec/finders/issues_finder_spec.rb | 45 +- spec/finders/pending_todos_finder_spec.rb | 10 + spec/finders/personal_access_tokens_finder_spec.rb | 21 +- .../members/effective_access_level_finder_spec.rb | 41 +- spec/finders/projects/topics_finder_spec.rb | 6 +- .../finders/releases/group_releases_finder_spec.rb | 204 ++++ spec/finders/releases_finder_spec.rb | 13 - spec/fixtures/api/schemas/deployment.json | 3 +- spec/fixtures/api/schemas/environment.json | 2 + spec/fixtures/api/schemas/list.json | 2 +- .../api/schemas/public_api/v4/deploy_token.json | 10 +- .../api/schemas/public_api/v4/system_hook.json | 24 + .../api/schemas/public_api/v4/system_hooks.json | 9 + .../emails/missing_delivered_to_header.eml | 35 + .../emails/service_desk_reply_to_and_from.eml | 28 + spec/fixtures/emails/valid_note_on_issuable.eml | 2 +- .../error_tracking/php_empty_transaction.json | 45 + .../markdown/markdown_golden_master_examples.yml | 40 +- .../master/gl-common-scanning-report.json | 140 +++ spec/frontend/__helpers__/flush_promises.js | 1 + spec/frontend/__helpers__/mocks/axios_utils.js | 2 + spec/frontend/__helpers__/vuex_action_helper.js | 1 + .../__snapshots__/expires_at_field_spec.js.snap | 45 +- .../components/expires_at_field_spec.js | 18 +- .../__snapshots__/delete_application_spec.js.snap | 20 + .../components/delete_application_spec.js | 69 ++ .../__snapshots__/remove_avatar_spec.js.snap | 20 + .../admin/topics/components/remove_avatar_spec.js | 85 ++ .../admin/users/components/user_actions_spec.js | 20 +- spec/frontend/api_spec.js | 22 + .../components/navigation_popover_spec.js | 86 ++ spec/frontend/authentication/webauthn/util_spec.js | 17 +- .../__snapshots__/blob_header_spec.js.snap | 3 +- spec/frontend/blob/components/blob_header_spec.js | 15 + spec/frontend/blob/components/mock_data.js | 2 +- spec/frontend/blob/csv/csv_viewer_spec.js | 26 +- spec/frontend/blob_edit/blob_bundle_spec.js | 6 +- spec/frontend/boards/components/board_form_spec.js | 14 +- .../boards/components/boards_selector_spec.js | 1 + spec/frontend/boards/mock_data.js | 31 + spec/frontend/boards/stores/actions_spec.js | 72 +- spec/frontend/boards/stores/mutations_spec.js | 40 +- .../frontend/branches/ajax_loading_spinner_spec.js | 32 - .../components/secure_files_list_spec.js | 139 +++ spec/frontend/ci_secure_files/mock_data.js | 18 + .../agents/components/create_token_button_spec.js | 257 +++++ .../clusters/agents/components/token_table_spec.js | 43 +- .../__snapshots__/new_cluster_spec.js.snap | 7 +- .../clusters/components/new_cluster_spec.js | 4 +- spec/frontend/clusters/mock_data.js | 57 + .../clusters_list/components/agent_table_spec.js | 17 +- .../clusters_list/components/agent_token_spec.js | 76 ++ .../components/available_agents_dropwdown_spec.js | 63 +- .../components/clusters_actions_spec.js | 122 ++- .../components/clusters_empty_state_spec.js | 4 +- .../components/clusters_main_view_spec.js | 149 ++- .../clusters_list/components/clusters_spec.js | 10 +- .../components/install_agent_modal_spec.js | 75 +- .../code_navigation/components/app_spec.js | 19 +- .../components/__snapshots__/step_spec.js.snap | 174 --- .../components/step_spec.js | 156 --- .../components/content_editor_alert_spec.js | 17 +- .../components/content_editor_spec.js | 77 +- .../components/editor_state_observer_spec.js | 63 +- .../components/loading_indicator_spec.js | 71 ++ .../components/toolbar_button_spec.js | 2 + .../components/toolbar_link_button_spec.js | 2 + .../components/toolbar_text_style_dropdown_spec.js | 2 + .../content_editor/extensions/attachment_spec.js | 17 +- .../extensions/paste_markdown_spec.js | 127 +++ .../markdown_processing_spec_helper.js | 2 +- .../content_editor/services/content_editor_spec.js | 39 +- .../services/markdown_deserializer_spec.js | 62 ++ .../services/markdown_serializer_spec.js | 13 +- .../services/markdown_sourcemap_spec.js | 29 +- spec/frontend/content_editor/test_utils.js | 20 + spec/frontend/contributors/store/getters_spec.js | 2 +- .../total_time_component_spec.js.snap | 28 - .../__snapshots__/total_time_spec.js.snap | 28 + spec/frontend/cycle_analytics/base_spec.js | 3 + .../limit_warning_component_spec.js | 41 - .../cycle_analytics/total_time_component_spec.js | 45 - spec/frontend/cycle_analytics/total_time_spec.js | 45 + .../cycle_analytics/value_stream_filters_spec.js | 54 + .../cycle_analytics/value_stream_metrics_spec.js | 6 +- .../deploy_tokens/components/revoke_button_spec.js | 5 - spec/frontend/diffs/components/diff_view_spec.js | 12 +- .../diffs/components/hidden_files_warning_spec.js | 18 +- .../diffs/store/getters_versions_dropdowns_spec.js | 37 +- .../dirty_submit/dirty_submit_form_spec.js | 33 + .../editor/source_editor_ci_schema_ext_spec.js | 9 +- spec/frontend/environment.js | 1 + .../environments/delete_environment_modal_spec.js | 31 + .../environments/enable_review_app_modal_spec.js | 14 +- .../environments/environment_actions_spec.js | 17 +- .../environments/environment_folder_spec.js | 132 +++ .../frontend/environments/environment_item_spec.js | 2 +- .../frontend/environments/environments_app_spec.js | 497 +++++---- .../environments/graphql/resolvers_spec.js | 31 +- .../environments/new_environment_folder_spec.js | 100 -- .../environments/new_environment_item_spec.js | 2 +- .../environments/new_environments_app_spec.js | 329 ------ .../components/error_details_spec.js | 6 +- .../components/error_tracking_list_spec.js | 78 +- .../error_tracking_settings/components/app_spec.js | 141 ++- spec/frontend/fixtures/merge_requests.rb | 19 + spec/frontend/fixtures/runner.rb | 48 +- spec/frontend/google_cloud/components/app_spec.js | 5 +- .../components/gcp_regions_form_spec.js | 59 + .../components/gcp_regions_list_spec.js | 79 ++ spec/frontend/google_cloud/components/home_spec.js | 3 + .../google_cloud/components/revoke_oauth_spec.js | 47 + .../components/service_accounts_form_spec.js | 2 +- spec/frontend/google_tag_manager/index_spec.js | 44 +- spec/frontend/graphql_shared/utils_spec.js | 2 +- .../header_search_autocomplete_items_spec.js | 13 +- spec/frontend/header_search/store/actions_spec.js | 16 +- spec/frontend/header_search/store/getters_spec.js | 100 +- .../frontend/header_search/store/mutations_spec.js | 4 + .../ide/components/file_templates/bar_spec.js | 8 +- .../ide/components/new_dropdown/modal_spec.js | 4 +- spec/frontend/ide/components/repo_editor_spec.js | 58 +- .../components/import_projects_table_spec.js | 2 +- .../incidents/components/incidents_list_spec.js | 116 +- spec/frontend/incidents/mocks/incidents.json | 8 +- .../edit/components/active_checkbox_spec.js | 9 + .../edit/components/integration_form_spec.js | 144 ++- .../edit/components/sections/connection_spec.js | 77 ++ .../edit/components/sections/jira_issues_spec.js | 34 + .../edit/components/sections/jira_trigger_spec.js | 34 + .../edit/components/trigger_fields_spec.js | 6 +- spec/frontend/integrations/edit/mock_data.js | 8 + .../integrations/edit/store/getters_spec.js | 21 +- .../components/invite_groups_modal_spec.js | 95 +- .../components/invite_members_modal_spec.js | 2 + .../components/invite_modal_base_spec.js | 36 +- .../components/related_issues_block_spec.js | 6 + .../components/related_issues_list_spec.js | 5 + .../issues/list/components/issues_list_app_spec.js | 29 +- spec/frontend/issues/list/utils_spec.js | 37 +- .../issues/show/components/description_spec.js | 83 +- .../issues/show/components/header_actions_spec.js | 71 +- .../components/incidents/incident_tabs_spec.js | 10 +- .../subscriptions/components/app_spec.js | 56 +- .../components/sign_in_button_spec.js | 58 - .../components/sign_in_legacy_button_spec.js | 58 + .../components/sign_in_oauth_button_spec.js | 204 ++++ .../subscriptions/components/user_link_spec.js | 45 +- .../subscriptions/pages/sign_in_spec.js | 111 +- .../jira_connect/subscriptions/pkce_spec.js | 48 + spec/frontend/jobs/components/job_app_spec.js | 1 - .../jobs/components/job_log_controllers_spec.js | 24 - .../components/job_sidebar_retry_button_spec.js | 13 +- spec/frontend/jobs/components/sidebar_spec.js | 43 +- .../jobs/components/stages_dropdown_spec.js | 209 ++-- .../components/table/graphql/cache_config_spec.js | 67 ++ .../jobs/components/table/job_table_app_spec.js | 59 +- spec/frontend/jobs/mock_data.js | 84 +- spec/frontend/lib/utils/array_utility_spec.js | 13 + spec/frontend/lib/utils/common_utils_spec.js | 25 - .../lib/utils/ignore_while_pending_spec.js | 136 +++ spec/frontend/lib/utils/resize_observer_spec.js | 41 +- spec/frontend/lib/utils/text_markdown_spec.js | 128 ++- spec/frontend/lib/utils/url_utility_spec.js | 53 +- spec/frontend/loading_icon_for_legacy_js_spec.js | 43 + .../action_buttons/user_action_buttons_spec.js | 10 +- .../members_filtered_search_bar_spec.js | 54 +- .../components/merge_conflict_resolver_app_spec.js | 33 +- spec/frontend/merge_request_tabs_spec.js | 1 + .../components/charts/time_series_spec.js | 15 - spec/frontend/notes/components/note_header_spec.js | 20 +- .../components/details_page/delete_alert_spec.js | 21 + .../components/details_page/details_header_spec.js | 30 +- .../container_registry/explorer/mock_data.js | 10 + .../explorer/pages/details_spec.js | 32 + .../container_registry/explorer/pages/list_spec.js | 106 +- .../__snapshots__/settings_form_spec.js.snap | 2 +- .../settings/components/settings_form_spec.js | 26 +- .../project/settings/graphql/cache_updated_spec.js | 25 +- .../projects/forks/new/components/app_spec.js | 13 +- .../forks/new/components/fork_form_spec.js | 28 +- .../new/components/fork_groups_list_item_spec.js | 73 -- .../forks/new/components/fork_groups_list_spec.js | 123 --- .../__snapshots__/learn_gitlab_spec.js.snap | 14 - .../components/learn_gitlab_section_link_spec.js | 8 +- .../learn_gitlab/components/learn_gitlab_spec.js | 12 - .../projects/learn_gitlab/components/mock_data.js | 1 + .../pages/projects/pages_domains/form_spec.js | 82 ++ .../shared/wikis/components/wiki_form_spec.js | 14 + .../components/detailed_metric_spec.js | 24 +- .../components/performance_bar_app_spec.js | 11 + .../components/request_selector_spec.js | 31 - spec/frontend/performance_bar/index_spec.js | 30 +- spec/frontend/persistent_user_callout_spec.js | 100 +- .../components/commit/commit_form_spec.js | 23 + .../components/commit/commit_section_spec.js | 1 + .../drawer/pipeline_editor_drawer_spec.js | 27 +- .../components/editor/text_editor_spec.js | 11 +- .../components/pipeline_editor_tabs_spec.js | 49 +- .../pipeline_editor/pipeline_editor_app_spec.js | 86 +- .../pipeline_wizard/components/input_spec.js | 79 ++ .../pipeline_wizard/components/step_spec.js | 227 ++++ .../components/widgets/list_spec.js | 212 ++++ .../pipeline_wizard/components/widgets_spec.js | 49 + .../pipeline_wizard/components/wrapper_spec.js | 250 +++++ spec/frontend/pipeline_wizard/mock/yaml.js | 85 ++ .../pipeline_wizard/pipeline_wizard_spec.js | 102 ++ spec/frontend/pipeline_wizard/validators_spec.js | 22 + .../pipelines/components/jobs/jobs_app_spec.js | 29 +- .../components/pipelines_filtered_search_spec.js | 2 + spec/frontend/pipelines/header_component_spec.js | 25 +- spec/frontend/pipelines/pipeline_labels_spec.js | 168 +++ spec/frontend/pipelines/pipeline_url_spec.js | 216 +--- .../pipelines/pipelines_ci_templates_spec.js | 107 +- spec/frontend/pipelines/pipelines_spec.js | 76 +- spec/frontend/pipelines/pipelines_table_spec.js | 63 +- .../tokens/pipeline_branch_name_token_spec.js | 47 + .../protected_branch_create_spec.js | 114 ++ .../protected_branch_edit_spec.js | 92 +- .../__snapshots__/ref_selector_spec.js.snap | 10 + spec/frontend/ref/stores/mutations_spec.js | 8 + .../releases/components/asset_links_form_spec.js | 4 +- .../releases/stores/modules/detail/getters_spec.js | 23 + .../components/blob_content_viewer_spec.js | 106 +- .../repository/components/blob_edit_spec.js | 100 -- .../components/blob_viewers/audio_viewer_spec.js | 23 + .../components/blob_viewers/csv_viewer_spec.js | 27 + .../blob_viewers/download_viewer_spec.js | 11 +- .../components/blob_viewers/lfs_viewer_spec.js | 11 +- .../components/blob_viewers/pdf_viewer_spec.js | 26 +- .../repository/components/breadcrumbs_spec.js | 14 + spec/frontend/repository/mock_data.js | 17 +- .../admin_runner_edit_app_spec.js | 4 +- .../admin_runner_show_app_spec.js | 4 +- .../runner/admin_runners/admin_runners_app_spec.js | 74 +- .../components/cells/runner_actions_cell_spec.js | 223 +--- .../registration_token_reset_dropdown_item_spec.js | 2 +- .../runner/components/runner_delete_button_spec.js | 233 ++++ .../frontend/runner/components/runner_jobs_spec.js | 4 +- .../frontend/runner/components/runner_list_spec.js | 40 +- .../runner/components/runner_pagination_spec.js | 18 +- .../runner/components/runner_pause_button_spec.js | 24 +- .../runner/components/runner_projects_spec.js | 4 +- .../runner/components/runner_update_form_spec.js | 2 +- .../runner/group_runners/group_runners_app_spec.js | 83 +- spec/frontend/runner/mock_data.js | 24 +- spec/frontend/search/topbar/components/app_spec.js | 33 +- .../components/feature_card_spec.js | 2 +- .../components/training_provider_list_spec.js | 189 +++- .../graphql/cache_utils_spec.js | 108 ++ spec/frontend/security_configuration/mock_data.js | 63 +- .../assignees/sidebar_assignees_widget_spec.js | 27 +- .../assignees/sidebar_participant_spec.js | 41 +- .../components/attention_requested_toggle_spec.js | 62 +- .../components/incidents/escalation_status_spec.js | 52 + .../components/incidents/escalation_utils_spec.js | 18 + .../sidebar/components/incidents/mock_data.js | 39 + .../incidents/sidebar_escalation_status_spec.js | 207 ++++ spec/frontend/sidebar/mock_data.js | 29 +- spec/frontend/sidebar/sidebar_assignees_spec.js | 31 +- spec/frontend/sidebar/sidebar_mediator_spec.js | 3 + .../__snapshots__/snippet_blob_edit_spec.js.snap | 1 + .../snippet_description_edit_spec.js.snap | 1 + .../terraform/components/empty_state_spec.js | 2 +- spec/frontend/test_setup.js | 1 + spec/frontend/toggle_buttons_spec.js | 115 -- spec/frontend/toggles/index_spec.js | 6 + spec/frontend/tracking/tracking_spec.js | 27 + spec/frontend/users_select/index_spec.js | 35 + .../components/extensions/child_content_spec.js | 40 + .../components/mr_widget_rebase_spec.js | 47 +- .../components/mr_widget_related_links_spec.js | 29 +- .../components/states/mr_widget_conflicts_spec.js | 6 +- .../states/mr_widget_ready_to_merge_spec.js | 114 +- .../extentions/accessibility/index_spec.js | 8 +- .../extentions/code_quality/index_spec.js | 145 +++ .../extentions/code_quality/mock_data.js | 87 ++ .../mr_widget_how_to_merge_modal_spec.js | 4 +- .../vue_mr_widget/mr_widget_options_spec.js | 2 + .../__snapshots__/content_transition_spec.js.snap | 41 + .../components/color_picker/color_picker_spec.js | 13 + .../components/content_transition_spec.js | 109 ++ .../filtered_search_bar/tokens/base_token_spec.js | 225 +++- .../vue_shared/components/markdown/field_spec.js | 41 +- .../vue_shared/components/markdown/header_spec.js | 9 + .../__snapshots__/noteable_warning_spec.js.snap | 10 +- .../components/notes/noteable_warning_spec.js | 5 +- .../paginated_table_with_search_and_tabs_spec.js | 2 +- .../runner_aws_deployments_modal_spec.js.snap | 216 ++-- .../runner_aws_deployments_modal_spec.js | 50 +- .../components/source_viewer/source_viewer_spec.js | 6 + .../components/source_viewer/utils_spec.js | 27 +- .../user_avatar/user_avatar_image_new_spec.js | 127 +++ .../user_avatar/user_avatar_image_old_spec.js | 122 +++ .../user_avatar/user_avatar_image_spec.js | 96 +- .../user_avatar/user_avatar_link_new_spec.js | 102 ++ .../user_avatar/user_avatar_link_old_spec.js | 102 ++ .../user_avatar/user_avatar_link_spec.js | 135 +-- .../components/user_popover/user_popover_spec.js | 6 + .../vue_shared/components/user_select_spec.js | 61 +- .../vue_shared/components/web_ide_link_spec.js | 40 + .../show/components/issuable_title_spec.js | 6 +- .../work_items/components/work_item_detail_spec.js | 40 + spec/frontend/work_items/mock_data.js | 60 +- .../work_items/pages/create_work_item_spec.js | 24 +- .../work_items/pages/work_item_root_spec.js | 45 +- spec/frontend/work_items/router_spec.js | 1 + .../frontend_integration/ide/helpers/ide_helper.js | 2 +- .../boards/issues/issue_move_list_spec.rb | 18 +- spec/graphql/mutations/ci/runner/delete_spec.rb | 72 +- spec/graphql/mutations/ci/runner/update_spec.rb | 6 +- .../mutations/release_asset_links/create_spec.rb | 16 +- .../graphql/mutations/saved_replies/create_spec.rb | 46 + .../graphql/mutations/saved_replies/update_spec.rb | 47 + .../usage_trends/measurements_resolver_spec.rb | 12 +- spec/graphql/resolvers/base_resolver_spec.rb | 10 +- spec/graphql/resolvers/blobs_resolver_spec.rb | 28 +- .../resolvers/board_list_issues_resolver_spec.rb | 12 +- .../graphql/resolvers/board_lists_resolver_spec.rb | 7 +- spec/graphql/resolvers/board_resolver_spec.rb | 4 +- spec/graphql/resolvers/ci/config_resolver_spec.rb | 40 +- .../resolvers/ci/job_token_scope_resolver_spec.rb | 6 +- .../resolvers/concerns/resolves_pipelines_spec.rb | 40 +- .../design_at_version_resolver_spec.rb | 12 +- .../design_management/design_resolver_spec.rb | 12 +- .../version/design_at_version_resolver_spec.rb | 6 +- .../version_in_collection_resolver_spec.rb | 6 +- .../design_management/version_resolver_spec.rb | 6 +- .../design_management/versions_resolver_spec.rb | 6 +- .../resolvers/environments_resolver_spec.rb | 8 +- .../resolvers/group_issues_resolver_spec.rb | 6 +- .../resolvers/group_labels_resolver_spec.rb | 6 +- .../notification_email_resolver_spec.rb | 46 + .../resolvers/group_milestones_resolver_spec.rb | 24 +- .../resolvers/issue_status_counts_resolver_spec.rb | 6 +- spec/graphql/resolvers/issues_resolver_spec.rb | 35 +- .../kas/agent_configurations_resolver_spec.rb | 6 +- spec/graphql/resolvers/labels_resolver_spec.rb | 4 +- .../resolvers/namespace_projects_resolver_spec.rb | 4 +- .../resolvers/package_pipelines_resolver_spec.rb | 24 +- .../resolvers/paginated_tree_resolver_spec.rb | 6 +- .../resolvers/project_milestones_resolver_spec.rb | 24 +- .../resolvers/project_pipeline_resolver_spec.rb | 10 +- spec/graphql/resolvers/project_resolver_spec.rb | 4 +- .../projects/jira_projects_resolver_spec.rb | 13 +- .../resolvers/projects/snippets_resolver_spec.rb | 6 +- spec/graphql/resolvers/snippets_resolver_spec.rb | 6 +- spec/graphql/resolvers/timelog_resolver_spec.rb | 49 +- spec/graphql/resolvers/topics_resolver_spec.rb | 6 +- .../user_discussions_count_resolver_spec.rb | 4 +- .../resolvers/user_notes_count_resolver_spec.rb | 12 +- spec/graphql/resolvers/user_resolver_spec.rb | 14 +- spec/graphql/resolvers/users_resolver_spec.rb | 13 +- spec/graphql/resolvers/work_item_resolver_spec.rb | 43 + .../resolvers/work_items/types_resolver_spec.rb | 41 +- .../types/alert_management/alert_type_spec.rb | 3 + spec/graphql/types/base_enum_spec.rb | 8 +- spec/graphql/types/base_field_spec.rb | 28 +- spec/graphql/types/ci/runner_web_url_edge_spec.rb | 13 + spec/graphql/types/commit_type_spec.rb | 2 + .../container_repository_details_type_spec.rb | 2 +- .../types/design_management/design_type_spec.rb | 4 +- spec/graphql/types/global_id_type_spec.rb | 14 +- spec/graphql/types/group_member_type_spec.rb | 2 +- spec/graphql/types/merge_request_type_spec.rb | 4 +- .../types/merge_requests/assignee_type_spec.rb | 7 + .../types/merge_requests/author_type_spec.rb | 7 + .../types/merge_requests/participant_type_spec.rb | 7 + .../types/merge_requests/reviewer_type_spec.rb | 48 +- .../types/projects/base_service_type_spec.rb | 2 +- .../types/projects/jira_service_type_spec.rb | 2 +- .../types/projects/service_type_enum_spec.rb | 55 + spec/graphql/types/projects/service_type_spec.rb | 2 +- spec/graphql/types/projects/services_enum_spec.rb | 13 - spec/graphql/types/query_type_spec.rb | 1 + spec/graphql/types/repository/blob_type_spec.rb | 4 + spec/graphql/types/saved_reply_type_spec.rb | 13 + spec/graphql/types/todo_type_spec.rb | 2 +- spec/graphql/types/todoable_interface_spec.rb | 29 + spec/graphql/types/user_type_spec.rb | 59 + spec/graphql/types/work_item_id_type_spec.rb | 51 + spec/graphql/types/work_item_type_spec.rb | 17 + spec/helpers/application_helper_spec.rb | 108 ++ spec/helpers/application_settings_helper_spec.rb | 23 +- spec/helpers/blob_helper_spec.rb | 93 -- spec/helpers/broadcast_messages_helper_spec.rb | 85 +- spec/helpers/ci/pipelines_helper_spec.rb | 59 + spec/helpers/clusters_helper_spec.rb | 137 ++- spec/helpers/commits_helper_spec.rb | 25 + .../container_expiration_policies_helper_spec.rb | 23 +- spec/helpers/container_registry_helper_spec.rb | 16 +- spec/helpers/deploy_tokens_helper_spec.rb | 20 + spec/helpers/explore_helper_spec.rb | 29 + spec/helpers/groups/crm_settings_helper_spec.rb | 40 +- spec/helpers/icons_helper_spec.rb | 32 +- spec/helpers/integrations_helper_spec.rb | 27 +- spec/helpers/invite_members_helper_spec.rb | 11 - spec/helpers/issues_helper_spec.rb | 8 +- spec/helpers/jira_connect_helper_spec.rb | 46 + spec/helpers/labels_helper_spec.rb | 8 +- spec/helpers/learn_gitlab_helper_spec.rb | 79 +- spec/helpers/listbox_helper_spec.rb | 9 +- spec/helpers/markup_helper_spec.rb | 32 +- spec/helpers/merge_requests_helper_spec.rb | 33 +- spec/helpers/nav/top_nav_helper_spec.rb | 2 - spec/helpers/notify_helper_spec.rb | 1 - spec/helpers/packages_helper_spec.rb | 165 +-- spec/helpers/preferences_helper_spec.rb | 24 + .../helpers/projects/cluster_agents_helper_spec.rb | 17 +- .../helpers/projects/error_tracking_helper_spec.rb | 53 +- spec/helpers/projects_helper_spec.rb | 22 +- .../routing/pseudonymization_helper_spec.rb | 26 +- spec/helpers/sessions_helper_spec.rb | 4 +- spec/helpers/sorting_helper_spec.rb | 12 + spec/helpers/storage_helper_spec.rb | 43 +- spec/helpers/tree_helper_spec.rb | 2 + spec/helpers/users/callouts_helper_spec.rb | 19 +- spec/helpers/web_ide_button_helper_spec.rb | 45 + spec/helpers/whats_new_helper_spec.rb | 6 +- spec/lib/api/entities/ci/job_artifact_file_spec.rb | 18 + .../api/entities/ci/job_request/dependency_spec.rb | 27 + spec/lib/api/entities/user_spec.rb | 57 + spec/lib/api/entities/wiki_page_spec.rb | 56 + spec/lib/api/helpers_spec.rb | 20 + spec/lib/atlassian/jira_connect/client_spec.rb | 22 +- .../jira_connect/serializers/build_entity_spec.rb | 2 +- .../serializers/deployment_entity_spec.rb | 23 +- spec/lib/atlassian/jira_connect_spec.rb | 29 + spec/lib/backup/artifacts_spec.rb | 2 +- spec/lib/backup/database_spec.rb | 47 +- spec/lib/backup/files_spec.rb | 38 +- spec/lib/backup/gitaly_backup_spec.rb | 120 +- spec/lib/backup/gitaly_rpc_backup_spec.rb | 23 +- spec/lib/backup/lfs_spec.rb | 2 +- spec/lib/backup/manager_spec.rb | 844 ++++++++------ spec/lib/backup/object_backup_spec.rb | 2 +- spec/lib/backup/pages_spec.rb | 2 +- spec/lib/backup/repositories_spec.rb | 53 +- spec/lib/backup/task_spec.rb | 27 + spec/lib/backup/uploads_spec.rb | 2 +- spec/lib/banzai/filter/front_matter_filter_spec.rb | 53 +- spec/lib/banzai/filter/image_link_filter_spec.rb | 62 +- .../issuable_reference_expansion_filter_spec.rb | 2 +- .../filter/reference_redactor_filter_spec.rb | 3 +- .../external_issue_reference_filter_spec.rb | 19 + .../references/label_reference_filter_spec.rb | 2 +- spec/lib/banzai/filter/task_list_filter_spec.rb | 13 + spec/lib/banzai/reference_redactor_spec.rb | 3 +- spec/lib/bulk_imports/clients/http_spec.rb | 4 +- .../common/pipelines/labels_pipeline_spec.rb | 2 +- spec/lib/container_registry/client_spec.rb | 100 +- .../container_registry/gitlab_api_client_spec.rb | 52 +- spec/lib/container_registry/registry_spec.rb | 7 +- spec/lib/feature_spec.rb | 4 +- .../analytics/cycle_analytics/median_spec.rb | 4 +- spec/lib/gitlab/auth/ldap/access_spec.rb | 2 +- spec/lib/gitlab/auth/ldap/authentication_spec.rb | 2 +- spec/lib/gitlab/auth/o_auth/provider_spec.rb | 8 +- spec/lib/gitlab/auth/o_auth/user_spec.rb | 132 ++- spec/lib/gitlab/auth/request_authenticator_spec.rb | 32 + .../backfill_issue_search_data_spec.rb | 57 + ...fill_member_namespace_for_group_members_spec.rb | 50 + .../backfill_snippet_repositories_spec.rb | 12 + ...t_namespace_per_group_batching_strategy_spec.rb | 2 +- .../batching_strategies/base_strategy_spec.rb | 15 + .../primary_key_batching_strategy_spec.rb | 4 +- .../encrypt_integration_properties_spec.rb | 63 ++ .../background_migration/job_coordinator_spec.rb | 60 +- ...l_namespace_project_maintainer_to_owner_spec.rb | 82 ++ .../nullify_orphan_runner_id_on_ci_builds_spec.rb | 49 + .../remove_all_trace_expiration_dates_spec.rb | 53 + ...ners_token_encrypted_values_on_projects_spec.rb | 37 + ...ate_ci_runners_token_values_on_projects_spec.rb | 37 + spec/lib/gitlab/ci/build/policy/refs_spec.rb | 23 +- spec/lib/gitlab/ci/config/entry/bridge_spec.rb | 24 + .../ci/config/entry/include/rules/rule_spec.rb | 4 +- spec/lib/gitlab/ci/config/entry/job_spec.rb | 24 +- spec/lib/gitlab/ci/config/entry/policy_spec.rb | 48 +- .../config/entry/reports/coverage_report_spec.rb | 57 + spec/lib/gitlab/ci/config/entry/reports_spec.rb | 47 +- spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb | 24 +- .../gitlab/ci/config/entry/trigger/forward_spec.rb | 64 ++ spec/lib/gitlab/ci/config/entry/trigger_spec.rb | 92 +- .../gitlab/ci/config/external/file/local_spec.rb | 10 + .../gitlab/ci/config/yaml/tags/reference_spec.rb | 17 + .../gitlab/ci/parsers/coverage/cobertura_spec.rb | 702 +----------- .../ci/parsers/coverage/sax_document_spec.rb | 725 ++++++++++++ spec/lib/gitlab/ci/parsers/security/common_spec.rb | 494 +++++---- .../security/validators/schema_validator_spec.rb | 63 +- .../chain/cancel_pending_pipelines_spec.rb | 75 +- spec/lib/gitlab/ci/pipeline/chain/create_spec.rb | 16 - spec/lib/gitlab/ci/reports/security/report_spec.rb | 10 + .../ci/status/build/waiting_for_approval_spec.rb | 49 +- .../templates/auto_devops_gitlab_ci_yaml_spec.rb | 22 +- spec/lib/gitlab/ci/variables/builder/group_spec.rb | 209 ++++ spec/lib/gitlab/ci/variables/builder_spec.rb | 100 +- spec/lib/gitlab/ci/yaml_processor/dag_spec.rb | 10 +- spec/lib/gitlab/ci/yaml_processor_spec.rb | 38 +- spec/lib/gitlab/color_spec.rb | 132 +++ spec/lib/gitlab/config/entry/validators_spec.rb | 43 + spec/lib/gitlab/current_settings_spec.rb | 15 + .../async_indexes/migration_helpers_spec.rb | 190 ++-- .../background_migration/batched_job_spec.rb | 112 +- .../batched_job_transition_log_spec.rb | 2 + .../batched_migration_runner_spec.rb | 23 + .../background_migration/batched_migration_spec.rb | 10 +- .../batched_migration_wrapper_spec.rb | 1 + spec/lib/gitlab/database/each_database_spec.rb | 82 +- .../database/load_balancing/configuration_spec.rb | 7 - .../gitlab/database/load_balancing/setup_spec.rb | 2 + spec/lib/gitlab/database/load_balancing_spec.rb | 12 + .../restrict_gitlab_schema_spec.rb | 561 ++++++++++ .../background_migration_helpers_spec.rb | 12 +- .../migrations/observers/query_details_spec.rb | 2 +- .../migrations/observers/query_log_spec.rb | 2 +- .../observers/transaction_duration_spec.rb | 2 +- spec/lib/gitlab/database/migrations/runner_spec.rb | 18 + .../migrations/test_background_runner_spec.rb | 120 ++ spec/lib/gitlab/database/partitioning_spec.rb | 14 + spec/lib/gitlab/database/query_analyzer_spec.rb | 17 + .../restrict_allowed_schemas_spec.rb | 161 +++ .../v1/rename_projects_spec.rb | 2 +- .../gitlab/database/transaction/context_spec.rb | 20 + .../gitlab/database/transaction/observer_spec.rb | 67 +- spec/lib/gitlab/database/type/color_spec.rb | 41 + spec/lib/gitlab/database_spec.rb | 71 +- spec/lib/gitlab/diff/file_spec.rb | 44 +- .../diff/rendered/notebook/diff_file_spec.rb | 107 ++ spec/lib/gitlab/email/attachment_uploader_spec.rb | 24 + .../email/handler/create_issue_handler_spec.rb | 31 +- .../email/handler/service_desk_handler_spec.rb | 15 +- spec/lib/gitlab/email/receiver_spec.rb | 34 +- .../processor/grpc_error_processor_spec.rb | 142 ++- .../processor/sidekiq_processor_spec.rb | 126 ++- spec/lib/gitlab/error_tracking_spec.rb | 131 ++- spec/lib/gitlab/etag_caching/middleware_spec.rb | 3 +- spec/lib/gitlab/etag_caching/router/rails_spec.rb | 136 +++ .../lib/gitlab/etag_caching/router/restful_spec.rb | 130 --- spec/lib/gitlab/etag_caching/router_spec.rb | 2 +- spec/lib/gitlab/experiment/rollout/feature_spec.rb | 19 +- .../experimentation/controller_concern_spec.rb | 2 +- spec/lib/gitlab/experimentation/experiment_spec.rb | 2 +- spec/lib/gitlab/fips_spec.rb | 51 + .../form_builders/gitlab_ui_form_builder_spec.rb | 23 + spec/lib/gitlab/git/wiki_spec.rb | 16 +- spec/lib/gitlab/git_access_snippet_spec.rb | 32 - .../gitlab/gitaly_client/operation_service_spec.rb | 163 ++- .../gitaly_client/repository_service_spec.rb | 42 + .../importer/diff_note_importer_spec.rb | 22 +- .../importer/pull_requests_importer_spec.rb | 9 +- .../github_import/parallel_scheduling_spec.rb | 64 +- .../graphql/loaders/batch_commit_loader_spec.rb | 48 + spec/lib/gitlab/graphql/markdown_field_spec.rb | 12 +- spec/lib/gitlab/graphql/mount_mutation_spec.rb | 8 +- spec/lib/gitlab/harbor/client_spec.rb | 28 + spec/lib/gitlab/health_checks/db_check_spec.rb | 17 +- spec/lib/gitlab/highlight_spec.rb | 76 +- spec/lib/gitlab/hook_data/issue_builder_spec.rb | 8 + spec/lib/gitlab/import_export/all_models.yml | 8 + .../base/relation_object_saver_spec.rb | 132 +++ .../gitlab/import_export/command_line_util_spec.rb | 153 +++ .../lib/gitlab/import_export/file_importer_spec.rb | 25 +- .../import_export/group/object_builder_spec.rb | 12 - .../import_export/group/tree_restorer_spec.rb | 282 ++--- .../json/streaming_serializer_spec.rb | 22 +- .../import_export/project/relation_factory_spec.rb | 16 + .../import_export/project/tree_restorer_spec.rb | 32 +- .../gitlab/import_export/safe_model_attributes.yml | 1 + spec/lib/gitlab/integrations/sti_type_spec.rb | 114 -- spec/lib/gitlab/json_cache_spec.rb | 88 +- .../gitlab/kubernetes/kubeconfig/template_spec.rb | 45 + spec/lib/gitlab/mail_room/authenticator_spec.rb | 20 +- spec/lib/gitlab/mail_room/mail_room_spec.rb | 184 +++- .../commit_message_generator_spec.rb | 88 ++ .../mergeability/check_result_spec.rb | 4 +- .../mergeability/results_store_spec.rb | 18 +- spec/lib/gitlab/metrics/dashboard/cache_spec.rb | 2 + spec/lib/gitlab/null_request_store_spec.rb | 2 +- spec/lib/gitlab/omniauth_initializer_spec.rb | 197 +++- spec/lib/gitlab/pages/settings_spec.rb | 2 +- .../patch/action_cable_redis_listener_spec.rb | 28 + spec/lib/gitlab/path_regex_spec.rb | 5 +- spec/lib/gitlab/process_supervisor_spec.rb | 170 +++ spec/lib/gitlab/profiler_spec.rb | 24 + spec/lib/gitlab/project_authorizations_spec.rb | 2 +- spec/lib/gitlab/regex_spec.rb | 15 + spec/lib/gitlab/runtime_spec.rb | 20 + spec/lib/gitlab/safe_request_loader_spec.rb | 180 +++ spec/lib/gitlab/safe_request_store_spec.rb | 4 +- spec/lib/gitlab/sanitizers/exif_spec.rb | 118 ++ spec/lib/gitlab/seeder_spec.rb | 20 + .../duplicate_jobs/duplicate_job_spec.rb | 86 -- .../gitlab/untrusted_regexp/ruby_syntax_spec.rb | 40 +- spec/lib/gitlab/url_blocker_spec.rb | 104 ++ spec/lib/gitlab/usage/metric_definition_spec.rb | 22 + .../cert_based_clusters_ff_metric_spec.rb | 21 + .../instrumentations/database_metric_spec.rb | 22 + .../metrics/names_suggestions/generator_spec.rb | 4 +- .../service_ping/instrumented_payload_spec.rb | 49 + .../service_ping/payload_keys_processor_spec.rb | 56 + spec/lib/gitlab/usage/service_ping_report_spec.rb | 222 +++- spec/lib/gitlab/usage_counters/pod_logs_spec.rb | 7 + .../usage_data_counters/hll_redis_counter_spec.rb | 5 +- .../service_usage_data_counter_spec.rb | 7 + .../work_item_activity_unique_counter_spec.rb | 63 ++ spec/lib/gitlab/usage_data_queries_spec.rb | 13 + spec/lib/gitlab/usage_data_spec.rb | 16 + spec/lib/gitlab/utils/strong_memoize_spec.rb | 30 + spec/lib/gitlab/utils_spec.rb | 71 +- .../gitlab/wiki_pages/front_matter_parser_spec.rb | 29 +- spec/lib/gitlab_spec.rb | 119 +- spec/lib/google_api/cloud_platform/client_spec.rb | 16 + spec/lib/learn_gitlab/onboarding_spec.rb | 2 +- spec/lib/learn_gitlab/project_spec.rb | 3 +- spec/lib/peek/views/active_record_spec.rb | 12 - .../ci_configuration/sast_build_action_spec.rb | 8 +- .../ci_configuration/sast_iac_build_action_spec.rb | 120 +- spec/lib/serializers/unsafe_json_spec.rb | 27 + .../sidebars/concerns/work_item_hierarchy_spec.rb | 21 - .../sidebars/groups/menus/kubernetes_menu_spec.rb | 10 + .../groups/menus/packages_registries_menu_spec.rb | 35 +- .../sidebars/groups/menus/settings_menu_spec.rb | 12 + .../projects/menus/infrastructure_menu_spec.rb | 8 + .../menus/packages_registries_menu_spec.rb | 21 + .../menus/project_information_menu_spec.rb | 6 - spec/mailers/emails/profile_spec.rb | 33 + spec/metrics_server/metrics_server_spec.rb | 48 +- ...ans_ci_daily_pipeline_schedule_triggers_spec.rb | 8 +- ...remove_duplicate_project_authorizations_spec.rb | 62 ++ ...04194347_encrypt_integration_properties_spec.rb | 40 + ...l_namespace_project_maintainer_to_owner_spec.rb | 20 + ...create_not_null_constraint_releases_tag_spec.rb | 23 + .../20220222192525_remove_null_releases_spec.rb | 22 + ...5223212_add_security_training_providers_spec.rb | 25 + ...0_remove_duplicate_project_tag_releases_spec.rb | 47 + ...eftover_external_pull_request_deletions_spec.rb | 43 + ...e_dependency_list_usage_data_from_redis_spec.rb | 23 + spec/migrations/add_new_trail_plans_spec.rb | 8 +- spec/migrations/add_open_source_plan_spec.rb | 8 +- .../backfill_all_project_namespaces_spec.rb | 37 + .../backfill_cycle_analytics_aggregations_spec.rb | 36 + ...l_member_namespace_id_for_group_members_spec.rb | 29 + ...on_name_and_id_parser_with_new_features_spec.rb | 28 + ...ot_null_contraint_on_title_from_sprints_spec.rb | 29 + ...egistry_exp_pol_worker_capacity_default_spec.rb | 40 + .../analytics/cycle_analytics/aggregation_spec.rb | 138 +++ spec/models/application_record_spec.rb | 12 + spec/models/application_setting_spec.rb | 4 +- spec/models/broadcast_message_spec.rb | 150 ++- spec/models/bulk_imports/export_status_spec.rb | 11 + spec/models/ci/bridge_spec.rb | 68 ++ spec/models/ci/build_spec.rb | 32 + spec/models/ci/group_variable_spec.rb | 8 + spec/models/ci/pipeline_schedule_spec.rb | 60 + spec/models/ci/pipeline_spec.rb | 37 +- spec/models/ci/runner_spec.rb | 2 +- spec/models/ci/secure_file_spec.rb | 4 + .../batch_destroy_dependent_associations_spec.rb | 31 +- .../concerns/blocks_json_serialization_spec.rb | 22 - .../concerns/blocks_unsafe_serialization_spec.rb | 17 + .../models/concerns/ci/has_deployment_name_spec.rb | 34 + spec/models/concerns/deployment_platform_spec.rb | 12 + spec/models/concerns/issuable_link_spec.rb | 43 + spec/models/concerns/issuable_spec.rb | 43 +- spec/models/concerns/mentionable_spec.rb | 1 + .../concerns/pg_full_text_searchable_spec.rb | 177 +++ .../concerns/runners_token_prefixable_spec.rb | 15 +- .../concerns/sensitive_serializable_hash_spec.rb | 150 +++ spec/models/concerns/spammable_spec.rb | 4 +- spec/models/concerns/token_authenticatable_spec.rb | 6 + .../token_authenticatable_strategies/base_spec.rb | 18 + .../digest_spec.rb | 18 + .../encrypted_spec.rb | 10 +- spec/models/container_repository_spec.rb | 54 +- spec/models/customer_relations/contact_spec.rb | 55 +- .../customer_relations/issue_contact_spec.rb | 30 +- .../models/customer_relations/organization_spec.rb | 17 +- spec/models/dependency_proxy/blob_spec.rb | 4 + spec/models/dependency_proxy/manifest_spec.rb | 4 + spec/models/environment_spec.rb | 15 + .../project_error_tracking_setting_spec.rb | 19 + spec/models/event_collection_spec.rb | 17 +- spec/models/external_pull_request_spec.rb | 4 - spec/models/group_spec.rb | 135 ++- spec/models/hooks/web_hook_log_spec.rb | 2 +- spec/models/hooks/web_hook_spec.rb | 12 + .../issuable_escalation_status_spec.rb | 2 +- spec/models/instance_configuration_spec.rb | 6 +- spec/models/integration_spec.rb | 306 +++++- .../models/integrations/base_issue_tracker_spec.rb | 22 +- spec/models/integrations/field_spec.rb | 118 ++ spec/models/integrations/harbor_spec.rb | 133 +++ spec/models/integrations/jira_spec.rb | 26 + spec/models/integrations/slack_spec.rb | 2 +- spec/models/issue_link_spec.rb | 65 +- spec/models/issue_spec.rb | 1 - spec/models/label_spec.rb | 15 +- spec/models/merge_request_assignee_spec.rb | 20 + spec/models/merge_request_reviewer_spec.rb | 20 + spec/models/merge_request_spec.rb | 109 +- spec/models/milestone_spec.rb | 11 + .../namespace/root_storage_statistics_spec.rb | 2 +- spec/models/namespace_spec.rb | 12 +- spec/models/packages/pypi/metadatum_spec.rb | 3 + spec/models/personal_access_token_spec.rb | 11 + .../environments/deployment_preloader_spec.rb | 18 + spec/models/project_authorization_spec.rb | 50 + spec/models/project_pages_metadatum_spec.rb | 11 - spec/models/project_spec.rb | 218 ++-- spec/models/project_team_spec.rb | 2 +- .../projects/build_artifacts_size_refresh_spec.rb | 227 ++++ spec/models/projects/topic_spec.rb | 14 +- spec/models/projects/triggered_hooks_spec.rb | 48 + spec/models/repository_spec.rb | 8 + spec/models/snippet_spec.rb | 81 +- spec/models/user_spec.rb | 108 +- spec/models/users/credit_card_validation_spec.rb | 2 +- spec/models/users/saved_reply_spec.rb | 16 + spec/models/wiki_page_spec.rb | 57 +- spec/models/work_item_spec.rb | 12 + spec/policies/application_setting_policy_spec.rb | 40 + spec/policies/global_policy_spec.rb | 30 - spec/policies/group_policy_spec.rb | 64 +- spec/policies/issue_policy_spec.rb | 32 + spec/policies/project_policy_spec.rb | 96 ++ spec/policies/work_item_policy_spec.rb | 94 ++ spec/presenters/blob_presenter_spec.rb | 46 +- spec/presenters/blobs/notebook_presenter_spec.rb | 21 + spec/presenters/ci/build_runner_presenter_spec.rb | 18 +- .../presenters/group_clusterable_presenter_spec.rb | 6 + .../instance_clusterable_presenter_spec.rb | 6 + spec/presenters/merge_request_presenter_spec.rb | 34 +- .../project_clusterable_presenter_spec.rb | 6 + spec/presenters/project_presenter_spec.rb | 6 - .../security/configuration_presenter_spec.rb | 2 - spec/presenters/search_service_presenter_spec.rb | 22 +- spec/presenters/user_presenter_spec.rb | 65 +- .../admin/background_migrations_controller_spec.rb | 8 +- spec/requests/api/admin/instance_clusters_spec.rb | 20 + spec/requests/api/broadcast_messages_spec.rb | 23 +- spec/requests/api/ci/jobs_spec.rb | 51 +- spec/requests/api/ci/pipelines_spec.rb | 17 + .../api/ci/runner/jobs_request_post_spec.rb | 6 +- spec/requests/api/ci/runner/jobs_trace_spec.rb | 8 +- spec/requests/api/ci/runner/runners_post_spec.rb | 8 +- spec/requests/api/ci/runner/runners_reset_spec.rb | 65 ++ spec/requests/api/ci/runners_spec.rb | 8 +- spec/requests/api/ci/secure_files_spec.rb | 187 +++- spec/requests/api/commits_spec.rb | 9 + spec/requests/api/container_repositories_spec.rb | 64 ++ spec/requests/api/deploy_tokens_spec.rb | 106 +- spec/requests/api/error_tracking/collector_spec.rb | 15 +- .../api/error_tracking/project_settings_spec.rb | 59 +- spec/requests/api/generic_packages_spec.rb | 11 - spec/requests/api/graphql/ci/pipelines_spec.rb | 33 + spec/requests/api/graphql/ci/runner_spec.rb | 33 - .../api/graphql/ci/runner_web_url_edge_spec.rb | 57 + .../container_repository_details_spec.rb | 80 +- .../api/graphql/group/group_members_spec.rb | 46 + spec/requests/api/graphql/group/issues_spec.rb | 25 + .../api/graphql/group/merge_requests_spec.rb | 21 + .../api/graphql/group/work_item_types_spec.rb | 4 +- .../mutations/issues/set_crm_contacts_spec.rb | 14 +- .../graphql/mutations/notes/create/note_spec.rb | 33 +- .../mutations/work_items/create_from_task_spec.rb | 87 ++ spec/requests/api/graphql/namespace_query_spec.rb | 3 +- .../api/graphql/project/jira_service_spec.rb | 9 +- .../api/graphql/project/merge_request_spec.rb | 18 + .../api/graphql/project/work_item_types_spec.rb | 4 +- spec/requests/api/graphql/query_spec.rb | 24 + spec/requests/api/graphql/work_item_spec.rb | 75 ++ spec/requests/api/group_clusters_spec.rb | 20 + spec/requests/api/group_labels_spec.rb | 10 +- spec/requests/api/integrations_spec.rb | 10 +- spec/requests/api/internal/kubernetes_spec.rb | 1 + spec/requests/api/internal/mail_room_spec.rb | 16 +- spec/requests/api/invitations_spec.rb | 21 +- .../api/issues/post_projects_issues_spec.rb | 2 +- .../api/issues/put_projects_issues_spec.rb | 22 +- spec/requests/api/labels_spec.rb | 22 +- spec/requests/api/members_spec.rb | 6 +- spec/requests/api/notes_spec.rb | 89 +- spec/requests/api/project_attributes.yml | 2 +- spec/requests/api/project_clusters_spec.rb | 20 + spec/requests/api/project_import_spec.rb | 87 +- spec/requests/api/project_snippets_spec.rb | 4 +- spec/requests/api/projects_spec.rb | 34 +- spec/requests/api/pypi_packages_spec.rb | 8 + spec/requests/api/releases_spec.rb | 6 +- spec/requests/api/repositories_spec.rb | 7 + spec/requests/api/search_spec.rb | 11 +- spec/requests/api/snippets_spec.rb | 4 +- spec/requests/api/system_hooks_spec.rb | 49 +- spec/requests/api/terraform/state_spec.rb | 4 +- spec/requests/api/topics_spec.rb | 52 +- spec/requests/api/user_counts_spec.rb | 15 + spec/requests/api/users_spec.rb | 58 +- spec/requests/api/wikis_spec.rb | 54 +- spec/requests/content_security_policy_spec.rb | 50 + spec/requests/git_http_spec.rb | 48 +- .../groups/crm/contacts_controller_spec.rb | 6 + .../groups/crm/organizations_controller_spec.rb | 6 + .../groups/deploy_tokens_controller_spec.rb | 40 + .../groups/harbor/repositories_controller_spec.rb | 69 ++ .../oauth_callbacks_controller_spec.rb | 22 + .../google_cloud/deployments_controller_spec.rb | 71 +- .../google_cloud/gcp_regions_controller_spec.rb | 152 +++ .../google_cloud/revoke_oauth_controller_spec.rb | 86 ++ .../service_accounts_controller_spec.rb | 105 +- .../projects/google_cloud_controller_spec.rb | 78 +- .../harbor/repositories_controller_spec.rb | 69 ++ spec/requests/projects/redirect_controller_spec.rb | 66 ++ spec/routing/project_routing_spec.rb | 40 +- .../cop/database/establish_connection_spec.rb | 2 +- .../cop/database/multiple_databases_spec.rb | 10 + .../cop/graphql/graphql_name_position_spec.rb | 44 + spec/rubocop/formatter/todo_formatter_spec.rb | 284 +++++ spec/rubocop/todo_dir_spec.rb | 218 ++++ spec/serializers/ci/pipeline_entity_spec.rb | 3 +- spec/serializers/cluster_error_entity_spec.rb | 35 - .../clusters/kubernetes_error_entity_spec.rb | 35 + spec/serializers/environment_entity_spec.rb | 2 +- spec/serializers/environment_serializer_spec.rb | 19 + spec/serializers/fork_namespace_entity_spec.rb | 22 - .../serializers/issue_sidebar_basic_entity_spec.rb | 28 +- spec/serializers/label_serializer_spec.rb | 2 +- .../merge_request_widget_entity_spec.rb | 2 +- spec/serializers/pipeline_details_entity_spec.rb | 14 - spec/serializers/service_event_entity_spec.rb | 4 +- spec/serializers/service_field_entity_spec.rb | 4 + ...ntainer_registry_authentication_service_spec.rb | 139 --- .../find_records_due_for_refresh_service_spec.rb | 33 +- .../bulk_create_integration_service_spec.rb | 20 +- spec/services/ci/abort_pipelines_service_spec.rb | 43 +- spec/services/ci/after_requeue_job_service_spec.rb | 255 ++++- .../ci/create_downstream_pipeline_service_spec.rb | 8 - .../ci/create_pipeline_service/artifacts_spec.rb | 67 ++ .../parameter_content_spec.rb | 2 +- .../ci/create_pipeline_service/tags_spec.rb | 25 - .../ci/destroy_secure_file_service_spec.rb | 32 + .../ci/job_artifacts/create_service_spec.rb | 2 +- .../ci/parse_dotenv_artifact_service_spec.rb | 24 +- spec/services/ci/register_runner_service_spec.rb | 234 ---- spec/services/ci/retry_pipeline_service_spec.rb | 46 +- .../ci/runners/assign_runner_service_spec.rb | 40 + .../ci/runners/register_runner_service_spec.rb | 234 ++++ .../reset_registration_token_service_spec.rb | 76 ++ .../ci/runners/unassign_runner_service_spec.rb | 43 + .../ci/runners/unregister_runner_service_spec.rb | 15 + .../ci/runners/update_runner_service_spec.rb | 70 ++ spec/services/ci/unregister_runner_service_spec.rb | 15 - spec/services/ci/update_runner_service_spec.rb | 70 -- .../services/concerns/rate_limited_service_spec.rb | 69 +- spec/services/error_tracking/base_service_spec.rb | 12 +- .../error_tracking/collect_error_service_spec.rb | 17 +- .../create_service_accounts_service_spec.rb | 25 + .../gcp_region_add_or_replace_service_spec.rb | 28 + .../google_cloud/service_accounts_service_spec.rb | 14 +- spec/services/groups/create_service_spec.rb | 20 +- .../groups/deploy_tokens/revoke_service_spec.rb | 28 + spec/services/groups/destroy_service_spec.rb | 13 +- ...create_project_from_remote_file_service_spec.rb | 201 ---- ...eate_project_from_uploaded_file_service_spec.rb | 71 -- .../gitlab_projects/create_project_service_spec.rb | 179 +++ .../file_upload_spec.rb | 26 + .../remote_file_s3_spec.rb | 136 +++ .../remote_file_spec.rb | 149 +++ spec/services/issue_links/create_service_spec.rb | 188 +--- spec/services/issue_links/destroy_service_spec.rb | 61 +- spec/services/issues/create_service_spec.rb | 85 +- .../issues/set_crm_contacts_service_spec.rb | 2 +- spec/services/issues/update_service_spec.rb | 38 +- spec/services/labels/create_service_spec.rb | 3 +- spec/services/labels/promote_service_spec.rb | 2 +- spec/services/labels/update_service_spec.rb | 2 +- .../members/projects/creator_service_spec.rb | 4 +- .../merge_requests/approval_service_spec.rb | 2 +- ...bulk_remove_attention_requested_service_spec.rb | 4 + .../services/merge_requests/create_service_spec.rb | 9 +- .../handle_assignees_change_service_spec.rb | 8 +- .../merge_orchestration_service_spec.rb | 4 +- .../check_broken_status_service_spec.rb | 43 + .../check_discussions_status_service_spec.rb | 57 + .../check_draft_status_service_spec.rb | 43 + .../mergeability/check_open_status_service_spec.rb | 43 + .../mergeability/run_checks_service_spec.rb | 15 +- .../reload_merge_head_diff_service_spec.rb | 10 - .../remove_attention_requested_service_spec.rb | 31 +- .../toggle_attention_requested_service_spec.rb | 25 +- .../services/merge_requests/update_service_spec.rb | 8 + spec/services/notification_service_spec.rb | 28 +- .../packages/pypi/create_package_service_spec.rb | 16 +- .../personal_access_tokens/create_service_spec.rb | 8 + .../projects/branches_by_mode_service_spec.rb | 26 +- .../cleanup_tags_service_spec.rb | 24 +- spec/services/projects/create_service_spec.rb | 27 +- spec/services/projects/destroy_service_spec.rb | 35 +- spec/services/projects/import_service_spec.rb | 22 +- ...build_artifacts_size_statistics_service_spec.rb | 102 ++ .../services/projects/update_pages_service_spec.rb | 1 - .../quick_actions/interpret_service_spec.rb | 10 +- .../repositories/destroy_rollback_service_spec.rb | 17 +- spec/services/repositories/destroy_service_spec.rb | 22 +- .../security/merge_reports_service_spec.rb | 13 +- .../service_ping/build_payload_service_spec.rb | 4 + spec/services/spam/spam_action_service_spec.rb | 95 +- spec/services/spam/spam_params_spec.rb | 50 +- spec/services/spam/spam_verdict_service_spec.rb | 30 + spec/services/system_note_service_spec.rb | 12 +- .../system_notes/issuables_service_spec.rb | 8 +- spec/services/todo_service_spec.rb | 22 +- .../refresh_authorized_projects_service_spec.rb | 31 +- .../users/saved_replies/create_service_spec.rb | 44 + .../users/saved_replies/update_service_spec.rb | 40 + spec/services/web_hook_service_spec.rb | 210 ++-- .../web_hooks/log_execution_service_spec.rb | 237 ++++ .../work_items/create_and_link_service_spec.rb | 96 ++ .../work_items/create_from_task_service_spec.rb | 97 ++ ...task_list_reference_replacement_service_spec.rb | 106 ++ spec/services/work_items/update_service_spec.rb | 4 + spec/spec_helper.rb | 26 +- .../enable_multiple_database_metrics_by_default.rb | 8 - spec/support/event_store.rb | 7 + spec/support/helpers/ci/template_helpers.rb | 2 +- .../helpers/content_security_policy_helpers.rb | 20 + .../support/helpers/database_connection_helpers.rb | 11 - spec/support/helpers/graphql_helpers.rb | 6 + spec/support/helpers/migrations_helpers.rb | 8 +- spec/support/helpers/navbar_structure_helper.rb | 8 + spec/support/helpers/next_found_instance_of.rb | 32 +- spec/support/helpers/search_helpers.rb | 6 +- spec/support/helpers/sorting_helper.rb | 1 + spec/support/helpers/stub_configuration.rb | 16 +- spec/support/helpers/terms_helper.rb | 4 +- spec/support/helpers/test_env.rb | 5 +- spec/support/helpers/usage_data_helpers.rb | 1 + spec/support/matchers/be_color.rb | 20 + spec/support/matchers/event_store.rb | 37 +- .../pushed_frontend_feature_flags_matcher.rb | 8 +- spec/support/sentry.rb | 13 + ...he_allowed_users_in_namespace_shared_context.rb | 4 +- .../container_repositories_shared_context.rb | 9 +- .../client_stubs_shared_context.rb | 20 + .../shared_contexts/navbar_structure_context.rb | 3 +- spec/support/shared_contexts/spam_constants.rb | 5 +- ...ttention_request_cache_invalidation_examples.rb | 15 + .../blocks_unsafe_serialization_shared_examples.rb | 26 + .../clusters_controller_shared_examples.rb | 30 + .../rate_limited_endpoint_shared_examples.rb | 14 +- .../controllers/unique_hll_events_examples.rb | 6 +- .../controllers/uploads_actions_shared_examples.rb | 30 +- .../controllers/wiki_actions_shared_examples.rb | 4 +- .../features/clusters_shared_examples.rb | 14 + .../features/container_registry_shared_examples.rb | 17 + ...st_slash_command_integration_shared_examples.rb | 4 +- .../manage_applications_shared_examples.rb | 2 +- ...multiple_assignees_widget_mr_shared_examples.rb | 47 + .../project_upload_files_shared_examples.rb | 4 +- .../wiki/user_creates_wiki_page_shared_examples.rb | 14 +- .../wiki/user_updates_wiki_page_shared_examples.rb | 20 +- .../wiki/user_views_wiki_page_shared_examples.rb | 15 +- .../wiki/user_views_wiki_pages_shared_examples.rb | 2 +- .../graphql/members_shared_examples.rb | 6 +- .../security/ci_configuration_shared_examples.rb | 18 +- ...ge_request_interactions_type_shared_examples.rb | 57 + .../integrations/integration_settings_form.rb | 11 +- .../background_migration_job_shared_examples.rb | 4 +- .../usage_counter_shared_examples.rb | 40 + .../shared_examples/lib/wikis_api_examples.rb | 76 +- .../active_record_subscriber_shared_examples.rb | 35 - .../models/atomic_internal_id_shared_examples.rb | 2 +- .../slack_mattermost_notifier_shared_examples.rb | 10 - .../models/concerns/limitable_shared_examples.rb | 2 +- .../models/concerns/timebox_shared_examples.rb | 11 - .../update_namespace_statistics_shared_examples.rb | 54 + .../models/issuable_link_shared_examples.rb | 65 ++ .../models/member_shared_examples.rb | 45 +- .../models/resource_event_shared_examples.rb | 28 +- .../models/runners_token_prefix_shared_examples.rb | 13 - .../shared_examples/models/wiki_shared_examples.rb | 27 +- .../namespaces/traversal_scope_examples.rb | 26 +- .../requests/api/conan_packages_shared_examples.rb | 21 + .../requests/api/time_tracking_shared_examples.rb | 8 +- ..._based_clusters_feature_flag_shared_examples.rb | 15 + .../shared_examples/row_lock_shared_examples.rb | 2 +- ...ds_git_audit_streaming_event_shared_examples.rb | 61 ++ .../environment_serializer_shared_examples.rb | 17 +- .../serializers/note_entity_shared_examples.rb | 23 + ...tainer_registry_auth_service_shared_examples.rb | 8 - .../services/incident_shared_examples.rb | 2 +- .../issuable_links/create_links_shared_examples.rb | 133 +++ .../destroyable_issuable_links_shared_examples.rb | 42 + .../rate_limited_service_shared_examples.rb | 73 ++ .../create_service_shared_examples.rb | 12 + ..._background_migration_worker_shared_examples.rb | 198 ++++ .../git_garbage_collect_methods_shared_examples.rb | 70 +- .../workers/concerns/reenqueuer_shared_examples.rb | 15 +- spec/support/silence_stdout.rb | 12 + spec/support/view_component.rb | 7 + spec/tasks/dev_rake_spec.rb | 38 + .../gitlab/background_migrations_rake_spec.rb | 127 ++- spec/tasks/gitlab/backup_rake_spec.rb | 29 +- spec/tasks/gitlab/db_rake_spec.rb | 115 +- ...ct_statistics_build_artifacts_size_rake_spec.rb | 48 + spec/tasks/gitlab/setup_rake_spec.rb | 141 +++ spec/tasks/rubocop_rake_spec.rb | 168 +++ spec/tooling/danger/changelog_spec.rb | 467 -------- spec/tooling/danger/datateam_spec.rb | 8 +- spec/tooling/danger/project_helper_spec.rb | 28 +- spec/tooling/docs/deprecation_handling_spec.rb | 2 +- spec/tooling/quality/test_level_spec.rb | 4 +- spec/validators/array_members_validator_spec.rb | 1 + spec/validators/color_validator_spec.rb | 24 + spec/validators/cron_validator_spec.rb | 2 + spec/validators/future_date_validator_spec.rb | 1 + .../gitlab_projects/remote_file_validator_spec.rb | 70 ++ .../application_settings/_eks.html.haml_spec.rb | 4 +- .../repository.html.haml_spec.rb | 5 +- .../broadcast_messages/index.html.haml_spec.rb | 36 + spec/views/devise/sessions/new.html.haml_spec.rb | 2 +- .../devise/shared/_signup_box.html.haml_spec.rb | 6 +- .../groups/group_members/index.html.haml_spec.rb | 42 +- .../views/layouts/_header_search.html.haml_spec.rb | 113 ++ .../_published_experiments.html.haml_spec.rb | 32 +- .../projects/commits/_commit.html.haml_spec.rb | 6 +- spec/views/projects/empty.html.haml_spec.rb | 15 + .../project_members/index.html.haml_spec.rb | 33 - .../runners/_specific_runners.html.haml_spec.rb | 4 +- spec/views/projects/tags/index.html.haml_spec.rb | 6 +- spec/views/search/_results.html.haml_spec.rb | 39 +- spec/views/shared/_gl_toggle.haml_spec.rb | 85 -- spec/views/shared/_global_alert.html.haml_spec.rb | 29 - .../shared/issuable/_sidebar.html.haml_spec.rb | 31 +- spec/views/shared/wikis/_sidebar.html.haml_spec.rb | 2 +- .../bulk_imports/export_request_worker_spec.rb | 25 + spec/workers/bulk_imports/pipeline_worker_spec.rb | 28 + .../ci_database_worker_spec.rb | 7 + .../batched_background_migration_worker_spec.rb | 118 +- spec/workers/deployments/hooks_worker_spec.rb | 4 +- spec/workers/every_sidekiq_worker_spec.rb | 1 + .../loose_foreign_keys/cleanup_worker_spec.rb | 49 +- ..._build_artifacts_size_statistics_worker_spec.rb | 96 ++ ..._build_artifacts_size_statistics_worker_spec.rb | 17 + .../quality/test_data_cleanup_worker_spec.rb | 44 + spec/workers/web_hook_worker_spec.rb | 9 - 1299 files changed, 40762 insertions(+), 15329 deletions(-) create mode 100644 spec/components/pajamas/component_spec.rb create mode 100644 spec/components/pajamas/toggle_component_spec.rb create mode 100644 spec/controllers/concerns/product_analytics_tracking_spec.rb create mode 100644 spec/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support_spec.rb create mode 100644 spec/controllers/projects/ci/secure_files_controller_spec.rb create mode 100644 spec/factories/analytics/cycle_analytics/aggregations.rb create mode 100644 spec/factories/ci/reports/security/evidence.rb create mode 100644 spec/factories/projects/build_artifacts_size_refreshes.rb create mode 100644 spec/factories/users/saved_replies.rb create mode 100644 spec/features/issues/spam_akismet_issue_creation_spec.rb delete mode 100644 spec/features/issues/spam_issues_spec.rb create mode 100644 spec/features/projects/ci/secure_files_spec.rb create mode 100644 spec/features/projects/commits/multi_view_diff_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/blobs/balsamiq_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_line_permalink_updater_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/blobs/blob_show_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/blobs/edit_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/blobs/shortcuts_blob_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/blobs/user_creates_new_blob_in_new_project_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/blobs/user_views_pipeline_editor_button_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/files/editing_a_file_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/files/find_file_keyboard_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/files/project_owner_creates_license_file_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/files/user_browses_files_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/files/user_browses_lfs_files_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/files/user_deletes_files_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/files/user_edits_files_spec.rb create mode 100644 spec/features/refactor_blob_viewer_disabled/projects/files/user_replaces_files_spec.rb create mode 100644 spec/finders/releases/group_releases_finder_spec.rb create mode 100644 spec/fixtures/api/schemas/public_api/v4/system_hook.json create mode 100644 spec/fixtures/api/schemas/public_api/v4/system_hooks.json create mode 100644 spec/fixtures/emails/missing_delivered_to_header.eml create mode 100644 spec/fixtures/emails/service_desk_reply_to_and_from.eml create mode 100644 spec/fixtures/error_tracking/php_empty_transaction.json create mode 100644 spec/frontend/admin/applications/components/__snapshots__/delete_application_spec.js.snap create mode 100644 spec/frontend/admin/applications/components/delete_application_spec.js create mode 100644 spec/frontend/admin/topics/components/__snapshots__/remove_avatar_spec.js.snap create mode 100644 spec/frontend/admin/topics/components/remove_avatar_spec.js create mode 100644 spec/frontend/attention_requests/components/navigation_popover_spec.js delete mode 100644 spec/frontend/branches/ajax_loading_spinner_spec.js create mode 100644 spec/frontend/ci_secure_files/components/secure_files_list_spec.js create mode 100644 spec/frontend/ci_secure_files/mock_data.js create mode 100644 spec/frontend/clusters/agents/components/create_token_button_spec.js create mode 100644 spec/frontend/clusters_list/components/agent_token_spec.js delete mode 100644 spec/frontend/code_quality_walkthrough/components/__snapshots__/step_spec.js.snap delete mode 100644 spec/frontend/code_quality_walkthrough/components/step_spec.js create mode 100644 spec/frontend/content_editor/components/loading_indicator_spec.js create mode 100644 spec/frontend/content_editor/extensions/paste_markdown_spec.js create mode 100644 spec/frontend/content_editor/services/markdown_deserializer_spec.js delete mode 100644 spec/frontend/cycle_analytics/__snapshots__/total_time_component_spec.js.snap create mode 100644 spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap delete mode 100644 spec/frontend/cycle_analytics/limit_warning_component_spec.js delete mode 100644 spec/frontend/cycle_analytics/total_time_component_spec.js create mode 100644 spec/frontend/cycle_analytics/total_time_spec.js create mode 100644 spec/frontend/environments/environment_folder_spec.js delete mode 100644 spec/frontend/environments/new_environment_folder_spec.js delete mode 100644 spec/frontend/environments/new_environments_app_spec.js create mode 100644 spec/frontend/google_cloud/components/gcp_regions_form_spec.js create mode 100644 spec/frontend/google_cloud/components/gcp_regions_list_spec.js create mode 100644 spec/frontend/google_cloud/components/revoke_oauth_spec.js create mode 100644 spec/frontend/integrations/edit/components/sections/connection_spec.js create mode 100644 spec/frontend/integrations/edit/components/sections/jira_issues_spec.js create mode 100644 spec/frontend/integrations/edit/components/sections/jira_trigger_spec.js delete mode 100644 spec/frontend/jira_connect/subscriptions/components/sign_in_button_spec.js create mode 100644 spec/frontend/jira_connect/subscriptions/components/sign_in_legacy_button_spec.js create mode 100644 spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js create mode 100644 spec/frontend/jira_connect/subscriptions/pkce_spec.js create mode 100644 spec/frontend/jobs/components/table/graphql/cache_config_spec.js create mode 100644 spec/frontend/lib/utils/ignore_while_pending_spec.js create mode 100644 spec/frontend/loading_icon_for_legacy_js_spec.js delete mode 100644 spec/frontend/pages/projects/forks/new/components/fork_groups_list_item_spec.js delete mode 100644 spec/frontend/pages/projects/forks/new/components/fork_groups_list_spec.js create mode 100644 spec/frontend/pages/projects/pages_domains/form_spec.js delete mode 100644 spec/frontend/performance_bar/components/request_selector_spec.js create mode 100644 spec/frontend/pipeline_wizard/components/input_spec.js create mode 100644 spec/frontend/pipeline_wizard/components/step_spec.js create mode 100644 spec/frontend/pipeline_wizard/components/widgets/list_spec.js create mode 100644 spec/frontend/pipeline_wizard/components/widgets_spec.js create mode 100644 spec/frontend/pipeline_wizard/components/wrapper_spec.js create mode 100644 spec/frontend/pipeline_wizard/mock/yaml.js create mode 100644 spec/frontend/pipeline_wizard/pipeline_wizard_spec.js create mode 100644 spec/frontend/pipeline_wizard/validators_spec.js create mode 100644 spec/frontend/pipelines/pipeline_labels_spec.js create mode 100644 spec/frontend/protected_branches/protected_branch_create_spec.js delete mode 100644 spec/frontend/repository/components/blob_edit_spec.js create mode 100644 spec/frontend/repository/components/blob_viewers/audio_viewer_spec.js create mode 100644 spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js create mode 100644 spec/frontend/runner/components/runner_delete_button_spec.js create mode 100644 spec/frontend/security_configuration/graphql/cache_utils_spec.js create mode 100644 spec/frontend/sidebar/components/incidents/escalation_status_spec.js create mode 100644 spec/frontend/sidebar/components/incidents/escalation_utils_spec.js create mode 100644 spec/frontend/sidebar/components/incidents/mock_data.js create mode 100644 spec/frontend/sidebar/components/incidents/sidebar_escalation_status_spec.js delete mode 100644 spec/frontend/toggle_buttons_spec.js create mode 100644 spec/frontend/vue_mr_widget/components/extensions/child_content_spec.js create mode 100644 spec/frontend/vue_mr_widget/extentions/code_quality/index_spec.js create mode 100644 spec/frontend/vue_mr_widget/extentions/code_quality/mock_data.js create mode 100644 spec/frontend/vue_shared/components/__snapshots__/content_transition_spec.js.snap create mode 100644 spec/frontend/vue_shared/components/content_transition_spec.js create mode 100644 spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js create mode 100644 spec/frontend/vue_shared/components/user_avatar/user_avatar_image_old_spec.js create mode 100644 spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js create mode 100644 spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js create mode 100644 spec/frontend/work_items/components/work_item_detail_spec.js create mode 100644 spec/graphql/mutations/saved_replies/create_spec.rb create mode 100644 spec/graphql/mutations/saved_replies/update_spec.rb create mode 100644 spec/graphql/resolvers/group_members/notification_email_resolver_spec.rb create mode 100644 spec/graphql/resolvers/work_item_resolver_spec.rb create mode 100644 spec/graphql/types/ci/runner_web_url_edge_spec.rb create mode 100644 spec/graphql/types/merge_requests/assignee_type_spec.rb create mode 100644 spec/graphql/types/merge_requests/author_type_spec.rb create mode 100644 spec/graphql/types/merge_requests/participant_type_spec.rb create mode 100644 spec/graphql/types/projects/service_type_enum_spec.rb delete mode 100644 spec/graphql/types/projects/services_enum_spec.rb create mode 100644 spec/graphql/types/saved_reply_type_spec.rb create mode 100644 spec/graphql/types/todoable_interface_spec.rb create mode 100644 spec/graphql/types/work_item_id_type_spec.rb create mode 100644 spec/graphql/types/work_item_type_spec.rb create mode 100644 spec/helpers/deploy_tokens_helper_spec.rb create mode 100644 spec/helpers/web_ide_button_helper_spec.rb create mode 100644 spec/lib/api/entities/ci/job_artifact_file_spec.rb create mode 100644 spec/lib/api/entities/ci/job_request/dependency_spec.rb create mode 100644 spec/lib/api/entities/wiki_page_spec.rb create mode 100644 spec/lib/atlassian/jira_connect_spec.rb create mode 100644 spec/lib/backup/task_spec.rb create mode 100644 spec/lib/banzai/filter/task_list_filter_spec.rb create mode 100644 spec/lib/gitlab/background_migration/backfill_issue_search_data_spec.rb create mode 100644 spec/lib/gitlab/background_migration/backfill_member_namespace_for_group_members_spec.rb create mode 100644 spec/lib/gitlab/background_migration/batching_strategies/base_strategy_spec.rb create mode 100644 spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb create mode 100644 spec/lib/gitlab/background_migration/migrate_personal_namespace_project_maintainer_to_owner_spec.rb create mode 100644 spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb create mode 100644 spec/lib/gitlab/background_migration/remove_all_trace_expiration_dates_spec.rb create mode 100644 spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects_spec.rb create mode 100644 spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects_spec.rb create mode 100644 spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb create mode 100644 spec/lib/gitlab/ci/config/entry/trigger/forward_spec.rb create mode 100644 spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb create mode 100644 spec/lib/gitlab/ci/variables/builder/group_spec.rb create mode 100644 spec/lib/gitlab/color_spec.rb create mode 100644 spec/lib/gitlab/config/entry/validators_spec.rb create mode 100644 spec/lib/gitlab/database/migration_helpers/restrict_gitlab_schema_spec.rb create mode 100644 spec/lib/gitlab/database/migrations/test_background_runner_spec.rb create mode 100644 spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb create mode 100644 spec/lib/gitlab/database/type/color_spec.rb create mode 100644 spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb create mode 100644 spec/lib/gitlab/etag_caching/router/rails_spec.rb delete mode 100644 spec/lib/gitlab/etag_caching/router/restful_spec.rb create mode 100644 spec/lib/gitlab/fips_spec.rb create mode 100644 spec/lib/gitlab/graphql/loaders/batch_commit_loader_spec.rb create mode 100644 spec/lib/gitlab/harbor/client_spec.rb create mode 100644 spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb delete mode 100644 spec/lib/gitlab/integrations/sti_type_spec.rb create mode 100644 spec/lib/gitlab/patch/action_cable_redis_listener_spec.rb create mode 100644 spec/lib/gitlab/process_supervisor_spec.rb create mode 100644 spec/lib/gitlab/safe_request_loader_spec.rb create mode 100644 spec/lib/gitlab/usage/metrics/instrumentations/cert_based_clusters_ff_metric_spec.rb create mode 100644 spec/lib/gitlab/usage/service_ping/instrumented_payload_spec.rb create mode 100644 spec/lib/gitlab/usage/service_ping/payload_keys_processor_spec.rb create mode 100644 spec/lib/gitlab/usage_counters/pod_logs_spec.rb create mode 100644 spec/lib/gitlab/usage_data_counters/service_usage_data_counter_spec.rb create mode 100644 spec/lib/gitlab/usage_data_counters/work_item_activity_unique_counter_spec.rb create mode 100644 spec/lib/serializers/unsafe_json_spec.rb delete mode 100644 spec/lib/sidebars/concerns/work_item_hierarchy_spec.rb create mode 100644 spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb create mode 100644 spec/migrations/20220204194347_encrypt_integration_properties_spec.rb create mode 100644 spec/migrations/20220208080921_schedule_migrate_personal_namespace_project_maintainer_to_owner_spec.rb create mode 100644 spec/migrations/20220222192524_create_not_null_constraint_releases_tag_spec.rb create mode 100644 spec/migrations/20220222192525_remove_null_releases_spec.rb create mode 100644 spec/migrations/20220305223212_add_security_training_providers_spec.rb create mode 100644 spec/migrations/20220307192610_remove_duplicate_project_tag_releases_spec.rb create mode 100644 spec/migrations/20220309084954_remove_leftover_external_pull_request_deletions_spec.rb create mode 100644 spec/migrations/20220310141349_remove_dependency_list_usage_data_from_redis_spec.rb create mode 100644 spec/migrations/backfill_all_project_namespaces_spec.rb create mode 100644 spec/migrations/backfill_cycle_analytics_aggregations_spec.rb create mode 100644 spec/migrations/backfill_member_namespace_id_for_group_members_spec.rb create mode 100644 spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb create mode 100644 spec/migrations/remove_not_null_contraint_on_title_from_sprints_spec.rb create mode 100644 spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb create mode 100644 spec/models/analytics/cycle_analytics/aggregation_spec.rb delete mode 100644 spec/models/concerns/blocks_json_serialization_spec.rb create mode 100644 spec/models/concerns/blocks_unsafe_serialization_spec.rb create mode 100644 spec/models/concerns/ci/has_deployment_name_spec.rb create mode 100644 spec/models/concerns/issuable_link_spec.rb create mode 100644 spec/models/concerns/pg_full_text_searchable_spec.rb create mode 100644 spec/models/concerns/sensitive_serializable_hash_spec.rb create mode 100644 spec/models/concerns/token_authenticatable_strategies/digest_spec.rb create mode 100644 spec/models/integrations/field_spec.rb create mode 100644 spec/models/integrations/harbor_spec.rb create mode 100644 spec/models/projects/build_artifacts_size_refresh_spec.rb create mode 100644 spec/models/projects/triggered_hooks_spec.rb create mode 100644 spec/models/users/saved_reply_spec.rb create mode 100644 spec/policies/application_setting_policy_spec.rb create mode 100644 spec/policies/work_item_policy_spec.rb create mode 100644 spec/presenters/blobs/notebook_presenter_spec.rb create mode 100644 spec/requests/api/ci/runner/runners_reset_spec.rb create mode 100644 spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb create mode 100644 spec/requests/api/graphql/mutations/work_items/create_from_task_spec.rb create mode 100644 spec/requests/api/graphql/work_item_spec.rb create mode 100644 spec/requests/content_security_policy_spec.rb create mode 100644 spec/requests/groups/deploy_tokens_controller_spec.rb create mode 100644 spec/requests/groups/harbor/repositories_controller_spec.rb create mode 100644 spec/requests/jira_connect/oauth_callbacks_controller_spec.rb create mode 100644 spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb create mode 100644 spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb create mode 100644 spec/requests/projects/harbor/repositories_controller_spec.rb create mode 100644 spec/requests/projects/redirect_controller_spec.rb create mode 100644 spec/rubocop/cop/graphql/graphql_name_position_spec.rb create mode 100644 spec/rubocop/formatter/todo_formatter_spec.rb create mode 100644 spec/rubocop/todo_dir_spec.rb delete mode 100644 spec/serializers/cluster_error_entity_spec.rb create mode 100644 spec/serializers/clusters/kubernetes_error_entity_spec.rb create mode 100644 spec/services/ci/create_pipeline_service/artifacts_spec.rb create mode 100644 spec/services/ci/destroy_secure_file_service_spec.rb delete mode 100644 spec/services/ci/register_runner_service_spec.rb create mode 100644 spec/services/ci/runners/assign_runner_service_spec.rb create mode 100644 spec/services/ci/runners/register_runner_service_spec.rb create mode 100644 spec/services/ci/runners/reset_registration_token_service_spec.rb create mode 100644 spec/services/ci/runners/unassign_runner_service_spec.rb create mode 100644 spec/services/ci/runners/unregister_runner_service_spec.rb create mode 100644 spec/services/ci/runners/update_runner_service_spec.rb delete mode 100644 spec/services/ci/unregister_runner_service_spec.rb delete mode 100644 spec/services/ci/update_runner_service_spec.rb create mode 100644 spec/services/google_cloud/gcp_region_add_or_replace_service_spec.rb create mode 100644 spec/services/groups/deploy_tokens/revoke_service_spec.rb delete mode 100644 spec/services/import/gitlab_projects/create_project_from_remote_file_service_spec.rb delete mode 100644 spec/services/import/gitlab_projects/create_project_from_uploaded_file_service_spec.rb create mode 100644 spec/services/import/gitlab_projects/create_project_service_spec.rb create mode 100644 spec/services/import/gitlab_projects/file_acquisition_strategies/file_upload_spec.rb create mode 100644 spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3_spec.rb create mode 100644 spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_spec.rb create mode 100644 spec/services/merge_requests/mergeability/check_broken_status_service_spec.rb create mode 100644 spec/services/merge_requests/mergeability/check_discussions_status_service_spec.rb create mode 100644 spec/services/merge_requests/mergeability/check_draft_status_service_spec.rb create mode 100644 spec/services/merge_requests/mergeability/check_open_status_service_spec.rb create mode 100644 spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb create mode 100644 spec/services/users/saved_replies/create_service_spec.rb create mode 100644 spec/services/users/saved_replies/update_service_spec.rb create mode 100644 spec/services/web_hooks/log_execution_service_spec.rb create mode 100644 spec/services/work_items/create_and_link_service_spec.rb create mode 100644 spec/services/work_items/create_from_task_service_spec.rb create mode 100644 spec/services/work_items/task_list_reference_replacement_service_spec.rb delete mode 100644 spec/support/enable_multiple_database_metrics_by_default.rb create mode 100644 spec/support/event_store.rb create mode 100644 spec/support/helpers/content_security_policy_helpers.rb delete mode 100644 spec/support/helpers/database_connection_helpers.rb create mode 100644 spec/support/matchers/be_color.rb create mode 100644 spec/support/sentry.rb create mode 100644 spec/support/shared_contexts/lib/container_registry/client_stubs_shared_context.rb create mode 100644 spec/support/shared_examples/attention_request_cache_invalidation_examples.rb create mode 100644 spec/support/shared_examples/blocks_unsafe_serialization_shared_examples.rb create mode 100644 spec/support/shared_examples/features/clusters_shared_examples.rb create mode 100644 spec/support/shared_examples/features/multiple_assignees_widget_mr_shared_examples.rb create mode 100644 spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb create mode 100644 spec/support/shared_examples/lib/gitlab/usage_data_counters/usage_counter_shared_examples.rb create mode 100644 spec/support/shared_examples/models/concerns/update_namespace_statistics_shared_examples.rb create mode 100644 spec/support/shared_examples/models/issuable_link_shared_examples.rb delete mode 100644 spec/support/shared_examples/models/runners_token_prefix_shared_examples.rb create mode 100644 spec/support/shared_examples/requests/clusters/certificate_based_clusters_feature_flag_shared_examples.rb create mode 100644 spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb create mode 100644 spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb create mode 100644 spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb create mode 100644 spec/support/shared_examples/services/rate_limited_service_shared_examples.rb create mode 100644 spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb create mode 100644 spec/support/silence_stdout.rb create mode 100644 spec/support/view_component.rb create mode 100644 spec/tasks/dev_rake_spec.rb create mode 100644 spec/tasks/gitlab/refresh_project_statistics_build_artifacts_size_rake_spec.rb create mode 100644 spec/tasks/gitlab/setup_rake_spec.rb create mode 100644 spec/tasks/rubocop_rake_spec.rb delete mode 100644 spec/tooling/danger/changelog_spec.rb create mode 100644 spec/validators/import/gitlab_projects/remote_file_validator_spec.rb create mode 100644 spec/views/admin/broadcast_messages/index.html.haml_spec.rb create mode 100644 spec/views/layouts/_header_search.html.haml_spec.rb delete mode 100644 spec/views/shared/_gl_toggle.haml_spec.rb create mode 100644 spec/workers/database/batched_background_migration/ci_database_worker_spec.rb create mode 100644 spec/workers/projects/refresh_build_artifacts_size_statistics_worker_spec.rb create mode 100644 spec/workers/projects/schedule_refresh_build_artifacts_size_statistics_worker_spec.rb create mode 100644 spec/workers/quality/test_data_cleanup_worker_spec.rb (limited to 'spec') diff --git a/spec/commands/sidekiq_cluster/cli_spec.rb b/spec/commands/sidekiq_cluster/cli_spec.rb index 15b738cacd1..2cb3f67b03d 100644 --- a/spec/commands/sidekiq_cluster/cli_spec.rb +++ b/spec/commands/sidekiq_cluster/cli_spec.rb @@ -5,8 +5,11 @@ require 'rspec-parameterized' require_relative '../../support/stub_settings_source' require_relative '../../../sidekiq_cluster/cli' +require_relative '../../support/helpers/next_instance_of' RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubocop:disable RSpec/FilePath + include NextInstanceOf + let(:cli) { described_class.new('/dev/null') } let(:timeout) { Gitlab::SidekiqCluster::DEFAULT_SOFT_TIMEOUT_SECONDS } let(:default_options) do @@ -37,6 +40,8 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo } end + let(:supervisor) { instance_double(Gitlab::SidekiqCluster::SidekiqProcessSupervisor) } + before do stub_env('RAILS_ENV', 'test') @@ -44,8 +49,11 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo config_file.close allow(::Settings).to receive(:source).and_return(config_file.path) - ::Settings.reload! + + allow(Gitlab::ProcessManagement).to receive(:write_pid) + allow(Gitlab::SidekiqCluster::SidekiqProcessSupervisor).to receive(:instance).and_return(supervisor) + allow(supervisor).to receive(:supervise) end after do @@ -60,12 +68,6 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo end context 'with arguments' do - before do - allow(cli).to receive(:write_pid) - allow(cli).to receive(:trap_signals) - allow(cli).to receive(:start_loop) - end - it 'starts the Sidekiq workers' do expect(Gitlab::SidekiqCluster).to receive(:start) .with([['foo']], default_options) @@ -81,7 +83,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo .to receive(:worker_queues).and_return(worker_queues) expect(Gitlab::SidekiqCluster) - .to receive(:start).with([worker_queues], default_options) + .to receive(:start).with([worker_queues], default_options).and_return([]) cli.run(%w(*)) end @@ -135,6 +137,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo it 'when given', 'starts Sidekiq workers with given timeout' do expect(Gitlab::SidekiqCluster).to receive(:start) .with([['foo']], default_options.merge(timeout: 10)) + .and_return([]) cli.run(%w(foo --timeout 10)) end @@ -142,6 +145,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo it 'when not given', 'starts Sidekiq workers with default timeout' do expect(Gitlab::SidekiqCluster).to receive(:start) .with([['foo']], default_options.merge(timeout: Gitlab::SidekiqCluster::DEFAULT_SOFT_TIMEOUT_SECONDS)) + .and_return([]) cli.run(%w(foo)) end @@ -257,7 +261,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo .to receive(:worker_queues).and_return(worker_queues) expect(Gitlab::SidekiqCluster) - .to receive(:start).with([worker_queues], default_options) + .to receive(:start).with([worker_queues], default_options).and_return([]) cli.run(%w(--queue-selector *)) end @@ -292,16 +296,13 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo context 'starting the server' do context 'without --dryrun' do + before do + allow(Gitlab::SidekiqCluster).to receive(:start).and_return([]) + end + context 'when there are no sidekiq_health_checks settings set' do let(:sidekiq_exporter_enabled) { true } - before do - allow(Gitlab::SidekiqCluster).to receive(:start) - allow(cli).to receive(:write_pid) - allow(cli).to receive(:trap_signals) - allow(cli).to receive(:start_loop) - end - it 'does not start a sidekiq metrics server' do expect(MetricsServer).not_to receive(:fork) @@ -312,13 +313,6 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo context 'when the sidekiq_exporter.port setting is not set' do let(:sidekiq_exporter_enabled) { true } - before do - allow(Gitlab::SidekiqCluster).to receive(:start) - allow(cli).to receive(:write_pid) - allow(cli).to receive(:trap_signals) - allow(cli).to receive(:start_loop) - end - it 'does not start a sidekiq metrics server' do expect(MetricsServer).not_to receive(:fork) @@ -342,13 +336,6 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo } end - before do - allow(Gitlab::SidekiqCluster).to receive(:start) - allow(cli).to receive(:write_pid) - allow(cli).to receive(:trap_signals) - allow(cli).to receive(:start_loop) - end - it 'does not start a sidekiq metrics server' do expect(MetricsServer).not_to receive(:fork) @@ -368,13 +355,6 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo } end - before do - allow(Gitlab::SidekiqCluster).to receive(:start) - allow(cli).to receive(:write_pid) - allow(cli).to receive(:trap_signals) - allow(cli).to receive(:start_loop) - end - it 'does not start a sidekiq metrics server' do expect(MetricsServer).not_to receive(:fork) @@ -397,13 +377,6 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo end with_them do - before do - allow(Gitlab::SidekiqCluster).to receive(:start) - allow(cli).to receive(:write_pid) - allow(cli).to receive(:trap_signals) - allow(cli).to receive(:start_loop) - end - specify do if start_metrics_server expect(MetricsServer).to receive(:fork).with('sidekiq', metrics_dir: metrics_dir, wipe_metrics_dir: true, reset_signals: trapped_signals) @@ -415,6 +388,23 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo end end end + + context 'when a PID is specified' do + it 'writes the PID to a file' do + expect(Gitlab::ProcessManagement).to receive(:write_pid).with('/dev/null') + + cli.option_parser.parse!(%w(-P /dev/null)) + cli.run(%w(foo)) + end + end + + context 'when no PID is specified' do + it 'does not write a PID' do + expect(Gitlab::ProcessManagement).not_to receive(:write_pid) + + cli.run(%w(foo)) + end + end end context 'with --dryrun set' do @@ -427,130 +417,46 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, stub_settings_source: true do # rubo end end end - - context 'supervising the server' do - let(:sidekiq_exporter_enabled) { true } - let(:sidekiq_health_checks_port) { '3907' } - - before do - allow(cli).to receive(:sleep).with(a_kind_of(Numeric)) - allow(MetricsServer).to receive(:fork).and_return(99) - cli.start_metrics_server - end - - it 'stops the metrics server when one of the processes has been terminated' do - allow(Gitlab::ProcessManagement).to receive(:process_died?).and_return(false) - allow(Gitlab::ProcessManagement).to receive(:all_alive?).with(an_instance_of(Array)).and_return(false) - allow(Gitlab::ProcessManagement).to receive(:signal_processes).with(an_instance_of(Array), :TERM) - - expect(Process).to receive(:kill).with(:TERM, 99) - - cli.start_loop - end - - it 'starts the metrics server when it is down' do - allow(Gitlab::ProcessManagement).to receive(:process_died?).and_return(true) - allow(Gitlab::ProcessManagement).to receive(:all_alive?).with(an_instance_of(Array)).and_return(false) - allow(cli).to receive(:stop_metrics_server) - - expect(MetricsServer).to receive(:fork).with( - 'sidekiq', metrics_dir: metrics_dir, wipe_metrics_dir: false, reset_signals: trapped_signals - ) - - cli.start_loop - end - end - end - end - - describe '#write_pid' do - context 'when a PID is specified' do - it 'writes the PID to a file' do - expect(Gitlab::ProcessManagement).to receive(:write_pid).with('/dev/null') - - cli.option_parser.parse!(%w(-P /dev/null)) - cli.write_pid - end end - context 'when no PID is specified' do - it 'does not write a PID' do - expect(Gitlab::ProcessManagement).not_to receive(:write_pid) + context 'supervising the cluster' do + let(:sidekiq_exporter_enabled) { true } + let(:sidekiq_health_checks_port) { '3907' } + let(:metrics_server_pid) { 99 } + let(:sidekiq_worker_pids) { [2, 42] } - cli.write_pid - end - end - end - - describe '#wait_for_termination' do - it 'waits for termination of all sub-processes and succeeds after 3 checks' do - expect(Gitlab::ProcessManagement).to receive(:any_alive?) - .with(an_instance_of(Array)).and_return(true, true, true, false) - - expect(Gitlab::ProcessManagement).to receive(:pids_alive) - .with([]).and_return([]) - - expect(Gitlab::ProcessManagement).to receive(:signal_processes) - .with([], "-KILL") - - stub_const("Gitlab::SidekiqCluster::CHECK_TERMINATE_INTERVAL_SECONDS", 0.1) - allow(cli).to receive(:terminate_timeout_seconds) { 1 } - - cli.wait_for_termination - end - - context 'with hanging workers' do before do - expect(cli).to receive(:write_pid) - expect(cli).to receive(:trap_signals) - expect(cli).to receive(:start_loop) + allow(Gitlab::SidekiqCluster).to receive(:start).and_return(sidekiq_worker_pids) end - it 'hard kills workers after timeout expires' do - worker_pids = [101, 102, 103] - expect(Gitlab::SidekiqCluster).to receive(:start) - .with([['foo']], default_options) - .and_return(worker_pids) - - expect(Gitlab::ProcessManagement).to receive(:any_alive?) - .with(worker_pids).and_return(true).at_least(10).times - - expect(Gitlab::ProcessManagement).to receive(:pids_alive) - .with(worker_pids).and_return([102]) - - expect(Gitlab::ProcessManagement).to receive(:signal_processes) - .with([102], "-KILL") + it 'stops the entire process cluster if one of the workers has been terminated' do + expect(supervisor).to receive(:alive).and_return(true) + expect(supervisor).to receive(:supervise).and_yield([2]) + expect(MetricsServer).to receive(:fork).once.and_return(metrics_server_pid) + expect(Gitlab::ProcessManagement).to receive(:signal_processes).with([42, 99], :TERM) cli.run(%w(foo)) - - stub_const("Gitlab::SidekiqCluster::CHECK_TERMINATE_INTERVAL_SECONDS", 0.1) - allow(cli).to receive(:terminate_timeout_seconds) { 1 } - - cli.wait_for_termination end - end - end - - describe '#trap_signals' do - it 'traps termination and sidekiq specific signals' do - expect(Gitlab::ProcessManagement).to receive(:trap_signals).with(%i[INT TERM]) - expect(Gitlab::ProcessManagement).to receive(:trap_signals).with(%i[TTIN USR1 USR2 HUP]) - cli.trap_signals - end - end - - describe '#start_loop' do - it 'runs until one of the processes has been terminated' do - allow(cli).to receive(:sleep).with(a_kind_of(Numeric)) + context 'when the supervisor is alive' do + it 'restarts the metrics server when it is down' do + expect(supervisor).to receive(:alive).and_return(true) + expect(supervisor).to receive(:supervise).and_yield([metrics_server_pid]) + expect(MetricsServer).to receive(:fork).twice.and_return(metrics_server_pid) - expect(Gitlab::ProcessManagement).to receive(:all_alive?) - .with(an_instance_of(Array)).and_return(false) + cli.run(%w(foo)) + end + end - expect(Gitlab::ProcessManagement).to receive(:signal_processes) - .with(an_instance_of(Array), :TERM) + context 'when the supervisor is shutting down' do + it 'does not restart the metrics server' do + expect(supervisor).to receive(:alive).and_return(false) + expect(supervisor).to receive(:supervise).and_yield([metrics_server_pid]) + expect(MetricsServer).to receive(:fork).once.and_return(metrics_server_pid) - cli.start_loop + cli.run(%w(foo)) + end + end end end end diff --git a/spec/components/pajamas/component_spec.rb b/spec/components/pajamas/component_spec.rb new file mode 100644 index 00000000000..96f6b43bac1 --- /dev/null +++ b/spec/components/pajamas/component_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Pajamas::Component do + describe '#filter_attribute' do + let(:allowed) { %w[default something] } + + it 'returns default value when no value is given' do + value = subject.send(:filter_attribute, nil, allowed, default: 'default') + + expect(value).to eq('default') + end + + it 'returns default value when invalid value is given' do + value = subject.send(:filter_attribute, 'invalid', allowed, default: 'default') + + expect(value).to eq('default') + end + + it 'returns given value when it is part of allowed list' do + value = subject.send(:filter_attribute, 'something', allowed, default: 'default') + + expect(value).to eq('something') + end + end +end diff --git a/spec/components/pajamas/toggle_component_spec.rb b/spec/components/pajamas/toggle_component_spec.rb new file mode 100644 index 00000000000..b2727dec318 --- /dev/null +++ b/spec/components/pajamas/toggle_component_spec.rb @@ -0,0 +1,107 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe Pajamas::ToggleComponent, type: :component do + context 'with defaults' do + before do + render_inline described_class.new(classes: 'js-feature-toggle') + end + + it 'renders a toggle container with provided class' do + expect(rendered_component).to have_selector "[class='js-feature-toggle']" + end + + it 'does not set a name' do + expect(rendered_component).not_to have_selector('[data-name]') + end + + it 'sets default is-checked attributes' do + expect(rendered_component).to have_selector('[data-is-checked="false"]') + end + + it 'sets default disabled attributes' do + expect(rendered_component).to have_selector('[data-disabled="false"]') + end + + it 'sets default is-loading attributes' do + expect(rendered_component).to have_selector('[data-is-loading="false"]') + end + + it 'does not set a label' do + expect(rendered_component).not_to have_selector('[data-label]') + end + + it 'does not set a label position' do + expect(rendered_component).not_to have_selector('[data-label-position]') + end + end + + context 'with custom options' do + before do + render_inline described_class.new( + classes: 'js-custom-gl-toggle', + name: 'toggle-name', + is_checked: true, + is_disabled: true, + is_loading: true, + label: 'Custom label', + label_position: :top, + data: { + foo: 'bar' + }) + end + + it 'sets the custom class' do + expect(rendered_component).to have_selector('.js-custom-gl-toggle') + end + + it 'sets the custom name' do + expect(rendered_component).to have_selector('[data-name="toggle-name"]') + end + + it 'sets the custom is-checked attributes' do + expect(rendered_component).to have_selector('[data-is-checked="true"]') + end + + it 'sets the custom disabled attributes' do + expect(rendered_component).to have_selector('[data-disabled="true"]') + end + + it 'sets the custom is-loading attributes' do + expect(rendered_component).to have_selector('[data-is-loading="true"]') + end + + it 'sets the custom label' do + expect(rendered_component).to have_selector('[data-label="Custom label"]') + end + + it 'sets the custom label position' do + expect(rendered_component).to have_selector('[data-label-position="top"]') + end + + it 'sets custom data attributes' do + expect(rendered_component).to have_selector('[data-foo="bar"]') + end + end + + context 'with setting label_position' do + using RSpec::Parameterized::TableSyntax + + where(:position, :count) do + :top | 1 + :left | 1 + :hidden | 1 + :bogus | 0 + 'bogus' | 0 + nil | 0 + end + + before do + render_inline described_class.new(classes: '_class_', label_position: position) + end + + with_them do + it { expect(rendered_component).to have_selector("[data-label-position='#{position}']", count: count) } + end + end +end diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb index fb4c0970653..f7b2bab3615 100644 --- a/spec/controllers/admin/application_settings_controller_spec.rb +++ b/spec/controllers/admin/application_settings_controller_spec.rb @@ -81,6 +81,18 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set expect(body).to include('counts') expect(response).to have_gitlab_http_status(:ok) end + + describe 'usage data counter' do + let(:counter) { Gitlab::UsageDataCounters::ServiceUsageDataCounter } + + it 'incremented when json generated' do + expect { get :usage_data, format: :json }.to change { counter.read(:download_payload_click) }.by(1) + end + + it 'not incremented when html format requested' do + expect { get :usage_data }.not_to change { counter.read(:download_payload_click) } + end + end end describe 'PUT #update' do diff --git a/spec/controllers/admin/clusters_controller_spec.rb b/spec/controllers/admin/clusters_controller_spec.rb index 25c4830a79a..fed9d2e8588 100644 --- a/spec/controllers/admin/clusters_controller_spec.rb +++ b/spec/controllers/admin/clusters_controller_spec.rb @@ -27,6 +27,10 @@ RSpec.describe Admin::ClustersController do create(:cluster, :disabled, :provided_by_gcp, :production_environment, :instance) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { get_index } + end + it 'lists available clusters and displays html' do get_index @@ -105,6 +109,10 @@ RSpec.describe Admin::ClustersController do get :new, params: { provider: provider } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality for new cluster' do context 'when omniauth has been configured' do let(:key) { 'secret-key' } @@ -226,6 +234,10 @@ RSpec.describe Admin::ClustersController do post :create_gcp, params: params end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { post_create_gcp } + end + describe 'functionality' do context 'when access token is valid' do before do @@ -318,6 +330,10 @@ RSpec.describe Admin::ClustersController do post :create_aws, params: params end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { post_create_aws } + end + it 'creates a new cluster' do expect(ClusterProvisionWorker).to receive(:perform_async) expect { post_create_aws }.to change { Clusters::Cluster.count } @@ -375,6 +391,10 @@ RSpec.describe Admin::ClustersController do post :create_user, params: params end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { post_create_user } + end + describe 'functionality' do context 'when creates a cluster' do it 'creates a new cluster' do @@ -445,6 +465,10 @@ RSpec.describe Admin::ClustersController do post :authorize_aws_role, params: params end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + before do allow(Clusters::Aws::FetchCredentialsService).to receive(:new) .and_return(double(execute: double)) @@ -495,6 +519,10 @@ RSpec.describe Admin::ClustersController do delete :clear_cache, params: { id: cluster } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + it 'deletes the namespaces associated with the cluster' do expect { go }.to change { Clusters::KubernetesNamespace.count } @@ -520,6 +548,10 @@ RSpec.describe Admin::ClustersController do format: :json end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { get_cluster_status } + end + describe 'functionality' do it 'responds with matching schema' do get_cluster_status @@ -555,6 +587,10 @@ RSpec.describe Admin::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { get_show } + end + describe 'functionality' do render_views @@ -603,6 +639,10 @@ RSpec.describe Admin::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { put_update } + end + it 'updates and redirects back to show page' do put_update @@ -694,6 +734,10 @@ RSpec.describe Admin::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { delete_destroy } + end + describe 'functionality' do context 'when cluster is provided by GCP' do context 'when cluster is created' do diff --git a/spec/controllers/admin/runner_projects_controller_spec.rb b/spec/controllers/admin/runner_projects_controller_spec.rb index e5f63025cf7..98f961f66bb 100644 --- a/spec/controllers/admin/runner_projects_controller_spec.rb +++ b/spec/controllers/admin/runner_projects_controller_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Admin::RunnerProjectsController do describe '#create' do let(:project_id) { project.path } - subject do + subject(:send_create) do post :create, params: { namespace_id: group.path, project_id: project_id, @@ -25,7 +25,7 @@ RSpec.describe Admin::RunnerProjectsController do let(:project_runner) { create(:ci_runner, :project, projects: [project]) } it 'redirects to the admin runner edit page' do - subject + send_create expect(response).to have_gitlab_http_status(:redirect) expect(response).to redirect_to edit_admin_runner_url(project_runner) @@ -37,7 +37,7 @@ RSpec.describe Admin::RunnerProjectsController do let(:source_project) { create(:project) } it 'redirects to the admin runner edit page' do - subject + send_create expect(response).to have_gitlab_http_status(:redirect) expect(response).to redirect_to edit_admin_runner_url(project_runner) @@ -50,7 +50,42 @@ RSpec.describe Admin::RunnerProjectsController do let(:project_id) { 0 } it 'shows 404 for unknown project' do - subject + send_create + + expect(response).to have_gitlab_http_status(:not_found) + end + end + end + + describe '#destroy' do + let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project]) } + + let(:project_id) { project.path } + + subject(:send_destroy) do + delete :destroy, params: { + namespace_id: group.path, + project_id: project_id, + id: runner_project_id + } + end + + context 'unassigning runner from project' do + let(:runner_project_id) { project_runner.runner_projects.last.id } + + it 'redirects to the admin runner edit page' do + send_destroy + + expect(response).to have_gitlab_http_status(:redirect) + expect(response).to redirect_to edit_admin_runner_url(project_runner) + end + end + + context 'for unknown project runner relationship' do + let(:runner_project_id) { 0 } + + it 'shows 404 for unknown project runner relationship' do + send_destroy expect(response).to have_gitlab_http_status(:not_found) end diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb index 74f352e8ec2..8f70cb32d3e 100644 --- a/spec/controllers/admin/runners_controller_spec.rb +++ b/spec/controllers/admin/runners_controller_spec.rb @@ -105,7 +105,7 @@ RSpec.describe Admin::RunnersController do describe '#destroy' do it 'destroys the runner' do - expect_next_instance_of(Ci::UnregisterRunnerService, runner) do |service| + expect_next_instance_of(Ci::Runners::UnregisterRunnerService, runner, user) do |service| expect(service).to receive(:execute).once.and_call_original end diff --git a/spec/controllers/admin/topics_controller_spec.rb b/spec/controllers/admin/topics_controller_spec.rb index 6d66cb43338..ea510f916da 100644 --- a/spec/controllers/admin/topics_controller_spec.rb +++ b/spec/controllers/admin/topics_controller_spec.rb @@ -88,6 +88,13 @@ RSpec.describe Admin::TopicsController do expect(errors).to contain_exactly(errors.full_message(:name, I18n.t('errors.messages.blank'))) end + it 'shows error message if topic not unique (case insensitive)' do + post :create, params: { projects_topic: { name: topic.name.upcase } } + + errors = assigns[:topic].errors + expect(errors).to contain_exactly(errors.full_message(:name, I18n.t('errors.messages.taken'))) + end + context 'as a normal user' do before do sign_in(user) @@ -116,6 +123,15 @@ RSpec.describe Admin::TopicsController do expect(errors).to contain_exactly(errors.full_message(:name, I18n.t('errors.messages.blank'))) end + it 'shows error message if topic not unique (case insensitive)' do + other_topic = create(:topic, name: 'other-topic') + + put :update, params: { id: topic.id, projects_topic: { name: other_topic.name.upcase } } + + errors = assigns[:topic].errors + expect(errors).to contain_exactly(errors.full_message(:name, I18n.t('errors.messages.taken'))) + end + context 'as a normal user' do before do sign_in(user) diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 004bea02580..ddd80b67639 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -501,6 +501,7 @@ RSpec.describe ApplicationController do describe '#append_info_to_payload' do controller(described_class) do attr_reader :last_payload + urgency :high, [:foo] def index @@ -1058,15 +1059,25 @@ RSpec.describe ApplicationController do describe 'setting permissions-policy header' do controller do skip_before_action :authenticate_user! + before_action :redirect_to_example, only: [:redirect] def index render html: 'It is a flock of sheep, not a floc of sheep.' end + + def redirect + raise 'Should not be reached' + end + + def redirect_to_example + redirect_to('https://example.com') + end end before do routes.draw do get 'index' => 'anonymous#index' + get 'redirect' => 'anonymous#redirect' end end @@ -1092,6 +1103,13 @@ RSpec.describe ApplicationController do expect(response.headers['Permissions-Policy']).to eq('interest-cohort=()') end + + it 'sets the Permissions-Policy header even when redirected before_action' do + get :redirect + + expect(response).to have_gitlab_http_status(:redirect) + expect(response.headers['Permissions-Policy']).to eq('interest-cohort=()') + end end end end diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb index 533d3896ee6..0a809e80fcd 100644 --- a/spec/controllers/autocomplete_controller_spec.rb +++ b/spec/controllers/autocomplete_controller_spec.rb @@ -235,7 +235,7 @@ RSpec.describe AutocompleteController do end end - it_behaves_like 'rate limited endpoint', rate_limit_key: :user_email_lookup do + it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do let(:current_user) { user } def request diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb index 29141582c6f..95334974e66 100644 --- a/spec/controllers/boards/lists_controller_spec.rb +++ b/spec/controllers/boards/lists_controller_spec.rb @@ -208,7 +208,7 @@ RSpec.describe Boards::ListsController do sign_in(user) params = { namespace_id: project.namespace.to_param, - project_id: project, + project_id: project.id, board_id: board.to_param, id: list.to_param, list: { position: position }, @@ -221,7 +221,7 @@ RSpec.describe Boards::ListsController do sign_in(user) params = { namespace_id: project.namespace.to_param, - project_id: project, + project_id: project.id, board_id: board.to_param, id: list.to_param, list: setting, diff --git a/spec/controllers/concerns/product_analytics_tracking_spec.rb b/spec/controllers/concerns/product_analytics_tracking_spec.rb new file mode 100644 index 00000000000..250cc3cf2cf --- /dev/null +++ b/spec/controllers/concerns/product_analytics_tracking_spec.rb @@ -0,0 +1,171 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe ProductAnalyticsTracking, :snowplow do + include TrackingHelpers + include SnowplowHelpers + + let(:user) { create(:user) } + let!(:group) { create(:group) } + + before do + allow(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event) + end + + controller(ApplicationController) do + include ProductAnalyticsTracking + + skip_before_action :authenticate_user!, only: :show + track_event(:index, :show, name: 'g_analytics_valuestream', destinations: [:redis_hll, :snowplow], + conditions: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id } + + def index + render html: 'index' + end + + def new + render html: 'new' + end + + def show + render html: 'show' + end + + def get_custom_id + 'some_custom_id' + end + + private + + def tracking_namespace_source + Group.first + end + + def custom_condition_one? + true + end + + def custom_condition_two? + true + end + end + + def expect_tracking(user: self.user) + expect(Gitlab::UsageDataCounters::HLLRedisCounter).to have_received(:track_event) + .with('g_analytics_valuestream', values: instance_of(String)) + + expect_snowplow_event( + category: anything, + action: 'g_analytics_valuestream', + namespace: group, + user: user + ) + end + + def expect_no_tracking + expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) + + expect_no_snowplow_event + end + + context 'when user is logged in' do + before do + sign_in(user) + end + + it 'tracks the event' do + get :index + + expect_tracking + end + + context 'when FF is disabled' do + before do + stub_feature_flags(route_hll_to_snowplow: false) + end + + it 'doesnt track snowplow event' do + get :index + + expect_no_snowplow_event + end + end + + it 'tracks the event if DNT is not enabled' do + stub_do_not_track('0') + + get :index + + expect_tracking + end + + it 'does not track the event if DNT is enabled' do + stub_do_not_track('1') + + get :index + + expect_no_tracking + end + + it 'does not track the event if the format is not HTML' do + get :index, format: :json + + expect_no_tracking + end + + it 'does not track the event if a custom condition returns false' do + allow(controller).to receive(:custom_condition_two?).and_return(false) + + get :index + + expect_no_tracking + end + + it 'does not track the event for untracked actions' do + get :new + + expect_no_tracking + end + end + + context 'when user is not logged in' do + let(:visitor_id) { SecureRandom.uuid } + + it 'tracks the event when there is a visitor id' do + cookies[:visitor_id] = { value: visitor_id, expires: 24.months } + + get :show, params: { id: 1 } + + expect_tracking(user: nil) + end + end + + context 'when user is not logged in and there is no visitor_id' do + it 'does not track the event' do + get :index + + expect_no_tracking + end + + it 'tracks the event when there is custom id' do + get :show, params: { id: 1 } + + expect_tracking(user: nil) + end + + it 'does not track the HLL event when there is no custom id' do + allow(controller).to receive(:get_custom_id).and_return(nil) + + get :show, params: { id: 2 } + + expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event) + expect_snowplow_event( + category: anything, + action: 'g_analytics_valuestream', + namespace: group, + user: nil + ) + end + end +end diff --git a/spec/controllers/concerns/spammable_actions/akismet_mark_as_spam_action_spec.rb b/spec/controllers/concerns/spammable_actions/akismet_mark_as_spam_action_spec.rb index 7c10dccdcb9..caa0fa2d437 100644 --- a/spec/controllers/concerns/spammable_actions/akismet_mark_as_spam_action_spec.rb +++ b/spec/controllers/concerns/spammable_actions/akismet_mark_as_spam_action_spec.rb @@ -7,12 +7,6 @@ RSpec.describe SpammableActions::AkismetMarkAsSpamAction do controller(ActionController::Base) do include SpammableActions::AkismetMarkAsSpamAction - - private - - def spammable_path - '/fake_spammable_path' - end end let(:spammable_type) { 'SpammableType' } @@ -22,7 +16,6 @@ RSpec.describe SpammableActions::AkismetMarkAsSpamAction do before do allow(Gitlab::Recaptcha).to receive(:load_configurations!) { true } routes.draw { get 'mark_as_spam' => 'anonymous#mark_as_spam' } - allow(controller).to receive(:spammable) { spammable } allow(controller).to receive(:current_user) { double(:current_user, admin?: admin) } allow(controller).to receive(:current_user).and_return(current_user) end @@ -31,6 +24,9 @@ RSpec.describe SpammableActions::AkismetMarkAsSpamAction do subject { post :mark_as_spam } before do + allow(controller).to receive(:spammable) { spammable } + allow(controller).to receive(:spammable_path) { '/fake_spammable_path' } + expect_next(Spam::AkismetMarkAsSpamService, target: spammable) .to receive(:execute).and_return(execute_result) end @@ -68,4 +64,16 @@ RSpec.describe SpammableActions::AkismetMarkAsSpamAction do end end end + + describe '#spammable' do + it 'raises when unimplemented' do + expect { controller.send(:spammable) }.to raise_error(NotImplementedError) + end + end + + describe '#spammable_path' do + it 'raises when unimplemented' do + expect { controller.send(:spammable_path) }.to raise_error(NotImplementedError) + end + end end diff --git a/spec/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support_spec.rb b/spec/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support_spec.rb index 53a78326397..c5d17e0232c 100644 --- a/spec/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support_spec.rb +++ b/spec/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support_spec.rb @@ -7,7 +7,7 @@ RSpec.describe SpammableActions::CaptchaCheck::HtmlFormatActionsSupport do include SpammableActions::CaptchaCheck::HtmlFormatActionsSupport def create - with_captcha_check_html_format { render :some_rendered_view } + with_captcha_check_html_format(spammable: spammable) { render :some_rendered_view } end end diff --git a/spec/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support_spec.rb b/spec/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support_spec.rb index d7a44351ad8..7796d9d1273 100644 --- a/spec/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support_spec.rb +++ b/spec/controllers/concerns/spammable_actions/captcha_check/json_format_actions_support_spec.rb @@ -7,7 +7,7 @@ RSpec.describe SpammableActions::CaptchaCheck::JsonFormatActionsSupport do include SpammableActions::CaptchaCheck::JsonFormatActionsSupport def some_action - with_captcha_check_json_format { render :some_rendered_view } + with_captcha_check_json_format(spammable: spammable) { render :some_rendered_view } end end diff --git a/spec/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support_spec.rb b/spec/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support_spec.rb new file mode 100644 index 00000000000..07c564b555e --- /dev/null +++ b/spec/controllers/concerns/spammable_actions/captcha_check/rest_api_actions_support_spec.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe SpammableActions::CaptchaCheck::RestApiActionsSupport do + include Rack::Test::Methods + + subject do + Class.new(Grape::API) do + helpers API::Helpers + helpers SpammableActions::CaptchaCheck::RestApiActionsSupport + + get ':id' do + # NOTE: This was the only way that seemed to work to inject the mock spammable into the + # Grape rack app instance. If there's a better way, improvements are welcome. + spammable = Object.fake_spammable_factory + with_captcha_check_rest_api(spammable: spammable) do + render_api_error!(spammable.errors, 400) + end + end + end + end + + def app + subject + end + + before do + allow(Gitlab::Recaptcha).to receive(:load_configurations!) { true } + end + + describe '#with_captcha_check_json_format' do + let(:spammable) { instance_double(Snippet) } + + before do + expect(spammable).to receive(:render_recaptcha?).at_least(:once) { render_recaptcha } + allow(Object).to receive(:fake_spammable_factory) { spammable } + end + + context 'when spammable.render_recaptcha? is true' do + let(:render_recaptcha) { true } + let(:spam_log) { instance_double(SpamLog, id: 1) } + let(:spammable) { instance_double(Snippet, spam?: true, render_recaptcha?: render_recaptcha, spam_log: spam_log) } + let(:recaptcha_site_key) { 'abc123' } + let(:err_msg) { 'You gotta solve the CAPTCHA' } + let(:spam_action_response_fields) do + { + spam: true, + needs_captcha_response: render_recaptcha, + spam_log_id: 1, + captcha_site_key: recaptcha_site_key + } + end + + it 'renders json containing spam_action_response_fields' do + allow(spammable).to receive_message_chain('errors.full_messages.to_sentence') { err_msg } + allow(Gitlab::CurrentSettings).to receive(:recaptcha_site_key) { recaptcha_site_key } + response = get '/test' + expected_response = { + 'needs_captcha_response' => render_recaptcha, + 'spam_log_id' => 1, + 'captcha_site_key' => recaptcha_site_key, + 'message' => { 'error' => err_msg } + } + expect(Gitlab::Json.parse(response.body)).to eq(expected_response) + expect(response.status).to eq(409) + end + end + + context 'when spammable.render_recaptcha? is false' do + let(:render_recaptcha) { false } + let(:errors) { { 'base' => "It's definitely spam" } } + + it 'yields to block' do + allow(spammable).to receive(:errors) { errors } + + response = get 'test' + expected_response = { + 'message' => errors + } + expect(Gitlab::Json.parse(response.body)).to eq(expected_response) + expect(response.status).to eq(400) + end + end + end +end diff --git a/spec/controllers/confirmations_controller_spec.rb b/spec/controllers/confirmations_controller_spec.rb index 1c7f8de32bb..3b5afbcebca 100644 --- a/spec/controllers/confirmations_controller_spec.rb +++ b/spec/controllers/confirmations_controller_spec.rb @@ -152,7 +152,7 @@ RSpec.describe ConfirmationsController do perform_request expect(response).to render_template(:new) - expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' + expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') end it 'successfully sends password reset when reCAPTCHA is solved' do diff --git a/spec/controllers/dashboard_controller_spec.rb b/spec/controllers/dashboard_controller_spec.rb index 8fae617ea65..aed310531e6 100644 --- a/spec/controllers/dashboard_controller_spec.rb +++ b/spec/controllers/dashboard_controller_spec.rb @@ -13,7 +13,22 @@ RSpec.describe DashboardController do end describe 'GET issues' do - it_behaves_like 'issuables list meta-data', :issue, :issues + context 'when issues_full_text_search is disabled' do + before do + stub_feature_flags(issues_full_text_search: false) + end + + it_behaves_like 'issuables list meta-data', :issue, :issues + end + + context 'when issues_full_text_search is enabled' do + before do + stub_feature_flags(issues_full_text_search: true) + end + + it_behaves_like 'issuables list meta-data', :issue, :issues + end + it_behaves_like 'issuables requiring filter', :issues end @@ -83,25 +98,49 @@ RSpec.describe DashboardController do context "no filters" do let(:params) { {} } + shared_examples_for 'no filters are set' do + it 'sets @no_filters_set to true' do + expect(assigns[:no_filters_set]).to eq(true) + end + end + + it_behaves_like 'no filters are set' + + context 'when key is present but value is not' do + let(:params) { { author_username: nil } } + + it_behaves_like 'no filters are set' + end + + context 'when in param is set but no search' do + let(:params) { { in: 'title' } } + + it_behaves_like 'no filters are set' + end + end + + shared_examples_for 'filters are set' do it 'sets @no_filters_set to false' do - expect(assigns[:no_filters_set]).to eq(true) + expect(assigns[:no_filters_set]).to eq(false) end end context "scalar filters" do let(:params) { { author_id: user.id } } - it 'sets @no_filters_set to false' do - expect(assigns[:no_filters_set]).to eq(false) - end + it_behaves_like 'filters are set' end context "array filters" do let(:params) { { label_name: ['bug'] } } - it 'sets @no_filters_set to false' do - expect(assigns[:no_filters_set]).to eq(false) - end + it_behaves_like 'filters are set' + end + + context 'search' do + let(:params) { { search: 'test' } } + + it_behaves_like 'filters are set' end end end diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb index 95f60156c40..dbaed8aaa19 100644 --- a/spec/controllers/graphql_controller_spec.rb +++ b/spec/controllers/graphql_controller_spec.rb @@ -139,8 +139,45 @@ RSpec.describe GraphqlController do context 'when user uses an API token' do let(:user) { create(:user, last_activity_on: Date.yesterday) } let(:token) { create(:personal_access_token, user: user, scopes: [:api]) } + let(:query) { '{ __typename }' } - subject { post :execute, params: { access_token: token.token } } + subject { post :execute, params: { query: query, access_token: token.token } } + + context 'when the user is a project bot' do + let(:user) { create(:user, :project_bot, last_activity_on: Date.yesterday) } + + it 'updates the users last_activity_on field' do + expect { subject }.to change { user.reload.last_activity_on } + end + + it "sets context's sessionless value as true" do + subject + + expect(assigns(:context)[:is_sessionless_user]).to be true + end + + it 'executes a simple query with no errors' do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to eq({ 'data' => { '__typename' => 'Query' } }) + end + + it 'can access resources the project_bot has access to' do + project_a, project_b = create_list(:project, 2, :private) + project_a.add_developer(user) + + post :execute, params: { query: <<~GQL, access_token: token.token } + query { + a: project(fullPath: "#{project_a.full_path}") { name } + b: project(fullPath: "#{project_b.full_path}") { name } + } + GQL + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to eq({ 'data' => { 'a' => { 'name' => project_a.name }, 'b' => nil } }) + end + end it 'updates the users last_activity_on field' do expect { subject }.to change { user.reload.last_activity_on } diff --git a/spec/controllers/groups/clusters_controller_spec.rb b/spec/controllers/groups/clusters_controller_spec.rb index 710e983dfbd..4eeae64b760 100644 --- a/spec/controllers/groups/clusters_controller_spec.rb +++ b/spec/controllers/groups/clusters_controller_spec.rb @@ -32,6 +32,10 @@ RSpec.describe Groups::ClustersController do create(:cluster, :disabled, :provided_by_gcp, :production_environment, cluster_type: :group_type, groups: [group]) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + it 'lists available clusters and renders html' do go @@ -116,6 +120,10 @@ RSpec.describe Groups::ClustersController do get :new, params: { group_id: group, provider: provider } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality for new cluster' do context 'when omniauth has been configured' do let(:key) { 'secret-key' } @@ -255,6 +263,10 @@ RSpec.describe Groups::ClustersController do post :create_gcp, params: params.merge(group_id: group) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do context 'when access token is valid' do before do @@ -349,6 +361,10 @@ RSpec.describe Groups::ClustersController do post :create_user, params: params.merge(group_id: group) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do context 'when creates a cluster' do it 'creates a new cluster' do @@ -457,6 +473,10 @@ RSpec.describe Groups::ClustersController do post :create_aws, params: params.merge(group_id: group) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { post_create_aws } + end + it 'creates a new cluster' do expect(ClusterProvisionWorker).to receive(:perform_async) expect { post_create_aws }.to change { Clusters::Cluster.count } @@ -519,6 +539,10 @@ RSpec.describe Groups::ClustersController do post :authorize_aws_role, params: params.merge(group_id: group) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + before do allow(Clusters::Aws::FetchCredentialsService).to receive(:new) .and_return(double(execute: double)) @@ -579,6 +603,10 @@ RSpec.describe Groups::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + it 'deletes the namespaces associated with the cluster' do expect { go }.to change { Clusters::KubernetesNamespace.count } @@ -611,6 +639,10 @@ RSpec.describe Groups::ClustersController do format: :json end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do it 'responds with matching schema' do go @@ -651,6 +683,10 @@ RSpec.describe Groups::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do render_views @@ -705,6 +741,10 @@ RSpec.describe Groups::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + it 'updates and redirects back to show page' do go @@ -802,6 +842,10 @@ RSpec.describe Groups::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do context 'when cluster is provided by GCP' do context 'when cluster is created' do diff --git a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb index 57a83da3425..61445603a2d 100644 --- a/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb +++ b/spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb @@ -170,6 +170,14 @@ RSpec.describe Groups::DependencyProxyForContainersController do end end + shared_examples 'namespace statistics refresh' do + it 'updates namespace statistics' do + expect(Groups::UpdateStatisticsWorker).to receive(:perform_async) + + subject + end + end + before do allow(Gitlab.config.dependency_proxy) .to receive(:enabled).and_return(true) @@ -403,13 +411,15 @@ RSpec.describe Groups::DependencyProxyForContainersController do context 'with a valid user' do before do group.add_guest(user) - - expect_next_found_instance_of(Group) do |instance| - expect(instance).to receive_message_chain(:dependency_proxy_blobs, :create!) - end end it_behaves_like 'a package tracking event', described_class.name, 'pull_blob' + + it 'creates a blob' do + expect { subject }.to change { group.dependency_proxy_blobs.count }.by(1) + end + + it_behaves_like 'namespace statistics refresh' end end @@ -473,6 +483,8 @@ RSpec.describe Groups::DependencyProxyForContainersController do expect(manifest.digest).to eq(digest) expect(manifest.file_name).to eq(file_name) end + + it_behaves_like 'namespace statistics refresh' end context 'with existing stale manifest' do @@ -483,6 +495,8 @@ RSpec.describe Groups::DependencyProxyForContainersController do expect { subject }.to change { group.dependency_proxy_manifests.count }.by(0) .and change { manifest.reload.digest }.from(old_digest).to(digest) end + + it_behaves_like 'namespace statistics refresh' end end end diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb index 04a9b9f5250..25d32436d58 100644 --- a/spec/controllers/groups/group_members_controller_spec.rb +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -38,12 +38,6 @@ RSpec.describe Groups::GroupMembersController do expect(assigns(:invited_members).map(&:invite_email)).to match_array(invited.map(&:invite_email)) end - it 'assigns skip groups' do - get :index, params: { group_id: group } - - expect(assigns(:skip_groups)).to match_array(group.related_group_ids) - end - it 'restricts search to one email' do get :index, params: { group_id: group, search_invited: invited.first.invite_email } @@ -68,11 +62,10 @@ RSpec.describe Groups::GroupMembersController do sign_in(user) end - it 'does not assign invited members or skip_groups', :aggregate_failures do + it 'does not assign invited members' do get :index, params: { group_id: group } expect(assigns(:invited_members)).to be_nil - expect(assigns(:skip_groups)).to be_nil end end @@ -106,107 +99,6 @@ RSpec.describe Groups::GroupMembersController do end end - describe 'POST create' do - let_it_be(:group_user) { create(:user) } - - before do - sign_in(user) - end - - context 'when user does not have enough rights' do - before do - group.add_developer(user) - end - - it 'returns 403', :aggregate_failures do - post :create, params: { - group_id: group, - user_ids: group_user.id, - access_level: Gitlab::Access::GUEST - } - - expect(response).to have_gitlab_http_status(:forbidden) - expect(group.users).not_to include group_user - end - end - - context 'when user has enough rights' do - before do - group.add_owner(user) - end - - it 'adds user to members', :aggregate_failures, :snowplow do - post :create, params: { - group_id: group, - user_ids: group_user.id, - access_level: Gitlab::Access::GUEST - } - - expect(controller).to set_flash.to 'Users were successfully added.' - expect(response).to redirect_to(group_group_members_path(group)) - expect(group.users).to include group_user - expect_snowplow_event( - category: 'Members::CreateService', - action: 'create_member', - label: 'group-members-page', - property: 'existing_user', - user: user - ) - end - - it 'adds no user to members', :aggregate_failures do - post :create, params: { - group_id: group, - user_ids: '', - access_level: Gitlab::Access::GUEST - } - - expect(controller).to set_flash.to 'No users specified.' - expect(response).to redirect_to(group_group_members_path(group)) - expect(group.users).not_to include group_user - end - end - - context 'access expiry date' do - before do - group.add_owner(user) - end - - subject do - post :create, params: { - group_id: group, - user_ids: group_user.id, - access_level: Gitlab::Access::GUEST, - expires_at: expires_at - } - end - - context 'when set to a date in the past' do - let(:expires_at) { 2.days.ago } - - it 'does not add user to members', :aggregate_failures do - subject - - expect(flash[:alert]).to include('Expires at cannot be a date in the past') - expect(response).to redirect_to(group_group_members_path(group)) - expect(group.users).not_to include group_user - end - end - - context 'when set to a date in the future' do - let(:expires_at) { 5.days.from_now } - - it 'adds user to members', :aggregate_failures do - subject - - expect(controller).to set_flash.to 'Users were successfully added.' - expect(response).to redirect_to(group_group_members_path(group)) - expect(group.users).to include group_user - end - end - end - end - describe 'PUT update' do let_it_be(:requester) { create(:group_member, :access_request, group: group) } @@ -515,14 +407,6 @@ RSpec.describe Groups::GroupMembersController do end end - describe 'POST #create' do - it 'is successful' do - post :create, params: { group_id: group, users: user, access_level: Gitlab::Access::GUEST } - - expect(response).to have_gitlab_http_status(:found) - end - end - describe 'PUT #update' do it 'is successful' do put :update, diff --git a/spec/controllers/groups/releases_controller_spec.rb b/spec/controllers/groups/releases_controller_spec.rb index 582a77b1c50..8b08f913e10 100644 --- a/spec/controllers/groups/releases_controller_spec.rb +++ b/spec/controllers/groups/releases_controller_spec.rb @@ -20,11 +20,11 @@ RSpec.describe Groups::ReleasesController do context 'as json' do let(:format) { :json } - subject { get :index, params: { group_id: group }, format: format } + subject(:index) { get :index, params: { group_id: group }, format: format } context 'json_response' do before do - subject + index end it 'returns an application/json content_type' do @@ -38,7 +38,7 @@ RSpec.describe Groups::ReleasesController do context 'the user is not authorized' do before do - subject + index end it 'does not return any releases' do @@ -54,12 +54,38 @@ RSpec.describe Groups::ReleasesController do it "returns all group's public and private project's releases as JSON, ordered by released_at" do sign_in(guest) - subject + index expect(json_response.map {|r| r['tag'] } ).to match_array(%w(p2 p1 v2 v1)) end end + context 'group_releases_finder_inoperator feature flag' do + before do + sign_in(guest) + end + + it 'calls old code when disabled' do + stub_feature_flags(group_releases_finder_inoperator: false) + + allow(ReleasesFinder).to receive(:new).and_call_original + + index + + expect(ReleasesFinder).to have_received(:new) + end + + it 'calls new code when enabled' do + stub_feature_flags(group_releases_finder_inoperator: true) + + allow(Releases::GroupReleasesFinder).to receive(:new).and_call_original + + index + + expect(Releases::GroupReleasesFinder).to have_received(:new) + end + end + context 'N+1 queries' do it 'avoids N+1 database queries' do control_count = ActiveRecord::QueryRecorder.new { subject }.count diff --git a/spec/controllers/groups/runners_controller_spec.rb b/spec/controllers/groups/runners_controller_spec.rb index 9f0615a96ae..b4950b93a3f 100644 --- a/spec/controllers/groups/runners_controller_spec.rb +++ b/spec/controllers/groups/runners_controller_spec.rb @@ -190,7 +190,7 @@ RSpec.describe Groups::RunnersController do end it 'destroys the runner and redirects' do - expect_next_instance_of(Ci::UnregisterRunnerService, runner) do |service| + expect_next_instance_of(Ci::Runners::UnregisterRunnerService, runner, user) do |service| expect(service).to receive(:execute).once.and_call_original end @@ -208,21 +208,39 @@ RSpec.describe Groups::RunnersController do end end - context 'when user is an owner and runner in multiple projects' do - let(:project_2) { create(:project, group: group) } + context 'with runner associated with multiple projects' do + let_it_be(:project_2) { create(:project, group: group) } + let(:runner_project_2) { create(:ci_runner, :project, projects: [project, project_2]) } let(:params_runner_project_2) { { group_id: group, id: runner_project_2 } } - before do - group.add_owner(user) + context 'when user is an admin', :enable_admin_mode do + let(:user) { create(:user, :admin) } + + before do + sign_in(user) + end + + it 'destroys the project runner and redirects' do + delete :destroy, params: params_runner_project_2 + + expect(response).to have_gitlab_http_status(:found) + expect(Ci::Runner.find_by(id: runner_project_2.id)).to be_nil + end end - it 'does not destroy the project runner' do - delete :destroy, params: params_runner_project_2 + context 'when user is an owner' do + before do + group.add_owner(user) + end + + it 'does not destroy the project runner' do + delete :destroy, params: params_runner_project_2 - expect(response).to have_gitlab_http_status(:found) - expect(flash[:alert]).to eq('Runner was not deleted because it is assigned to multiple projects.') - expect(Ci::Runner.find_by(id: runner_project_2.id)).to be_present + expect(response).to have_gitlab_http_status(:found) + expect(flash[:alert]).to eq('Runner cannot be deleted, please contact your administrator.') + expect(Ci::Runner.find_by(id: runner_project_2.id)).to be_present + end end end diff --git a/spec/controllers/jira_connect/events_controller_spec.rb b/spec/controllers/jira_connect/events_controller_spec.rb index 2a70a2ea683..2129b24b2fb 100644 --- a/spec/controllers/jira_connect/events_controller_spec.rb +++ b/spec/controllers/jira_connect/events_controller_spec.rb @@ -43,14 +43,15 @@ RSpec.describe JiraConnect::EventsController do end describe '#installed' do - let(:client_key) { '1234' } - let(:shared_secret) { 'secret' } + let_it_be(:client_key) { '1234' } + let_it_be(:shared_secret) { 'secret' } + let_it_be(:base_url) { 'https://test.atlassian.net' } let(:params) do { clientKey: client_key, sharedSecret: shared_secret, - baseUrl: 'https://test.atlassian.net' + baseUrl: base_url } end @@ -77,11 +78,11 @@ RSpec.describe JiraConnect::EventsController do expect(installation.base_url).to eq('https://test.atlassian.net') end - context 'when it is a version update and shared_secret is not sent' do + context 'when the shared_secret param is missing' do let(:params) do { clientKey: client_key, - baseUrl: 'https://test.atlassian.net' + baseUrl: base_url } end @@ -90,13 +91,48 @@ RSpec.describe JiraConnect::EventsController do expect(response).to have_gitlab_http_status(:unprocessable_entity) end + end + + context 'when an installation already exists' do + let_it_be(:installation) { create(:jira_connect_installation, base_url: base_url, client_key: client_key, shared_secret: shared_secret) } + + it 'validates the JWT token in authorization header and returns 200 without creating a new installation', :aggregate_failures do + expect { subject }.not_to change { JiraConnectInstallation.count } + expect(response).to have_gitlab_http_status(:ok) + end + + context 'when parameters include a new shared secret and base_url' do + let(:shared_secret) { 'new_secret' } + let(:base_url) { 'https://new_test.atlassian.net' } - context 'and an installation exists' do - let!(:installation) { create(:jira_connect_installation, client_key: client_key, shared_secret: shared_secret) } + it 'updates the installation', :aggregate_failures do + subject - it 'validates the JWT token in authorization header and returns 200 without creating a new installation' do - expect { subject }.not_to change { JiraConnectInstallation.count } expect(response).to have_gitlab_http_status(:ok) + expect(installation.reload).to have_attributes( + shared_secret: shared_secret, + base_url: base_url + ) + end + + context 'when the `jira_connect_installation_update` feature flag is disabled' do + before do + stub_feature_flags(jira_connect_installation_update: false) + end + + it 'does not update the installation', :aggregate_failures do + expect { subject }.not_to change { installation.reload.attributes } + expect(response).to have_gitlab_http_status(:ok) + end + end + end + + context 'when the new base_url is invalid' do + let(:base_url) { 'invalid' } + + it 'renders 422', :aggregate_failures do + expect { subject }.not_to change { installation.reload.base_url } + expect(response).to have_gitlab_http_status(:unprocessable_entity) end end end diff --git a/spec/controllers/passwords_controller_spec.rb b/spec/controllers/passwords_controller_spec.rb index 01c032d9e3b..82014282c6e 100644 --- a/spec/controllers/passwords_controller_spec.rb +++ b/spec/controllers/passwords_controller_spec.rb @@ -121,7 +121,7 @@ RSpec.describe PasswordsController do perform_request expect(response).to render_template(:new) - expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' + expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') end it 'successfully sends password reset when reCAPTCHA is solved' do diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb index 53efcc65066..cc807098498 100644 --- a/spec/controllers/projects/blob_controller_spec.rb +++ b/spec/controllers/projects/blob_controller_spec.rb @@ -366,8 +366,8 @@ RSpec.describe Projects::BlobController do it_behaves_like 'tracking unique hll events' do subject(:request) { put :update, params: default_params } - let(:target_id) { 'g_edit_by_sfe' } - let(:expected_type) { instance_of(Integer) } + let(:target_event) { 'g_edit_by_sfe' } + let(:expected_value) { instance_of(Integer) } end end @@ -516,8 +516,8 @@ RSpec.describe Projects::BlobController do subject(:request) { post :create, params: default_params } it_behaves_like 'tracking unique hll events' do - let(:target_id) { 'g_edit_by_sfe' } - let(:expected_type) { instance_of(Integer) } + let(:target_event) { 'g_edit_by_sfe' } + let(:expected_value) { instance_of(Integer) } end it 'redirects to blob' do @@ -525,24 +525,5 @@ RSpec.describe Projects::BlobController do expect(response).to redirect_to(project_blob_path(project, 'master/docs/EXAMPLE_FILE')) end - - context 'when code_quality_walkthrough param is present' do - let(:default_params) { super().merge(code_quality_walkthrough: true) } - - it 'redirects to the pipelines page' do - request - - expect(response).to redirect_to(project_pipelines_path(project, code_quality_walkthrough: true)) - end - - it 'creates an "commit_created" experiment tracking event' do - experiment = double(track: true) - expect(controller).to receive(:experiment).with(:code_quality_walkthrough, namespace: project.root_ancestor).and_return(experiment) - - request - - expect(experiment).to have_received(:track).with(:commit_created) - end - end end end diff --git a/spec/controllers/projects/ci/pipeline_editor_controller_spec.rb b/spec/controllers/projects/ci/pipeline_editor_controller_spec.rb index d55aad20689..37406d704f1 100644 --- a/spec/controllers/projects/ci/pipeline_editor_controller_spec.rb +++ b/spec/controllers/projects/ci/pipeline_editor_controller_spec.rb @@ -36,17 +36,5 @@ RSpec.describe Projects::Ci::PipelineEditorController do expect(response).to have_gitlab_http_status(:not_found) end end - - describe 'pipeline_editor_walkthrough experiment' do - before do - project.add_developer(user) - end - - subject(:action) { show_request } - - it_behaves_like 'tracks assignment and records the subject', :pipeline_editor_walkthrough, :namespace do - subject { project.namespace } - end - end end end diff --git a/spec/controllers/projects/ci/secure_files_controller_spec.rb b/spec/controllers/projects/ci/secure_files_controller_spec.rb new file mode 100644 index 00000000000..1138897bcc6 --- /dev/null +++ b/spec/controllers/projects/ci/secure_files_controller_spec.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::Ci::SecureFilesController do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + + subject(:show_request) { get :show, params: { namespace_id: project.namespace, project_id: project } } + + describe 'GET #show' do + context 'with enough privileges' do + before do + sign_in(user) + project.add_developer(user) + show_request + end + + it { expect(response).to have_gitlab_http_status(:ok) } + + it 'renders show page' do + expect(response).to render_template :show + end + end + + context 'without enough privileges' do + before do + sign_in(user) + project.add_reporter(user) + show_request + end + + it 'responds with 404' do + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'an unauthenticated user' do + before do + show_request + end + + it 'redirects to sign in' do + expect(response).to have_gitlab_http_status(:found) + expect(response).to redirect_to('/users/sign_in') + end + end + end +end diff --git a/spec/controllers/projects/clusters_controller_spec.rb b/spec/controllers/projects/clusters_controller_spec.rb index d0bef810ec8..44bdc958805 100644 --- a/spec/controllers/projects/clusters_controller_spec.rb +++ b/spec/controllers/projects/clusters_controller_spec.rb @@ -26,6 +26,10 @@ RSpec.describe Projects::ClustersController do let!(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let!(:disabled_cluster) { create(:cluster, :disabled, :provided_by_gcp, :production_environment, projects: [project]) } + include_examples ':certificate_based_clusters feature flag index responses' do + let(:subject) { go } + end + it 'lists available clusters and renders html' do go @@ -118,6 +122,10 @@ RSpec.describe Projects::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality for new cluster' do context 'when omniauth has been configured' do let(:key) { 'secret-key' } @@ -264,6 +272,10 @@ RSpec.describe Projects::ClustersController do post :create_gcp, params: params.merge(namespace_id: project.namespace, project_id: project) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do context 'when access token is valid' do before do @@ -360,6 +372,10 @@ RSpec.describe Projects::ClustersController do post :create_user, params: params.merge(namespace_id: project.namespace, project_id: project) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do context 'when creates a cluster' do it 'creates a new cluster' do @@ -477,6 +493,10 @@ RSpec.describe Projects::ClustersController do post :create_aws, params: params.merge(namespace_id: project.namespace, project_id: project) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { post_create_aws } + end + it 'creates a new cluster' do expect(ClusterProvisionWorker).to receive(:perform_async) expect { post_create_aws }.to change { Clusters::Cluster.count } @@ -548,6 +568,10 @@ RSpec.describe Projects::ClustersController do .and_return(double(execute: double)) end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + it 'updates the associated role with the supplied ARN' do go @@ -603,6 +627,10 @@ RSpec.describe Projects::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + it 'deletes the namespaces associated with the cluster' do expect { go }.to change { Clusters::KubernetesNamespace.count } @@ -640,6 +668,10 @@ RSpec.describe Projects::ClustersController do format: :json end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do it "responds with matching schema" do go @@ -685,6 +717,10 @@ RSpec.describe Projects::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do render_views @@ -749,6 +785,10 @@ RSpec.describe Projects::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + it "updates and redirects back to show page" do go @@ -842,6 +882,10 @@ RSpec.describe Projects::ClustersController do } end + include_examples ':certificate_based_clusters feature flag controller responses' do + let(:subject) { go } + end + describe 'functionality' do context 'when cluster is provided by GCP' do context 'when cluster is created' do diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb index 0fcdeb2edde..fdfc21887a6 100644 --- a/spec/controllers/projects/environments_controller_spec.rb +++ b/spec/controllers/projects/environments_controller_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Projects::EnvironmentsController do include MetricsDashboardHelpers include KubernetesHelpers - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project, :repository) } let_it_be(:maintainer) { create(:user, name: 'main-dos').tap { |u| project.add_maintainer(u) } } let_it_be(:reporter) { create(:user, name: 'repo-dos').tap { |u| project.add_reporter(u) } } @@ -55,11 +55,11 @@ RSpec.describe Projects::EnvironmentsController do let(:environments) { json_response['environments'] } context 'with default parameters' do - before do - get :index, params: environment_params(format: :json) - end + subject { get :index, params: environment_params(format: :json) } it 'responds with a flat payload describing available environments' do + subject + expect(environments.count).to eq 3 expect(environments.first).to include('name' => 'production', 'name_without_type' => 'production') expect(environments.second).to include('name' => 'staging/review-1', 'name_without_type' => 'review-1') @@ -69,9 +69,28 @@ RSpec.describe Projects::EnvironmentsController do end it 'sets the polling interval header' do + subject + expect(response).to have_gitlab_http_status(:ok) expect(response.headers['Poll-Interval']).to eq("3000") end + + context 'validates latest deployment' do + let_it_be(:test_environment) do + create(:environment, project: project, name: 'staging/review-4', state: :available) + end + + before do + create_list(:deployment, 2, :success, environment: test_environment, project: project) + end + + it 'responds with the latest deployment for the environment' do + subject + + environment = environments.find { |env| env['id'] == test_environment.id } + expect(environment['last_deployment']['id']).to eq(test_environment.deployments.last.id) + end + end end context 'when a folder-based nested structure is requested' do diff --git a/spec/controllers/projects/error_tracking_controller_spec.rb b/spec/controllers/projects/error_tracking_controller_spec.rb index 822778779eb..b4f21e070c6 100644 --- a/spec/controllers/projects/error_tracking_controller_spec.rb +++ b/spec/controllers/projects/error_tracking_controller_spec.rb @@ -50,9 +50,7 @@ RSpec.describe Projects::ErrorTrackingController do let(:external_url) { 'http://example.com' } context 'no data' do - let(:permitted_params) do - ActionController::Parameters.new({}).permit! - end + let(:permitted_params) { permit_index_parameters!({}) } before do expect(ErrorTracking::ListIssuesService) @@ -75,9 +73,7 @@ RSpec.describe Projects::ErrorTrackingController do let(:search_term) { 'something' } let(:sort) { 'last_seen' } let(:params) { project_params(format: :json, search_term: search_term, sort: sort, cursor: cursor) } - let(:permitted_params) do - ActionController::Parameters.new(search_term: search_term, sort: sort, cursor: cursor).permit! - end + let(:permitted_params) { permit_index_parameters!(search_term: search_term, sort: sort, cursor: cursor) } before do expect(ErrorTracking::ListIssuesService) @@ -114,7 +110,7 @@ RSpec.describe Projects::ErrorTrackingController do context 'without extra params' do before do expect(ErrorTracking::ListIssuesService) - .to receive(:new).with(project, user, {}) + .to receive(:new).with(project, user, permit_index_parameters!({})) .and_return(list_issues_service) end @@ -179,6 +175,15 @@ RSpec.describe Projects::ErrorTrackingController do end end end + + private + + def permit_index_parameters!(params) + ActionController::Parameters.new( + **params, + tracking_event: :error_tracking_view_list + ).permit! + end end describe 'GET #issue_details' do @@ -188,7 +193,8 @@ RSpec.describe Projects::ErrorTrackingController do let(:permitted_params) do ActionController::Parameters.new( - { issue_id: issue_id.to_s } + issue_id: issue_id.to_s, + tracking_event: :error_tracking_view_details ).permit! end diff --git a/spec/controllers/projects/forks_controller_spec.rb b/spec/controllers/projects/forks_controller_spec.rb index 0f8f3b49e02..962ef93dc72 100644 --- a/spec/controllers/projects/forks_controller_spec.rb +++ b/spec/controllers/projects/forks_controller_spec.rb @@ -199,15 +199,6 @@ RSpec.describe Projects::ForksController do expect(json_response['namespaces'][1]['id']).to eq(group.id) end - it 'responds with group only when fork_project_form feature flag is disabled' do - stub_feature_flags(fork_project_form: false) - do_request - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response['namespaces'].length).to eq(1) - expect(json_response['namespaces'][0]['id']).to eq(group.id) - end - context 'N+1 queries' do before do create(:fork_network, root_project: project) diff --git a/spec/controllers/projects/incidents_controller_spec.rb b/spec/controllers/projects/incidents_controller_spec.rb index 460821634b0..20cf0dcfd3a 100644 --- a/spec/controllers/projects/incidents_controller_spec.rb +++ b/spec/controllers/projects/incidents_controller_spec.rb @@ -43,6 +43,7 @@ RSpec.describe Projects::IncidentsController do expect(response).to have_gitlab_http_status(:ok) expect(response).to render_template(:index) + expect(Gon.features).to include('incidentEscalations' => true) end context 'when user is unauthorized' do diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index bf0b833b311..9d3711d8a96 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -72,7 +72,21 @@ RSpec.describe Projects::IssuesController do project.add_developer(user) end - it_behaves_like "issuables list meta-data", :issue + context 'when issues_full_text_search is disabled' do + before do + stub_feature_flags(issues_full_text_search: false) + end + + it_behaves_like 'issuables list meta-data', :issue + end + + context 'when issues_full_text_search is enabled' do + before do + stub_feature_flags(issues_full_text_search: true) + end + + it_behaves_like 'issuables list meta-data', :issue + end it_behaves_like 'set sort order from user preference' do let(:sorting_param) { 'updated_asc' } @@ -605,11 +619,11 @@ RSpec.describe Projects::IssuesController do end end - context 'when the SpamVerdictService disallows' do + context 'when an issue is identified as spam' do before do stub_application_setting(recaptcha_enabled: true) - expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service| - expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW) + allow_next_instance_of(Spam::AkismetService) do |akismet_service| + allow(akismet_service).to receive(:spam?).and_return(true) end end @@ -926,8 +940,8 @@ RSpec.describe Projects::IssuesController do context 'when an issue is identified as spam' do context 'when recaptcha is not verified' do before do - expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service| - expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW) + allow_next_instance_of(Spam::AkismetService) do |akismet_service| + allow(akismet_service).to receive(:spam?).and_return(true) end end @@ -1004,6 +1018,7 @@ RSpec.describe Projects::IssuesController do end it 'returns 200 status' do + update_verified_issue expect(response).to have_gitlab_http_status(:ok) end @@ -1051,35 +1066,6 @@ RSpec.describe Projects::IssuesController do .not_to exceed_query_limit(control_count + 2 * labels.count) end - context 'real-time sidebar feature flag' do - let_it_be(:project) { create(:project, :public) } - let_it_be(:issue) { create(:issue, project: project) } - - context 'when enabled' do - before do - stub_feature_flags(real_time_issue_sidebar: true) - end - - it 'pushes the correct value to the frontend' do - go(id: issue.to_param) - - expect(Gon.features).to include('realTimeIssueSidebar' => true) - end - end - - context 'when disabled' do - before do - stub_feature_flags(real_time_issue_sidebar: false) - end - - it 'pushes the correct value to the frontend' do - go(id: issue.to_param) - - expect(Gon.features).to include('realTimeIssueSidebar' => false) - end - end - end - it 'logs the view with Gitlab::Search::RecentIssues' do sign_in(user) recent_issues_double = instance_double(::Gitlab::Search::RecentIssues, log_view: nil) @@ -1260,11 +1246,11 @@ RSpec.describe Projects::IssuesController do end end - context 'when SpamVerdictService requires recaptcha' do + context 'when an issue is identified as spam and requires recaptcha' do context 'when captcha is not verified' do before do - expect_next_instance_of(Spam::SpamVerdictService) do |verdict_service| - expect(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW) + allow_next_instance_of(Spam::AkismetService) do |akismet_service| + allow(akismet_service).to receive(:spam?).and_return(true) end end diff --git a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb index a5c59b7e22d..367781c0e76 100644 --- a/spec/controllers/projects/merge_requests/diffs_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/diffs_controller_spec.rb @@ -220,29 +220,6 @@ RSpec.describe Projects::MergeRequests::DiffsController do end end - context "with the :default_merge_ref_for_diffs flag on" do - let(:diffable_merge_ref) { true } - - subject do - go(diff_head: true, - diff_id: merge_request.merge_request_diff.id, - start_sha: merge_request.merge_request_diff.start_commit_sha) - end - - it "correctly generates the right diff between versions" do - MergeRequests::MergeToRefService.new(project: project, current_user: merge_request.author).execute(merge_request) - - expect_next_instance_of(CompareService) do |service| - expect(service).to receive(:execute).with( - project, - merge_request.merge_request_diff.head_commit_sha, - straight: true) - end - - subject - end - end - context 'with diff_head param passed' do before do allow(merge_request).to receive(:diffable_merge_ref?) @@ -259,6 +236,23 @@ RSpec.describe Projects::MergeRequests::DiffsController do expect(response).to have_gitlab_http_status(:ok) end + + context 'when diff_id and start_sha are set' do + it 'correctly generates the right diff between versions' do + MergeRequests::MergeToRefService.new(project: project, current_user: merge_request.author).execute(merge_request) + + expect_next_instance_of(CompareService) do |service| + expect(service).to receive(:execute).with( + project, + merge_request.merge_request_diff.head_commit_sha, + straight: true) + end + + go(diff_head: true, + diff_id: merge_request.merge_request_diff.id, + start_sha: merge_request.merge_request_diff.start_commit_sha) + end + end end context 'the merge request cannot be compared with head' do diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index 2390687c3ea..f6db809c2e3 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -68,6 +68,18 @@ RSpec.describe Projects::MergeRequestsController do end describe 'as html' do + it 'sets the endpoint_metadata_url' do + go + + expect(assigns["endpoint_metadata_url"]).to eq( + diffs_metadata_project_json_merge_request_path( + project, + merge_request, + 'json', + diff_head: true, + view: 'inline')) + end + context 'when diff files were cleaned' do render_views @@ -85,23 +97,6 @@ RSpec.describe Projects::MergeRequestsController do end end - context 'with `default_merge_ref_for_diffs` feature flag enabled' do - before do - stub_feature_flags(default_merge_ref_for_diffs: true) - go - end - - it 'adds the diff_head parameter' do - expect(assigns["endpoint_metadata_url"]).to eq( - diffs_metadata_project_json_merge_request_path( - project, - merge_request, - 'json', - diff_head: true, - view: 'inline')) - end - end - context 'when diff is missing' do render_views diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb index 4a51e2ed5a0..8fae82d54a2 100644 --- a/spec/controllers/projects/pipelines_controller_spec.rb +++ b/spec/controllers/projects/pipelines_controller_spec.rb @@ -292,12 +292,8 @@ RSpec.describe Projects::PipelinesController do subject { project.namespace } - context 'code_quality_walkthrough experiment' do - it_behaves_like 'tracks assignment and records the subject', :code_quality_walkthrough, :namespace - end - - context 'ci_runner_templates experiment' do - it_behaves_like 'tracks assignment and records the subject', :ci_runner_templates, :namespace + context 'runners_availability_section experiment' do + it_behaves_like 'tracks assignment and records the subject', :runners_availability_section, :namespace end end @@ -936,6 +932,33 @@ RSpec.describe Projects::PipelinesController do expect(response).to have_gitlab_http_status(:not_found) end end + + context 'when access denied' do + it 'returns an error' do + sign_in(create(:user)) + + post_retry + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when service returns an error' do + before do + service_response = ServiceResponse.error(message: 'some error', http_status: 404) + allow_next_instance_of(::Ci::RetryPipelineService) do |service| + allow(service).to receive(:check_access).and_return(service_response) + end + end + + it 'does not retry' do + post_retry + + expect(response).to have_gitlab_http_status(:not_found) + expect(response.body).to include('some error') + expect(::Ci::RetryPipelineWorker).not_to have_received(:perform_async).with(pipeline.id, user.id) + end + end end describe 'POST cancel.json' do diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index d8ef95cf11a..20a114bbe8c 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -147,137 +147,6 @@ RSpec.describe Projects::ProjectMembersController do end end - describe 'POST create' do - let_it_be(:project_user) { create(:user) } - - before do - sign_in(user) - end - - context 'when user does not have enough rights' do - before do - project.add_developer(user) - end - - it 'returns 404', :aggregate_failures do - post :create, params: { - namespace_id: project.namespace, - project_id: project, - user_ids: project_user.id, - access_level: Gitlab::Access::GUEST - } - - expect(response).to have_gitlab_http_status(:not_found) - expect(project.users).not_to include project_user - end - end - - context 'when user has enough rights' do - before do - project.add_maintainer(user) - end - - it 'adds user to members', :aggregate_failures, :snowplow do - post :create, params: { - namespace_id: project.namespace, - project_id: project, - user_ids: project_user.id, - access_level: Gitlab::Access::GUEST - } - - expect(controller).to set_flash.to 'Users were successfully added.' - expect(response).to redirect_to(project_project_members_path(project)) - expect(project.users).to include project_user - expect_snowplow_event( - category: 'Members::CreateService', - action: 'create_member', - label: 'project-members-page', - property: 'existing_user', - user: user - ) - end - - it 'adds no user to members', :aggregate_failures do - expect_next_instance_of(Members::CreateService) do |instance| - expect(instance).to receive(:execute).and_return(status: :failure, message: 'Message') - end - - post :create, params: { - namespace_id: project.namespace, - project_id: project, - user_ids: '', - access_level: Gitlab::Access::GUEST - } - - expect(controller).to set_flash.to 'Message' - expect(response).to redirect_to(project_project_members_path(project)) - end - end - - context 'adding project bot' do - let_it_be(:project_bot) { create(:user, :project_bot) } - - before do - project.add_maintainer(user) - - unrelated_project = create(:project) - unrelated_project.add_maintainer(project_bot) - end - - it 'returns error', :aggregate_failures do - post :create, params: { - namespace_id: project.namespace, - project_id: project, - user_ids: project_bot.id, - access_level: Gitlab::Access::GUEST - } - - expect(flash[:alert]).to include('project bots cannot be added to other groups / projects') - expect(response).to redirect_to(project_project_members_path(project)) - end - end - - context 'access expiry date' do - before do - project.add_maintainer(user) - end - - subject do - post :create, params: { - namespace_id: project.namespace, - project_id: project, - user_ids: project_user.id, - access_level: Gitlab::Access::GUEST, - expires_at: expires_at - } - end - - context 'when set to a date in the past' do - let(:expires_at) { 2.days.ago } - - it 'does not add user to members', :aggregate_failures do - subject - - expect(flash[:alert]).to include('Expires at cannot be a date in the past') - expect(response).to redirect_to(project_project_members_path(project)) - expect(project.users).not_to include project_user - end - end - - context 'when set to a date in the future' do - let(:expires_at) { 5.days.from_now } - - it 'adds user to members', :aggregate_failures do - subject - - expect(controller).to set_flash.to 'Users were successfully added.' - expect(response).to redirect_to(project_project_members_path(project)) - expect(project.users).to include project_user - end - end - end - end - describe 'PUT update' do let_it_be(:requester) { create(:project_member, :access_request, project: project) } @@ -603,99 +472,6 @@ RSpec.describe Projects::ProjectMembersController do end end - describe 'POST apply_import' do - let_it_be(:another_project) { create(:project, :private) } - let_it_be(:member) { create(:user) } - - before do - project.add_maintainer(user) - another_project.add_guest(member) - sign_in(user) - end - - shared_context 'import applied' do - before do - post(:apply_import, params: { - namespace_id: project.namespace, - project_id: project, - source_project_id: another_project.id - }) - end - end - - context 'when user can admin source project members' do - before do - another_project.add_maintainer(user) - end - - include_context 'import applied' - - it 'imports source project members', :aggregate_failures do - expect(project.team_members).to include member - expect(controller).to set_flash.to 'Successfully imported' - expect(response).to redirect_to( - project_project_members_path(project) - ) - end - end - - context "when user can't admin source project members" do - before do - another_project.add_developer(user) - end - - include_context 'import applied' - - it 'does not import team members' do - expect(project.team_members).not_to include member - end - - it 'responds with not found' do - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - - describe 'POST create' do - let_it_be(:stranger) { create(:user) } - - context 'when creating owner' do - before do - project.add_maintainer(user) - sign_in(user) - end - - it 'does not create a member' do - expect do - post :create, params: { - user_ids: stranger.id, - namespace_id: project.namespace, - access_level: Member::OWNER, - project_id: project - } - end.to change { project.members.count }.by(0) - end - end - - context 'when create maintainer' do - before do - project.add_maintainer(user) - sign_in(user) - end - - it 'creates a member' do - expect do - post :create, params: { - user_ids: stranger.id, - namespace_id: project.namespace, - access_level: Member::MAINTAINER, - project_id: project - } - end.to change { project.members.count }.by(1) - end - end - end - describe 'POST resend_invite' do let_it_be(:member) { create(:project_member, project: project) } diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb index 120020273f9..9dd18e58109 100644 --- a/spec/controllers/projects/releases_controller_spec.rb +++ b/spec/controllers/projects/releases_controller_spec.rb @@ -222,6 +222,168 @@ RSpec.describe Projects::ReleasesController do end end + describe 'GET #latest_permalink' do + # Uses default order_by=released_at parameter. + subject do + get :latest_permalink, params: { namespace_id: project.namespace, project_id: project } + end + + before do + sign_in(user) + end + + let(:release) { create(:release, project: project) } + let(:tag) { CGI.escape(release.tag) } + + context 'when user is a guest' do + let(:project) { private_project } + let(:user) { guest } + + it 'proceeds with the redirect' do + subject + + expect(response).to have_gitlab_http_status(:redirect) + end + end + + context 'when user is an external user for the project' do + let(:project) { private_project } + let(:user) { create(:user) } + + it 'behaves like not found' do + subject + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'when there are no releases for the project' do + let(:project) { create(:project, :repository, :public) } + let(:user) { developer } + + before do + project.releases.destroy_all # rubocop: disable Cop/DestroyAll + end + + it 'behaves like not found' do + subject + + expect(response).to have_gitlab_http_status(:not_found) + end + end + + context 'multiple releases' do + let(:user) { developer } + + it 'redirects to the latest release' do + create(:release, project: project, released_at: 1.day.ago) + latest_release = create(:release, project: project, released_at: Time.current) + + subject + + expect(response).to redirect_to("#{project_releases_path(project)}/#{latest_release.tag}") + end + end + + context 'suffix path redirection' do + let(:user) { developer } + let(:suffix_path) { 'downloads/zips/helm-hello-world.zip' } + let!(:latest_release) { create(:release, project: project, released_at: Time.current) } + + subject do + get :latest_permalink, params: { + namespace_id: project.namespace, + project_id: project, + suffix_path: suffix_path + } + end + + it 'redirects to the latest release with suffix path and format' do + subject + + expect(response).to redirect_to( + "#{project_releases_path(project)}/#{latest_release.tag}/#{suffix_path}") + end + + context 'suffix path abuse' do + let(:suffix_path) { 'downloads/zips/../../../../../../../robots.txt'} + + it 'raises attack error' do + expect do + subject + end.to raise_error(Gitlab::Utils::PathTraversalAttackError) + end + end + + context 'url parameters' do + let(:suffix_path) { 'downloads/zips/helm-hello-world.zip' } + + subject do + get :latest_permalink, params: { + namespace_id: project.namespace, + project_id: project, + suffix_path: suffix_path, + order_by: 'released_at', + param_1: 1, + param_2: 2 + } + end + + it 'carries over query parameters without order_by parameter in the redirect' do + subject + + expect(response).to redirect_to( + "#{project_releases_path(project)}/#{latest_release.tag}/#{suffix_path}?param_1=1¶m_2=2") + end + end + end + + context 'order_by parameter' do + let!(:latest_release) { create(:release, project: project, released_at: Time.current, tag: 'latest') } + + shared_examples_for 'redirects to latest release ordered by using released_at' do + it do + expect(Release).to receive(:order_released_desc).and_call_original + + subject + + expect(response).to redirect_to("#{project_releases_path(project)}/#{latest_release.tag}") + end + end + + before do + create(:release, project: project, released_at: 1.day.ago, tag: 'alpha') + create(:release, project: project, released_at: 2.days.ago, tag: 'beta') + end + + context 'invalid parameter' do + let(:user) { developer } + + subject do + get :latest_permalink, params: { + namespace_id: project.namespace, + project_id: project, + order_by: 'unsupported' + } + end + + it_behaves_like 'redirects to latest release ordered by using released_at' + end + + context 'valid parameter' do + subject do + get :latest_permalink, params: { + namespace_id: project.namespace, + project_id: project, + order_by: 'released_at' + } + end + + it_behaves_like 'redirects to latest release ordered by using released_at' + end + end + end + # `GET #downloads` is addressed in spec/requests/projects/releases_controller_spec.rb private diff --git a/spec/controllers/projects/runners_controller_spec.rb b/spec/controllers/projects/runners_controller_spec.rb index 246a37129d7..57d1695b842 100644 --- a/spec/controllers/projects/runners_controller_spec.rb +++ b/spec/controllers/projects/runners_controller_spec.rb @@ -37,7 +37,7 @@ RSpec.describe Projects::RunnersController do describe '#destroy' do it 'destroys the runner' do - expect_next_instance_of(Ci::UnregisterRunnerService, runner) do |service| + expect_next_instance_of(Ci::Runners::UnregisterRunnerService, runner, user) do |service| expect(service).to receive(:execute).once.and_call_original end diff --git a/spec/controllers/projects/serverless/functions_controller_spec.rb b/spec/controllers/projects/serverless/functions_controller_spec.rb index 860bbc1c5cc..f8cee09006c 100644 --- a/spec/controllers/projects/serverless/functions_controller_spec.rb +++ b/spec/controllers/projects/serverless/functions_controller_spec.rb @@ -39,9 +39,24 @@ RSpec.describe Projects::Serverless::FunctionsController do project_id: project.to_param) end + shared_examples_for 'behind :deprecated_serverless feature flag' do + before do + stub_feature_flags(deprecated_serverless: false) + end + + it 'returns 404' do + action + expect(response).to have_gitlab_http_status(:not_found) + end + end + describe 'GET #index' do let(:expected_json) { { 'knative_installed' => knative_state, 'functions' => functions } } + it_behaves_like 'behind :deprecated_serverless feature flag' do + let(:action) { get :index, params: params({ format: :json }) } + end + context 'when cache is being read' do let(:knative_state) { 'checking' } let(:functions) { [] } @@ -147,6 +162,10 @@ RSpec.describe Projects::Serverless::FunctionsController do end describe 'GET #show' do + it_behaves_like 'behind :deprecated_serverless feature flag' do + let(:action) { get :show, params: params({ format: :json, environment_id: "*", id: "foo" }) } + end + context 'with function that does not exist' do it 'returns 404' do get :show, params: params({ format: :json, environment_id: "*", id: "foo" }) @@ -239,6 +258,10 @@ RSpec.describe Projects::Serverless::FunctionsController do end describe 'GET #metrics' do + it_behaves_like 'behind :deprecated_serverless feature flag' do + let(:action) { get :metrics, params: params({ format: :json, environment_id: "*", id: "foo" }) } + end + context 'invalid data' do it 'has a bad function name' do get :metrics, params: params({ format: :json, environment_id: "*", id: "foo" }) diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index f3c7b501faa..35e5422d072 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -353,7 +353,16 @@ RSpec.describe Projects::ServicesController do it 'does not modify integration' do expect { put :update, params: project_params.merge(service: integration_params) } - .not_to change { project.prometheus_integration.reload.attributes } + .not_to change { prometheus_integration_as_data } + end + + def prometheus_integration_as_data + pi = project.prometheus_integration.reload + attrs = pi.attributes.except('encrypted_properties', + 'encrypted_properties_iv', + 'encrypted_properties_tmp') + + [attrs, pi.encrypted_properties_tmp] end end diff --git a/spec/controllers/projects/tags/releases_controller_spec.rb b/spec/controllers/projects/tags/releases_controller_spec.rb index b3d4d944440..1d2385f54f9 100644 --- a/spec/controllers/projects/tags/releases_controller_spec.rb +++ b/spec/controllers/projects/tags/releases_controller_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe Projects::Tags::ReleasesController do let!(:project) { create(:project, :repository) } let!(:user) { create(:user) } - let!(:release) { create(:release, project: project) } + let!(:release) { create(:release, project: project, tag: "v1.1.0") } let!(:tag) { release.tag } before do @@ -27,7 +27,7 @@ RSpec.describe Projects::Tags::ReleasesController do end it 'retrieves an existing release' do - response = get :edit, params: { namespace_id: project.namespace, project_id: project, tag_id: release.tag } + response = get :edit, params: { namespace_id: project.namespace, project_id: project, tag_id: tag } release = assigns(:release) expect(release).not_to be_nil diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb index f955f9d0248..d0971e96910 100644 --- a/spec/controllers/projects/tags_controller_spec.rb +++ b/spec/controllers/projects/tags_controller_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Projects::TagsController do let(:project) { create(:project, :public, :repository) } - let!(:release) { create(:release, project: project) } + let!(:release) { create(:release, project: project, tag: "v1.1.0") } let!(:invalid_release) { create(:release, project: project, tag: 'does-not-exist') } let(:user) { create(:user) } diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb index 08d1d88fcda..c098ea71f7a 100644 --- a/spec/controllers/projects_controller_spec.rb +++ b/spec/controllers/projects_controller_spec.rb @@ -1211,16 +1211,6 @@ RSpec.describe ProjectsController do expect(response).to have_gitlab_http_status(:success) end - - context 'when "strong_parameters_for_project_controller" FF is disabled' do - before do - stub_feature_flags(strong_parameters_for_project_controller: false) - end - - it 'raises an exception' do - expect { request }.to raise_error(TypeError) - end - end end end @@ -1600,71 +1590,22 @@ RSpec.describe ProjectsController do get :show, format: :atom, params: { id: public_project, namespace_id: public_project.namespace } - expect(response).to render_template('xml.atom') + expect(response).to have_gitlab_http_status(:success) + expect(response).to render_template(:show) + expect(response).to render_template(layout: :xml) expect(assigns(:events)).to eq([event]) end it 'filters by calling event.visible_to_user?' do get :show, format: :atom, params: { id: public_project, namespace_id: public_project.namespace } - expect(response).to render_template('xml.atom') + expect(response).to have_gitlab_http_status(:success) + expect(response).to render_template(:show) + expect(response).to render_template(layout: :xml) expect(assigns(:events)).to eq([event]) end end - describe 'GET resolve' do - shared_examples 'resolvable endpoint' do - it 'redirects to the project page' do - get :resolve, params: { id: project.id } - - expect(response).to have_gitlab_http_status(:found) - expect(response).to redirect_to(project_path(project)) - end - end - - context 'with an authenticated user' do - before do - sign_in(user) - end - - context 'when user has access to the project' do - before do - project.add_developer(user) - end - - it_behaves_like 'resolvable endpoint' - end - - context 'when user has no access to the project' do - it 'gives 404 for existing project' do - get :resolve, params: { id: project.id } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - it 'gives 404 for non-existing project' do - get :resolve, params: { id: '0' } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - - context 'non authenticated user' do - context 'with a public project' do - let(:project) { public_project } - - it_behaves_like 'resolvable endpoint' - end - - it 'gives 404 for private project' do - get :resolve, params: { id: project.id } - - expect(response).to have_gitlab_http_status(:not_found) - end - end - end - it 'updates Service Desk attributes' do project.add_maintainer(user) sign_in(user) diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index 0f1501d4c3c..9482448fc03 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -251,8 +251,8 @@ RSpec.describe SearchController do it_behaves_like 'tracking unique hll events' do subject(:request) { get :show, params: { scope: 'projects', search: 'term' } } - let(:target_id) { 'i_search_total' } - let(:expected_type) { instance_of(String) } + let(:target_event) { 'i_search_total' } + let(:expected_value) { instance_of(String) } end end @@ -291,7 +291,7 @@ RSpec.describe SearchController do end end - it_behaves_like 'rate limited endpoint', rate_limit_key: :user_email_lookup do + it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do let(:current_user) { user } def request @@ -355,7 +355,7 @@ RSpec.describe SearchController do expect(json_response).to eq({ 'count' => '0' }) end - it_behaves_like 'rate limited endpoint', rate_limit_key: :user_email_lookup do + it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do let(:current_user) { user } def request @@ -375,7 +375,7 @@ RSpec.describe SearchController do expect(json_response).to match_array([]) end - it_behaves_like 'rate limited endpoint', rate_limit_key: :user_email_lookup do + it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do let(:current_user) { user } def request @@ -445,6 +445,26 @@ RSpec.describe SearchController do end context 'unauthorized user' do + describe 'search rate limits' do + using RSpec::Parameterized::TableSyntax + + let(:project) { create(:project, :public) } + + where(:endpoint, :params) do + :show | { search: 'hello', scope: 'projects' } + :count | { search: 'hello', scope: 'projects' } + :autocomplete | { term: 'hello', scope: 'projects' } + end + + with_them do + it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit_unauthenticated do + def request + get endpoint, params: params.merge(project_id: project.id) + end + end + end + end + describe 'GET #opensearch' do render_views diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 31de00dd8bd..03d053e6f97 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -235,7 +235,7 @@ RSpec.describe SessionsController do unsuccesful_login(user_params) expect(response).to render_template(:new) - expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' + expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') expect(subject.current_user).to be_nil end @@ -259,7 +259,7 @@ RSpec.describe SessionsController do unsuccesful_login(user_params, sesion_params: { failed_login_attempts: 6 }) expect(response).to render_template(:new) - expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' + expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') expect(subject.current_user).to be_nil end @@ -279,7 +279,7 @@ RSpec.describe SessionsController do unsuccesful_login(user_params) expect(response).to render_template(:new) - expect(flash[:alert]).to include 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.' + expect(flash[:alert]).to include _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.') expect(subject.current_user).to be_nil end diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb index a82c44fcc44..18b2d3b14ec 100644 --- a/spec/controllers/snippets_controller_spec.rb +++ b/spec/controllers/snippets_controller_spec.rb @@ -176,8 +176,8 @@ RSpec.describe SnippetsController do it_behaves_like 'tracking unique hll events' do subject(:request) { get :show, params: { id: public_snippet.to_param } } - let(:target_id) { 'i_snippets_show' } - let(:expected_type) { instance_of(String) } + let(:target_event) { 'i_snippets_show' } + let(:expected_value) { instance_of(String) } end end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb index 2608a13a399..177a565bbc0 100644 --- a/spec/db/schema_spec.rb +++ b/spec/db/schema_spec.rb @@ -18,6 +18,7 @@ RSpec.describe 'Database schema' do approvals: %w[user_id], approver_groups: %w[target_id], approvers: %w[target_id user_id], + analytics_cycle_analytics_aggregations: %w[last_full_run_issues_id last_full_run_merge_requests_id last_incremental_issues_id last_incremental_merge_requests_id], analytics_cycle_analytics_merge_request_stage_events: %w[author_id group_id merge_request_id milestone_id project_id stage_event_hash_id state_id], analytics_cycle_analytics_issue_stage_events: %w[author_id group_id issue_id milestone_id project_id stage_event_hash_id state_id], audit_events: %w[author_id entity_id target_id], @@ -66,6 +67,7 @@ RSpec.describe 'Database schema' do oauth_access_tokens: %w[resource_owner_id application_id], oauth_applications: %w[owner_id], product_analytics_events_experimental: %w[event_id txn_id user_id], + project_build_artifacts_size_refreshes: %w[last_job_artifact_id], project_group_links: %w[group_id], project_statistics: %w[namespace_id], projects: %w[creator_id ci_id mirror_user_id], diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb index 70ee4bd3c5a..15b45099a06 100644 --- a/spec/experiments/application_experiment_spec.rb +++ b/spec/experiments/application_experiment_spec.rb @@ -18,68 +18,33 @@ RSpec.describe ApplicationExperiment, :experiment do allow(application_experiment).to receive(:enabled?).and_return(true) end - it "doesn't raise an exception without a defined control" do - # because we have a default behavior defined + it "registers a default control behavior for anonymous experiments" do + # This default control behavior is not inherited, intentionally, but it + # does provide anonymous experiments with a base control behavior to keep + # them optional there. - expect { experiment('namespaced/stub') { } }.not_to raise_error + expect(experiment(:example)).to register_behavior(:control).with(nil) + expect { experiment(:example) { } }.not_to raise_error end describe "#publish" do - let(:should_track) { true } - - before do - allow(application_experiment).to receive(:should_track?).and_return(should_track) - end - it "tracks the assignment", :snowplow do - application_experiment.publish - - expect_snowplow_event( - category: 'namespaced/stub', - action: 'assignment', - context: [{ schema: anything, data: anything }] - ) - end - - it "publishes to the client" do - expect(application_experiment).to receive(:publish_to_client) + expect(application_experiment).to receive(:track).with(:assignment) application_experiment.publish end - context 'when we should not track' do - let(:should_track) { false } - - it 'does not track an event to Snowplow', :snowplow do - application_experiment.publish - - expect_no_snowplow_event - end - end - - describe "#publish_to_client" do - it "adds the data into Gon" do - signature = { key: '86208ac54ca798e11f127e8b23ec396a', variant: 'control' } - expect(Gon).to receive(:push).with({ experiment: { 'namespaced/stub' => hash_including(signature) } }, true) - - application_experiment.publish_to_client - end - - it "handles when Gon raises exceptions (like when it can't be pushed into)" do - expect(Gon).to receive(:push).and_raise(NoMethodError) - - expect { application_experiment.publish_to_client }.not_to raise_error - end - - context 'when we should not track' do - let(:should_track) { false } - - it 'returns early' do - expect(Gon).not_to receive(:push) + it "adds to the published experiments" do + # These are surfaced in the client layer by rendering them in the + # _published_experiments.html.haml partial. + application_experiment.publish - application_experiment.publish_to_client - end - end + expect(ApplicationExperiment.published_experiments['namespaced/stub']).to include( + experiment: 'namespaced/stub', + excluded: false, + key: anything, + variant: 'control' + ) end describe '#publish_to_database' do @@ -278,12 +243,12 @@ RSpec.describe ApplicationExperiment, :experiment do with_them do it "returns the url or nil if invalid" do - allow(Gitlab).to receive(:dev_env_or_com?).and_return(true) + allow(Gitlab).to receive(:com?).and_return(true) expect(application_experiment.process_redirect_url(url)).to eq(processed_url) end it "considers all urls invalid when not on dev or com" do - allow(Gitlab).to receive(:dev_env_or_com?).and_return(false) + allow(Gitlab).to receive(:com?).and_return(false) expect(application_experiment.process_redirect_url(url)).to be_nil end end @@ -340,7 +305,7 @@ RSpec.describe ApplicationExperiment, :experiment do end it "caches the variant determined by the variant resolver" do - expect(application_experiment.variant.name).to eq('candidate') # we should be in the experiment + expect(application_experiment.assigned.name).to eq('candidate') # we should be in the experiment application_experiment.run @@ -355,7 +320,7 @@ RSpec.describe ApplicationExperiment, :experiment do # the control. stub_feature_flags(namespaced_stub: false) # simulate being not rolled out - expect(application_experiment.variant.name).to eq('control') # if we ask, it should be control + expect(application_experiment.assigned.name).to eq('control') # if we ask, it should be control application_experiment.run diff --git a/spec/factories/analytics/cycle_analytics/aggregations.rb b/spec/factories/analytics/cycle_analytics/aggregations.rb new file mode 100644 index 00000000000..78e82f166d0 --- /dev/null +++ b/spec/factories/analytics/cycle_analytics/aggregations.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :cycle_analytics_aggregation, class: 'Analytics::CycleAnalytics::Aggregation' do + group + + enabled { true } + + trait :disabled do + enabled { false } + end + + trait :enabled do + enabled { true } + end + end +end diff --git a/spec/factories/ci/reports/security/evidence.rb b/spec/factories/ci/reports/security/evidence.rb new file mode 100644 index 00000000000..ed744644447 --- /dev/null +++ b/spec/factories/ci/reports/security/evidence.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :ci_reports_security_evidence, class: '::Gitlab::Ci::Reports::Security::Evidence' do + data do + { + summary: 'Credit card detected', + request: { + headers: [{ name: 'Accept', value: '*/*' }], + method: 'GET', + url: 'http://goat:8080/WebGoat/logout', + body: nil + }, + response: { + headers: [{ name: 'Content-Length', value: '0' }], + reason_phrase: 'OK', + status_code: 200, + body: nil + }, + source: { + id: 'assert:Response Body Analysis', + name: 'Response Body Analysis', + url: 'htpp://hostname/documentation' + }, + supporting_messages: [ + { + name: 'Origional', + request: { + headers: [{ name: 'Accept', value: '*/*' }], + method: 'GET', + url: 'http://goat:8080/WebGoat/logout', + body: '' + } + }, + { + name: 'Recorded', + request: { + headers: [{ name: 'Accept', value: '*/*' }], + method: 'GET', + url: 'http://goat:8080/WebGoat/logout', + body: '' + }, + response: { + headers: [{ name: 'Content-Length', value: '0' }], + reason_phrase: 'OK', + status_code: 200, + body: '' + } + } + ] + } + end + + skip_create + + initialize_with do + ::Gitlab::Ci::Reports::Security::Evidence.new(**attributes) + end + end +end diff --git a/spec/factories/ci/reports/security/findings.rb b/spec/factories/ci/reports/security/findings.rb index 8a39fce971f..78c11210f97 100644 --- a/spec/factories/ci/reports/security/findings.rb +++ b/spec/factories/ci/reports/security/findings.rb @@ -6,6 +6,7 @@ FactoryBot.define do confidence { :medium } identifiers { Array.new(1) { association(:ci_reports_security_identifier) } } location factory: :ci_reports_security_locations_sast + evidence factory: :ci_reports_security_evidence metadata_version { 'sast:1.0' } name { 'Cipher with no integrity' } report_type { :sast } @@ -25,7 +26,53 @@ FactoryBot.define do name: "Cipher does not check for integrity first?", url: "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first" } - ] + ], + evidence: { + summary: 'Credit card detected', + request: { + headers: [{ name: 'Accept', value: '*/*' }], + method: 'GET', + url: 'http://goat:8080/WebGoat/logout', + body: nil + }, + response: { + headers: [{ name: 'Content-Length', value: '0' }], + reason_phrase: 'OK', + status_code: 200, + body: nil + }, + source: { + id: 'assert:Response Body Analysis', + name: 'Response Body Analysis', + url: 'htpp://hostname/documentation' + }, + supporting_messages: [ + { + name: 'Origional', + request: { + headers: [{ name: 'Accept', value: '*/*' }], + method: 'GET', + url: 'http://goat:8080/WebGoat/logout', + body: '' + } + }, + { + name: 'Recorded', + request: { + headers: [{ name: 'Accept', value: '*/*' }], + method: 'GET', + url: 'http://goat:8080/WebGoat/logout', + body: '' + }, + response: { + headers: [{ name: 'Content-Length', value: '0' }], + reason_phrase: 'OK', + status_code: 200, + body: '' + } + } + ] + } }.deep_stringify_keys end scanner factory: :ci_reports_security_scanner diff --git a/spec/factories/customer_relations/issue_customer_relations_contacts.rb b/spec/factories/customer_relations/issue_customer_relations_contacts.rb index 6a4fecfb3cf..8ea1a521a33 100644 --- a/spec/factories/customer_relations/issue_customer_relations_contacts.rb +++ b/spec/factories/customer_relations/issue_customer_relations_contacts.rb @@ -21,7 +21,7 @@ FactoryBot.define do trait :for_issue do issue { raise ArgumentError, '`issue` is manadatory' } - contact { association(:contact, group: issue.project.group) } + contact { association(:contact, group: issue.project.root_ancestor) } end end end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 152ae061605..aa264ad3377 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -118,5 +118,14 @@ FactoryBot.define do create(:crm_settings, group: group, enabled: true) end end + + trait :test_group do + path { "test-group-fulfillment#{SecureRandom.hex(4)}" } + created_at { 4.days.ago } + + after(:create) do |group| + group.add_owner(create(:user, email: "test-user-#{SecureRandom.hex(4)}@test.com")) + end + end end end diff --git a/spec/factories/integrations.rb b/spec/factories/integrations.rb index f3a00ac083a..0ffa15ad403 100644 --- a/spec/factories/integrations.rb +++ b/spec/factories/integrations.rb @@ -12,6 +12,16 @@ FactoryBot.define do issue_tracker end + factory :jenkins_integration, class: 'Integrations::Jenkins' do + project + active { true } + type { 'Integrations::Jenkins' } + jenkins_url { 'http://jenkins.example.com/' } + project_name { 'my-project' } + username { 'jenkings-user' } + password { 'passw0rd' } + end + factory :datadog_integration, class: 'Integrations::Datadog' do project active { true } @@ -20,7 +30,7 @@ FactoryBot.define do factory :emails_on_push_integration, class: 'Integrations::EmailsOnPush' do project - type { 'EmailsOnPushService' } + type { 'Integrations::EmailsOnPush' } active { true } push_events { true } tag_push_events { true } @@ -54,7 +64,7 @@ FactoryBot.define do factory :jira_integration, class: 'Integrations::Jira' do project active { true } - type { 'JiraService' } + type { 'Integrations::Jira' } transient do create_data { true } @@ -88,7 +98,7 @@ FactoryBot.define do factory :zentao_integration, class: 'Integrations::Zentao' do project active { true } - type { 'ZentaoService' } + type { 'Integrations::Zentao' } transient do create_data { true } @@ -167,7 +177,7 @@ FactoryBot.define do factory :external_wiki_integration, class: 'Integrations::ExternalWiki' do project - type { 'ExternalWikiService' } + type { 'Integrations::ExternalWiki' } active { true } external_wiki_url { 'http://external-wiki-url.com' } end @@ -178,27 +188,59 @@ FactoryBot.define do password { 'my-secret-password' } end + trait :chat_notification do + webhook { 'https://example.com/webhook' } + end + + trait :inactive do + active { false } + end + + factory :mattermost_integration, class: 'Integrations::Mattermost' do + chat_notification + project + type { 'Integrations::Mattermost' } + active { true } + end + # avoids conflict with slack_integration factory factory :integrations_slack, class: 'Integrations::Slack' do + chat_notification project active { true } - webhook { 'https://slack.service.url' } - type { 'SlackService' } + type { 'Integrations::Slack' } end factory :slack_slash_commands_integration, class: 'Integrations::SlackSlashCommands' do project active { true } - type { 'SlackSlashCommandsService' } + type { 'Integrations::SlackSlashCommands' } end factory :pipelines_email_integration, class: 'Integrations::PipelinesEmail' do project active { true } - type { 'PipelinesEmailService' } + type { 'Integrations::PipelinesEmail' } recipients { 'test@example.com' } end + factory :pivotaltracker_integration, class: 'Integrations::Pivotaltracker' do + project + active { true } + token { 'test' } + end + + factory :harbor_integration, class: 'Integrations::Harbor' do + project + active { true } + type { 'HarborService' } + + url { 'https://demo.goharbor.io' } + project_name { 'testproject' } + username { 'harborusername' } + password { 'harborpassword' } + end + # this is for testing storing values inside properties, which is deprecated and will be removed in # https://gitlab.com/gitlab-org/gitlab/issues/29404 trait :without_properties_callback do @@ -217,11 +259,6 @@ FactoryBot.define do end end - trait :template do - project { nil } - template { true } - end - trait :group do group project { nil } diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index 6f706546402..26804b38db8 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -33,6 +33,10 @@ FactoryBot.define do title { generate(:jira_title) } end + trait :jira_description do + description { generate(:jira_description) } + end + trait :jira_branch do source_branch { generate(:jira_branch) } end diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb index 88c06b3857a..e0b61526ba0 100644 --- a/spec/factories/project_hooks.rb +++ b/spec/factories/project_hooks.rb @@ -25,5 +25,9 @@ FactoryBot.define do feature_flag_events { true } releases_events { true } end + + trait :with_push_branch_filter do + push_events_branch_filter { 'my-branch-*' } + end end end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 8a406f95f58..ef1313541f8 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -35,6 +35,7 @@ FactoryBot.define do metrics_dashboard_access_level { ProjectFeature::PRIVATE } operations_access_level { ProjectFeature::ENABLED } container_registry_access_level { ProjectFeature::ENABLED } + security_and_compliance_access_level { ProjectFeature::PRIVATE } # we can't assign the delegated `#ci_cd_settings` attributes directly, as the # `#ci_cd_settings` relation needs to be created first @@ -70,7 +71,8 @@ FactoryBot.define do metrics_dashboard_access_level: evaluator.metrics_dashboard_access_level, operations_access_level: evaluator.operations_access_level, analytics_access_level: evaluator.analytics_access_level, - container_registry_access_level: evaluator.container_registry_access_level + container_registry_access_level: evaluator.container_registry_access_level, + security_and_compliance_access_level: evaluator.security_and_compliance_access_level } project.build_project_feature(hash) @@ -82,7 +84,7 @@ FactoryBot.define do # user have access to the project. Our specs don't use said service class, # thus we must manually refresh things here. unless project.group || project.pending_delete - project.add_maintainer(project.first_owner) + project.add_owner(project.first_owner) end project.group&.refresh_members_authorized_projects @@ -154,6 +156,10 @@ FactoryBot.define do archived { true } end + trait :hidden do + hidden { true } + end + trait :last_repository_check_failed do last_repository_check_failed { true } end @@ -355,6 +361,9 @@ FactoryBot.define do trait(:container_registry_enabled) { container_registry_access_level { ProjectFeature::ENABLED } } trait(:container_registry_disabled) { container_registry_access_level { ProjectFeature::DISABLED } } trait(:container_registry_private) { container_registry_access_level { ProjectFeature::PRIVATE } } + trait(:security_and_compliance_enabled) { security_and_compliance_access_level { ProjectFeature::ENABLED } } + trait(:security_and_compliance_disabled) { security_and_compliance_access_level { ProjectFeature::DISABLED } } + trait(:security_and_compliance_private) { security_and_compliance_access_level { ProjectFeature::PRIVATE } } trait :auto_devops do association :auto_devops, factory: :project_auto_devops @@ -379,6 +388,10 @@ FactoryBot.define do service_desk_enabled { true } end + trait :with_error_tracking_setting do + error_tracking_setting { association :project_error_tracking_setting } + end + # Project with empty repository # # This is a case when you just created a project diff --git a/spec/factories/projects/build_artifacts_size_refreshes.rb b/spec/factories/projects/build_artifacts_size_refreshes.rb new file mode 100644 index 00000000000..b05f5dfab1c --- /dev/null +++ b/spec/factories/projects/build_artifacts_size_refreshes.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :project_build_artifacts_size_refresh, class: 'Projects::BuildArtifactsSizeRefresh' do + project factory: :project + + trait :created do + state { Projects::BuildArtifactsSizeRefresh::STATES[:created] } + end + + trait :pending do + state { Projects::BuildArtifactsSizeRefresh::STATES[:pending] } + refresh_started_at { Time.zone.now } + end + + trait :running do + state { Projects::BuildArtifactsSizeRefresh::STATES[:running] } + refresh_started_at { Time.zone.now } + end + + trait :stale do + running + refresh_started_at { 30.days.ago } + updated_at { 30.days.ago } + end + end +end diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb index 0e79f2e6d3a..52a9341b955 100644 --- a/spec/factories/releases.rb +++ b/spec/factories/releases.rb @@ -2,7 +2,9 @@ FactoryBot.define do factory :release do - tag { "v1.1.0" } + sequence :tag do |n| + "v1.#{n}.0" + end sha { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' } name { tag } description { "Awesome release" } diff --git a/spec/factories/sequences.rb b/spec/factories/sequences.rb index 893865962d8..6b86154aa91 100644 --- a/spec/factories/sequences.rb +++ b/spec/factories/sequences.rb @@ -18,6 +18,7 @@ FactoryBot.define do sequence(:draft_title) { |n| "Draft: #{n}" } sequence(:wip_title) { |n| "WIP: #{n}" } sequence(:jira_title) { |n| "[PROJ-#{n}]: fix bug" } + sequence(:jira_description) { |n| "This is a description\n here is the description\n Related to: PROJ-#{n}" } sequence(:jira_branch) { |n| "feature/PROJ-#{n}" } sequence(:job_name) { |n| "job #{n}" } sequence(:work_item_type_name) { |n| "bug#{n}" } diff --git a/spec/factories/usage_data.rb b/spec/factories/usage_data.rb index 86799af1719..316e0c2b8d6 100644 --- a/spec/factories/usage_data.rb +++ b/spec/factories/usage_data.rb @@ -5,8 +5,7 @@ FactoryBot.define do skip_create # non-model factories (i.e. without #save) initialize_with do - projects = create_list(:project, 3) - projects << create(:project, :repository) + projects = create_list(:project, 4, :repository) group = create(:group) create(:board, project: projects[0]) create(:jira_integration, project: projects[0]) @@ -19,16 +18,21 @@ FactoryBot.define do create(:jira_import_state, :finished, project: projects[1], label: jira_label, imported_issues_count: 3) create(:jira_import_state, :scheduled, project: projects[1], label: jira_label) create(:prometheus_integration, project: projects[1]) - create(:integration, project: projects[1], type: 'JenkinsService', active: true) - create(:integration, project: projects[0], type: 'SlackSlashCommandsService', active: true) - create(:integration, project: projects[1], type: 'SlackService', active: true) - create(:integration, project: projects[2], type: 'SlackService', active: true) - create(:integration, project: projects[2], type: 'MattermostService', active: false) - create(:integration, group: group, project: nil, type: 'MattermostService', active: true) - mattermost_instance = create(:integration, :instance, type: 'MattermostService', active: true) - create(:integration, project: projects[1], type: 'MattermostService', active: true, inherit_from_id: mattermost_instance.id) - create(:integration, group: group, project: nil, type: 'SlackService', active: true, inherit_from_id: mattermost_instance.id) - create(:integration, project: projects[2], type: 'CustomIssueTrackerService', active: true) + create(:jenkins_integration, project: projects[1]) + + # slack + create(:slack_slash_commands_integration, project: projects[0]) + create(:integrations_slack, project: projects[1]) + create(:integrations_slack, project: projects[2]) + + # mattermost + create(:mattermost_integration, project: projects[2], active: false) + create(:mattermost_integration, group: group, project: nil) + mattermost_instance = create(:mattermost_integration, :instance) + create(:mattermost_integration, project: projects[1], inherit_from_id: mattermost_instance.id) + create(:integrations_slack, group: group, project: nil, active: true, inherit_from_id: mattermost_instance.id) + + create(:custom_issue_tracker_integration, project: projects[2], active: true) create(:project_error_tracking_setting, project: projects[0]) create(:project_error_tracking_setting, project: projects[1], enabled: false) alert_bot_issues = create_list(:incident, 2, project: projects[0], author: User.alert_bot) diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 8764ac90af8..eb89cb0a40a 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -151,7 +151,7 @@ FactoryBot.define do transient do extern_uid { '123456' } - provider { 'ldapmain' } + provider { 'twitter' } end after(:create) do |user, evaluator| @@ -166,6 +166,12 @@ FactoryBot.define do user.identities << create(:identity, identity_attrs) end + + trait :ldap do + transient do + provider { 'ldapmain' } + end + end end factory :atlassian_user do diff --git a/spec/factories/users/saved_replies.rb b/spec/factories/users/saved_replies.rb new file mode 100644 index 00000000000..a3c450fb1f1 --- /dev/null +++ b/spec/factories/users/saved_replies.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :saved_reply, class: 'Users::SavedReply' do + sequence(:name) { |n| "saved_reply_#{n}" } + content { 'Saved Reply Content' } + + user + end +end diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb index 1485edcd97d..ce3c9af22f1 100644 --- a/spec/fast_spec_helper.rb +++ b/spec/fast_spec_helper.rb @@ -18,6 +18,9 @@ require_relative '../config/settings' require_relative 'support/rspec' require 'active_support/all' +require_relative 'simplecov_env' +SimpleCovEnv.start! + unless ActiveSupport::Dependencies.autoload_paths.frozen? ActiveSupport::Dependencies.autoload_paths << 'lib' ActiveSupport::Dependencies.autoload_paths << 'ee/lib' diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb index 0785c736cfb..8bf8ef56353 100644 --- a/spec/features/admin/admin_appearance_spec.rb +++ b/spec/features/admin/admin_appearance_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'Admin Appearance' do fill_in 'appearance_profile_image_guidelines', with: 'Custom profile image guidelines' click_button 'Update appearance settings' - expect(current_path).to eq admin_application_settings_appearances_path + expect(page).to have_current_path admin_application_settings_appearances_path, ignore_query: true expect(page).to have_content 'Appearance' expect(page).to have_field('appearance_title', with: 'MyCompany') diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb index 476dd4469bc..e40f4c4678c 100644 --- a/spec/features/admin/admin_broadcast_messages_spec.rb +++ b/spec/features/admin/admin_broadcast_messages_spec.rb @@ -7,7 +7,12 @@ RSpec.describe 'Admin Broadcast Messages' do admin = create(:admin) sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) - create(:broadcast_message, :expired, message: 'Migration to new server') + create( + :broadcast_message, + :expired, + message: 'Migration to new server', + target_access_levels: [Gitlab::Access::DEVELOPER] + ) visit admin_broadcast_messages_path end @@ -21,10 +26,13 @@ RSpec.describe 'Admin Broadcast Messages' do fill_in 'broadcast_message_target_path', with: '*/user_onboarded' fill_in 'broadcast_message_font', with: '#b94a48' select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i' + check 'Guest' + check 'Owner' click_button 'Add broadcast message' - expect(current_path).to eq admin_broadcast_messages_path + expect(page).to have_current_path admin_broadcast_messages_path, ignore_query: true expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' + expect(page).to have_content 'Guest, Owner' expect(page).to have_content '*/user_onboarded' expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"]) @@ -35,10 +43,14 @@ RSpec.describe 'Admin Broadcast Messages' do fill_in 'broadcast_message_target_path', with: '*/user_onboarded' select 'Notification', from: 'broadcast_message_broadcast_type' select Date.today.next_year.year, from: 'broadcast_message_ends_at_1i' + check 'Reporter' + check 'Developer' + check 'Maintainer' click_button 'Add broadcast message' - expect(current_path).to eq admin_broadcast_messages_path + expect(page).to have_current_path admin_broadcast_messages_path, ignore_query: true expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST' + expect(page).to have_content 'Reporter, Developer, Maintainer' expect(page).to have_content '*/user_onboarded' expect(page).to have_content 'Notification' expect(page).to have_selector 'strong', text: '4:00 CST to 5:00 CST' @@ -47,16 +59,21 @@ RSpec.describe 'Admin Broadcast Messages' do it 'edit an existing broadcast message' do click_link 'Edit' fill_in 'broadcast_message_message', with: 'Application update RIGHT NOW' + check 'Reporter' click_button 'Update broadcast message' - expect(current_path).to eq admin_broadcast_messages_path + expect(page).to have_current_path admin_broadcast_messages_path, ignore_query: true expect(page).to have_content 'Application update RIGHT NOW' + + page.within('.table-responsive') do + expect(page).to have_content 'Reporter, Developer' + end end it 'remove an existing broadcast message' do click_link 'Remove' - expect(current_path).to eq admin_broadcast_messages_path + expect(page).to have_current_path admin_broadcast_messages_path, ignore_query: true expect(page).not_to have_content 'Migration to new server' end diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb index 88b8fcd8d5e..56b8c7fce14 100644 --- a/spec/features/admin/admin_deploy_keys_spec.rb +++ b/spec/features/admin/admin_deploy_keys_spec.rb @@ -47,7 +47,7 @@ RSpec.describe 'admin deploy keys', :js do fill_in 'deploy_key_key', with: new_ssh_key click_button 'Create' - expect(current_path).to eq admin_deploy_keys_path + expect(page).to have_current_path admin_deploy_keys_path, ignore_query: true page.within(find('[data-testid="deploy-keys-list"]', match: :first)) do expect(page).to have_content('laptop') @@ -67,7 +67,7 @@ RSpec.describe 'admin deploy keys', :js do fill_in 'deploy_key_title', with: 'new-title' click_button 'Save changes' - expect(current_path).to eq admin_deploy_keys_path + expect(page).to have_current_path admin_deploy_keys_path, ignore_query: true page.within(find('[data-testid="deploy-keys-list"]', match: :first)) do expect(page).to have_content('new-title') @@ -87,7 +87,7 @@ RSpec.describe 'admin deploy keys', :js do end end - expect(current_path).to eq admin_deploy_keys_path + expect(page).to have_current_path admin_deploy_keys_path, ignore_query: true page.within(find('[data-testid="deploy-keys-list"]', match: :first)) do expect(page).not_to have_content(deploy_key.title) end diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb index a0a41061d64..3b3289a8487 100644 --- a/spec/features/admin/admin_groups_spec.rb +++ b/spec/features/admin/admin_groups_spec.rb @@ -58,7 +58,7 @@ RSpec.describe 'Admin Groups' do fill_in 'group_admin_note_attributes_note', with: group_admin_note click_button "Create group" - expect(current_path).to eq admin_group_path(Group.find_by(path: path_component)) + expect(page).to have_current_path admin_group_path(Group.find_by(path: path_component)), ignore_query: true content = page.find('#content-body') h3_texts = content.all('h3').collect(&:text).join("\n") expect(h3_texts).to match group_name diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb index 837cab49bd4..fd51fd71fea 100644 --- a/spec/features/admin/admin_hook_logs_spec.rb +++ b/spec/features/admin/admin_hook_logs_spec.rb @@ -39,6 +39,6 @@ RSpec.describe 'Admin::HookLogs' do click_link 'View details' click_link 'Resend Request' - expect(current_path).to eq(edit_admin_hook_path(system_hook)) + expect(page).to have_current_path(edit_admin_hook_path(system_hook), ignore_query: true) end end diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index 32e4d18227e..388ab02d8e8 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -18,7 +18,7 @@ RSpec.describe 'Admin::Hooks' do click_on 'System Hooks', match: :first end - expect(current_path).to eq(admin_hooks_path) + expect(page).to have_current_path(admin_hooks_path, ignore_query: true) end it 'has hooks list' do @@ -49,7 +49,7 @@ RSpec.describe 'Admin::Hooks' do expect { click_button 'Add system hook' }.to change(SystemHook, :count).by(1) expect(page).to have_content 'SSL Verification: enabled' - expect(current_path).to eq(admin_hooks_path) + expect(page).to have_current_path(admin_hooks_path, ignore_query: true) expect(page).to have_content(url) end end @@ -70,7 +70,7 @@ RSpec.describe 'Admin::Hooks' do click_button 'Save changes' expect(page).to have_content 'SSL Verification: enabled' - expect(current_path).to eq(admin_hooks_path) + expect(page).to have_current_path(admin_hooks_path, ignore_query: true) expect(page).to have_content(new_url) end end @@ -111,7 +111,7 @@ RSpec.describe 'Admin::Hooks' do click_link 'Push events' end - it { expect(current_path).to eq(admin_hooks_path) } + it { expect(page).to have_current_path(admin_hooks_path, ignore_query: true) } end context 'Merge request hook' do @@ -126,7 +126,7 @@ RSpec.describe 'Admin::Hooks' do check 'Merge request events' expect { click_button 'Add system hook' }.to change(SystemHook, :count).by(1) - expect(current_path).to eq(admin_hooks_path) + expect(page).to have_current_path(admin_hooks_path, ignore_query: true) expect(page).to have_content(url) end end diff --git a/spec/features/admin/admin_mode/login_spec.rb b/spec/features/admin/admin_mode/login_spec.rb index c8ee6c14499..659f66a67d2 100644 --- a/spec/features/admin/admin_mode/login_spec.rb +++ b/spec/features/admin/admin_mode/login_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Admin Mode Login' do enter_code(repeated_otp) - expect(current_path).to eq admin_session_path + expect(page).to have_current_path admin_session_path, ignore_query: true expect(page).to have_content('Invalid two-factor code') end @@ -51,7 +51,7 @@ RSpec.describe 'Admin Mode Login' do travel_to(30.seconds.from_now) do enter_code(user.current_otp) - expect(current_path).to eq admin_root_path + expect(page).to have_current_path admin_root_path, ignore_query: true expect(page).to have_content('Admin mode enabled') end end @@ -74,7 +74,7 @@ RSpec.describe 'Admin Mode Login' do enter_code(user.current_otp) - expect(current_path).to eq admin_root_path + expect(page).to have_current_path admin_root_path, ignore_query: true expect(page).to have_content('Admin mode enabled') end end @@ -93,7 +93,7 @@ RSpec.describe 'Admin Mode Login' do it 'allows login' do enter_code(codes.sample) - expect(current_path).to eq admin_root_path + expect(page).to have_current_path admin_root_path, ignore_query: true expect(page).to have_content('Admin mode enabled') end @@ -146,7 +146,7 @@ RSpec.describe 'Admin Mode Login' do enable_admin_mode_using_saml! expect(page).not_to have_content('Two-Factor Authentication') - expect(current_path).to eq admin_root_path + expect(page).to have_current_path admin_root_path, ignore_query: true expect(page).to have_content('Admin mode enabled') end end @@ -166,7 +166,7 @@ RSpec.describe 'Admin Mode Login' do travel_to(30.seconds.from_now) do enter_code(user.current_otp) - expect(current_path).to eq admin_root_path + expect(page).to have_current_path admin_root_path, ignore_query: true expect(page).to have_content('Admin mode enabled') end end @@ -218,7 +218,7 @@ RSpec.describe 'Admin Mode Login' do travel_to(30.seconds.from_now) do enter_code(user.current_otp) - expect(current_path).to eq admin_root_path + expect(page).to have_current_path admin_root_path, ignore_query: true expect(page).to have_content('Admin mode enabled') end end diff --git a/spec/features/admin/admin_mode/logout_spec.rb b/spec/features/admin/admin_mode/logout_spec.rb index f2f6e26fbee..3ca66ef0d6a 100644 --- a/spec/features/admin/admin_mode/logout_spec.rb +++ b/spec/features/admin/admin_mode/logout_spec.rb @@ -20,7 +20,7 @@ RSpec.describe 'Admin Mode Logout', :js do it 'disable removes admin mode and redirects to root page' do gitlab_disable_admin_mode - expect(current_path).to eq root_path + expect(page).to have_current_path root_path, ignore_query: true open_top_nav @@ -43,7 +43,7 @@ RSpec.describe 'Admin Mode Logout', :js do it 'disable removes admin mode and redirects to root page' do gitlab_disable_admin_mode - expect(current_path).to eq root_path + expect(page).to have_current_path root_path, ignore_query: true open_top_nav diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 8938bab60d7..b0737377de0 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -5,7 +5,6 @@ require 'spec_helper' RSpec.describe "Admin::Projects" do include Spec::Support::Helpers::Features::MembersHelpers include Spec::Support::Helpers::Features::InviteMembersModalHelper - include Select2Helper include Spec::Support::Helpers::ModalHelpers let(:user) { create :user } @@ -26,7 +25,7 @@ RSpec.describe "Admin::Projects" do end it "is ok" do - expect(current_path).to eq(admin_projects_path) + expect(page).to have_current_path(admin_projects_path, ignore_query: true) end it 'renders projects list without archived project' do @@ -63,7 +62,7 @@ RSpec.describe "Admin::Projects" do end it "has project info" do - expect(current_path).to eq admin_project_path(project) + expect(page).to have_current_path admin_project_path(project), ignore_query: true expect(page).to have_content(project.path) expect(page).to have_content(project.name) expect(page).to have_content(project.full_name) @@ -117,18 +116,6 @@ RSpec.describe "Admin::Projects" do expect(find_member_row(current_user)).to have_content('Developer') end - - context 'with the invite_members_group_modal feature flag disabled' do - it 'adds admin to the project as developer' do - stub_feature_flags(invite_members_group_modal: false) - - visit project_project_members_path(project) - - add_member_using_form(current_user.id, role: 'Developer') - - expect(find_member_row(current_user)).to have_content('Developer') - end - end end describe 'admin removes themselves from the project', :js do @@ -150,22 +137,7 @@ RSpec.describe "Admin::Projects" do click_button('Leave') end - expect(current_path).to match dashboard_projects_path - end - end - - # temporary method for the form until the :invite_members_group_modal feature flag is - # enabled: https://gitlab.com/gitlab-org/gitlab/-/issues/247208 - def add_member_using_form(id, role: 'Developer') - page.within '.invite-users-form' do - select2(id, from: '#user_ids', multiple: true) - - fill_in 'expires_at', with: 5.days.from_now.to_date - find_field('expires_at').native.send_keys :enter - - select(role, from: "access_level") - - click_on 'Invite' + expect(page).to have_current_path(dashboard_projects_path, ignore_query: true, url: false) end end end diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb index 25ff4022454..3f0c7e64a1f 100644 --- a/spec/features/admin/admin_runners_spec.rb +++ b/spec/features/admin/admin_runners_spec.rb @@ -11,6 +11,8 @@ RSpec.describe "Admin Runners" do admin = create(:admin) sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) + + wait_for_requests end describe "Runners page", :js do @@ -21,7 +23,7 @@ RSpec.describe "Admin Runners" do context "when there are runners" do it 'has all necessary texts' do - create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: Time.now) + create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: Time.zone.now) create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.week.ago) create(:ci_runner, :instance, created_at: 1.year.ago, contacted_at: 1.year.ago) @@ -156,9 +158,9 @@ RSpec.describe "Admin Runners" do let!(:never_contacted) { create(:ci_runner, :instance, description: 'runner-never-contacted', contacted_at: nil) } before do - create(:ci_runner, :instance, description: 'runner-1', contacted_at: Time.now) - create(:ci_runner, :instance, description: 'runner-2', contacted_at: Time.now) - create(:ci_runner, :instance, description: 'runner-paused', active: false, contacted_at: Time.now) + create(:ci_runner, :instance, description: 'runner-1', contacted_at: Time.zone.now) + create(:ci_runner, :instance, description: 'runner-2', contacted_at: Time.zone.now) + create(:ci_runner, :instance, description: 'runner-paused', active: false, contacted_at: Time.zone.now) visit admin_runners_path end diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb index a3d0c7bdd4d..d05a09a79ef 100644 --- a/spec/features/admin/admin_sees_background_migrations_spec.rb +++ b/spec/features/admin/admin_sees_background_migrations_spec.rb @@ -56,7 +56,13 @@ RSpec.describe "Admin > Admin sees background migrations" do context 'when there are failed migrations' do before do allow_next_instance_of(Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy) do |batch_class| - allow(batch_class).to receive(:next_batch).with(anything, anything, batch_min_value: 6, batch_size: 5).and_return([6, 10]) + allow(batch_class).to receive(:next_batch).with( + anything, + anything, + batch_min_value: 6, + batch_size: 5, + job_arguments: failed_migration.job_arguments + ).and_return([6, 10]) end end diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index ca452264c02..df93bd773a6 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -373,7 +373,8 @@ RSpec.describe 'Admin updates settings' do { container_registry_delete_tags_service_timeout: 'Container Registry delete tags service execution timeout', container_registry_expiration_policies_worker_capacity: 'Cleanup policy maximum workers running concurrently', - container_registry_cleanup_tags_service_max_list_size: 'Cleanup policy maximum number of tags to be deleted' + container_registry_cleanup_tags_service_max_list_size: 'Cleanup policy maximum number of tags to be deleted', + container_registry_expiration_policies_caching: 'Enable container expiration caching' } end @@ -393,26 +394,16 @@ RSpec.describe 'Admin updates settings' do %i[container_registry_delete_tags_service_timeout container_registry_expiration_policies_worker_capacity container_registry_cleanup_tags_service_max_list_size].each do |setting| context "for container registry setting #{setting}" do - context 'with feature flag enabled' do - context 'with client supporting tag delete' do - it 'changes the setting' do - visit ci_cd_admin_application_settings_path - - page.within('.as-registry') do - fill_in "application_setting_#{setting}", with: 400 - click_button 'Save changes' - end - - expect(current_settings.public_send(setting)).to eq(400) - expect(page).to have_content "Application settings saved successfully" - end - end - - context 'with client not supporting tag delete' do - let(:client_support) { false } + it 'changes the setting' do + visit ci_cd_admin_application_settings_path - it_behaves_like 'not having container registry setting', setting + page.within('.as-registry') do + fill_in "application_setting_#{setting}", with: 400 + click_button 'Save changes' end + + expect(current_settings.public_send(setting)).to eq(400) + expect(page).to have_content "Application settings saved successfully" end context 'with feature flag disabled' do @@ -422,6 +413,28 @@ RSpec.describe 'Admin updates settings' do end end end + + context 'for container registry setting container_registry_expiration_policies_caching' do + it 'updates container_registry_expiration_policies_caching' do + old_value = current_settings.container_registry_expiration_policies_caching + + visit ci_cd_admin_application_settings_path + + page.within('.as-registry') do + find('#application_setting_container_registry_expiration_policies_caching.form-check-input').click + click_button 'Save changes' + end + + expect(current_settings.container_registry_expiration_policies_caching).to eq(!old_value) + expect(page).to have_content "Application settings saved successfully" + end + + context 'with feature flag disabled' do + let(:feature_flag_enabled) { false } + + it_behaves_like 'not having container registry setting', :container_registry_expiration_policies_caching + end + end end end @@ -694,6 +707,20 @@ RSpec.describe 'Admin updates settings' do include_examples 'regular throttle rate limit settings' end + + it 'changes search rate limits' do + visit network_admin_application_settings_path + + page.within('.as-search-limits') do + fill_in 'Maximum number of requests per minute for an authenticated user', with: 98 + fill_in 'Maximum number of requests per minute for an unauthenticated IP address', with: 76 + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + expect(current_settings.search_rate_limit).to eq(98) + expect(current_settings.search_rate_limit_unauthenticated).to eq(76) + end end context 'Preferences page' do @@ -838,7 +865,7 @@ RSpec.describe 'Admin updates settings' do end it 'loads admin settings page without redirect for reauthentication' do - expect(current_path).to eq general_admin_application_settings_path + expect(page).to have_current_path general_admin_application_settings_path, ignore_query: true end end diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb index 95e3f5c70e5..f4b7fa45e4f 100644 --- a/spec/features/admin/admin_users_spec.rb +++ b/spec/features/admin/admin_users_spec.rb @@ -54,7 +54,7 @@ RSpec.describe "Admin::Users" do visit admin_users_path(tab: 'cohorts') - expect(page).to have_content("#{Time.now.strftime('%b %Y')} 3 0") + expect(page).to have_content("#{Time.zone.now.strftime('%b %Y')} 3 0") end end diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index c13313609b5..4e6aae7c46f 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -43,7 +43,7 @@ RSpec.describe 'Admin uses repository checks', :request_store do project = create(:project) project.update_columns( last_repository_check_failed: true, - last_repository_check_at: Time.now + last_repository_check_at: Time.zone.now ) visit_admin_project_page(project) diff --git a/spec/features/admin/clusters/eks_spec.rb b/spec/features/admin/clusters/eks_spec.rb index bb2678de2ae..71d2bba73b1 100644 --- a/spec/features/admin/clusters/eks_spec.rb +++ b/spec/features/admin/clusters/eks_spec.rb @@ -8,13 +8,15 @@ RSpec.describe 'Instance-level AWS EKS Cluster', :js do before do sign_in(user) gitlab_enable_admin_mode_sign_in(user) + stub_application_setting(eks_integration_enabled: true) end context 'when user does not have a cluster and visits group clusters page' do before do visit admin_clusters_path - click_link 'Connect with a certificate' + click_button 'Actions' + click_link 'Create a new cluster' end context 'when user creates a cluster on AWS EKS' do @@ -23,7 +25,7 @@ RSpec.describe 'Instance-level AWS EKS Cluster', :js do end it 'user sees a form to create an EKS cluster' do - expect(page).to have_content('Create new cluster on EKS') + expect(page).to have_content('Authenticate with Amazon Web Services') end end end diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb index 0d053329627..7e8dee9cc0b 100644 --- a/spec/features/admin/users/user_spec.rb +++ b/spec/features/admin/users/user_spec.rb @@ -220,13 +220,13 @@ RSpec.describe 'Admin::Users::User' do context 'a user with an expired password' do before do - another_user.update!(password_expires_at: Time.now - 5.minutes) + another_user.update!(password_expires_at: Time.zone.now - 5.minutes) end it 'does not redirect to password change page' do subject - expect(current_path).to eq('/') + expect(page).to have_current_path('/') end end end @@ -250,18 +250,18 @@ RSpec.describe 'Admin::Users::User' do it 'is redirected back to the impersonated users page in the admin after stopping' do subject - expect(current_path).to eq("/admin/users/#{another_user.username}") + expect(page).to have_current_path("/admin/users/#{another_user.username}", ignore_query: true) end context 'a user with an expired password' do before do - another_user.update!(password_expires_at: Time.now - 5.minutes) + another_user.update!(password_expires_at: Time.zone.now - 5.minutes) end it 'is redirected back to the impersonated users page in the admin after stopping' do subject - expect(current_path).to eq("/admin/users/#{another_user.username}") + expect(page).to have_current_path("/admin/users/#{another_user.username}", ignore_query: true) end end end diff --git a/spec/features/admin/users/users_spec.rb b/spec/features/admin/users/users_spec.rb index 5b0b6e085c9..4d9a7f31911 100644 --- a/spec/features/admin/users/users_spec.rb +++ b/spec/features/admin/users/users_spec.rb @@ -21,7 +21,7 @@ RSpec.describe 'Admin::Users' do end it "is ok" do - expect(current_path).to eq(admin_users_path) + expect(page).to have_current_path(admin_users_path, ignore_query: true) end it "has users list" do @@ -132,7 +132,7 @@ RSpec.describe 'Admin::Users' do end it 'searches with respect of sorting' do - visit admin_users_path(sort: 'Name') + visit admin_users_path(sort: 'name_asc') fill_in :search_query, with: 'Foo' click_button('Search users') @@ -338,6 +338,8 @@ RSpec.describe 'Admin::Users' do end it 'displays count of the users authorized groups' do + visit admin_users_path + wait_for_requests expect(page.find("[data-testid='user-group-count-#{current_user.id}']").text).to eq("2") @@ -574,7 +576,7 @@ RSpec.describe 'Admin::Users' do user.reload expect(user.name).to eq('Big Bang') expect(user.admin?).to be_truthy - expect(user.password_expires_at).to be <= Time.now + expect(user.password_expires_at).to be <= Time.zone.now end end @@ -602,8 +604,8 @@ RSpec.describe 'Admin::Users' do def sort_by(option) page.within('.filtered-search-block') do - find('.dropdown-menu-toggle').click - click_link option + find('.gl-new-dropdown').click + find('.gl-new-dropdown-item', text: option).click end end end diff --git a/spec/features/boards/board_filters_spec.rb b/spec/features/boards/board_filters_spec.rb index e37bf515088..537b677cbd0 100644 --- a/spec/features/boards/board_filters_spec.rb +++ b/spec/features/boards/board_filters_spec.rb @@ -22,8 +22,6 @@ RSpec.describe 'Issue board filters', :js do let(:filter_submit) { find('.gl-search-box-by-click-search-button') } before do - stub_feature_flags(issue_boards_filtered_search: true) - project.add_maintainer(user) sign_in(user) diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb index 2ca4ff94911..5dd627f3b76 100644 --- a/spec/features/boards/boards_spec.rb +++ b/spec/features/boards/boards_spec.rb @@ -13,12 +13,17 @@ RSpec.describe 'Project issue boards', :js do let_it_be(:user) { create(:user) } let_it_be(:user2) { create(:user) } + let(:filtered_search) { find('[data-testid="issue-board-filtered-search"]') } + let(:filter_input) { find('.gl-filtered-search-term-input') } + let(:filter_submit) { find('.gl-search-box-by-click-search-button') } + context 'signed in user' do before do project.add_maintainer(user) project.add_maintainer(user2) sign_in(user) + stub_feature_flags(gl_avatar_for_all_user_avatars: false) set_cookie('sidebar_collapsed', 'true') end @@ -90,8 +95,7 @@ RSpec.describe 'Project issue boards', :js do end it 'search closed list' do - find('.filtered-search').set(issue8.title) - find('.filtered-search').native.send_keys(:enter) + set_filter_and_search_by_token_value(issue8.title) wait_for_requests @@ -101,8 +105,7 @@ RSpec.describe 'Project issue boards', :js do end it 'search list' do - find('.filtered-search').set(issue5.title) - find('.filtered-search').native.send_keys(:enter) + set_filter_and_search_by_token_value(issue5.title) wait_for_requests @@ -111,26 +114,6 @@ RSpec.describe 'Project issue boards', :js do expect(find('.board:nth-child(4)')).to have_selector('.board-card', count: 0) end - context 'search list negation queries' do - before do - visit_project_board_path_without_query_limit(project, board) - end - - it 'does not have the != option' do - find('.filtered-search').set('label:') - - wait_for_requests - within('#js-dropdown-operator') do - tokens = all(:css, 'li.filter-dropdown-item') - expect(tokens.count).to eq(2) - button = tokens[0].find('button') - expect(button).to have_content('=') - button = tokens[1].find('button') - expect(button).to have_content('!=') - end - end - end - it 'allows user to delete board' do remove_list @@ -309,8 +292,8 @@ RSpec.describe 'Project issue boards', :js do context 'filtering' do it 'filters by author' do set_filter("author", user2.username) - click_filter_link(user2.username) - submit_filter + click_on user2.username + filter_submit.click wait_for_requests wait_for_board_cards(2, 1) @@ -319,8 +302,8 @@ RSpec.describe 'Project issue boards', :js do it 'filters by assignee' do set_filter("assignee", user.username) - click_filter_link(user.username) - submit_filter + click_on user.username + filter_submit.click wait_for_requests @@ -330,8 +313,8 @@ RSpec.describe 'Project issue boards', :js do it 'filters by milestone' do set_filter("milestone", "\"#{milestone.title}") - click_filter_link(milestone.title) - submit_filter + click_on milestone.title + filter_submit.click wait_for_requests wait_for_board_cards(2, 1) @@ -341,8 +324,8 @@ RSpec.describe 'Project issue boards', :js do it 'filters by label' do set_filter("label", testing.title) - click_filter_link(testing.title) - submit_filter + click_on testing.title + filter_submit.click wait_for_requests wait_for_board_cards(2, 1) @@ -351,8 +334,10 @@ RSpec.describe 'Project issue boards', :js do it 'filters by label with encoded character' do set_filter("label", a_plus.title) - click_filter_link(a_plus.title) - submit_filter + # This one is a char encoding issue like the & issue + click_on a_plus.title + filter_submit.click + wait_for_requests wait_for_board_cards(1, 1) wait_for_empty_boards((2..4)) @@ -360,8 +345,8 @@ RSpec.describe 'Project issue boards', :js do it 'filters by label with space after reload', :quarantine do set_filter("label", "\"#{accepting.title}") - click_filter_link(accepting.title) - submit_filter + click_on accepting.title + filter_submit.click # Test after reload page.evaluate_script 'window.location.reload()' @@ -384,13 +369,13 @@ RSpec.describe 'Project issue boards', :js do it 'removes filtered labels' do inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do set_filter("label", testing.title) - click_filter_link(testing.title) - submit_filter + click_on testing.title + filter_submit.click wait_for_board_cards(2, 1) - find('.clear-search').click - submit_filter + find('[data-testid="filtered-search-clear-button"]').click + filter_submit.click end wait_for_board_cards(2, 8) @@ -400,9 +385,9 @@ RSpec.describe 'Project issue boards', :js do create_list(:labeled_issue, 30, project: project, labels: [planning, testing]) set_filter("label", testing.title) - click_filter_link(testing.title) + click_on testing.title inspect_requests(inject_headers: { 'X-GITLAB-DISABLE-SQL-QUERY-LIMIT' => 'https://gitlab.com/gitlab-org/gitlab/-/issues/323426' }) do - submit_filter + filter_submit.click end wait_for_requests @@ -442,10 +427,10 @@ RSpec.describe 'Project issue boards', :js do it 'filters by multiple labels', :quarantine do set_filter("label", testing.title) - click_filter_link(testing.title) + click_on testing.title set_filter("label", bug.title) - click_filter_link(bug.title) + click_on bug.title submit_filter @@ -463,7 +448,7 @@ RSpec.describe 'Project issue boards', :js do wait_for_requests end - page.within('.tokens-container') do + page.within('.gl-filtered-search-token') do expect(page).to have_content(bug.title) end @@ -561,19 +546,26 @@ RSpec.describe 'Project issue boards', :js do end end + def set_filter_and_search_by_token_value(value) + filter_input.click + filter_input.set(value) + filter_submit.click + end + def set_filter(type, text) - find('.filtered-search').native.send_keys("#{type}:=#{text}") + filter_input.click + filter_input.native.send_keys("#{type}:=#{text}") end def submit_filter - find('.filtered-search').native.send_keys(:enter) + filter_input.native.send_keys(:enter) end def click_filter_link(link_text) - page.within('.filtered-search-box') do + page.within(filtered_search) do expect(page).to have_button(link_text) - click_button(link_text) + click_on link_text end end diff --git a/spec/features/callouts/registration_enabled_spec.rb b/spec/features/callouts/registration_enabled_spec.rb index 4055965273f..79e99712183 100644 --- a/spec/features/callouts/registration_enabled_spec.rb +++ b/spec/features/callouts/registration_enabled_spec.rb @@ -5,6 +5,8 @@ require 'spec_helper' RSpec.describe 'Registration enabled callout' do let_it_be(:admin) { create(:admin) } let_it_be(:non_admin) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:callout_title) { _('Anyone can register for an account.') } context 'when "Sign-up enabled" setting is `true`' do before do @@ -14,23 +16,42 @@ RSpec.describe 'Registration enabled callout' do context 'when an admin is logged in' do before do sign_in(admin) + end + + it 'displays callout on admin and dashboard pages and root page' do + visit root_path + + expect(page).to have_content callout_title + expect(page).to have_link _('Turn off'), href: general_admin_application_settings_path(anchor: 'js-signup-settings') + visit root_dashboard_path + + expect(page).to have_content callout_title + + visit admin_root_path + + expect(page).to have_content callout_title end - it 'displays callout' do - expect(page).to have_content 'Open registration is enabled on your instance.' - expect(page).to have_link 'View setting', href: general_admin_application_settings_path(anchor: 'js-signup-settings') + it 'does not display callout on pages other than root, admin, or dashboard' do + visit project_issues_path(project) + + expect(page).not_to have_content callout_title end context 'when callout is dismissed', :js do before do + visit admin_root_path + find('[data-testid="close-registration-enabled-callout"]').click + wait_for_requests + visit root_dashboard_path end it 'does not display callout' do - expect(page).not_to have_content 'Open registration is enabled on your instance.' + expect(page).not_to have_content callout_title end end end @@ -42,7 +63,7 @@ RSpec.describe 'Registration enabled callout' do end it 'does not display callout' do - expect(page).not_to have_content 'Open registration is enabled on your instance.' + expect(page).not_to have_content callout_title end end end diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb index 06e3e00db7d..09e042b00cc 100644 --- a/spec/features/clusters/cluster_detail_page_spec.rb +++ b/spec/features/clusters/cluster_detail_page_spec.rb @@ -36,6 +36,20 @@ RSpec.describe 'Clusterable > Show page' do expect(page).not_to have_selector('[data-testid="cluster-environments-tab"]') end + + context 'content-security policy' do + it 'has AWS domains in the CSP' do + visit cluster_path + + expect(response_headers['Content-Security-Policy']).to include(::Clusters::ClustersController::AWS_CSP_DOMAINS.join(' ')) + end + + it 'keeps existing connect-src in the CSP' do + visit cluster_path + + expect(response_headers['Content-Security-Policy']).to include("connect-src #{Gitlab::ContentSecurityPolicy::Directives.connect_src}") + end + end end shared_examples 'editing a GCP cluster' do diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb index e600a99e3b6..db841ffc627 100644 --- a/spec/features/commits_spec.rb +++ b/spec/features/commits_spec.rb @@ -30,23 +30,7 @@ RSpec.describe 'Commits' do project.add_reporter(user) end - describe 'Commit builds with jobs_tab_vue feature flag off' do - before do - stub_feature_flags(jobs_tab_vue: false) - visit builds_project_pipeline_path(project, pipeline) - end - - it { expect(page).to have_content pipeline.sha[0..7] } - - it 'contains generic commit status build' do - page.within('.table-holder') do - expect(page).to have_content "##{status.id}" # build id - expect(page).to have_content 'generic' # build name - end - end - end - - describe 'Commit builds with jobs_tab_vue feature flag on', :js do + describe 'Commit builds', :js do before do visit builds_project_pipeline_path(project, pipeline) @@ -107,20 +91,7 @@ RSpec.describe 'Commits' do end end - context 'Download artifacts with jobs_tab_vue feature flag off' do - before do - stub_feature_flags(jobs_tab_vue: false) - create(:ci_job_artifact, :archive, file: artifacts_file, job: build) - end - - it do - visit pipeline_path(pipeline) - click_on 'Download artifacts' - expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) - end - end - - context 'Download artifacts with jobs_tab_vue feature flag on', :js do + context 'Download artifacts', :js do before do create(:ci_job_artifact, :archive, file: artifacts_file, job: build) end @@ -149,28 +120,7 @@ RSpec.describe 'Commits' do end end - context "when logged as reporter and with jobs_tab_vue feature flag off" do - before do - stub_feature_flags(jobs_tab_vue: false) - project.add_reporter(user) - create(:ci_job_artifact, :archive, file: artifacts_file, job: build) - visit pipeline_path(pipeline) - end - - it 'renders header', :js do - expect(page).to have_content pipeline.sha[0..7] - expect(page).to have_content pipeline.git_commit_message.gsub!(/\s+/, ' ') - expect(page).to have_content pipeline.user.name - expect(page).not_to have_link('Cancel running') - expect(page).not_to have_link('Retry') - end - - it do - expect(page).to have_link('Download artifacts') - end - end - - context "when logged as reporter and with jobs_tab_vue feature flag on", :js do + context "when logged as reporter", :js do before do project.add_reporter(user) create(:ci_job_artifact, :archive, file: artifacts_file, job: build) diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb index 02cbdc7c777..f1283d29f4c 100644 --- a/spec/features/dashboard/group_spec.rb +++ b/spec/features/dashboard/group_spec.rb @@ -23,7 +23,7 @@ RSpec.describe 'Dashboard Group' do fill_in 'group_name', with: new_name click_button 'Create group' - expect(current_path).to eq group_path(Group.find_by(name: new_name)) + expect(page).to have_current_path group_path(Group.find_by(name: new_name)), ignore_query: true expect(page).to have_content(new_name) end end diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb index 6700ec07765..aa445265eec 100644 --- a/spec/features/dashboard/issuables_counter_spec.rb +++ b/spec/features/dashboard/issuables_counter_spec.rb @@ -8,41 +8,68 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d let(:issue) { create(:issue, project: project) } let(:merge_request) { create(:merge_request, source_project: project) } - before do - issue.assignees = [user] - merge_request.update!(assignees: [user]) - sign_in(user) - end + describe 'feature flag mr_attention_requests is disabled' do + before do + stub_feature_flags(mr_attention_requests: false) - it 'reflects dashboard issues count' do - visit issues_path + issue.assignees = [user] + merge_request.update!(assignees: [user]) + sign_in(user) + end - expect_counters('issues', '1', n_("%d assigned issue", "%d assigned issues", 1) % 1) + it 'reflects dashboard issues count' do + visit issues_path - issue.assignees = [] + expect_counters('issues', '1', n_("%d assigned issue", "%d assigned issues", 1) % 1) - user.invalidate_cache_counts + issue.assignees = [] - travel_to(3.minutes.from_now) do - visit issues_path + user.invalidate_cache_counts + + travel_to(3.minutes.from_now) do + visit issues_path - expect_counters('issues', '0', n_("%d assigned issue", "%d assigned issues", 0) % 0) + expect_counters('issues', '0', n_("%d assigned issue", "%d assigned issues", 0) % 0) + end + end + + it 'reflects dashboard merge requests count', :js do + visit merge_requests_path + + expect_counters('merge_requests', '1', n_("%d merge request", "%d merge requests", 1) % 1) + + merge_request.update!(assignees: []) + + user.invalidate_cache_counts + + travel_to(3.minutes.from_now) do + visit merge_requests_path + + expect_counters('merge_requests', '0', n_("%d merge request", "%d merge requests", 0) % 0) + end end end - it 'reflects dashboard merge requests count' do - visit merge_requests_path + describe 'feature flag mr_attention_requests is enabled' do + before do + merge_request.update!(assignees: [user]) + sign_in(user) + end - expect_counters('merge_requests', '1', n_("%d merge request", "%d merge requests", 1) % 1) + it 'reflects dashboard merge requests count', :js do + visit merge_requests_attention_path - merge_request.update!(assignees: []) + expect_counters('merge_requests', '1', n_("%d merge request", "%d merge requests", 1) % 1) - user.invalidate_cache_counts + merge_request.find_assignee(user).update!(state: :reviewed) - travel_to(3.minutes.from_now) do - visit merge_requests_path + user.invalidate_attention_requested_count - expect_counters('merge_requests', '0', n_("%d merge request", "%d merge requests", 0) % 0) + travel_to(3.minutes.from_now) do + visit merge_requests_attention_path + + expect_counters('merge_requests', '0', n_("%d merge request", "%d merge requests", 0) % 0) + end end end @@ -54,14 +81,15 @@ RSpec.describe 'Navigation bar counter', :use_clean_rails_memory_store_caching d merge_requests_dashboard_path(assignee_username: user.username) end + def merge_requests_attention_path + merge_requests_dashboard_path(attention: user.username) + end + def expect_counters(issuable_type, count, badge_label) dashboard_count = find('.gl-tabs-nav li a.active') - nav_count = find(".dashboard-shortcuts-#{issuable_type}") expect(dashboard_count).to have_content(count) - expect(nav_count).to have_content(count) - within("span[aria-label='#{badge_label}']") do - expect(page).to have_content(count) - end + expect(page).to have_css(".dashboard-shortcuts-#{issuable_type}", visible: :all, text: count) + expect(page).to have_css("span[aria-label='#{badge_label}']", visible: :all, text: count) end end diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb index 9758454ab61..3f89955b12b 100644 --- a/spec/features/dashboard/milestones_spec.rb +++ b/spec/features/dashboard/milestones_spec.rb @@ -9,7 +9,7 @@ RSpec.describe 'Dashboard > Milestones' do end it 'is redirected to sign-in page' do - expect(current_path).to eq new_user_session_path + expect(page).to have_current_path new_user_session_path, ignore_query: true end end @@ -27,7 +27,7 @@ RSpec.describe 'Dashboard > Milestones' do end it 'sees milestones' do - expect(current_path).to eq dashboard_milestones_path + expect(page).to have_current_path dashboard_milestones_path, ignore_query: true expect(page).to have_content(milestone.title) expect(page).to have_content(group.name) expect(first('.milestone')).to have_content('Merge requests') @@ -43,7 +43,7 @@ RSpec.describe 'Dashboard > Milestones' do find('.js-new-project-item-link').click - expect(current_path).to eq(new_group_milestone_path(group)) + expect(page).to have_current_path(new_group_milestone_path(group), ignore_query: true) end end end @@ -61,7 +61,7 @@ RSpec.describe 'Dashboard > Milestones' do end it 'does not see milestones' do - expect(current_path).to eq dashboard_milestones_path + expect(page).to have_current_path dashboard_milestones_path, ignore_query: true expect(page).to have_content(milestone.title) expect(first('.milestone')).to have_no_content('Merge Requests') end diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 82288a6c1a6..847d0faf60d 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -48,7 +48,7 @@ RSpec.describe 'Dashboard Projects' do context 'when last_repository_updated_at, last_activity_at and update_at are present' do it 'shows the last_repository_updated_at attribute as the update date' do - project.update!(last_repository_updated_at: Time.now, last_activity_at: 1.hour.ago) + project.update!(last_repository_updated_at: Time.zone.now, last_activity_at: 1.hour.ago) visit dashboard_projects_path @@ -56,7 +56,7 @@ RSpec.describe 'Dashboard Projects' do end it 'shows the last_activity_at attribute as the update date' do - project.update!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.now) + project.update!(last_repository_updated_at: 1.hour.ago, last_activity_at: Time.zone.now) visit dashboard_projects_path @@ -236,7 +236,7 @@ RSpec.describe 'Dashboard Projects' do end expect(page).to have_selector('.merge-request-form') - expect(current_path).to eq project_new_merge_request_path(project) + expect(page).to have_current_path project_new_merge_request_path(project), ignore_query: true expect(find('#merge_request_target_project_id', visible: false).value).to eq project.id.to_s expect(page).to have_content "From feature into master" end diff --git a/spec/features/dashboard/todos/todos_filtering_spec.rb b/spec/features/dashboard/todos/todos_filtering_spec.rb index 53209db3107..938e42623f6 100644 --- a/spec/features/dashboard/todos/todos_filtering_spec.rb +++ b/spec/features/dashboard/todos/todos_filtering_spec.rb @@ -178,7 +178,7 @@ RSpec.describe 'Dashboard > User filters todos', :js do review_requested: ' requested a review of ', mentioned: ' mentioned ', marked: ' added a todo for ', - build_failed: ' build failed for ' + build_failed: ' pipeline failed in ' } action_name_text = action_names.delete(action_name) diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb index b00bdeac3b9..68d979bb1cf 100644 --- a/spec/features/dashboard/todos/todos_spec.rb +++ b/spec/features/dashboard/todos/todos_spec.rb @@ -400,7 +400,7 @@ RSpec.describe 'Dashboard Todos' do end it 'shows the todo' do - expect(page).to have_content 'The build failed for merge request' + expect(page).to have_content 'The pipeline failed in merge request' end it 'links to the pipelines for the merge request' do @@ -441,7 +441,7 @@ RSpec.describe 'Dashboard Todos' do target.project, target.issue, target.filename ) - expect(current_path).to eq(expectation) + expect(page).to have_current_path(expectation, ignore_query: true) end end end diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb index f6821ae66e8..2cf56f93cf9 100644 --- a/spec/features/dashboard/user_filters_projects_spec.rb +++ b/spec/features/dashboard/user_filters_projects_spec.rb @@ -79,11 +79,11 @@ RSpec.describe 'Dashboard > User filters projects' do page.find('.filtered-search-block #filtered-search-sorting-dropdown .reverse-sort-btn').click end - def select_dropdown_option(selector, label) + def select_dropdown_option(selector, label, option_selector = '.dropdown-menu a') dropdown = page.find(selector) dropdown.click - dropdown.find('.dropdown-menu a', text: label, match: :first).click + dropdown.find(option_selector, text: label, match: :first).click end def expect_to_see_projects(sorted_projects) @@ -125,7 +125,7 @@ RSpec.describe 'Dashboard > User filters projects' do end it 'filters private projects only' do - select_dropdown_option '#filtered-search-visibility-dropdown', 'Private' + select_dropdown_option '#filtered-search-visibility-dropdown > .dropdown', 'Private', '.dropdown-item' expect(current_url).to match(/visibility_level=0/) @@ -135,7 +135,7 @@ RSpec.describe 'Dashboard > User filters projects' do end it 'filters internal projects only' do - select_dropdown_option '#filtered-search-visibility-dropdown', 'Internal' + select_dropdown_option '#filtered-search-visibility-dropdown > .dropdown', 'Internal', '.dropdown-item' expect(current_url).to match(/visibility_level=10/) @@ -145,7 +145,7 @@ RSpec.describe 'Dashboard > User filters projects' do end it 'filters any project' do - select_dropdown_option '#filtered-search-visibility-dropdown', 'Any' + select_dropdown_option '#filtered-search-visibility-dropdown > .dropdown', 'Any', '.dropdown-item' list = page.all('.projects-list .project-name').map(&:text) expect(list).to contain_exactly("Internal project", "Private project", "Treasure", "Victorialand") diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb index 63e16946a0b..98282e47488 100644 --- a/spec/features/expand_collapse_diffs_spec.rb +++ b/spec/features/expand_collapse_diffs_spec.rb @@ -13,6 +13,8 @@ RSpec.describe 'Expand and collapse diffs', :js do sign_in(admin) gitlab_enable_admin_mode_sign_in(admin) + wait_for_requests + # Ensure that undiffable.md is in .gitattributes project.repository.copy_gitattributes(branch) visit project_commit_path(project, project.commit(branch)) diff --git a/spec/features/explore/topics_spec.rb b/spec/features/explore/topics_spec.rb index 9d2e76bc3a1..d6f3d6a123d 100644 --- a/spec/features/explore/topics_spec.rb +++ b/spec/features/explore/topics_spec.rb @@ -7,7 +7,7 @@ RSpec.describe 'Explore Topics' do it 'renders empty message', :aggregate_failures do visit topics_explore_projects_path - expect(current_path).to eq topics_explore_projects_path + expect(page).to have_current_path topics_explore_projects_path, ignore_query: true expect(page).to have_content('There are no topics to show.') end end @@ -18,7 +18,7 @@ RSpec.describe 'Explore Topics' do it 'renders topic list' do visit topics_explore_projects_path - expect(current_path).to eq topics_explore_projects_path + expect(page).to have_current_path topics_explore_projects_path, ignore_query: true expect(page).to have_content('topic1') end end diff --git a/spec/features/file_uploads/user_avatar_spec.rb b/spec/features/file_uploads/user_avatar_spec.rb index c30e3452201..34cfb4a4128 100644 --- a/spec/features/file_uploads/user_avatar_spec.rb +++ b/spec/features/file_uploads/user_avatar_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Upload a user avatar', :js do expect(page).to have_content 'Profile was successfully updated' expect(user.reload.avatar.file).to be_present expect(user.avatar).to be_instance_of AvatarUploader - expect(current_path).to eq(profile_path) + expect(page).to have_current_path(profile_path, ignore_query: true) end end diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb index 0397e72502a..baa691d244e 100644 --- a/spec/features/global_search_spec.rb +++ b/spec/features/global_search_spec.rb @@ -72,6 +72,10 @@ RSpec.describe 'Global search' do # TODO: Remove this along with feature flag #339348 stub_feature_flags(new_header_search: true) visit dashboard_projects_path + + # intialize javascript loaded input search input field + find('#search').click + find('body').click end it 'renders updated search bar' do diff --git a/spec/features/groups/clusters/eks_spec.rb b/spec/features/groups/clusters/eks_spec.rb index fe62efbd3bf..3cca2d0919c 100644 --- a/spec/features/groups/clusters/eks_spec.rb +++ b/spec/features/groups/clusters/eks_spec.rb @@ -13,13 +13,15 @@ RSpec.describe 'Group AWS EKS Cluster', :js do allow(Groups::ClustersController).to receive(:STATUS_POLLING_INTERVAL) { 100 } allow_any_instance_of(Clusters::Kubernetes::CreateOrUpdateNamespaceService).to receive(:execute) allow_any_instance_of(Clusters::Cluster).to receive(:retrieve_connection_status).and_return(:connected) + stub_application_setting(eks_integration_enabled: true) end context 'when user does not have a cluster and visits group clusters page' do before do visit group_clusters_path(group) - click_link 'Connect with a certificate' + click_button 'Actions' + click_link 'Create a new cluster' end context 'when user creates a cluster on AWS EKS' do @@ -28,7 +30,7 @@ RSpec.describe 'Group AWS EKS Cluster', :js do end it 'user sees a form to create an EKS cluster' do - expect(page).to have_content('Create new cluster on EKS') + expect(page).to have_content('Authenticate with Amazon Web Services') end end end diff --git a/spec/features/groups/clusters/user_spec.rb b/spec/features/groups/clusters/user_spec.rb index 1788167c94c..2ed6ddc09ab 100644 --- a/spec/features/groups/clusters/user_spec.rb +++ b/spec/features/groups/clusters/user_spec.rb @@ -26,7 +26,6 @@ RSpec.describe 'User Cluster', :js do visit group_clusters_path(group) click_link 'Connect with a certificate' - click_link 'Connect existing cluster' end context 'when user filled form with valid parameters' do @@ -94,16 +93,7 @@ RSpec.describe 'User Cluster', :js do expect(page).to have_button('Save changes') end - context 'when user disables the cluster' do - before do - page.find(:css, '.js-cluster-enable-toggle-area .js-project-feature-toggle').click - page.within('.js-cluster-details-form') { click_button 'Save changes' } - end - - it 'user sees the successful message' do - expect(page).to have_content('Kubernetes cluster was successfully updated.') - end - end + include_examples "user disables a cluster" context 'when user changes cluster parameters' do before do diff --git a/spec/features/groups/container_registry_spec.rb b/spec/features/groups/container_registry_spec.rb index f5af9ba8b7b..7bef2dc9416 100644 --- a/spec/features/groups/container_registry_spec.rb +++ b/spec/features/groups/container_registry_spec.rb @@ -97,6 +97,8 @@ RSpec.describe 'Container Registry', :js do expect(find('.modal .modal-title')).to have_content _('Remove tag') find('.modal .modal-footer .btn-danger').click end + + it_behaves_like 'rejecting tags destruction for an importing repository on', tags: ['latest'] end end diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb index 30a81333547..50982cb1452 100644 --- a/spec/features/groups/group_settings_spec.rb +++ b/spec/features/groups/group_settings_spec.rb @@ -20,7 +20,7 @@ RSpec.describe 'Edit group settings' do update_path(new_group_path) visit new_group_full_path - expect(current_path).to eq(new_group_full_path) + expect(page).to have_current_path(new_group_full_path, ignore_query: true) expect(find('h1.home-panel-title')).to have_content(group.name) end @@ -28,7 +28,7 @@ RSpec.describe 'Edit group settings' do update_path(new_group_path) visit old_group_full_path - expect(current_path).to eq(new_group_full_path) + expect(page).to have_current_path(new_group_full_path, ignore_query: true) expect(find('h1.home-panel-title')).to have_content(group.name) end @@ -41,7 +41,7 @@ RSpec.describe 'Edit group settings' do update_path(new_group_path) visit new_subgroup_full_path - expect(current_path).to eq(new_subgroup_full_path) + expect(page).to have_current_path(new_subgroup_full_path, ignore_query: true) expect(find('h1.home-panel-title')).to have_content(subgroup.name) end @@ -49,7 +49,7 @@ RSpec.describe 'Edit group settings' do update_path(new_group_path) visit old_subgroup_full_path - expect(current_path).to eq(new_subgroup_full_path) + expect(page).to have_current_path(new_subgroup_full_path, ignore_query: true) expect(find('h1.home-panel-title')).to have_content(subgroup.name) end end @@ -71,7 +71,7 @@ RSpec.describe 'Edit group settings' do update_path(new_group_path) visit new_project_full_path - expect(current_path).to eq(new_project_full_path) + expect(page).to have_current_path(new_project_full_path, ignore_query: true) expect(find('.breadcrumbs')).to have_content(project.path) end @@ -79,7 +79,7 @@ RSpec.describe 'Edit group settings' do update_path(new_group_path) visit old_project_full_path - expect(current_path).to eq(new_project_full_path) + expect(page).to have_current_path(new_project_full_path, ignore_query: true) expect(find('.breadcrumbs')).to have_content(project.path) end end @@ -154,32 +154,50 @@ RSpec.describe 'Edit group settings' do namespace_select.find('button').click namespace_select.find('.dropdown-menu p', text: target_group_name, match: :first).click - click_button "Transfer group" + click_button 'Transfer group' end page.within(confirm_modal) do - expect(page).to have_text "You are going to transfer #{selected_group.name} to another namespace. Are you ABSOLUTELY sure? " + expect(page).to have_text "You are going to transfer #{selected_group.name} to another namespace. Are you ABSOLUTELY sure?" - fill_in "confirm_name_input", with: selected_group.name - click_button "Confirm" + fill_in 'confirm_name_input', with: selected_group.name + click_button 'Confirm' end expect(page).to have_text "Group '#{selected_group.name}' was successfully transferred." + expect(current_url).to include(selected_group.reload.full_path) end end - context 'with a sub group' do + context 'from a subgroup' do let(:selected_group) { create(:group, path: 'foo-subgroup', parent: group) } - let(:target_group_name) { "No parent group" } - it_behaves_like 'can transfer the group' + context 'to no parent group' do + let(:target_group_name) { 'No parent group' } + + it_behaves_like 'can transfer the group' + end + + context 'to a different parent group' do + let(:target_group) { create(:group, path: 'foo-parentgroup') } + let(:target_group_name) { target_group.name } + + before do + target_group.add_owner(user) + end + + it_behaves_like 'can transfer the group' + end end - context 'with a root group' do + context 'from a root group' do let(:selected_group) { create(:group, path: 'foo-rootgroup') } - let(:target_group_name) { group.name } - it_behaves_like 'can transfer the group' + context 'to a parent group' do + let(:target_group_name) { group.name } + + it_behaves_like 'can transfer the group' + end end end diff --git a/spec/features/groups/issues_spec.rb b/spec/features/groups/issues_spec.rb index 3fc1484826c..6b663445124 100644 --- a/spec/features/groups/issues_spec.rb +++ b/spec/features/groups/issues_spec.rb @@ -108,6 +108,22 @@ RSpec.describe 'Group issues page' do end end + context 'group with no issues', :js do + let!(:group_with_no_issues) { create(:group) } + let!(:subgroup_with_issues) { create(:group, parent: group_with_no_issues) } + let!(:subgroup_project) { create(:project, :public, group: subgroup_with_issues) } + let!(:subgroup_issue) { create(:issue, project: subgroup_project) } + + before do + stub_feature_flags(vue_issues_list: true) + visit issues_group_path(group_with_no_issues) + end + + it 'shows issues from subgroups on issues list' do + expect(page).to have_text subgroup_issue.title + end + end + context 'projects with issues disabled' do describe 'issue dropdown' do let(:user_in_group) { create(:group_member, :maintainer, user: create(:user), group: group ).user } diff --git a/spec/features/groups/labels/create_spec.rb b/spec/features/groups/labels/create_spec.rb index 9c1a3672ebd..19433e612ff 100644 --- a/spec/features/groups/labels/create_spec.rb +++ b/spec/features/groups/labels/create_spec.rb @@ -18,6 +18,6 @@ RSpec.describe 'Create a group label' do click_button 'Create label' expect(page).to have_content 'test-label' - expect(current_path).to eq(group_labels_path(group)) + expect(page).to have_current_path(group_labels_path(group), ignore_query: true) end end diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb index 8e6560af352..cf1729af97d 100644 --- a/spec/features/groups/labels/edit_spec.rb +++ b/spec/features/groups/labels/edit_spec.rb @@ -19,7 +19,7 @@ RSpec.describe 'Edit group label' do fill_in 'label_title', with: 'new label name' click_button 'Save changes' - expect(current_path).to eq(root_path) + expect(page).to have_current_path(root_path, ignore_query: true) expect(label.reload.title).to eq('new label name') end diff --git a/spec/features/groups/labels/sort_labels_spec.rb b/spec/features/groups/labels/sort_labels_spec.rb index df75ff7c3cb..fba166449f8 100644 --- a/spec/features/groups/labels/sort_labels_spec.rb +++ b/spec/features/groups/labels/sort_labels_spec.rb @@ -28,7 +28,7 @@ RSpec.describe 'Sort labels', :js do it 'sorts by date' do click_button 'Name' - sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text) + sort_options = find('ul.dropdown-menu').all('li').collect(&:text) expect(sort_options[0]).to eq('Name') expect(sort_options[1]).to eq('Name, descending') @@ -37,7 +37,7 @@ RSpec.describe 'Sort labels', :js do expect(sort_options[4]).to eq('Updated date') expect(sort_options[5]).to eq('Oldest updated') - click_link 'Name, descending' + click_button 'Name, descending' # assert default sorting within '.other-labels' do diff --git a/spec/features/groups/members/leave_group_spec.rb b/spec/features/groups/members/leave_group_spec.rb index 9612c6625f6..50d5db46cee 100644 --- a/spec/features/groups/members/leave_group_spec.rb +++ b/spec/features/groups/members/leave_group_spec.rb @@ -21,7 +21,7 @@ RSpec.describe 'Groups > Members > Leave group' do visit group_path(group) click_link 'Leave group' - expect(current_path).to eq(dashboard_groups_path) + expect(page).to have_current_path(dashboard_groups_path, ignore_query: true) expect(page).to have_content left_group_message(group) expect(group.users).not_to include(user) end @@ -35,7 +35,7 @@ RSpec.describe 'Groups > Members > Leave group' do page.accept_confirm wait_for_all_requests - expect(current_path).to eq(dashboard_groups_path) + expect(page).to have_current_path(dashboard_groups_path, ignore_query: true) expect(group.users).not_to include(user) end @@ -45,7 +45,7 @@ RSpec.describe 'Groups > Members > Leave group' do visit group_path(group) click_link 'Leave group' - expect(current_path).to eq(dashboard_groups_path) + expect(page).to have_current_path(dashboard_groups_path, ignore_query: true) expect(page).to have_content left_group_message(group) expect(group.users).not_to include(user) end @@ -57,7 +57,7 @@ RSpec.describe 'Groups > Members > Leave group' do visit group_path(group) click_link 'Leave group' - expect(current_path).to eq(dashboard_groups_path) + expect(page).to have_current_path(dashboard_groups_path, ignore_query: true) expect(page).to have_content left_group_message(group) expect(group.users).not_to include(user) end diff --git a/spec/features/groups/members/manage_groups_spec.rb b/spec/features/groups/members/manage_groups_spec.rb index 61c6709f9cc..5ab5a7ea716 100644 --- a/spec/features/groups/members/manage_groups_spec.rb +++ b/spec/features/groups/members/manage_groups_spec.rb @@ -14,34 +14,6 @@ RSpec.describe 'Groups > Members > Manage groups', :js do sign_in(user) end - context 'with invite_members_group_modal disabled' do - before do - stub_feature_flags(invite_members_group_modal: false) - end - - context 'when group link does not exist' do - let_it_be(:group) { create(:group) } - let_it_be(:group_to_add) { create(:group) } - - before do - group.add_owner(user) - group_to_add.add_owner(user) - visit group_group_members_path(group) - end - - it 'can share group with group' do - add_group(group_to_add.id, 'Reporter') - - click_groups_tab - - page.within(first_row) do - expect(page).to have_content(group_to_add.name) - expect(page).to have_content('Reporter') - end - end - end - end - context 'when group link does not exist' do it 'can share a group with group' do group = create(:group) @@ -177,32 +149,14 @@ RSpec.describe 'Groups > Members > Manage groups', :js do end context 'when sharing with groups outside the hierarchy is enabled' do - context 'when the invite members group modal is disabled' do - before do - stub_feature_flags(invite_members_group_modal: false) - end - - it 'shows groups within and outside the hierarchy in search results' do - visit group_group_members_path(group) - - click_on 'Invite group' - click_on 'Search for a group' - - expect(page).to have_text group_within_hierarchy.name - expect(page).to have_text group_outside_hierarchy.name - end - end - - context 'when the invite members group modal is enabled' do - it 'shows groups within and outside the hierarchy in search results' do - visit group_group_members_path(group) + it 'shows groups within and outside the hierarchy in search results' do + visit group_group_members_path(group) - click_on 'Invite a group' - click_on 'Select a group' + click_on 'Invite a group' + click_on 'Select a group' - expect(page).to have_text group_within_hierarchy.name - expect(page).to have_text group_outside_hierarchy.name - end + expect(page).to have_text group_within_hierarchy.name + expect(page).to have_text group_outside_hierarchy.name end end @@ -211,45 +165,18 @@ RSpec.describe 'Groups > Members > Manage groups', :js do group.namespace_settings.update!(prevent_sharing_groups_outside_hierarchy: true) end - context 'when the invite members group modal is disabled' do - before do - stub_feature_flags(invite_members_group_modal: false) - end - - it 'shows only groups within the hierarchy in search results' do - visit group_group_members_path(group) - - click_on 'Invite group' - click_on 'Search for a group' - - expect(page).to have_text group_within_hierarchy.name - expect(page).not_to have_text group_outside_hierarchy.name - end - end - - context 'when the invite members group modal is enabled' do - it 'shows only groups within the hierarchy in search results' do - visit group_group_members_path(group) + it 'shows only groups within the hierarchy in search results' do + visit group_group_members_path(group) - click_on 'Invite a group' - click_on 'Select a group' + click_on 'Invite a group' + click_on 'Select a group' - expect(page).to have_text group_within_hierarchy.name - expect(page).not_to have_text group_outside_hierarchy.name - end + expect(page).to have_text group_within_hierarchy.name + expect(page).not_to have_text group_outside_hierarchy.name end end end - def add_group(id, role) - page.click_link 'Invite group' - page.within ".invite-group-form" do - select2(id, from: "#shared_with_group_id") - select(role, from: "shared_group_access") - click_button "Invite" - end - end - def click_groups_tab expect(page).to have_link 'Groups' click_link "Groups" diff --git a/spec/features/groups/members/manage_members_spec.rb b/spec/features/groups/members/manage_members_spec.rb index e5dad5ee4be..533d2118b30 100644 --- a/spec/features/groups/members/manage_members_spec.rb +++ b/spec/features/groups/members/manage_members_spec.rb @@ -15,42 +15,18 @@ RSpec.describe 'Groups > Members > Manage members' do sign_in(user1) end - shared_examples 'includes the correct Invite link' do |should_include, should_not_include| - it 'includes either the form or the modal trigger', :aggregate_failures do + shared_examples 'includes the correct Invite link' do |should_include| + it 'includes the modal trigger', :aggregate_failures do group.add_owner(user1) visit group_group_members_path(group) expect(page).to have_selector(should_include) - expect(page).not_to have_selector(should_not_include) end end - shared_examples 'does not include either invite modal or either invite form' do - it 'does not include either of the invite members or invite group modal buttons', :aggregate_failures do - expect(page).not_to have_selector '.js-invite-members-modal' - expect(page).not_to have_selector '.js-invite-group-modal' - end - - it 'does not include either of the invite users or invite group forms', :aggregate_failures do - expect(page).not_to have_selector '.invite-users-form' - expect(page).not_to have_selector '.invite-group-form' - end - end - - context 'when Invite Members modal is enabled' do - it_behaves_like 'includes the correct Invite link', '.js-invite-members-trigger', '.invite-users-form' - it_behaves_like 'includes the correct Invite link', '.js-invite-group-trigger', '.invite-group-form' - end - - context 'when Invite Members modal is disabled' do - before do - stub_feature_flags(invite_members_group_modal: false) - end - - it_behaves_like 'includes the correct Invite link', '.invite-users-form', '.js-invite-members-trigger' - it_behaves_like 'includes the correct Invite link', '.invite-group-form', '.js-invite-group-trigger' - end + it_behaves_like 'includes the correct Invite link', '.js-invite-members-trigger' + it_behaves_like 'includes the correct Invite link', '.js-invite-group-trigger' it 'update user to owner level', :js do group.add_owner(user1) @@ -106,33 +82,6 @@ RSpec.describe 'Groups > Members > Manage members' do expect(page).to have_content('Invite "undisclosed_email@gitlab.com" by email') end - context 'when Invite Members modal is disabled' do - before do - stub_feature_flags(invite_members_group_modal: false) - end - - it 'do not disclose email addresses', :js do - group.add_owner(user1) - create(:user, email: 'undisclosed_email@gitlab.com', name: "Jane 'invisible' Doe") - - visit group_group_members_path(group) - - find('.select2-container').click - select_input = find('.select2-input') - - select_input.send_keys('@gitlab.com') - wait_for_requests - - expect(page).to have_content('No matches found') - - select_input.native.clear - select_input.send_keys('undisclosed_email@gitlab.com') - wait_for_requests - - expect(page).to have_content('Invite "undisclosed_email@gitlab.com" by email') - end - end - it 'remove user from group', :js do group.add_owner(user1) group.add_developer(user2) @@ -205,30 +154,11 @@ RSpec.describe 'Groups > Members > Manage members' do visit group_group_members_path(group) end - it_behaves_like 'does not include either invite modal or either invite form' - - it 'does not include a button on the members page list to manage or remove the existing member', :js, :aggregate_failures do - page.within(second_row) do - # Can not modify user2 role - expect(page).not_to have_button 'Developer' - - # Can not remove user2 - expect(page).not_to have_selector 'button[title="Remove member"]' - end - end - end - - context 'when user is a guest and the :invite_members_group_modal feature flag is disabled' do - before do - stub_feature_flags(invite_members_group_modal: false) - group.add_guest(user1) - group.add_developer(user2) - - visit group_group_members_path(group) + it 'does not include either of the invite members or invite group modal buttons', :aggregate_failures do + expect(page).not_to have_selector '.js-invite-members-modal' + expect(page).not_to have_selector '.js-invite-group-modal' end - it_behaves_like 'does not include either invite modal or either invite form' - it 'does not include a button on the members page list to manage or remove the existing member', :js, :aggregate_failures do page.within(second_row) do # Can not modify user2 role diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb index c5d2f5e6733..e4b44d65438 100644 --- a/spec/features/groups/navbar_spec.rb +++ b/spec/features/groups/navbar_spec.rb @@ -18,6 +18,7 @@ RSpec.describe 'Group navbar' do stub_feature_flags(customer_relations: false) stub_config(dependency_proxy: { enabled: false }) stub_config(registry: { enabled: false }) + stub_feature_flags(harbor_registry_integration: false) stub_group_wikis(false) group.add_maintainer(user) sign_in(user) @@ -59,6 +60,18 @@ RSpec.describe 'Group navbar' do it_behaves_like 'verified navigation bar' end + context 'when customer_relations feature and flag is enabled but subgroup' do + let(:group) { create(:group, :crm_enabled, parent: create(:group)) } + + before do + stub_feature_flags(customer_relations: true) + + visit group_path(group) + end + + it_behaves_like 'verified navigation bar' + end + context 'when dependency proxy is available' do before do stub_config(dependency_proxy: { enabled: true }) @@ -70,4 +83,16 @@ RSpec.describe 'Group navbar' do it_behaves_like 'verified navigation bar' end + + context 'when harbor registry is available' do + before do + stub_feature_flags(harbor_registry_integration: true) + + insert_harbor_registry_nav(_('Package Registry')) + + visit group_path(group) + end + + it_behaves_like 'verified navigation bar' + end end diff --git a/spec/features/groups/settings/ci_cd_spec.rb b/spec/features/groups/settings/ci_cd_spec.rb index b059cd8da29..8851aeb6381 100644 --- a/spec/features/groups/settings/ci_cd_spec.rb +++ b/spec/features/groups/settings/ci_cd_spec.rb @@ -13,6 +13,24 @@ RSpec.describe 'Group CI/CD settings' do sign_in(user) end + describe 'new group runners view banner' do + it 'displays banner' do + visit group_settings_ci_cd_path(group) + + expect(page).to have_content(s_('Runners|New group runners view')) + expect(page).to have_link(href: group_runners_path(group)) + end + + it 'does not display banner' do + stub_feature_flags(runner_list_group_view_vue_ui: false) + + visit group_settings_ci_cd_path(group) + + expect(page).not_to have_content(s_('Runners|New group runners view')) + expect(page).not_to have_link(href: group_runners_path(group)) + end + end + describe 'runners registration token' do let!(:token) { group.runners_token } diff --git a/spec/features/groups/settings/repository_spec.rb b/spec/features/groups/settings/repository_spec.rb index d95eaf3c92c..159deb2a4e3 100644 --- a/spec/features/groups/settings/repository_spec.rb +++ b/spec/features/groups/settings/repository_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Group Repository settings' do end end - context 'Default initial branch name' do + context 'Default branch' do before do visit group_settings_repository_path(group) end @@ -37,8 +37,8 @@ RSpec.describe 'Group Repository settings' do it 'renders the correct setting section content' do within("#js-default-branch-name") do - expect(page).to have_content("Default initial branch name") - expect(page).to have_content("The default name for the initial branch of new repositories created in the group.") + expect(page).to have_content("Default branch") + expect(page).to have_content("Set the initial name and protections for the default branch of new repositories created in the group.") end end end diff --git a/spec/features/groups/settings/user_searches_in_settings_spec.rb b/spec/features/groups/settings/user_searches_in_settings_spec.rb index abf56232aff..c7b7b25caa7 100644 --- a/spec/features/groups/settings/user_searches_in_settings_spec.rb +++ b/spec/features/groups/settings/user_searches_in_settings_spec.rb @@ -32,7 +32,7 @@ RSpec.describe 'User searches group settings', :js do visit group_settings_repository_path(group) end - it_behaves_like 'can search settings', 'Deploy tokens', 'Default initial branch name' + it_behaves_like 'can search settings', 'Deploy tokens', 'Default branch' end context 'in CI/CD page' do diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb index 925bbc47cf6..08183badda1 100644 --- a/spec/features/groups_spec.rb +++ b/spec/features/groups_spec.rb @@ -32,7 +32,7 @@ RSpec.describe 'Group' do group = Group.find_by(name: 'test-group') expect(group.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC) - expect(current_path).to eq(group_path(group)) + expect(page).to have_current_path(group_path(group), ignore_query: true) expect(page).to have_selector '.visibility-icon [data-testid="earth-icon"]' end end @@ -51,7 +51,7 @@ RSpec.describe 'Group' do fill_in 'Group URL', with: 'space group' click_button 'Create group' - expect(current_path).to eq(new_group_path) + expect(page).to have_current_path(new_group_path, ignore_query: true) expect(page).to have_text('Choose a group path that does not start with a dash or end with a period. It can also contain alphanumeric characters and underscores.') end end @@ -62,7 +62,7 @@ RSpec.describe 'Group' do fill_in 'Group URL', with: 'atom_group.atom' click_button 'Create group' - expect(current_path).to eq(groups_path) + expect(page).to have_current_path(groups_path, ignore_query: true) expect(page).to have_namespace_error_message end end @@ -73,7 +73,7 @@ RSpec.describe 'Group' do fill_in 'Group URL', with: 'git_group.git' click_button 'Create group' - expect(current_path).to eq(groups_path) + expect(page).to have_current_path(groups_path, ignore_query: true) expect(page).to have_namespace_error_message end end @@ -211,7 +211,7 @@ RSpec.describe 'Group' do fill_in 'Group name', with: 'bar' click_button 'Create group' - expect(current_path).to eq(group_path('foo/bar')) + expect(page).to have_current_path(group_path('foo/bar'), ignore_query: true) expect(page).to have_selector 'h1', text: 'bar' end end @@ -237,7 +237,7 @@ RSpec.describe 'Group' do fill_in 'Group name', with: 'bar' click_button 'Create group' - expect(current_path).to eq(group_path('foo/bar')) + expect(page).to have_current_path(group_path('foo/bar'), ignore_query: true) expect(page).to have_selector 'h1', text: 'bar' end end @@ -474,4 +474,69 @@ RSpec.describe 'Group' do fill_in 'confirm_name_input', with: confirm_with click_button 'Confirm' end + + describe 'storage_enforcement_banner', :js do + let_it_be(:group) { create(:group) } + let_it_be_with_refind(:user) { create(:user) } + + before_all do + group.add_owner(user) + sign_in(user) + end + + context 'with storage_enforcement_date set' do + let_it_be(:storage_enforcement_date) { Date.today + 30 } + + before do + allow_next_found_instance_of(Group) do |g| + allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + end + end + + it 'displays the banner in the group page' do + visit group_path(group) + expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + end + + it 'does not display the banner in a paid group page' do + allow_next_found_instance_of(Group) do |g| + allow(g).to receive(:paid?).and_return(true) + end + visit group_path(group) + expect_page_not_to_have_storage_enforcement_banner + end + + it 'does not display the banner if user has previously closed unless threshold has changed' do + visit group_path(group) + expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + find('.js-storage-enforcement-banner [data-testid="close-icon"]').click + page.refresh + expect_page_not_to_have_storage_enforcement_banner + + storage_enforcement_date = Date.today + 13 + allow_next_found_instance_of(Group) do |g| + allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date) + end + page.refresh + expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + end + end + + context 'with storage_enforcement_date not set' do + # This test should break and be rewritten after the implementation of the storage_enforcement_date + # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632 + it 'does not display the banner in the group page' do + visit group_path(group) + expect_page_not_to_have_storage_enforcement_banner + end + end + end + + def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date) + expect(page).to have_text "From #{storage_enforcement_date} storage limits will apply to this namespace" + end + + def expect_page_not_to_have_storage_enforcement_banner + expect(page).not_to have_text "storage limits will apply to this namespace" + end end diff --git a/spec/features/incidents/incident_details_spec.rb b/spec/features/incidents/incident_details_spec.rb index b704a0515c8..dad3dfd3440 100644 --- a/spec/features/incidents/incident_details_spec.rb +++ b/spec/features/incidents/incident_details_spec.rb @@ -6,6 +6,7 @@ RSpec.describe 'Incident details', :js do let_it_be(:project) { create(:project) } let_it_be(:developer) { create(:user) } let_it_be(:incident) { create(:incident, project: project, author: developer, description: 'description') } + let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: incident) } before_all do project.add_developer(developer) @@ -21,7 +22,7 @@ RSpec.describe 'Incident details', :js do context 'when a developer+ displays the incident' do it 'shows the incident' do page.within('.issuable-details') do - expect(find('h2')).to have_content(incident.title) + expect(find('h1')).to have_content(incident.title) end end @@ -33,7 +34,7 @@ RSpec.describe 'Incident details', :js do page.within('.issuable-details') do incident_tabs = find('[data-testid="incident-tabs"]') - expect(find('h2')).to have_content(incident.title) + expect(find('h1')).to have_content(incident.title) expect(incident_tabs).to have_content('Summary') expect(incident_tabs).to have_content(incident.description) end @@ -46,6 +47,42 @@ RSpec.describe 'Incident details', :js do expect(page).to have_selector('.right-sidebar[data-issuable-type="issue"]') expect(sidebar).to have_selector('.incident-severity') expect(sidebar).to have_selector('.milestone') + expect(sidebar).to have_selector('[data-testid="escalation_status_container"]') + end + end + + context 'escalation status' do + let(:sidebar) { page.find('.right-sidebar') } + let(:widget) { sidebar.find('[data-testid="escalation_status_container"]') } + let(:expected_dropdown_options) { escalation_status.class::STATUSES.keys.take(3).map { |key| key.to_s.titleize } } + + it 'has an interactable escalation status widget' do + expect(current_status).to have_text(escalation_status.status_name.to_s.titleize) + + # list the available statuses + widget.find('[data-testid="edit-button"]').click + expect(dropdown_options.map(&:text)).to eq(expected_dropdown_options) + expect(widget).not_to have_selector('#escalation-status-help') + + # update the status + select_resolved(dropdown_options) + expect(current_status).to have_text('Resolved') + expect(escalation_status.reload).to be_resolved + end + + private + + def dropdown_options + widget.all('[data-testid="status-dropdown-item"]', count: 3) + end + + def select_resolved(options) + options.last.click + wait_for_requests + end + + def current_status + widget.find('[data-testid="collapsed-content"]') end end end diff --git a/spec/features/incidents/incidents_list_spec.rb b/spec/features/incidents/incidents_list_spec.rb index c65c83b2804..789cc89e083 100644 --- a/spec/features/incidents/incidents_list_spec.rb +++ b/spec/features/incidents/incidents_list_spec.rb @@ -34,5 +34,28 @@ RSpec.describe 'Incident Management index', :js do it 'alert page title' do expect(page).to have_content('Incidents') end + + it 'has expected columns' do + table = page.find('.gl-table') + + expect(table).to have_content('Severity') + expect(table).to have_content('Incident') + expect(table).to have_content('Status') + expect(table).to have_content('Date created') + expect(table).to have_content('Assignees') + end + + context 'when :incident_escalations feature is disabled' do + before do + stub_feature_flags(incident_escalations: false) + end + + it 'does not include the Status columns' do + visit project_incidents_path(project) + wait_for_requests + + expect(page.find('.gl-table')).not_to have_content('Status') + end + end end end diff --git a/spec/features/incidents/user_views_incident_spec.rb b/spec/features/incidents/user_views_incident_spec.rb index fe54f7708c9..a669966502e 100644 --- a/spec/features/incidents/user_views_incident_spec.rb +++ b/spec/features/incidents/user_views_incident_spec.rb @@ -26,7 +26,7 @@ RSpec.describe "User views incident" do it 'shows the merge request and incident actions', :js, :aggregate_failures do click_button 'Incident actions' - expect(page).to have_link('New incident', href: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident', description: "Related to \##{incident.iid}.\n\n" } })) + expect(page).to have_link('New related incident', href: new_project_issue_path(project, { issuable_template: 'incident', issue: { issue_type: 'incident' }, add_related_issue: incident.iid })) expect(page).to have_button('Create merge request') expect(page).to have_button('Close incident') end @@ -40,10 +40,8 @@ RSpec.describe "User views incident" do visit(project_issues_incident_path(project, incident)) end - it 'does not show the incident action', :js, :aggregate_failures do - click_button 'Incident actions' - - expect(page).not_to have_link('New incident') + it 'does not show the incident actions', :js, :aggregate_failures do + expect(page).not_to have_button('Incident actions') end end end diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb index 9cb9416e7a0..965e97baadd 100644 --- a/spec/features/invites_spec.rb +++ b/spec/features/invites_spec.rb @@ -57,7 +57,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end it 'renders sign up page with sign up notice' do - expect(current_path).to eq(new_user_registration_path) + expect(page).to have_current_path(new_user_registration_path, ignore_query: true) expect(page).to have_content('To accept this invitation, create an account or sign in') end @@ -85,7 +85,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do fill_in_sign_in_form(user) - expect(current_path).to eq(activity_group_path(group)) + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) end end @@ -98,7 +98,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end it 'shows message user already a member' do - expect(current_path).to eq(invite_path(group_invite.raw_invite_token)) + expect(page).to have_current_path(invite_path(group_invite.raw_invite_token), ignore_query: true) expect(page).to have_link(user.name, href: user_path(user)) expect(page).to have_content('You are already a member of this group.') end @@ -127,7 +127,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end it 'declines application and redirects to dashboard' do - expect(current_path).to eq(dashboard_projects_path) + expect(page).to have_current_path(dashboard_projects_path, ignore_query: true) expect(page).to have_content('You have declined the invitation to join group Owned.') expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound end @@ -139,7 +139,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do end it 'declines application and redirects to sign in page' do - expect(current_path).to eq(decline_invite_path(group_invite.raw_invite_token)) + expect(page).to have_current_path(decline_invite_path(group_invite.raw_invite_token), ignore_query: true) expect(page).not_to have_content('You have declined the invitation to join') expect(page).to have_content('You successfully declined the invitation') expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound @@ -174,7 +174,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do it 'does not sign the user in' do fill_in_sign_up_form(new_user) - expect(current_path).to eq(new_user_session_path) + expect(page).to have_current_path(new_user_session_path, ignore_query: true) expect(page).to have_content('You have signed up successfully. However, we could not sign you in because your account is awaiting approval from your GitLab administrator') end end @@ -186,7 +186,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do fill_in_sign_up_form(new_user) fill_in_welcome_form - expect(current_path).to eq(activity_group_path(group)) + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) expect(page).to have_content('You have been granted Owner access to group Owned.') end @@ -197,7 +197,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do fill_in_sign_up_form(new_user) fill_in_welcome_form - expect(current_path).to eq(activity_group_path(group)) + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) end end end @@ -209,7 +209,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do it 'fails sign up and redirects back to sign up', :aggregate_failures do expect { fill_in_sign_up_form(new_user) }.not_to change { User.count } expect(page).to have_content('prohibited this user from being saved') - expect(current_path).to eq(user_registration_path) + expect(page).to have_current_path(user_registration_path, ignore_query: true) end end @@ -230,7 +230,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do fill_in_sign_up_form(new_user) fill_in_welcome_form - expect(current_path).to eq(activity_group_path(group)) + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) end context 'the user sign-up using a different email address' do @@ -248,7 +248,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do fill_in_sign_in_form(new_user) fill_in_welcome_form - expect(current_path).to eq(activity_group_path(group)) + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) end end @@ -262,7 +262,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do fill_in_sign_up_form(new_user) fill_in_welcome_form - expect(current_path).to eq(activity_group_path(group)) + expect(page).to have_current_path(activity_group_path(group), ignore_query: true) end end end @@ -273,11 +273,11 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do it 'lands on sign up page and then registers' do visit invite_path(group_invite.raw_invite_token) - expect(current_path).to eq(new_user_registration_path) + expect(page).to have_current_path(new_user_registration_path, ignore_query: true) fill_in_sign_up_form(new_user, 'Register') - expect(current_path).to eq(users_sign_up_welcome_path) + expect(page).to have_current_path(users_sign_up_welcome_path, ignore_query: true) end end @@ -285,7 +285,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do it 'declines application and shows a decline page' do visit decline_invite_path(group_invite.raw_invite_token) - expect(current_path).to eq(decline_invite_path(group_invite.raw_invite_token)) + expect(page).to have_current_path(decline_invite_path(group_invite.raw_invite_token), ignore_query: true) expect(page).to have_content('You successfully declined the invitation') expect { group_invite.reload }.to raise_error ActiveRecord::RecordNotFound end diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb index e873ebb21c4..3ba2f7e788d 100644 --- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb +++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb @@ -12,14 +12,14 @@ RSpec.describe 'Dropdown assignee', :js do let(:js_dropdown_assignee) { '#js-dropdown-assignee' } let(:filter_dropdown) { find("#{js_dropdown_assignee} .filter-dropdown") } - before do - project.add_maintainer(user) - sign_in(user) + describe 'behavior' do + before do + project.add_maintainer(user) + sign_in(user) - visit project_issues_path(project) - end + visit project_issues_path(project) + end - describe 'behavior' do it 'loads all the assignees when opened' do input_filtered_search('assignee:=', submit: false, extra_space: false) @@ -35,6 +35,11 @@ RSpec.describe 'Dropdown assignee', :js do describe 'selecting from dropdown without Ajax call' do before do + project.add_maintainer(user) + sign_in(user) + + visit project_issues_path(project) + Gitlab::Testing::RequestBlockerMiddleware.block_requests! input_filtered_search('assignee:=', submit: false, extra_space: false) end @@ -51,4 +56,60 @@ RSpec.describe 'Dropdown assignee', :js do expect_filtered_search_input_empty end end + + context 'assignee suggestions' do + let!(:group) { create(:group) } + let!(:group_project) { create(:project, namespace: group) } + let!(:group_user) { create(:user) } + + let!(:subgroup) { create(:group, parent: group) } + let!(:subgroup_project) { create(:project, namespace: subgroup) } + let!(:subgroup_project_issue) { create(:issue, project: subgroup_project) } + let!(:subgroup_user) { create(:user) } + + let!(:subsubgroup) { create(:group, parent: subgroup) } + let!(:subsubgroup_project) { create(:project, namespace: subsubgroup) } + let!(:subsubgroup_user) { create(:user) } + + let!(:invited_to_group_group) { create(:group) } + let!(:invited_to_group_group_user) { create(:user) } + + let!(:invited_to_project_group) { create(:group) } + let!(:invited_to_project_group_user) { create(:user) } + + before do + group.add_developer(group_user) + subgroup.add_developer(subgroup_user) + subsubgroup.add_developer(subsubgroup_user) + invited_to_group_group.add_developer(invited_to_group_group_user) + invited_to_project_group.add_developer(invited_to_project_group_user) + + create(:group_group_link, shared_group: subgroup, shared_with_group: invited_to_group_group) + create(:project_group_link, project: subgroup_project, group: invited_to_project_group) + + sign_in(subgroup_user) + end + + it 'shows inherited, direct, and invited group members but not descendent members', :aggregate_failures do + visit issues_group_path(subgroup) + + input_filtered_search('assignee:=', submit: false, extra_space: false) + + expect(page).to have_text group_user.name + expect(page).to have_text subgroup_user.name + expect(page).to have_text invited_to_group_group_user.name + expect(page).not_to have_text subsubgroup_user.name + expect(page).not_to have_text invited_to_project_group_user.name + + visit project_issues_path(subgroup_project) + + input_filtered_search('assignee:=', submit: false, extra_space: false) + + expect(page).to have_text group_user.name + expect(page).to have_text subgroup_user.name + expect(page).to have_text invited_to_project_group_user.name + expect(page).not_to have_text subsubgroup_user.name + expect(page).not_to have_text invited_to_group_group_user.name + end + end end diff --git a/spec/features/issues/filtered_search/filter_issues_spec.rb b/spec/features/issues/filtered_search/filter_issues_spec.rb index edf3df7c16e..1375384d1aa 100644 --- a/spec/features/issues/filtered_search/filter_issues_spec.rb +++ b/spec/features/issues/filtered_search/filter_issues_spec.rb @@ -497,6 +497,8 @@ RSpec.describe 'Filter issues', :js do end it 'filters issues by searched text containing special characters' do + stub_feature_flags(issues_full_text_search: false) + issue = create(:issue, project: project, author: user, title: "issue with !@\#{$%^&*()-+") search = '!@#{$%^&*()-+' @@ -514,6 +516,14 @@ RSpec.describe 'Filter issues', :js do expect_no_issues_list expect_filtered_search_input(search) end + + it 'filters issues by issue reference' do + search = '#1' + input_filtered_search(search) + + expect_issues_list_count(1) + expect_filtered_search_input(search) + end end context 'searched text with other filters' do diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb index b26f65316c5..0700423983f 100644 --- a/spec/features/issues/form_spec.rb +++ b/spec/features/issues/form_spec.rb @@ -5,19 +5,22 @@ require 'spec_helper' RSpec.describe 'New/edit issue', :js do include ActionView::Helpers::JavaScriptHelper - let_it_be(:project) { create(:project) } + let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { create(:user) } let_it_be(:user2) { create(:user) } + let_it_be(:guest) { create(:user) } let_it_be(:milestone) { create(:milestone, project: project) } let_it_be(:label) { create(:label, project: project) } let_it_be(:label2) { create(:label, project: project) } let_it_be(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) } + let_it_be(:confidential_issue) { create(:issue, project: project, assignees: [user], milestone: milestone, confidential: true) } let(:current_user) { user } before_all do project.add_maintainer(user) project.add_maintainer(user2) + project.add_guest(guest) end before do @@ -184,6 +187,14 @@ RSpec.describe 'New/edit issue', :js do end end + it 'displays an error message when submitting an invalid form' do + click_button 'Create issue' + + page.within('[data-testid="issue-title-input-field"]') do + expect(page).to have_text(_('This field is required.')) + end + end + it 'correctly updates the dropdown toggle when removing a label' do click_button 'Labels' @@ -310,6 +321,108 @@ RSpec.describe 'New/edit issue', :js do end end + describe 'new issue with query parameters' do + before do + project.repository.create_file( + current_user, + '.gitlab/issue_templates/test_template.md', + 'description from template', + message: 'Add test_template.md', + branch_name: project.default_branch_or_main + ) + end + + after do + project.repository.delete_file( + current_user, + '.gitlab/issue_templates/test_template.md', + message: 'Remove test_template.md', + branch_name: project.default_branch_or_main + ) + end + + it 'leaves the description blank if no query parameters are specified' do + visit new_project_issue_path(project) + + expect(find('#issue_description').value).to be_empty + end + + it 'fills the description from the issue[description] query parameter' do + visit new_project_issue_path(project, issue: { description: 'description from query parameter' }) + + expect(find('#issue_description').value).to match('description from query parameter') + end + + it 'fills the description from the issuable_template query parameter' do + visit new_project_issue_path(project, issuable_template: 'test_template') + wait_for_requests + + expect(find('#issue_description').value).to match('description from template') + end + + it 'fills the description from the issuable_template and issue[description] query parameters' do + visit new_project_issue_path(project, issuable_template: 'test_template', issue: { description: 'description from query parameter' }) + wait_for_requests + + expect(find('#issue_description').value).to match('description from template\ndescription from query parameter') + end + end + + describe 'new issue from related issue' do + it 'does not offer to link the new issue to any other issues if the URL parameter is absent' do + visit new_project_issue_path(project) + expect(page).not_to have_selector '#add_related_issue' + expect(page).not_to have_text "Relate to" + end + + context 'guest' do + let(:current_user) { guest } + + it 'does not offer to link the new issue to an issue that the user does not have access to' do + visit new_project_issue_path(project, { add_related_issue: confidential_issue.iid }) + expect(page).not_to have_selector '#add_related_issue' + expect(page).not_to have_text "Relate to" + end + end + + it 'links the new issue and the issue of origin' do + visit new_project_issue_path(project, { add_related_issue: issue.iid }) + expect(page).to have_selector '#add_related_issue' + expect(page).to have_text "Relate to issue \##{issue.iid}" + expect(page).to have_text 'Adds this issue as related to the issue it was created from' + fill_in 'issue_title', with: 'title' + click_button 'Create issue' + page.within '#related-issues' do + expect(page).to have_text "\##{issue.iid}" + end + end + + it 'links the new incident and the incident of origin' do + incident = create(:incident, project: project) + visit new_project_issue_path(project, { add_related_issue: incident.iid }) + expect(page).to have_selector '#add_related_issue' + expect(page).to have_text "Relate to incident \##{incident.iid}" + expect(page).to have_text 'Adds this incident as related to the incident it was created from' + fill_in 'issue_title', with: 'title' + click_button 'Create issue' + page.within '#related-issues' do + expect(page).to have_text "\##{incident.iid}" + end + end + + it 'does not link the new issue to any other issues if the checkbox is not checked' do + visit new_project_issue_path(project, { add_related_issue: issue.iid }) + expect(page).to have_selector '#add_related_issue' + expect(page).to have_text "Relate to issue \##{issue.iid}" + uncheck "Relate to issue \##{issue.iid}" + fill_in 'issue_title', with: 'title' + click_button 'Create issue' + page.within '#related-issues' do + expect(page).not_to have_text "\##{issue.iid}" + end + end + end + describe 'edit issue' do before do visit edit_project_issue_path(project, issue) diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb index b4d1b0aeab9..6f4a13c5fad 100644 --- a/spec/features/issues/gfm_autocomplete_spec.rb +++ b/spec/features/issues/gfm_autocomplete_spec.rb @@ -111,6 +111,20 @@ RSpec.describe 'GFM autocomplete', :js do fill_in 'Comment', with: "test\n\n@" expect(find_autocomplete_menu).to be_visible end + + it 'does not open label autocomplete menu after strikethrough', :aggregate_failures do + fill_in 'Comment', with: "~~" + expect(page).not_to have_css('.atwho-view') + + fill_in 'Comment', with: "~~gone~~" + expect(page).not_to have_css('.atwho-view') + + fill_in 'Comment', with: "~" + expect(find_autocomplete_menu).to be_visible + + fill_in 'Comment', with: "test\n\n~" + expect(find_autocomplete_menu).to be_visible + end end context 'xss checks' do @@ -406,6 +420,14 @@ RSpec.describe 'GFM autocomplete', :js do end end end + + context 'when typing enter for autocomplete in a markdown list' do + it 'does not create a new list item' do + fill_in 'Comment', with: "- @#{user.username}\n" + + expect(find_field('Comment').value).to eq "- @#{user.username}\n" + end + end end private diff --git a/spec/features/issues/incident_issue_spec.rb b/spec/features/issues/incident_issue_spec.rb index 3033a138551..2956ddede2e 100644 --- a/spec/features/issues/incident_issue_spec.rb +++ b/spec/features/issues/incident_issue_spec.rb @@ -38,7 +38,7 @@ RSpec.describe 'Incident Detail', :js do incident_tabs = find('[data-testid="incident-tabs"]') aggregate_failures 'shows title and Summary tab' do - expect(find('h2')).to have_content(incident.title) + expect(find('h1')).to have_content(incident.title) expect(incident_tabs).to have_content('Summary') expect(incident_tabs).to have_content(incident.description) end diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb index b37c8e9d1cf..88709d66887 100644 --- a/spec/features/issues/issue_detail_spec.rb +++ b/spec/features/issues/issue_detail_spec.rb @@ -17,7 +17,7 @@ RSpec.describe 'Issue Detail', :js do it 'shows the issue' do page.within('.issuable-details') do - expect(find('h2')).to have_content(issue.title) + expect(find('h1')).to have_content(issue.title) end end end @@ -85,7 +85,7 @@ RSpec.describe 'Issue Detail', :js do it 'shows the issue' do page.within('.issuable-details') do - expect(find('h2')).to have_content(issue.reload.title) + expect(find('h1')).to have_content(issue.reload.title) end end end diff --git a/spec/features/issues/issue_header_spec.rb b/spec/features/issues/issue_header_spec.rb index 3e27ce81860..165015013dd 100644 --- a/spec/features/issues/issue_header_spec.rb +++ b/spec/features/issues/issue_header_spec.rb @@ -25,8 +25,8 @@ RSpec.describe 'issue header', :js do click_button 'Issue actions' end - it 'shows the "New issue", "Report abuse", and "Delete issue" items', :aggregate_failures do - expect(page).to have_link 'New issue' + it 'shows the "New related issue", "Report abuse", and "Delete issue" items', :aggregate_failures do + expect(page).to have_link 'New related issue' expect(page).to have_link 'Report abuse' expect(page).to have_button 'Delete issue' expect(page).not_to have_link 'Submit as spam' @@ -114,8 +114,8 @@ RSpec.describe 'issue header', :js do click_button 'Issue actions' end - it 'only shows the "New issue" and "Report abuse" items', :aggregate_failures do - expect(page).to have_link 'New issue' + it 'only shows the "New related issue" and "Report abuse" items', :aggregate_failures do + expect(page).to have_link 'New related issue' expect(page).to have_link 'Report abuse' expect(page).not_to have_link 'Submit as spam' expect(page).not_to have_button 'Delete issue' diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb index 868946814c3..aaa478378a9 100644 --- a/spec/features/issues/issue_sidebar_spec.rb +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -106,6 +106,7 @@ RSpec.describe 'Issue Sidebar' do end context 'when GraphQL assignees widget feature flag is enabled' do + # TODO: Move to shared examples when feature flag is removed: https://gitlab.com/gitlab-org/gitlab/-/issues/328185 context 'when a privileged user can invite' do it 'shows a link for inviting members and launches invite modal' do project.add_maintainer(user) @@ -236,6 +237,12 @@ RSpec.describe 'Issue Sidebar' do it_behaves_like 'labels sidebar widget' end + context 'escalation status', :js do + it 'is not available for default issue type' do + expect(page).not_to have_selector('.block.escalation-status') + end + end + context 'interacting with collapsed sidebar', :js do collapsed_sidebar_selector = 'aside.right-sidebar.right-sidebar-collapsed' expanded_sidebar_selector = 'aside.right-sidebar.right-sidebar-expanded' diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb index ee2fbf0865e..054b7b3855b 100644 --- a/spec/features/issues/move_spec.rb +++ b/spec/features/issues/move_spec.rb @@ -50,7 +50,7 @@ RSpec.describe 'issue move to another project' do expect(page).to have_content("Text with #{cross_reference}#{mr.to_reference}") expect(page).to have_content("moved from #{cross_reference}#{issue.to_reference}") expect(page).to have_content(issue.title) - expect(page.current_path).to include project_path(new_project) + expect(page).to have_current_path(%r(#{project_path(new_project)})) end it 'searching project dropdown', :js do diff --git a/spec/features/issues/spam_akismet_issue_creation_spec.rb b/spec/features/issues/spam_akismet_issue_creation_spec.rb new file mode 100644 index 00000000000..4cc4c4cf607 --- /dev/null +++ b/spec/features/issues/spam_akismet_issue_creation_spec.rb @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Spam detection on issue creation', :js do + include StubENV + + let(:project) { create(:project, :public) } + let(:user) { create(:user) } + + include_context 'includes Spam constants' + + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + + Gitlab::CurrentSettings.update!( + akismet_enabled: true, + akismet_api_key: 'testkey', + spam_check_api_key: 'testkey', + recaptcha_enabled: true, + recaptcha_site_key: 'test site key', + recaptcha_private_key: 'test private key' + ) + + project.add_maintainer(user) + sign_in(user) + visit new_project_issue_path(project) + + fill_in 'issue_title', with: 'issue title' + fill_in 'issue_description', with: 'issue description' + end + + shared_examples 'disallows issue creation' do + it 'disallows issue creation' do + click_button 'Create issue' + + expect(page).to have_content('discarded') + expect(page).not_to have_css('.recaptcha') + expect(page).not_to have_content('issue title') + end + end + + shared_examples 'allows issue creation with CAPTCHA' do + it 'allows issue creation' do + click_button 'Create issue' + + # it is impossible to test reCAPTCHA automatically and there is no possibility to fill in recaptcha + # reCAPTCHA verification is skipped in test environment and it always returns true + expect(page).not_to have_content('issue title') + expect(page).to have_css('.recaptcha') + + click_button 'Create issue' + + expect(page.find('.issue-details h1.title')).to have_content('issue title') + expect(page.find('.issue-details .description')).to have_content('issue description') + end + end + + shared_examples 'allows issue creation without CAPTCHA' do + it 'allows issue creation without need to solve CAPTCHA' do + click_button 'Create issue' + + expect(page).not_to have_css('.recaptcha') + expect(page.find('.issue-details h1.title')).to have_content('issue title') + expect(page.find('.issue-details .description')).to have_content('issue description') + end + end + + shared_examples 'creates a spam_log record' do + it 'creates a spam_log record' do + expect { click_button 'Create issue' } + .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') + end + end + + shared_examples 'does not create a spam_log record' do + it 'does not creates a spam_log record' do + expect { click_button 'Create issue' } + .not_to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') + end + end + + shared_context 'when spammable is identified as possible spam' do + before do + allow_next_instance_of(Spam::AkismetService) do |akismet_service| + allow(akismet_service).to receive(:spam?).and_return(true) + end + end + end + + shared_context 'when spammable is not identified as possible spam' do + before do + allow_next_instance_of(Spam::AkismetService) do |akismet_service| + allow(akismet_service).to receive(:spam?).and_return(false) + end + end + end + + shared_context 'when CAPTCHA is enabled' do + before do + stub_application_setting(recaptcha_enabled: true) + end + end + + shared_context 'when CAPTCHA is not enabled' do + before do + stub_application_setting(recaptcha_enabled: false) + end + end + + shared_context 'when allow_possible_spam feature flag is true' do + before do + stub_feature_flags(allow_possible_spam: true) + end + end + + shared_context 'when allow_possible_spam feature flag is false' do + before do + stub_feature_flags(allow_possible_spam: false) + end + end + + describe 'spam handling' do + # verdict, spam_flagged, captcha_enabled, allow_possible_spam_flag, creates_spam_log + # TODO: Add example for BLOCK_USER verdict when we add support for testing SpamCheck - see https://gitlab.com/groups/gitlab-org/-/epics/5527#lacking-coverage-for-spamcheck-vs-akismet + # DISALLOW, true, false, false, true + # CONDITIONAL_ALLOW, true, true, false, true + # OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM, true, true, true, true + # OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM, true, false, true, true + # ALLOW, false, true, false, false + # TODO: Add example for NOOP verdict when we add support for testing SpamCheck - see https://gitlab.com/groups/gitlab-org/-/epics/5527#lacking-coverage-for-spamcheck-vs-akismet + + context 'DISALLOW: spam_flagged=true, captcha_enabled=true, allow_possible_spam=true' do + include_context 'when spammable is identified as possible spam' + include_context 'when CAPTCHA is enabled' + include_context 'when allow_possible_spam feature flag is true' + + it_behaves_like 'allows issue creation without CAPTCHA' + it_behaves_like 'creates a spam_log record' + end + + context 'CONDITIONAL_ALLOW: spam_flagged=true, captcha_enabled=true, allow_possible_spam=false' do + include_context 'when spammable is identified as possible spam' + include_context 'when CAPTCHA is enabled' + include_context 'when allow_possible_spam feature flag is false' + + it_behaves_like 'allows issue creation with CAPTCHA' + it_behaves_like 'creates a spam_log record' + end + + context 'OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM: spam_flagged=true, captcha_enabled=true, allow_possible_spam=true' do + include_context 'when spammable is identified as possible spam' + include_context 'when CAPTCHA is enabled' + include_context 'when allow_possible_spam feature flag is true' + + it_behaves_like 'allows issue creation without CAPTCHA' + it_behaves_like 'creates a spam_log record' + end + + context 'OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM: spam_flagged=true, captcha_enabled=false, allow_possible_spam=true' do + include_context 'when spammable is identified as possible spam' + include_context 'when CAPTCHA is not enabled' + include_context 'when allow_possible_spam feature flag is true' + + it_behaves_like 'allows issue creation without CAPTCHA' + it_behaves_like 'creates a spam_log record' + end + + context 'ALLOW: spam_flagged=false, captcha_enabled=true, allow_possible_spam=false' do + include_context 'when spammable is not identified as possible spam' + include_context 'when CAPTCHA is not enabled' + include_context 'when allow_possible_spam feature flag is false' + + it_behaves_like 'allows issue creation without CAPTCHA' + it_behaves_like 'does not create a spam_log record' + end + end +end diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb deleted file mode 100644 index 70d7deadec3..00000000000 --- a/spec/features/issues/spam_issues_spec.rb +++ /dev/null @@ -1,188 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe 'New issue', :js do - include StubENV - - let(:project) { create(:project, :public) } - let(:user) { create(:user)} - - before do - stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') - - Gitlab::CurrentSettings.update!( - akismet_enabled: true, - akismet_api_key: 'testkey', - spam_check_api_key: 'testkey', - recaptcha_enabled: true, - recaptcha_site_key: 'test site key', - recaptcha_private_key: 'test private key' - ) - - project.add_maintainer(user) - sign_in(user) - end - - context 'when SpamVerdictService disallows' do - include_context 'includes Spam constants' - - before do - allow_next_instance_of(Spam::SpamVerdictService) do |verdict_service| - allow(verdict_service).to receive(:execute).and_return(DISALLOW) - end - - visit new_project_issue_path(project) - end - - context 'when allow_possible_spam feature flag is false' do - before do - stub_feature_flags(allow_possible_spam: false) - - fill_in 'issue_title', with: 'issue title' - fill_in 'issue_description', with: 'issue description' - end - - it 'rejects issue creation' do - click_button 'Create issue' - - expect(page).to have_content('discarded') - expect(page).not_to have_content('potential spam') - expect(page).not_to have_content('issue title') - end - - it 'creates a spam log record' do - expect { click_button 'Create issue' } - .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') - end - end - - context 'when allow_possible_spam feature flag is true' do - before do - fill_in 'issue_title', with: 'issue title' - fill_in 'issue_description', with: 'issue description' - end - - it 'allows issue creation' do - click_button 'Create issue' - - expect(page.find('.issue-details h2.title')).to have_content('issue title') - expect(page.find('.issue-details .description')).to have_content('issue description') - end - - it 'creates a spam log record' do - expect { click_button 'Create issue' } - .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') - end - end - end - - context 'when SpamVerdictService requires recaptcha' do - include_context 'includes Spam constants' - - before do - allow_next_instance_of(Spam::SpamVerdictService) do |verdict_service| - allow(verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW) - end - - visit new_project_issue_path(project) - end - - context 'when recaptcha is enabled' do - before do - stub_application_setting(recaptcha_enabled: true) - end - - context 'when allow_possible_spam feature flag is false' do - before do - stub_feature_flags(allow_possible_spam: false) - end - - it 'creates an issue after solving reCaptcha' do - fill_in 'issue_title', with: 'issue title' - fill_in 'issue_description', with: 'issue description' - - click_button 'Create issue' - - # it is impossible to test reCAPTCHA automatically and there is no possibility to fill in recaptcha - # reCAPTCHA verification is skipped in test environment and it always returns true - expect(page).not_to have_content('issue title') - expect(page).to have_css('.recaptcha') - - click_button 'Create issue' - - expect(page.find('.issue-details h2.title')).to have_content('issue title') - expect(page.find('.issue-details .description')).to have_content('issue description') - end - end - - context 'when allow_possible_spam feature flag is true' do - before do - fill_in 'issue_title', with: 'issue title' - fill_in 'issue_description', with: 'issue description' - end - - it 'creates an issue without a need to solve reCAPTCHA' do - click_button 'Create issue' - - expect(page).not_to have_css('.recaptcha') - expect(page.find('.issue-details h2.title')).to have_content('issue title') - expect(page.find('.issue-details .description')).to have_content('issue description') - end - - it 'creates a spam log record' do - expect { click_button 'Create issue' } - .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') - end - end - end - - context 'when reCAPTCHA is not enabled' do - before do - stub_application_setting(recaptcha_enabled: false) - end - - context 'when allow_possible_spam feature flag is true' do - before do - fill_in 'issue_title', with: 'issue title' - fill_in 'issue_description', with: 'issue description' - end - - it 'creates an issue without a need to solve reCaptcha' do - click_button 'Create issue' - - expect(page).not_to have_css('.recaptcha') - expect(page.find('.issue-details h2.title')).to have_content('issue title') - expect(page.find('.issue-details .description')).to have_content('issue description') - end - - it 'creates a spam log record' do - expect { click_button 'Create issue' } - .to log_spam(title: 'issue title', description: 'issue description', user_id: user.id, noteable_type: 'Issue') - end - end - end - end - - context 'when the SpamVerdictService allows' do - include_context 'includes Spam constants' - - before do - allow_next_instance_of(Spam::SpamVerdictService) do |verdict_service| - allow(verdict_service).to receive(:execute).and_return(ALLOW) - end - - visit new_project_issue_path(project) - end - - it 'creates an issue' do - fill_in 'issue_title', with: 'issue title' - fill_in 'issue_description', with: 'issue description' - - click_button 'Create issue' - - expect(page.find('.issue-details h2.title')).to have_content('issue title') - expect(page.find('.issue-details .description')).to have_content('issue description') - end - end -end diff --git a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb index 8c80e19810e..ae1bce7ea4c 100644 --- a/spec/features/issues/user_creates_branch_and_merge_request_spec.rb +++ b/spec/features/issues/user_creates_branch_and_merge_request_spec.rb @@ -85,7 +85,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do wait_for_requests expect(page).to have_selector('.dropdown-toggle-text ', text: '1-cherry-coloured-funk') - expect(current_path).to eq project_tree_path(project, '1-cherry-coloured-funk') + expect(page).to have_current_path project_tree_path(project, '1-cherry-coloured-funk'), ignore_query: true end end @@ -110,7 +110,7 @@ RSpec.describe 'User creates branch and merge request on issue page', :js do wait_for_requests expect(page).to have_selector('.dropdown-toggle-text ', text: branch_name) - expect(current_path).to eq project_tree_path(project, branch_name) + expect(page).to have_current_path project_tree_path(project, branch_name), ignore_query: true end end end diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb index 37e324e6ded..446f13dc4d0 100644 --- a/spec/features/issues/user_creates_issue_spec.rb +++ b/spec/features/issues/user_creates_issue_spec.rb @@ -22,11 +22,11 @@ RSpec.describe "User creates issue" do click_link "New issue" end - expect(current_path).to eq new_user_session_path + expect(page).to have_current_path new_user_session_path, ignore_query: true gitlab_sign_in(create(:user)) - expect(current_path).to eq new_project_issue_path(project) + expect(page).to have_current_path new_project_issue_path(project), ignore_query: true end end diff --git a/spec/features/issues/user_sorts_issues_spec.rb b/spec/features/issues/user_sorts_issues_spec.rb index f3eaff379a1..86bdaf5d706 100644 --- a/spec/features/issues/user_sorts_issues_spec.rb +++ b/spec/features/issues/user_sorts_issues_spec.rb @@ -9,9 +9,9 @@ RSpec.describe "User sorts issues" do let_it_be(:user) { create(:user) } let_it_be(:group) { create(:group) } let_it_be(:project) { create(:project_empty_repo, :public, group: group) } - let_it_be(:issue1, reload: true) { create(:issue, title: 'foo', created_at: Time.now, project: project) } - let_it_be(:issue2, reload: true) { create(:issue, title: 'bar', created_at: Time.now - 60, project: project) } - let_it_be(:issue3, reload: true) { create(:issue, title: 'baz', created_at: Time.now - 120, project: project) } + let_it_be(:issue1, reload: true) { create(:issue, title: 'foo', created_at: Time.zone.now, project: project) } + let_it_be(:issue2, reload: true) { create(:issue, title: 'bar', created_at: Time.zone.now - 60, project: project) } + let_it_be(:issue3, reload: true) { create(:issue, title: 'baz', created_at: Time.zone.now - 120, project: project) } let_it_be(:newer_due_milestone) { create(:milestone, project: project, due_date: '2013-12-11') } let_it_be(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') } @@ -75,7 +75,7 @@ RSpec.describe "User sorts issues" do end it 'sorts by most recently updated', :js do - issue3.updated_at = Time.now + 100 + issue3.updated_at = Time.zone.now + 100 issue3.save! visit project_issues_path(project, sort: sort_value_recently_updated) diff --git a/spec/features/issues/user_views_issue_spec.rb b/spec/features/issues/user_views_issue_spec.rb index 31bf7649470..eca698bb2f4 100644 --- a/spec/features/issues/user_views_issue_spec.rb +++ b/spec/features/issues/user_views_issue_spec.rb @@ -25,7 +25,7 @@ RSpec.describe "User views issue" do it 'shows the merge request and issue actions', :js, :aggregate_failures do click_button 'Issue actions' - expect(page).to have_link('New issue', href: new_project_issue_path(project, { issue: { description: "Related to \##{issue.iid}.\n\n" } })) + expect(page).to have_link('New related issue', href: new_project_issue_path(project, { add_related_issue: issue.iid })) expect(page).to have_button('Create merge request') expect(page).to have_button('Close issue') end diff --git a/spec/features/jira_connect/subscriptions_spec.rb b/spec/features/jira_connect/subscriptions_spec.rb index e1589ba997e..0b7321bf271 100644 --- a/spec/features/jira_connect/subscriptions_spec.rb +++ b/spec/features/jira_connect/subscriptions_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe 'Subscriptions Content Security Policy' do + include ContentSecurityPolicyHelpers + let(:installation) { create(:jira_connect_installation) } let(:qsh) { Atlassian::Jwt.create_query_string_hash('https://gitlab.test/subscriptions', 'GET', 'https://gitlab.test') } let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) } @@ -11,10 +13,7 @@ RSpec.describe 'Subscriptions Content Security Policy' do context 'when there is no global config' do before do - expect_next_instance_of(JiraConnect::SubscriptionsController) do |controller| - expect(controller).to receive(:current_content_security_policy) - .and_return(ActionDispatch::ContentSecurityPolicy.new) - end + setup_csp_for_controller(JiraConnect::SubscriptionsController) end it 'does not add CSP directives' do @@ -31,9 +30,7 @@ RSpec.describe 'Subscriptions Content Security Policy' do p.style_src :self, 'https://some-cdn.test' end - expect_next_instance_of(JiraConnect::SubscriptionsController) do |controller| - expect(controller).to receive(:current_content_security_policy).and_return(csp) - end + setup_existing_csp_for_controller(JiraConnect::SubscriptionsController, csp) end it 'appends to CSP directives' do diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 6c8d41fd96f..479199b72b7 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -160,7 +160,7 @@ RSpec.describe 'Labels Hierarchy', :js do find('.btn-confirm').click - expect(page.find('.issue-details h2.title')).to have_content('new created issue') + expect(page.find('.issue-details h1.title')).to have_content('new created issue') expect(page).to have_selector('span.gl-label-text', text: grandparent_group_label.title) expect(page).to have_selector('span.gl-label-text', text: parent_group_label.title) expect(page).to have_selector('span.gl-label-text', text: project_label_1.title) @@ -179,38 +179,6 @@ RSpec.describe 'Labels Hierarchy', :js do it_behaves_like 'assigning labels from sidebar' end - - context 'on project board issue sidebar' 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 - - find('.board-card').click - end - - it_behaves_like 'assigning labels from sidebar' - end - - context 'on group board issue sidebar' do - let(:board) { create(:board, group: parent) } - - before do - parent.add_developer(user) - - visit group_board_path(parent, board) - - wait_for_requests - - find('.board-card').click - end - - it_behaves_like 'assigning labels from sidebar' - end end context 'issuable filtering' do @@ -242,29 +210,5 @@ RSpec.describe 'Labels Hierarchy', :js do it_behaves_like 'filtering by ancestor labels for groups' end - - context 'on project boards filter' do - let(:board) { create(:board, project: project_1) } - - before do - project_1.add_developer(user) - - visit project_board_path(project_1, board) - end - - it_behaves_like 'filtering by ancestor labels for projects', true - end - - context 'on group boards filter' do - let(:board) { create(:board, group: parent) } - - before do - parent.add_developer(user) - - visit group_board_path(parent, board) - end - - it_behaves_like 'filtering by ancestor labels for groups', true - end end end diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb index 6951d8298e5..d472134a2c7 100644 --- a/spec/features/markdown/copy_as_gfm_spec.rb +++ b/spec/features/markdown/copy_as_gfm_spec.rb @@ -7,10 +7,6 @@ RSpec.describe 'Copy as GFM', :js do include RepoHelpers include ActionView::Helpers::JavaScriptHelper - before do - stub_feature_flags(refactor_blob_viewer: false) # This stub will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/350454 - end - describe 'Copying rendered GFM' do before do @feat = MarkdownFeature.new @@ -764,8 +760,8 @@ RSpec.describe 'Copy as GFM', :js do context 'selecting one word of text' do it 'copies as inline code' do verify( - '.line[id="LC9"] .no', - '`RuntimeError`' + '.line[id="LC10"]', + '`end`' ) end end @@ -834,6 +830,7 @@ RSpec.describe 'Copy as GFM', :js do end def verify(selector, gfm, target: nil) + expect(page).to have_selector('.js-syntax-highlight') html = html_for_selector(selector) output_gfm = html_to_gfm(html, 'transformCodeSelection', target: target) wait_for_requests diff --git a/spec/features/markdown/keyboard_shortcuts_spec.rb b/spec/features/markdown/keyboard_shortcuts_spec.rb index 81b1928658c..82288af1f9f 100644 --- a/spec/features/markdown/keyboard_shortcuts_spec.rb +++ b/spec/features/markdown/keyboard_shortcuts_spec.rb @@ -37,6 +37,14 @@ RSpec.describe 'Markdown keyboard shortcuts', :js do expect(markdown_field.value).to eq('_italic_') end + it 'strikes text when ++x is pressed' do + type_and_select('strikethrough') + + markdown_field.send_keys([modifier_key, :shift, 'x']) + + expect(markdown_field.value).to eq('~~strikethrough~~') + end + it 'links text when +K is pressed' do type_and_select('link') diff --git a/spec/features/markdown/sandboxed_mermaid_spec.rb b/spec/features/markdown/sandboxed_mermaid_spec.rb index f118fb3db66..05fe83b3107 100644 --- a/spec/features/markdown/sandboxed_mermaid_spec.rb +++ b/spec/features/markdown/sandboxed_mermaid_spec.rb @@ -26,7 +26,7 @@ RSpec.describe 'Sandboxed Mermaid rendering', :js do wait_for_requests - expected = %(