summaryrefslogtreecommitdiff
path: root/spec/support
diff options
context:
space:
mode:
Diffstat (limited to 'spec/support')
-rw-r--r--spec/support/ability_check.rb73
-rw-r--r--spec/support/ability_check_todo.yml73
-rw-r--r--spec/support/banzai/filter_timeout_shared_examples.rb37
-rw-r--r--spec/support/capybara.rb63
-rw-r--r--spec/support/capybara_wait_for_all_requests.rb47
-rw-r--r--spec/support/database/prevent_cross_joins.rb2
-rw-r--r--spec/support/fast_quarantine.rb37
-rw-r--r--spec/support/finder_collection_allowlist.yml5
-rw-r--r--spec/support/flaky_tests.rb37
-rw-r--r--spec/support/helpers/api_internal_base_helpers.rb16
-rw-r--r--spec/support/helpers/board_helpers.rb16
-rw-r--r--spec/support/helpers/callouts_test_helper.rb9
-rw-r--r--spec/support/helpers/chunked_io_helpers.rb (renamed from spec/support/chunked_io/chunked_io_helpers.rb)0
-rw-r--r--spec/support/helpers/ci/source_pipeline_helpers.rb12
-rw-r--r--spec/support/helpers/ci/template_helpers.rb4
-rw-r--r--spec/support/helpers/content_editor_helpers.rb49
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb15
-rw-r--r--spec/support/helpers/cycle_analytics_helpers/test_generation.rb (renamed from spec/support/cycle_analytics_helpers/test_generation.rb)50
-rw-r--r--spec/support/helpers/database/database_helpers.rb8
-rw-r--r--spec/support/helpers/database/inject_failure_helpers.rb41
-rw-r--r--spec/support/helpers/database/multiple_databases_helpers.rb22
-rw-r--r--spec/support/helpers/email_helpers.rb21
-rw-r--r--spec/support/helpers/every_sidekiq_worker_test_helper.rb9
-rw-r--r--spec/support/helpers/fake_u2f_device.rb47
-rw-r--r--spec/support/helpers/fake_webauthn_device.rb2
-rw-r--r--spec/support/helpers/feature_flag_helpers.rb24
-rw-r--r--spec/support/helpers/features/access_token_helpers.rb23
-rw-r--r--spec/support/helpers/features/admin_users_helpers.rb28
-rw-r--r--spec/support/helpers/features/blob_spec_helpers.rb18
-rw-r--r--spec/support/helpers/features/branches_helpers.rb33
-rw-r--r--spec/support/helpers/features/canonical_link_helpers.rb22
-rw-r--r--spec/support/helpers/features/invite_members_modal_helper.rb154
-rw-r--r--spec/support/helpers/features/invite_members_modal_helpers.rb148
-rw-r--r--spec/support/helpers/features/iteration_helpers.rb9
-rw-r--r--spec/support/helpers/features/list_rows_helpers.rb28
-rw-r--r--spec/support/helpers/features/members_helpers.rb114
-rw-r--r--spec/support/helpers/features/merge_request_helpers.rb32
-rw-r--r--spec/support/helpers/features/mirroring_helpers.rb28
-rw-r--r--spec/support/helpers/features/notes_helpers.rb76
-rw-r--r--spec/support/helpers/features/releases_helpers.rb107
-rw-r--r--spec/support/helpers/features/responsive_table_helpers.rb22
-rw-r--r--spec/support/helpers/features/runners_helpers.rb92
-rw-r--r--spec/support/helpers/features/snippet_helpers.rb89
-rw-r--r--spec/support/helpers/features/snippet_spec_helpers.rb83
-rw-r--r--spec/support/helpers/features/sorting_helpers.rb36
-rw-r--r--spec/support/helpers/features/source_editor_spec_helpers.rb26
-rw-r--r--spec/support/helpers/features/top_nav_spec_helpers.rb46
-rw-r--r--spec/support/helpers/features/two_factor_helpers.rb123
-rw-r--r--spec/support/helpers/features/web_ide_spec_helpers.rb167
-rw-r--r--spec/support/helpers/filtered_search_helpers.rb19
-rw-r--r--spec/support/helpers/fixture_helpers.rb2
-rw-r--r--spec/support/helpers/gitaly_setup.rb54
-rw-r--r--spec/support/helpers/google_api/cloud_platform_helpers.rb (renamed from spec/support/google_api/cloud_platform_helpers.rb)106
-rw-r--r--spec/support/helpers/graphql/arguments.rb (renamed from spec/support/graphql/arguments.rb)0
-rw-r--r--spec/support/helpers/graphql/fake_query_type.rb (renamed from spec/support/graphql/fake_query_type.rb)1
-rw-r--r--spec/support/helpers/graphql/fake_tracer.rb (renamed from spec/support/graphql/fake_tracer.rb)0
-rw-r--r--spec/support/helpers/graphql/field_inspection.rb (renamed from spec/support/graphql/field_inspection.rb)0
-rw-r--r--spec/support/helpers/graphql/field_selection.rb (renamed from spec/support/graphql/field_selection.rb)0
-rw-r--r--spec/support/helpers/graphql/resolver_factories.rb (renamed from spec/support/graphql/resolver_factories.rb)0
-rw-r--r--spec/support/helpers/graphql/subscriptions/action_cable/mock_action_cable.rb (renamed from spec/support/graphql/subscriptions/action_cable/mock_action_cable.rb)4
-rw-r--r--spec/support/helpers/graphql/subscriptions/action_cable/mock_gitlab_schema.rb (renamed from spec/support/graphql/subscriptions/action_cable/mock_gitlab_schema.rb)0
-rw-r--r--spec/support/helpers/graphql/subscriptions/notes/helper.rb (renamed from spec/support/graphql/subscriptions/notes/helper.rb)0
-rw-r--r--spec/support/helpers/graphql/var.rb (renamed from spec/support/graphql/var.rb)0
-rw-r--r--spec/support/helpers/graphql_helpers.rb46
-rw-r--r--spec/support/helpers/http_io_helpers.rb (renamed from spec/support/http_io/http_io_helpers.rb)4
-rw-r--r--spec/support/helpers/jira_integration_helpers.rb2
-rw-r--r--spec/support/helpers/keyset_pagination_helpers.rb23
-rw-r--r--spec/support/helpers/login_helpers.rb26
-rw-r--r--spec/support/helpers/markdown_feature.rb8
-rw-r--r--spec/support/helpers/metrics_dashboard_helpers.rb4
-rw-r--r--spec/support/helpers/migrations_helpers.rb2
-rw-r--r--spec/support/helpers/migrations_helpers/cluster_helpers.rb (renamed from spec/support/migrations_helpers/cluster_helpers.rb)0
-rw-r--r--spec/support/helpers/migrations_helpers/namespaces_helper.rb15
-rw-r--r--spec/support/helpers/migrations_helpers/schema_version_finder.rb (renamed from spec/support/migrations_helpers/schema_version_finder.rb)3
-rw-r--r--spec/support/helpers/migrations_helpers/vulnerabilities_findings_helper.rb (renamed from spec/support/migrations_helpers/vulnerabilities_findings_helper.rb)12
-rw-r--r--spec/support/helpers/models/ci/partitioning_testing/cascade_check.rb (renamed from spec/support/models/ci/partitioning_testing/cascade_check.rb)2
-rw-r--r--spec/support/helpers/models/ci/partitioning_testing/partition_identifiers.rb (renamed from spec/support/models/ci/partitioning_testing/partition_identifiers.rb)0
-rw-r--r--spec/support/helpers/models/ci/partitioning_testing/rspec_hooks.rb (renamed from spec/support/models/ci/partitioning_testing/rspec_hooks.rb)4
-rw-r--r--spec/support/helpers/models/ci/partitioning_testing/schema_helpers.rb (renamed from spec/support/models/ci/partitioning_testing/schema_helpers.rb)0
-rw-r--r--spec/support/helpers/models/merge_request_without_merge_request_diff.rb (renamed from spec/support/models/merge_request_without_merge_request_diff.rb)2
-rw-r--r--spec/support/helpers/navbar_structure_helper.rb20
-rw-r--r--spec/support/helpers/note_interaction_helpers.rb2
-rw-r--r--spec/support/helpers/project_template_test_helper.rb4
-rw-r--r--spec/support/helpers/prometheus/metric_builders.rb (renamed from spec/support/prometheus/metric_builders.rb)4
-rw-r--r--spec/support/helpers/query_recorder.rb4
-rw-r--r--spec/support/helpers/redis_helpers.rb (renamed from spec/support/redis/redis_helpers.rb)0
-rw-r--r--spec/support/helpers/repo_helpers.rb4
-rw-r--r--spec/support/helpers/search_helpers.rb8
-rw-r--r--spec/support/helpers/session_helpers.rb6
-rw-r--r--spec/support/helpers/snowplow_helpers.rb10
-rw-r--r--spec/support/helpers/stub_configuration.rb6
-rw-r--r--spec/support/helpers/stub_gitlab_calls.rb6
-rw-r--r--spec/support/helpers/stub_object_storage.rb126
-rw-r--r--spec/support/helpers/test_env.rb4
-rw-r--r--spec/support/helpers/test_reports_helper.rb (renamed from spec/support/test_reports/test_reports_helper.rb)8
-rw-r--r--spec/support/helpers/trace_helpers.rb (renamed from spec/support/trace/trace_helpers.rb)0
-rw-r--r--spec/support/helpers/usage_data_helpers.rb13
-rw-r--r--spec/support/helpers/user_login_helper.rb16
-rw-r--r--spec/support/helpers/wait_for_requests.rb3
-rw-r--r--spec/support/helpers/workhorse_helpers.rb58
-rw-r--r--spec/support/import_export/common_util.rb25
-rw-r--r--spec/support/import_export/export_file_helper.rb22
-rw-r--r--spec/support/matchers/background_migrations_matchers.rb14
-rw-r--r--spec/support/matchers/be_a_foreign_key_column_of.rb19
-rw-r--r--spec/support/matchers/be_indexed_by.rb26
-rw-r--r--spec/support/matchers/exceed_redis_call_limit.rb57
-rw-r--r--spec/support/matchers/have_plain_text_content.rb16
-rw-r--r--spec/support/matchers/markdown_matchers.rb13
-rw-r--r--spec/support/matchers/request_urgency_matcher.rb29
-rw-r--r--spec/support/matchers/snapshot_matcher.rb55
-rw-r--r--spec/support/migrations_helpers/namespaces_helper.rb14
-rw-r--r--spec/support/permissions_check.rb18
-rw-r--r--spec/support/protected_branch_helpers.rb23
-rw-r--r--spec/support/protected_tags/access_control_ce_shared_examples.rb49
-rw-r--r--spec/support/rspec.rb12
-rw-r--r--spec/support/rspec_order.rb2
-rw-r--r--spec/support/rspec_order_todo.yml358
-rw-r--r--spec/support/shared_contexts/bulk_imports_requests_shared_context.rb25
-rw-r--r--spec/support/shared_contexts/design_management_shared_contexts.rb33
-rw-r--r--spec/support/shared_contexts/features/integrations/instance_and_group_integrations_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/features/integrations/integrations_shared_context.rb209
-rw-r--r--spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb1
-rw-r--r--spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb78
-rw-r--r--spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb42
-rw-r--r--spec/support/shared_contexts/finders/work_items_finder_shared_contexts.rb78
-rw-r--r--spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb2
-rw-r--r--spec/support/shared_contexts/graphql/types/query_type_shared_context.rb1
-rw-r--r--spec/support/shared_contexts/issuable/merge_request_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/lib/gitlab/database/load_balancing/wal_tracking_shared_context.rb68
-rw-r--r--spec/support/shared_contexts/lib/gitlab/database/partitioning/list_partitioning_shared_context.rb92
-rw-r--r--spec/support/shared_contexts/merge_request_create_shared_context.rb15
-rw-r--r--spec/support/shared_contexts/merge_request_edit_shared_context.rb12
-rw-r--r--spec/support/shared_contexts/merge_requests_allowing_collaboration_shared_context.rb12
-rw-r--r--spec/support/shared_contexts/models/distribution_shared_context.rb22
-rw-r--r--spec/support/shared_contexts/navbar_structure_context.rb56
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb7
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb15
-rw-r--r--spec/support/shared_contexts/rack_attack_shared_context.rb3
-rw-r--r--spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb3
-rw-r--r--spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb56
-rw-r--r--spec/support/shared_contexts/security_and_compliance_permissions_shared_context.rb4
-rw-r--r--spec/support/shared_contexts/services/clusters/create_service_shared_context.rb19
-rw-r--r--spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb8
-rw-r--r--spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb3
-rw-r--r--spec/support/shared_examples/analytics/cycle_analytics/flow_metrics_examples.rb629
-rw-r--r--spec/support/shared_examples/analytics/cycle_analytics/request_params_examples.rb131
-rw-r--r--spec/support/shared_examples/banzai/filters/filter_timeout_shared_examples.rb70
-rw-r--r--spec/support/shared_examples/banzai/filters/inline_embeds_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/banzai/filters/reference_filter_shared_examples.rb (renamed from spec/support/banzai/reference_filter_shared_examples.rb)10
-rw-r--r--spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/bulk_imports/visibility_level_examples.rb37
-rw-r--r--spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/controllers/import_controller_status_shared_examples.rb22
-rw-r--r--spec/support/shared_examples/controllers/project_import_rate_limiter_shared_examples.rb (renamed from spec/support/controllers/project_import_rate_limiter_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb25
-rw-r--r--spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/controllers/unique_hll_events_examples.rb3
-rw-r--r--spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb56
-rw-r--r--spec/support/shared_examples/db/seeds/data_seeder_shared_examples.rb111
-rw-r--r--spec/support/shared_examples/features/2fa_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/features/abuse_report_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/features/access_tokens_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/confidential_notes_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/content_editor_shared_examples.rb415
-rw-r--r--spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/features/deploy_token_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/features/editable_merge_request_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/features/explore/sidebar_shared_examples.rb28
-rw-r--r--spec/support/shared_examples/features/incident_details_routing_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/manage_applications_shared_examples.rb93
-rw-r--r--spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb2
-rw-r--r--spec/support/shared_examples/features/milestone_editing_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/features/packages_shared_examples.rb69
-rw-r--r--spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb105
-rw-r--r--spec/support/shared_examples/features/protected_tags_with_deploy_keys_examples.rb61
-rw-r--r--spec/support/shared_examples/features/reportable_note_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/rss_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/features/runners_shared_examples.rb45
-rw-r--r--spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb202
-rw-r--r--spec/support/shared_examples/features/secure_oauth_authorizations_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/trial_email_validation_shared_example.rb67
-rw-r--r--spec/support/shared_examples/features/variable_list_pagination_shared_examples.rb66
-rw-r--r--spec/support/shared_examples/features/variable_list_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/features/wiki/user_views_wiki_sidebar_shared_examples.rb38
-rw-r--r--spec/support/shared_examples/features/work_items_shared_examples.rb265
-rw-r--r--spec/support/shared_examples/finders/issues_finder_shared_examples.rb39
-rw-r--r--spec/support/shared_examples/graphql/members_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/graphql/mutation_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/graphql/mutations/members/bulk_update_shared_examples.rb123
-rw-r--r--spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb195
-rw-r--r--spec/support/shared_examples/graphql/resolvers/data_transfer_resolver_shared_examples.rb23
-rw-r--r--spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/helpers/callouts_for_web_hooks.rb49
-rw-r--r--spec/support/shared_examples/integrations/integration_settings_form.rb6
-rw-r--r--spec/support/shared_examples/lib/api/ai_workhorse_shared_examples.rb43
-rw-r--r--spec/support/shared_examples/lib/api/terraform_state_enabled_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb9
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb61
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/async_constraints_validation_shared_examples.rb131
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb38
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb26
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/table_validators_shared_examples.rb84
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb33
-rw-r--r--spec/support/shared_examples/lib/gitlab/gitaly_client_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/lib/gitlab/json_logger_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/lib/gitlab/project_search_results_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/lib/gitlab/search_language_filter_shared_examples.rb25
-rw-r--r--spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb28
-rw-r--r--spec/support/shared_examples/lib/gitlab/utils/username_and_email_generator_shared_examples.rb104
-rw-r--r--spec/support/shared_examples/lib/menus_shared_examples.rb55
-rw-r--r--spec/support/shared_examples/lib/sentry/client_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/lib/sidebars/admin/menus/admin_menus_shared_examples.rb78
-rw-r--r--spec/support/shared_examples/lib/sidebars/user_profile/user_profile_menus_shared_examples.rb93
-rw-r--r--spec/support/shared_examples/lib/sidebars/user_settings/menus/user_settings_menus_shared_examples.rb52
-rw-r--r--spec/support/shared_examples/mailers/export_csv_shared_examples.rb37
-rw-r--r--spec/support/shared_examples/mailers/notify_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/metrics_instrumentation_shared_examples.rb (renamed from spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/migrations/add_work_item_widget_shared_examples.rb33
-rw-r--r--spec/support/shared_examples/models/active_record_enum_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/models/chat_integration_shared_examples.rb28
-rw-r--r--spec/support/shared_examples/models/ci/token_format_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/models/clusters/prometheus_client_shared.rb10
-rw-r--r--spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb114
-rw-r--r--spec/support/shared_examples/models/concerns/cascading_namespace_setting_shared_examples.rb84
-rw-r--r--spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb61
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb40
-rw-r--r--spec/support/shared_examples/models/concerns/protected_branch_access_examples.rb19
-rw-r--r--spec/support/shared_examples/models/concerns/protected_ref_access_allowed_access_levels_examples.rb36
-rw-r--r--spec/support/shared_examples/models/concerns/protected_ref_access_examples.rb106
-rw-r--r--spec/support/shared_examples/models/concerns/protected_tag_access_examples.rb21
-rw-r--r--spec/support/shared_examples/models/concerns/timebox_shared_examples.rb9
-rw-r--r--spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb9
-rw-r--r--spec/support/shared_examples/models/database_event_tracking_shared_examples.rb56
-rw-r--r--spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb10
-rw-r--r--spec/support/shared_examples/models/member_shared_examples.rb48
-rw-r--r--spec/support/shared_examples/models/members_notifications_shared_example.rb2
-rw-r--r--spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb21
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb265
-rw-r--r--spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/resource_event_shared_examples.rb40
-rw-r--r--spec/support/shared_examples/models/wiki_shared_examples.rb49
-rw-r--r--spec/support/shared_examples/observability/csp_shared_examples.rb25
-rw-r--r--spec/support/shared_examples/observability/embed_observabilities_examples.rb61
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb60
-rw-r--r--spec/support/shared_examples/prometheus/additional_metrics_shared_examples.rb (renamed from spec/support/prometheus/additional_metrics_shared_examples.rb)20
-rw-r--r--spec/support/shared_examples/protected_tags/access_control_ce_shared_examples.rb32
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb20
-rw-r--r--spec/support/shared_examples/quick_actions/issue/issue_links_quick_actions_shared_examples.rb123
-rw-r--r--spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/redis/redis_new_instance_shared_examples.rb (renamed from spec/support/redis/redis_new_instance_shared_examples.rb)29
-rw-r--r--spec/support/shared_examples/redis/redis_shared_examples.rb (renamed from spec/support/redis/redis_shared_examples.rb)56
-rw-r--r--spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/admin_mode_shared_examples.rb111
-rw-r--r--spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb36
-rw-r--r--spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb38
-rw-r--r--spec/support/shared_examples/requests/api/discussions_shared_examples.rb15
-rw-r--r--spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/graphql/mutations/subscription_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb16
-rw-r--r--spec/support/shared_examples/requests/api/hooks_shared_examples.rb100
-rw-r--r--spec/support/shared_examples/requests/api/integrations/github_enterprise_jira_dvcs_end_of_life_shared_examples.rb23
-rw-r--r--spec/support/shared_examples/requests/api/integrations/slack/slack_request_verification_shared_examples.rb70
-rw-r--r--spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb9
-rw-r--r--spec/support/shared_examples/requests/api/labels_api_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/milestones_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/requests/api/ml/mlflow/mlflow_shared_examples.rb69
-rw-r--r--spec/support/shared_examples/requests/api/notes_shared_examples.rb88
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb448
-rw-r--r--spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb46
-rw-r--r--spec/support/shared_examples/requests/api/pipelines/visibility_table_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb9
-rw-r--r--spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/requests/api/snippets_shared_examples.rb27
-rw-r--r--spec/support/shared_examples/requests/api/status_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/requests/applications_controller_shared_examples.rb30
-rw-r--r--spec/support/shared_examples/requests/graphql_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/projects/aws/aws__ff_examples.rb18
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/requests/self_monitoring_shared_examples.rb130
-rw-r--r--spec/support/shared_examples/requests/user_activity_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/security_training_providers_importer.rb4
-rw-r--r--spec/support/shared_examples/serializers/diff_file_entity_shared_examples.rb81
-rw-r--r--spec/support/shared_examples/serializers/note_entity_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/services/base_helm_service_shared_examples.rb22
-rw-r--r--spec/support/shared_examples/services/clusters/create_service_shared_examples.rb (renamed from spec/support/services/clusters/create_service_shared.rb)36
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb55
-rw-r--r--spec/support/shared_examples/services/deploy_token_shared_examples.rb (renamed from spec/support/services/deploy_token_shared_examples.rb)6
-rw-r--r--spec/support/shared_examples/services/import_csv_service_shared_examples.rb38
-rw-r--r--spec/support/shared_examples/services/incident_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/issuable/issuable_description_quick_actions_shared_examples.rb (renamed from spec/support/services/issuable_description_quick_actions_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/services/issuable/issuable_import_csv_service_shared_examples.rb (renamed from spec/support/services/issuable_import_csv_service_shared_examples.rb)43
-rw-r--r--spec/support/shared_examples/services/issuable/issuable_update_service_shared_examples.rb (renamed from spec/support/services/issuable_update_service_shared_examples.rb)60
-rw-r--r--spec/support/shared_examples/services/issuable/update_service_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/issues/move_and_clone_services_shared_examples.rb (renamed from spec/support/services/issues/move_and_clone_services_shared_examples.rb)0
-rw-r--r--spec/support/shared_examples/services/migrate_to_ghost_user_service_shared_examples.rb (renamed from spec/support/services/migrate_to_ghost_user_service_shared_examples.rb)4
-rw-r--r--spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb141
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb18
-rw-r--r--spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/services/service_response_shared_examples.rb (renamed from spec/support/services/service_response_shared_examples.rb)8
-rw-r--r--spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb42
-rw-r--r--spec/support/shared_examples/views/pipeline_status_changes_email.rb14
-rw-r--r--spec/support/shared_examples/work_items/export_and_import_shared_examples.rb39
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb14
-rw-r--r--spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb206
-rw-r--r--spec/support/shared_examples/workers/self_monitoring_shared_examples.rb28
-rw-r--r--spec/support/stub_dot_com_check.rb20
-rw-r--r--spec/support/stub_member_access_level.rb46
-rw-r--r--spec/support/tmpdir.rb2
340 files changed, 8628 insertions, 4109 deletions
diff --git a/spec/support/ability_check.rb b/spec/support/ability_check.rb
new file mode 100644
index 00000000000..213944506bb
--- /dev/null
+++ b/spec/support/ability_check.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'gitlab/utils/strong_memoize'
+
+module Support
+ module AbilityCheck
+ def self.inject(mod)
+ mod.prepend AbilityExtension
+ end
+
+ module AbilityExtension
+ def before_check(policy, ability, user, subject, opts)
+ return super if Checker.ok?(policy, ability)
+
+ ActiveSupport::Deprecation.warn(<<~WARNING)
+ Ability #{ability.inspect} in #{policy.class} not found.
+ user=#{user.inspect}, subject=#{subject}, opts=#{opts.inspect}"
+
+ To exclude this check add this entry to #{Checker::TODO_YAML}:
+ #{policy.class}:
+ - #{ability}
+ WARNING
+ end
+ end
+
+ module Checker
+ include Gitlab::Utils::StrongMemoize
+ extend self
+
+ TODO_YAML = File.join(__dir__, 'ability_check_todo.yml')
+
+ def ok?(policy, ability)
+ ignored?(policy, ability) || ability_found?(policy, ability)
+ end
+
+ private
+
+ def ignored?(policy, ability)
+ todo_list[policy.class.name]&.include?(ability.to_s)
+ end
+
+ # Use Policy#has_ability? instead after it has been accepted and released.
+ # See https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/issues/25
+ def ability_found?(policy, ability)
+ # NilPolicy has no abilities. Ignore it.
+ return true if policy.is_a?(DeclarativePolicy::NilPolicy)
+
+ # Search in current policy first
+ return true if policy.class.ability_map.map.key?(ability)
+
+ # Search recursively in all delegations otherwise.
+ # This is potentially slow.
+ # Stolen from:
+ # https://gitlab.com/gitlab-org/ruby/gems/declarative-policy/-/blob/d691e/lib/declarative_policy/base.rb#L360-369
+ policy.class.delegations.any? do |_, block|
+ new_subject = policy.instance_eval(&block)
+ new_policy = policy.policy_for(new_subject)
+
+ ability_found?(new_policy, ability)
+ end
+ end
+
+ def todo_list
+ hash = YAML.load_file(TODO_YAML)
+ return {} unless hash.is_a?(Hash)
+
+ hash.transform_values(&:to_set)
+ end
+
+ strong_memoize_attr :todo_list
+ end
+ end
+end
diff --git a/spec/support/ability_check_todo.yml b/spec/support/ability_check_todo.yml
new file mode 100644
index 00000000000..eafd595b137
--- /dev/null
+++ b/spec/support/ability_check_todo.yml
@@ -0,0 +1,73 @@
+# This list tracks unknown abilities per policy.
+#
+# This file is used by `spec/support/ability_check.rb`.
+#
+# Each TODO entry means that an ability wasn't found in
+# the particular policy class or its delegations.
+#
+# This could be one of the reasons:
+# * The ability is misspelled.
+# - Suggested action: Fix typo.
+# * The ability has been removed from a policy but is still in use.
+# - Remove production code in question.
+# * The ability is defined in EE policy but is used in FOSS code.
+# - Guard the check or move it to EE folder.
+# - See https://docs.gitlab.com/ee/development/ee_features.html
+# * The ability is defined in another policy but delegation is missing.
+# - Add delegation policy or guard the check with a type check.
+# - See https://docs.gitlab.com/ee/development/policies.html#delegation
+# * The ability check is polymorphic (for example, Issuable) and some policies
+# do not implement this ability.
+# - Exclude TODO permanently below.
+# - Guard the check with a type check.
+# * The ability check is defined on GraphQL field which does not support
+# authorization on resolved field values yet.
+# See https://gitlab.com/gitlab-org/gitlab/-/issues/300922
+---
+# <Policy class>:
+# - <ability name>
+# - <ability name>
+# ...
+
+# Temporary excludes:
+
+Ci::BridgePolicy:
+- read_job_artifacts
+CommitStatusPolicy:
+- read_job_artifacts
+EpicPolicy:
+- create_timelog
+- read_emoji
+- set_issue_crm_contacts
+GlobalPolicy:
+- read_achievement
+- read_on_demand_dast_scan
+- update_max_pages_size
+GroupPolicy:
+- admin_merge_request
+- change_push_rules
+- manage_owners
+IssuePolicy:
+- create_test_case
+MergeRequestPolicy:
+- set_confidentiality
+- set_issue_crm_contacts
+Namespaces::UserNamespacePolicy:
+- read_crm_contact
+PersonalSnippetPolicy:
+- read_internal_note
+- read_project
+ProjectMemberPolicy:
+- override_project_member
+ProjectPolicy:
+- admin_feature_flags_issue_links
+- admin_vulnerability
+- create_requirement
+- create_test_case
+- read_group_saml_identity
+UserPolicy:
+- admin_observability
+- admin_terraform_state
+- read_observability
+
+# Permanent excludes (please provide a reason):
diff --git a/spec/support/banzai/filter_timeout_shared_examples.rb b/spec/support/banzai/filter_timeout_shared_examples.rb
deleted file mode 100644
index 1f2ebe6fef6..00000000000
--- a/spec/support/banzai/filter_timeout_shared_examples.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-# This shared_example requires the following variables:
-# - text: The text to be run through the filter
-#
-# Usage:
-#
-# it_behaves_like 'filter timeout' do
-# let(:text) { 'some text' }
-# end
-RSpec.shared_examples 'filter timeout' do
- context 'when rendering takes too long' do
- let_it_be(:project) { create(:project) }
- let_it_be(:context) { { project: project } }
-
- it 'times out' do
- stub_const("Banzai::Filter::TimeoutHtmlPipelineFilter::RENDER_TIMEOUT", 0.1)
- allow_next_instance_of(described_class) do |instance|
- allow(instance).to receive(:call_with_timeout) do
- sleep(0.2)
- text
- end
- end
-
- expect(Gitlab::RenderTimeout).to receive(:timeout).and_call_original
- expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
- instance_of(Timeout::Error),
- project_id: context[:project].id,
- class_name: described_class.name.demodulize
- )
-
- result = filter(text)
-
- expect(result.to_html).to eq text
- end
- end
-end
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index fe9bff827dc..0de1300bc50 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -7,7 +7,7 @@ require 'capybara-screenshot/rspec'
require 'selenium-webdriver'
# Give CI some extra time
-timeout = ENV['CI'] || ENV['CI_SERVER'] ? 30 : 10
+timeout = ENV['CI'] || ENV['CI_SERVER'] ? 45 : 10
# Support running Capybara on a specific port to allow saving commonly used pages
Capybara.server_port = ENV['CAPYBARA_PORT'] if ENV['CAPYBARA_PORT']
@@ -24,13 +24,21 @@ JS_CONSOLE_FILTER = Regexp.union(
'Download the Vue Devtools extension',
'Download the Apollo DevTools',
"Unrecognized feature: 'interest-cohort'",
- 'Does this page need fixes or improvements?'
+ 'Does this page need fixes or improvements?',
+
+ # Needed after https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60933
+ # which opts out gitlab from FloC by default
+ # see https://web.dev/floc/ for more info on FloC
+ "Origin trial controlled feature not enabled: 'interest-cohort'",
+
+ # ERR_CONNECTION error could happen due to automated test session disabling browser network request
+ 'net::ERR_CONNECTION'
]
)
CAPYBARA_WINDOW_SIZE = [1366, 768].freeze
-SCREENSHOT_FILENAME_LENGTH = ENV['CI'] || ENV['CI_SERVER'] ? 255 : 99
+SCREENSHOT_FILENAME_LENGTH = ENV['CI'] || ENV['CI_SERVER'] ? 150 : 99
@blackhole_tcp_server = nil
@@ -42,21 +50,20 @@ Capybara.register_server :puma_via_workhorse do |app, port, host, **options|
file.close! # We just want the filename
TestEnv.with_workhorse(host, port, socket_path) do
+ # In cases of multiple installations of chromedriver, prioritize the version installed by SeleniumManager
+ # selenium-manager doesn't work with Linux arm64 yet:
+ # https://github.com/SeleniumHQ/selenium/issues/11357
+ if RUBY_PLATFORM =~ /x86_64-linux|darwin/
+ chrome_options = Selenium::WebDriver::Chrome::Options.chrome
+ chromedriver_path = File.dirname(Selenium::WebDriver::SeleniumManager.driver_path(chrome_options))
+ ENV['PATH'] = "#{chromedriver_path}:#{ENV['PATH']}" # rubocop:disable RSpec/EnvAssignment
+ end
+
Capybara.servers[:puma].call(app, nil, socket_path, **options)
end
end
Capybara.register_driver :chrome do |app|
- capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
- # This enables access to logs with `page.driver.manage.get_log(:browser)`
- loggingPrefs: {
- browser: "ALL",
- client: "ALL",
- driver: "ALL",
- server: "ALL"
- }
- )
-
options = Selenium::WebDriver::Chrome::Options.new
# Force the browser's scale factor to prevent inconsistencies on high-res devices
@@ -82,9 +89,6 @@ Capybara.register_driver :chrome do |app|
options.add_preference("download.prompt_for_download", false)
end
- # Chrome 75 defaults to W3C mode which doesn't allow console log access
- options.add_option(:w3c, false)
-
# Set up a proxy server to block all external traffic.
@blackhole_tcp_server = TCPServer.new(0)
Thread.new do
@@ -99,18 +103,11 @@ Capybara.register_driver :chrome do |app|
Capybara::Selenium::Driver.new(
app,
browser: :chrome,
- desired_capabilities: capabilities,
options: options
)
end
Capybara.register_driver :firefox do |app|
- capabilities = Selenium::WebDriver::Remote::Capabilities.firefox(
- log: {
- level: :trace
- }
- )
-
options = Selenium::WebDriver::Firefox::Options.new(log_level: :trace)
options.add_argument("--window-size=#{CAPYBARA_WINDOW_SIZE.join(',')}")
@@ -121,7 +118,6 @@ Capybara.register_driver :firefox do |app|
Capybara::Selenium::Driver.new(
app,
browser: :firefox,
- desired_capabilities: capabilities,
options: options
)
end
@@ -213,20 +209,11 @@ RSpec.configure do |config|
# fixed. If we raised the `JSException` the fixed test would be marked as
# failed again.
if example.exception && !example.exception.is_a?(RSpec::Core::Pending::PendingExampleFixedError)
- begin
- console = page.driver.browser.manage.logs.get(:browser)&.reject { |log| log.message =~ JS_CONSOLE_FILTER }
-
- if console.present?
- message = "Unexpected browser console output:\n" + console.map(&:message).join("\n")
- raise JSConsoleError, message
- end
- rescue Selenium::WebDriver::Error::WebDriverError => error
- if error.message =~ %r{unknown command: session/[0-9a-zA-Z]+(?:/se)?/log}
- message = "Unable to access Chrome javascript console logs. You may be using an outdated version of ChromeDriver."
- raise JSConsoleError, message
- else
- raise error
- end
+ console = page.driver.browser.logs.get(:browser)&.reject { |log| log.message =~ JS_CONSOLE_FILTER }
+
+ if console.present?
+ message = "Unexpected browser console output:\n" + console.map(&:message).join("\n")
+ raise JSConsoleError, message
end
end
diff --git a/spec/support/capybara_wait_for_all_requests.rb b/spec/support/capybara_wait_for_all_requests.rb
new file mode 100644
index 00000000000..36b63619b08
--- /dev/null
+++ b/spec/support/capybara_wait_for_all_requests.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require_relative 'helpers/capybara_helpers'
+require_relative 'helpers/wait_for_requests'
+
+module Capybara
+ class Session
+ module WaitForAllRequestsAfterVisitPage
+ include CapybaraHelpers
+ include WaitForRequests
+
+ def visit(visit_uri)
+ super
+
+ wait_for_all_requests
+ end
+ end
+
+ prepend WaitForAllRequestsAfterVisitPage
+ end
+
+ module Node
+ module Actions
+ include CapybaraHelpers
+ include WaitForRequests
+
+ module WaitForAllRequestsAfterClickButton
+ def click_button(locator = nil, **options)
+ super
+
+ wait_for_all_requests
+ end
+ end
+
+ module WaitForAllRequestsAfterClickLink
+ def click_link(locator = nil, **options)
+ super
+
+ wait_for_all_requests
+ end
+ end
+
+ prepend WaitForAllRequestsAfterClickButton
+ prepend WaitForAllRequestsAfterClickLink
+ end
+ end
+end
diff --git a/spec/support/database/prevent_cross_joins.rb b/spec/support/database/prevent_cross_joins.rb
index 8e08824c464..c44bf96a268 100644
--- a/spec/support/database/prevent_cross_joins.rb
+++ b/spec/support/database/prevent_cross_joins.rb
@@ -40,7 +40,7 @@ module Database
return
end
- schemas = ::Gitlab::Database::GitlabSchema.table_schemas(tables)
+ schemas = ::Gitlab::Database::GitlabSchema.table_schemas!(tables)
schemas.subtract(IGNORED_SCHEMAS)
if schemas.many?
diff --git a/spec/support/fast_quarantine.rb b/spec/support/fast_quarantine.rb
new file mode 100644
index 00000000000..b5ed1a2aa96
--- /dev/null
+++ b/spec/support/fast_quarantine.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+return unless ENV['CI']
+return if ENV['FAST_QUARANTINE'] == "false"
+return if ENV['CI_MERGE_REQUEST_LABELS'].to_s.include?('pipeline:run-flaky-tests')
+
+require_relative '../../tooling/lib/tooling/fast_quarantine'
+
+RSpec.configure do |config|
+ fast_quarantine_local_path = ENV.fetch('RSPEC_FAST_QUARANTINE_LOCAL_PATH', 'rspec/fast_quarantine-gitlab.txt')
+ fast_quarantine_path = ENV.fetch(
+ 'RSPEC_FAST_QUARANTINE_PATH',
+ File.expand_path("../../#{fast_quarantine_local_path}", __dir__)
+ )
+ fast_quarantine = Tooling::FastQuarantine.new(fast_quarantine_path: fast_quarantine_path)
+ skipped_examples = []
+
+ config.around do |example|
+ if fast_quarantine.skip_example?(example)
+ skipped_examples << example.id
+ skip "Skipping #{example.id} because it's been fast-quarantined."
+ else
+ example.run
+ end
+ end
+
+ config.after(:suite) do
+ next if skipped_examples.empty?
+
+ skipped_tests_report_path = ENV.fetch(
+ 'SKIPPED_TESTS_REPORT_PATH',
+ File.expand_path("../../rspec/flaky/skipped_tests.txt", __dir__)
+ )
+
+ File.write(skipped_tests_report_path, "#{ENV.fetch('CI_JOB_URL', 'local-run')}\n#{skipped_examples.join("\n")}\n\n")
+ end
+end
diff --git a/spec/support/finder_collection_allowlist.yml b/spec/support/finder_collection_allowlist.yml
index 750295e16c4..8fcb4ee7b9c 100644
--- a/spec/support/finder_collection_allowlist.yml
+++ b/spec/support/finder_collection_allowlist.yml
@@ -24,7 +24,8 @@
- Ci::CommitStatusesFinder
- Ci::DailyBuildGroupReportResultsFinder
- ClusterAncestorsFinder
-- Clusters::AgentAuthorizationsFinder
+- Clusters::Agents::Authorizations::CiAccess::Finder
+- Clusters::Agents::Authorizations::UserAccess::Finder
- Clusters::KubernetesNamespaceFinder
- ComplianceManagement::MergeRequests::ComplianceViolationsFinder
- ContainerRepositoriesFinder
@@ -62,6 +63,7 @@
- Security::TrainingUrlsFinder
- Security::TrainingProviders::KontraUrlFinder
- Security::TrainingProviders::SecureCodeWarriorUrlFinder
+- Security::TrainingProviders::SecureFlagUrlFinder
- SentryIssueFinder
- ServerlessDomainFinder
- TagsFinder
@@ -69,3 +71,4 @@
- UploaderFinder
- UserGroupNotificationSettingsFinder
- UserGroupsCounter
+- DataTransfer::MockedTransferFinder # Can be removed when https://gitlab.com/gitlab-org/gitlab/-/issues/397693 is closed
diff --git a/spec/support/flaky_tests.rb b/spec/support/flaky_tests.rb
deleted file mode 100644
index 4df0d23bfc3..00000000000
--- a/spec/support/flaky_tests.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-return unless ENV['CI']
-return if ENV['SKIP_FLAKY_TESTS_AUTOMATICALLY'] == "false"
-return if ENV['CI_MERGE_REQUEST_LABELS'].to_s.include?('pipeline:run-flaky-tests')
-
-require_relative '../../tooling/rspec_flaky/config'
-require_relative '../../tooling/rspec_flaky/report'
-
-RSpec.configure do |config|
- $flaky_test_example_ids = begin # rubocop:disable Style/GlobalVars
- raise "#{RspecFlaky::Config.suite_flaky_examples_report_path} doesn't exist" unless File.exist?(RspecFlaky::Config.suite_flaky_examples_report_path)
-
- RspecFlaky::Report.load(RspecFlaky::Config.suite_flaky_examples_report_path).map { |_, flaky_test_data| flaky_test_data.to_h[:example_id] }
- rescue => e # rubocop:disable Style/RescueStandardError
- puts e
- []
- end
- $skipped_flaky_tests_report = [] # rubocop:disable Style/GlobalVars
-
- config.around do |example|
- # Skip flaky tests automatically
- if $flaky_test_example_ids.include?(example.id) # rubocop:disable Style/GlobalVars
- puts "Skipping #{example.id} '#{example.full_description}' because it's flaky."
- $skipped_flaky_tests_report << example.id # rubocop:disable Style/GlobalVars
- else
- example.run
- end
- end
-
- config.after(:suite) do
- next unless RspecFlaky::Config.skipped_flaky_tests_report_path
- next if $skipped_flaky_tests_report.empty? # rubocop:disable Style/GlobalVars
-
- File.write(RspecFlaky::Config.skipped_flaky_tests_report_path, "#{ENV['CI_JOB_URL']}\n#{$skipped_flaky_tests_report.join("\n")}\n\n") # rubocop:disable Style/GlobalVars
- end
-end
diff --git a/spec/support/helpers/api_internal_base_helpers.rb b/spec/support/helpers/api_internal_base_helpers.rb
index e89716571f9..0c334e164a6 100644
--- a/spec/support/helpers/api_internal_base_helpers.rb
+++ b/spec/support/helpers/api_internal_base_helpers.rb
@@ -13,8 +13,6 @@ module APIInternalBaseHelpers
Gitlab::GlRepository::PROJECT.identifier_for_container(container)
when Snippet
Gitlab::GlRepository::SNIPPET.identifier_for_container(container)
- else
- nil
end
end
@@ -44,12 +42,14 @@ module APIInternalBaseHelpers
end
def push(key, container, protocol = 'ssh', env: nil, changes: nil)
- push_with_path(key,
- full_path: full_path_for(container),
- gl_repository: gl_repository_for(container),
- protocol: protocol,
- env: env,
- changes: changes)
+ push_with_path(
+ key,
+ full_path: full_path_for(container),
+ gl_repository: gl_repository_for(container),
+ protocol: protocol,
+ env: env,
+ changes: changes
+ )
end
def push_with_path(key, full_path:, gl_repository: nil, protocol: 'ssh', env: nil, changes: nil)
diff --git a/spec/support/helpers/board_helpers.rb b/spec/support/helpers/board_helpers.rb
index d7277ba9a20..c7a7993c52b 100644
--- a/spec/support/helpers/board_helpers.rb
+++ b/spec/support/helpers/board_helpers.rb
@@ -29,13 +29,15 @@ module BoardHelpers
# ensure there is enough horizontal space for four board lists
resize_window(2000, 800)
- drag_to(selector: selector,
- scrollable: '#board-app',
- list_from_index: list_from_index,
- from_index: from_index,
- to_index: to_index,
- list_to_index: list_to_index,
- perform_drop: perform_drop)
+ drag_to(
+ selector: selector,
+ scrollable: '#board-app',
+ list_from_index: list_from_index,
+ from_index: from_index,
+ to_index: to_index,
+ list_to_index: list_to_index,
+ perform_drop: perform_drop
+ )
end
wait_for_requests
diff --git a/spec/support/helpers/callouts_test_helper.rb b/spec/support/helpers/callouts_test_helper.rb
deleted file mode 100644
index 8c7faa71d9f..00000000000
--- a/spec/support/helpers/callouts_test_helper.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module CalloutsTestHelper
- def callouts_trials_link_path
- '/-/trial_registrations/new?glm_content=gold-callout&glm_source=gitlab.com'
- end
-end
-
-CalloutsTestHelper.prepend_mod
diff --git a/spec/support/chunked_io/chunked_io_helpers.rb b/spec/support/helpers/chunked_io_helpers.rb
index 278f577f3cb..278f577f3cb 100644
--- a/spec/support/chunked_io/chunked_io_helpers.rb
+++ b/spec/support/helpers/chunked_io_helpers.rb
diff --git a/spec/support/helpers/ci/source_pipeline_helpers.rb b/spec/support/helpers/ci/source_pipeline_helpers.rb
index b99f499cc16..ef3aea7de52 100644
--- a/spec/support/helpers/ci/source_pipeline_helpers.rb
+++ b/spec/support/helpers/ci/source_pipeline_helpers.rb
@@ -3,11 +3,13 @@
module Ci
module SourcePipelineHelpers
def create_source_pipeline(upstream, downstream)
- create(:ci_sources_pipeline,
- source_job: create(:ci_build, pipeline: upstream),
- source_project: upstream.project,
- pipeline: downstream,
- project: downstream.project)
+ create(
+ :ci_sources_pipeline,
+ source_job: create(:ci_build, pipeline: upstream),
+ source_project: upstream.project,
+ pipeline: downstream,
+ project: downstream.project
+ )
end
end
end
diff --git a/spec/support/helpers/ci/template_helpers.rb b/spec/support/helpers/ci/template_helpers.rb
index cd3ab4bd82d..1818dec5fc7 100644
--- a/spec/support/helpers/ci/template_helpers.rb
+++ b/spec/support/helpers/ci/template_helpers.rb
@@ -6,6 +6,10 @@ module Ci
'registry.gitlab.com'
end
+ def auto_build_image_repository
+ "gitlab-org/cluster-integration/auto-build-image"
+ end
+
def public_image_exist?(registry, repository, image)
public_image_manifest(registry, repository, image).present?
end
diff --git a/spec/support/helpers/content_editor_helpers.rb b/spec/support/helpers/content_editor_helpers.rb
new file mode 100644
index 00000000000..83c18f8073f
--- /dev/null
+++ b/spec/support/helpers/content_editor_helpers.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module ContentEditorHelpers
+ def switch_to_content_editor
+ click_button("Switch to rich text")
+ end
+
+ def type_in_content_editor(keys)
+ find(content_editor_testid).send_keys keys
+ end
+
+ def click_attachment_button
+ page.find('svg[data-testid="paperclip-icon"]').click
+ end
+
+ def set_source_editor_content(content)
+ find('.js-gfm-input').set content
+ end
+
+ def expect_media_bubble_menu_to_be_visible
+ expect(page).to have_css('[data-testid="media-bubble-menu"]')
+ end
+
+ def upload_asset(fixture_name)
+ attach_file('content_editor_image', Rails.root.join('spec', 'fixtures', fixture_name), make_visible: true)
+ end
+
+ def wait_until_hidden_field_is_updated(value)
+ expect(page).to have_field(with: value, type: 'hidden')
+ end
+
+ def display_media_bubble_menu(media_element_selector, fixture_file)
+ upload_asset fixture_file
+
+ wait_for_requests
+
+ expect(page).to have_css(media_element_selector)
+
+ page.find(media_element_selector).click
+ end
+
+ def click_edit_diagram_button
+ page.find('[data-testid="edit-diagram"]').click
+ end
+
+ def expect_drawio_editor_is_opened
+ expect(page).to have_css('#drawio-frame', visible: :hidden)
+ end
+end
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index eba5771e062..0accb341cb9 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -91,13 +91,13 @@ module CycleAnalyticsHelpers
wait_for_requests
end
- def create_value_stream_group_aggregation(group)
- aggregation = Analytics::CycleAnalytics::Aggregation.safe_create_for_namespace(group)
+ def create_value_stream_aggregation(group_or_project_namespace)
+ aggregation = Analytics::CycleAnalytics::Aggregation.safe_create_for_namespace(group_or_project_namespace)
Analytics::CycleAnalytics::AggregatorService.new(aggregation: aggregation).execute
end
def select_group_and_custom_value_stream(group, custom_value_stream_name)
- create_value_stream_group_aggregation(group)
+ create_value_stream_aggregation(group)
select_group(group)
select_value_stream(custom_value_stream_name)
@@ -235,4 +235,13 @@ module CycleAnalyticsHelpers
pipeline: dummy_pipeline(project),
protected: false)
end
+
+ def create_deployment(args)
+ project = args[:project]
+ environment = project.environments.production.first || create(:environment, :production, project: project)
+ create(:deployment, :success, args.merge(environment: environment))
+
+ # this is needed for the DORA API so we have aggregated data
+ ::Dora::DailyMetrics::RefreshWorker.new.perform(environment.id, Time.current.to_date.to_s) if Gitlab.ee?
+ end
end
diff --git a/spec/support/cycle_analytics_helpers/test_generation.rb b/spec/support/helpers/cycle_analytics_helpers/test_generation.rb
index 816caf5f775..1c7c45c06a1 100644
--- a/spec/support/cycle_analytics_helpers/test_generation.rb
+++ b/spec/support/helpers/cycle_analytics_helpers/test_generation.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: true
+# rubocop:disable Layout/LineLength
+# rubocop:disable Metrics/CyclomaticComplexity
+# rubocop:disable Metrics/PerceivedComplexity
# rubocop:disable Metrics/AbcSize
# Note: The ABC size is large here because we have a method generating test cases with
@@ -30,7 +33,7 @@ module CycleAnalyticsHelpers
let_it_be(:other_project) { create(:project, :repository) }
before do
- other_project.add_developer(self.user)
+ other_project.add_developer(user)
end
context "start condition: #{start_time_conditions.map(&:first).to_sentence}" do
@@ -41,14 +44,14 @@ module CycleAnalyticsHelpers
start_time = (index * 10).days.from_now
end_time = start_time + rand(1..5).days
- start_time_conditions.each do |condition_name, condition_fn|
+ start_time_conditions.each_value do |condition_fn|
travel_to(start_time) { condition_fn[self, data] }
end
# Run `before_end_fn` at the midpoint between `start_time` and `end_time`
- travel_to(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
+ travel_to(start_time + ((end_time - start_time) / 2)) { before_end_fn[self, data] } if before_end_fn
- end_time_conditions.each do |condition_name, condition_fn|
+ end_time_conditions.each_value do |condition_fn|
travel_to(end_time) { condition_fn[self, data] }
end
@@ -73,11 +76,11 @@ module CycleAnalyticsHelpers
start_time = Time.now
end_time = rand(1..10).days.from_now
- start_time_conditions.each do |condition_name, condition_fn|
+ start_time_conditions.each_value do |condition_fn|
travel_to(start_time) { condition_fn[self, data] }
end
- end_time_conditions.each do |condition_name, condition_fn|
+ end_time_conditions.each_value do |condition_fn|
travel_to(end_time) { condition_fn[self, data] }
end
@@ -97,13 +100,13 @@ module CycleAnalyticsHelpers
end_time = start_time + rand(1..5).days
# Run `before_end_fn` at the midpoint between `start_time` and `end_time`
- travel_to(start_time + (end_time - start_time) / 2) { before_end_fn[self, data] } if before_end_fn
+ travel_to(start_time + ((end_time - start_time) / 2)) { before_end_fn[self, data] } if before_end_fn
- end_time_conditions.each do |condition_name, condition_fn|
+ end_time_conditions.each_value do |condition_fn|
travel_to(start_time) { condition_fn[self, data] }
end
- start_time_conditions.each do |condition_name, condition_fn|
+ start_time_conditions.each_value do |condition_fn|
travel_to(end_time) { condition_fn[self, data] }
end
@@ -113,36 +116,34 @@ module CycleAnalyticsHelpers
end
end
end
- end
- context "start condition NOT PRESENT: #{start_time_conditions.map(&:first).to_sentence}" do
- context "end condition: #{end_time_conditions.map(&:first).to_sentence}" do
+ context "end condition NOT PRESENT: #{end_time_conditions.map(&:first).to_sentence}" do
it "returns nil" do
data = data_fn[self]
- end_time = rand(1..10).days.from_now
+ start_time = Time.now
- end_time_conditions.each_with_index do |(_condition_name, condition_fn), index|
- travel_to(end_time + index.days) { condition_fn[self, data] }
+ start_time_conditions.each_value do |condition_fn|
+ travel_to(start_time) { condition_fn[self, data] }
end
- travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
+ post_fn[self, data] if post_fn
expect(subject[phase].project_median).to be_nil
end
end
end
- context "start condition: #{start_time_conditions.map(&:first).to_sentence}" do
- context "end condition NOT PRESENT: #{end_time_conditions.map(&:first).to_sentence}" do
+ context "start condition NOT PRESENT: #{start_time_conditions.map(&:first).to_sentence}" do
+ context "end condition: #{end_time_conditions.map(&:first).to_sentence}" do
it "returns nil" do
data = data_fn[self]
- start_time = Time.now
+ end_time = rand(1..10).days.from_now
- start_time_conditions.each do |condition_name, condition_fn|
- travel_to(start_time) { condition_fn[self, data] }
+ end_time_conditions.each_with_index do |(_condition_name, condition_fn), index|
+ travel_to(end_time + index.days) { condition_fn[self, data] }
end
- post_fn[self, data] if post_fn
+ travel_to(end_time + 1.day) { post_fn[self, data] } if post_fn
expect(subject[phase].project_median).to be_nil
end
@@ -158,3 +159,8 @@ module CycleAnalyticsHelpers
end
end
end
+
+# rubocop:enable Layout/LineLength
+# rubocop:enable Metrics/CyclomaticComplexity
+# rubocop:enable Metrics/PerceivedComplexity
+# rubocop:enable Metrics/AbcSize
diff --git a/spec/support/helpers/database/database_helpers.rb b/spec/support/helpers/database/database_helpers.rb
index ecc42041e93..ff694bcd15b 100644
--- a/spec/support/helpers/database/database_helpers.rb
+++ b/spec/support/helpers/database/database_helpers.rb
@@ -4,11 +4,13 @@ module Database
module DatabaseHelpers
# In order to directly work with views using factories,
# we can swapout the view for a table of identical structure.
- def swapout_view_for_table(view, connection:)
+ def swapout_view_for_table(view, connection:, schema: nil)
+ table_name = [schema, "_test_#{view}_copy"].compact.join('.')
+
connection.execute(<<~SQL.squish)
- CREATE TABLE #{view}_copy (LIKE #{view});
+ CREATE TABLE #{table_name} (LIKE #{view});
DROP VIEW #{view};
- ALTER TABLE #{view}_copy RENAME TO #{view};
+ ALTER TABLE #{table_name} RENAME TO #{view};
SQL
end
diff --git a/spec/support/helpers/database/inject_failure_helpers.rb b/spec/support/helpers/database/inject_failure_helpers.rb
new file mode 100644
index 00000000000..df98f45e69f
--- /dev/null
+++ b/spec/support/helpers/database/inject_failure_helpers.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Database
+ module InjectFailureHelpers
+ # These methods are used by specs that inject faults into the migration procedure and then ensure
+ # that it migrates correctly when rerun
+ def fail_first_time
+ # We can't directly use a boolean here, as we need something that will be passed by-reference to the proc
+ fault_status = { faulted: false }
+ proc do |m, *args, **kwargs|
+ next m.call(*args, **kwargs) if fault_status[:faulted]
+
+ fault_status[:faulted] = true
+ raise 'fault!'
+ end
+ end
+
+ def fail_sql_matching(regex)
+ proc do
+ allow(migration_context.connection).to receive(:execute).and_call_original
+ allow(migration_context.connection).to receive(:execute).with(regex).and_wrap_original(&fail_first_time)
+ end
+ end
+
+ def fail_adding_fk(from_table, to_table)
+ proc do
+ allow(migration_context.connection).to receive(:add_foreign_key).and_call_original
+ expect(migration_context.connection).to receive(:add_foreign_key).with(from_table, to_table, any_args)
+ .and_wrap_original(&fail_first_time)
+ end
+ end
+
+ def fail_removing_fk(from_table, to_table)
+ proc do
+ allow(migration_context.connection).to receive(:remove_foreign_key).and_call_original
+ expect(migration_context.connection).to receive(:remove_foreign_key).with(from_table, to_table, any_args)
+ .and_wrap_original(&fail_first_time)
+ end
+ end
+ end
+end
diff --git a/spec/support/helpers/database/multiple_databases_helpers.rb b/spec/support/helpers/database/multiple_databases_helpers.rb
index 5083ea1ff53..3c9a5762c47 100644
--- a/spec/support/helpers/database/multiple_databases_helpers.rb
+++ b/spec/support/helpers/database/multiple_databases_helpers.rb
@@ -4,6 +4,28 @@ module Database
module MultipleDatabasesHelpers
EXTRA_DBS = ::Gitlab::Database::DATABASE_NAMES.map(&:to_sym) - [:main]
+ def database_exists?(database_name)
+ ::Gitlab::Database.has_database?(database_name)
+ end
+
+ def skip_if_shared_database(database_name)
+ skip "Skipping because #{database_name} is shared or doesn't not exist" unless database_exists?(database_name)
+ end
+
+ def skip_if_database_exists(database_name)
+ skip "Skipping because database #{database_name} exists" if database_exists?(database_name)
+ end
+
+ def execute_on_each_database(query, databases: %I[main ci])
+ databases = databases.select { |database_name| database_exists?(database_name) }
+
+ Gitlab::Database::EachDatabase.each_database_connection(only: databases, include_shared: false) do |connection, _|
+ next unless Gitlab::Database.gitlab_schemas_for_connection(connection).include?(:gitlab_shared)
+
+ connection.execute(query)
+ end
+ end
+
def skip_if_multiple_databases_not_setup(*databases)
unless (databases - EXTRA_DBS).empty?
raise "Unsupported database in #{databases}. It must be one of #{EXTRA_DBS}."
diff --git a/spec/support/helpers/email_helpers.rb b/spec/support/helpers/email_helpers.rb
index f4bdaa7e425..57386233775 100644
--- a/spec/support/helpers/email_helpers.rb
+++ b/spec/support/helpers/email_helpers.rb
@@ -76,4 +76,25 @@ module EmailHelpers
composed_expectation.and(have_enqueued_mail(mailer_class, mailer_method).with(*arguments))
end
end
+
+ def expect_sender(user, sender_email: nil)
+ sender = subject.header[:from].addrs[0]
+ expect(sender.display_name).to eq("#{user.name} (@#{user.username})")
+ expect(sender.address).to eq(sender_email.presence || gitlab_sender)
+ end
+
+ def expect_service_desk_custom_email_delivery_options(service_desk_setting)
+ expect(subject.delivery_method).to be_a Mail::SMTP
+ expect(service_desk_setting.custom_email_credential).to be_present
+
+ credential = service_desk_setting.custom_email_credential
+
+ expect(subject.delivery_method.settings).to include(
+ address: credential.smtp_address,
+ port: credential.smtp_port,
+ user_name: credential.smtp_username,
+ password: credential.smtp_password,
+ domain: service_desk_setting.custom_email.split('@').last
+ )
+ end
end
diff --git a/spec/support/helpers/every_sidekiq_worker_test_helper.rb b/spec/support/helpers/every_sidekiq_worker_test_helper.rb
new file mode 100644
index 00000000000..b053ed04b58
--- /dev/null
+++ b/spec/support/helpers/every_sidekiq_worker_test_helper.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module EverySidekiqWorkerTestHelper
+ def extra_retry_exceptions
+ {}
+ end
+end
+
+EverySidekiqWorkerTestHelper.prepend_mod
diff --git a/spec/support/helpers/fake_u2f_device.rb b/spec/support/helpers/fake_u2f_device.rb
deleted file mode 100644
index 2ed1222ebd3..00000000000
--- a/spec/support/helpers/fake_u2f_device.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-class FakeU2fDevice
- attr_reader :name
-
- def initialize(page, name, device = nil)
- @page = page
- @name = name
- @u2f_device = device
- end
-
- def respond_to_u2f_registration
- app_id = @page.evaluate_script('gon.u2f.app_id')
- challenges = @page.evaluate_script('gon.u2f.challenges')
-
- json_response = u2f_device(app_id).register_response(challenges[0])
-
- @page.execute_script("
- u2f.register = function(appId, registerRequests, signRequests, callback) {
- callback(#{json_response});
- };
- ")
- end
-
- def respond_to_u2f_authentication
- app_id = @page.evaluate_script('gon.u2f.app_id')
- challenge = @page.evaluate_script('gon.u2f.challenge')
- json_response = u2f_device(app_id).sign_response(challenge)
-
- @page.execute_script("
- u2f.sign = function(appId, challenges, signRequests, callback) {
- callback(#{json_response});
- };
- window.gl.u2fAuthenticate.start();
- ")
- end
-
- def fake_u2f_authentication
- @page.execute_script("window.gl.u2fAuthenticate.renderAuthenticated('abc');")
- end
-
- private
-
- def u2f_device(app_id)
- @u2f_device ||= U2F::FakeU2F.new(app_id)
- end
-end
diff --git a/spec/support/helpers/fake_webauthn_device.rb b/spec/support/helpers/fake_webauthn_device.rb
index d2c2f7d6bf3..5a535735817 100644
--- a/spec/support/helpers/fake_webauthn_device.rb
+++ b/spec/support/helpers/fake_webauthn_device.rb
@@ -45,7 +45,7 @@ class FakeWebauthnDevice
return Promise.resolve(result);
};
JS
- @page.click_link('Try again?', href: false)
+ @page.click_button(_('Try again?'))
end
def fake_webauthn_authentication
diff --git a/spec/support/helpers/feature_flag_helpers.rb b/spec/support/helpers/feature_flag_helpers.rb
index 4e57002a7c6..3cf611c66e6 100644
--- a/spec/support/helpers/feature_flag_helpers.rb
+++ b/spec/support/helpers/feature_flag_helpers.rb
@@ -2,22 +2,32 @@
module FeatureFlagHelpers
def create_flag(project, name, active = true, description: nil, version: Operations::FeatureFlag.versions['new_version_flag'])
- create(:operations_feature_flag, name: name, active: active, version: version,
- description: description, project: project)
+ create(
+ :operations_feature_flag,
+ name: name,
+ active: active,
+ version: version,
+ description: description,
+ project: project
+ )
end
def create_scope(feature_flag, environment_scope, active = true, strategies = [{ name: "default", parameters: {} }])
- create(:operations_feature_flag_scope,
+ create(
+ :operations_feature_flag_scope,
feature_flag: feature_flag,
environment_scope: environment_scope,
active: active,
- strategies: strategies)
+ strategies: strategies
+ )
end
def create_strategy(feature_flag, name = 'default', parameters = {})
- create(:operations_strategy,
+ create(
+ :operations_strategy,
feature_flag: feature_flag,
- name: name)
+ name: name
+ )
end
def within_feature_flag_row(index)
@@ -95,6 +105,6 @@ module FeatureFlagHelpers
end
def expect_user_to_see_feature_flags_index_page
- expect(page).to have_text('Feature Flags')
+ expect(page).to have_text('Feature flags')
end
end
diff --git a/spec/support/helpers/features/access_token_helpers.rb b/spec/support/helpers/features/access_token_helpers.rb
index f4bdb70c160..bc839642914 100644
--- a/spec/support/helpers/features/access_token_helpers.rb
+++ b/spec/support/helpers/features/access_token_helpers.rb
@@ -1,18 +1,15 @@
# frozen_string_literal: true
-module Spec
- module Support
- module Helpers
- module AccessTokenHelpers
- def active_access_tokens
- find("[data-testid='active-tokens']")
- end
- def created_access_token
- within('[data-testid=access-token-section]') do
- find('[data-testid=toggle-visibility-button]').click
- find_field('new-access-token').value
- end
- end
+module Features
+ module AccessTokenHelpers
+ def active_access_tokens
+ find("[data-testid='active-tokens']")
+ end
+
+ def created_access_token
+ within('[data-testid=access-token-section]') do
+ find('[data-testid=toggle-visibility-button]').click
+ find_field('new-access-token').value
end
end
end
diff --git a/spec/support/helpers/features/admin_users_helpers.rb b/spec/support/helpers/features/admin_users_helpers.rb
index 99b19eedcff..9a87ccf113a 100644
--- a/spec/support/helpers/features/admin_users_helpers.rb
+++ b/spec/support/helpers/features/admin_users_helpers.rb
@@ -1,24 +1,18 @@
# frozen_string_literal: true
-module Spec
- module Support
- module Helpers
- module Features
- module AdminUsersHelpers
- def click_user_dropdown_toggle(user_id)
- page.within("[data-testid='user-actions-#{user_id}']") do
- find("[data-testid='dropdown-toggle']").click
- end
- end
+module Features
+ module AdminUsersHelpers
+ def click_user_dropdown_toggle(user_id)
+ page.within("[data-testid='user-actions-#{user_id}']") do
+ find("[data-testid='dropdown-toggle']").click
+ end
+ end
- def click_action_in_user_dropdown(user_id, action)
- click_user_dropdown_toggle(user_id)
+ def click_action_in_user_dropdown(user_id, action)
+ click_user_dropdown_toggle(user_id)
- within find("[data-testid='user-actions-#{user_id}']") do
- find('li button', exact_text: action).click
- end
- end
- end
+ within find("[data-testid='user-actions-#{user_id}']") do
+ find('li button', exact_text: action).click
end
end
end
diff --git a/spec/support/helpers/features/blob_spec_helpers.rb b/spec/support/helpers/features/blob_spec_helpers.rb
index 7ccfc9be7e2..8254e1d76bd 100644
--- a/spec/support/helpers/features/blob_spec_helpers.rb
+++ b/spec/support/helpers/features/blob_spec_helpers.rb
@@ -1,14 +1,16 @@
# frozen_string_literal: true
-# These helpers help you interact within the blobs page and blobs edit page (Single file editor).
-module BlobSpecHelpers
- include ActionView::Helpers::JavaScriptHelper
+module Features
+ # These helpers help you interact within the blobs page and blobs edit page (Single file editor).
+ module BlobSpecHelpers
+ include ActionView::Helpers::JavaScriptHelper
- def set_default_button(type)
- evaluate_script("localStorage.setItem('gl-web-ide-button-selected', '#{type}')")
- end
+ def set_default_button(type)
+ evaluate_script("localStorage.setItem('gl-web-ide-button-selected', '#{type}')")
+ end
- def unset_default_button
- set_default_button('')
+ def unset_default_button
+ set_default_button('')
+ end
end
end
diff --git a/spec/support/helpers/features/branches_helpers.rb b/spec/support/helpers/features/branches_helpers.rb
index dc4fa448167..9fb6236d052 100644
--- a/spec/support/helpers/features/branches_helpers.rb
+++ b/spec/support/helpers/features/branches_helpers.rb
@@ -4,31 +4,28 @@
#
# Usage:
# describe "..." do
-# include Spec::Support::Helpers::Features::BranchesHelpers
+# include Features::BranchesHelpers
# ...
#
# create_branch("feature")
# select_branch("master")
#
-module Spec
- module Support
- module Helpers
- module Features
- module BranchesHelpers
- def create_branch(branch_name, source_branch_name = "master")
- fill_in("branch_name", with: branch_name)
- select_branch(source_branch_name)
- click_button("Create branch")
- end
+module Features
+ module BranchesHelpers
+ include ListboxHelpers
- def select_branch(branch_name)
- wait_for_requests
+ def create_branch(branch_name, source_branch_name = "master")
+ fill_in("branch_name", with: branch_name)
+ select_branch(source_branch_name)
+ click_button("Create branch")
+ end
+
+ def select_branch(branch_name)
+ wait_for_requests
- click_button branch_name
- send_keys branch_name
- end
- end
- end
+ click_button branch_name
+ send_keys branch_name
+ select_listbox_item(branch_name)
end
end
end
diff --git a/spec/support/helpers/features/canonical_link_helpers.rb b/spec/support/helpers/features/canonical_link_helpers.rb
index da3a28f1cb2..6ef934a924b 100644
--- a/spec/support/helpers/features/canonical_link_helpers.rb
+++ b/spec/support/helpers/features/canonical_link_helpers.rb
@@ -4,25 +4,19 @@
#
# Usage:
# describe "..." do
-# include Spec::Support::Helpers::Features::CanonicalLinkHelpers
+# include Features::CanonicalLinkHelpers
# ...
#
# expect(page).to have_canonical_link(url)
#
-module Spec
- module Support
- module Helpers
- module Features
- module CanonicalLinkHelpers
- def have_canonical_link(url)
- have_xpath("//link[@rel=\"canonical\" and @href=\"#{url}\"]", visible: false)
- end
+module Features
+ module CanonicalLinkHelpers
+ def have_canonical_link(url)
+ have_xpath("//link[@rel=\"canonical\" and @href=\"#{url}\"]", visible: false)
+ end
- def have_any_canonical_links
- have_xpath('//link[@rel="canonical"]', visible: false)
- end
- end
- end
+ def have_any_canonical_links
+ have_xpath('//link[@rel="canonical"]', visible: false)
end
end
end
diff --git a/spec/support/helpers/features/invite_members_modal_helper.rb b/spec/support/helpers/features/invite_members_modal_helper.rb
deleted file mode 100644
index 47cbd6b5208..00000000000
--- a/spec/support/helpers/features/invite_members_modal_helper.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-# frozen_string_literal: true
-
-module Spec
- module Support
- module Helpers
- module Features
- module InviteMembersModalHelper
- def invite_member(names, role: 'Guest', expires_at: nil)
- click_on 'Invite members'
-
- page.within invite_modal_selector do
- select_members(names)
- choose_options(role, expires_at)
- submit_invites
- end
-
- wait_for_requests
- end
-
- def invite_member_by_email(role)
- click_on _('Invite members')
-
- page.within invite_modal_selector do
- choose_options(role, nil)
- find(member_dropdown_selector).set('new_email@gitlab.com')
- wait_for_requests
-
- find('.dropdown-item', text: 'Invite "new_email@gitlab.com" by email').click
-
- submit_invites
-
- wait_for_requests
- end
- end
-
- def input_invites(names)
- click_on 'Invite members'
-
- page.within invite_modal_selector do
- select_members(names)
- end
- end
-
- def select_members(names)
- Array.wrap(names).each do |name|
- find(member_dropdown_selector).set(name)
-
- wait_for_requests
- click_button name
- end
- end
-
- def invite_group(name, role: 'Guest', expires_at: nil)
- click_on 'Invite a group'
-
- click_on 'Select a group'
- wait_for_requests
- click_button name
- choose_options(role, expires_at)
-
- submit_invites
- end
-
- def submit_invites
- click_button 'Invite'
- end
-
- def choose_options(role, expires_at)
- select role, from: 'Select a role'
- fill_in 'YYYY-MM-DD', with: expires_at.strftime('%Y-%m-%d') if expires_at
- end
-
- def click_groups_tab
- expect(page).to have_link 'Groups'
- click_link "Groups"
- end
-
- def group_dropdown_selector
- '[data-testid="group-select-dropdown"]'
- end
-
- def member_dropdown_selector
- '[data-testid="members-token-select-input"]'
- end
-
- def invite_modal_selector
- '[data-testid="invite-modal"]'
- end
-
- def member_token_error_selector(id)
- "[data-testid='error-icon-#{id}']"
- end
-
- def member_token_avatar_selector
- "[data-testid='token-avatar']"
- end
-
- def member_token_selector(id)
- "[data-token-id='#{id}']"
- end
-
- def more_invite_errors_button_selector
- "[data-testid='accordion-button']"
- end
-
- def limited_invite_error_selector
- "[data-testid='errors-limited-item']"
- end
-
- def expanded_invite_error_selector
- "[data-testid='errors-expanded-item']"
- end
-
- def remove_token(id)
- page.within member_token_selector(id) do
- find('[data-testid="close-icon"]').click
- end
- end
-
- def expect_to_have_successful_invite_indicator(page, user)
- expect(page).to have_selector("#{member_token_selector(user.id)} .gl-bg-green-100")
- expect(page).not_to have_text("#{user.name}: ")
- end
-
- def expect_to_have_invalid_invite_indicator(page, user, message: true)
- expect(page).to have_selector("#{member_token_selector(user.id)} .gl-bg-red-100")
- expect(page).to have_selector(member_token_error_selector(user.id))
- expect(page).to have_text("#{user.name}: Access level should be greater than or equal to") if message
- end
-
- def expect_to_have_normal_invite_indicator(page, user)
- expect(page).to have_selector(member_token_selector(user.id))
- expect(page).not_to have_selector("#{member_token_selector(user.id)} .gl-bg-red-100")
- expect(page).not_to have_selector("#{member_token_selector(user.id)} .gl-bg-green-100")
- expect(page).not_to have_text("#{user.name}: ")
- end
-
- def expect_to_have_invite_removed(page, user)
- expect(page).not_to have_selector(member_token_selector(user.id))
- expect(page).not_to have_text("#{user.name}: Access level should be greater than or equal to")
- end
-
- def expect_to_have_group(group)
- expect(page).to have_selector("[entity-id='#{group.id}']")
- end
-
- def expect_not_to_have_group(group)
- expect(page).not_to have_selector("[entity-id='#{group.id}']")
- end
- end
- end
- end
- end
-end
diff --git a/spec/support/helpers/features/invite_members_modal_helpers.rb b/spec/support/helpers/features/invite_members_modal_helpers.rb
new file mode 100644
index 00000000000..75573616686
--- /dev/null
+++ b/spec/support/helpers/features/invite_members_modal_helpers.rb
@@ -0,0 +1,148 @@
+# frozen_string_literal: true
+
+module Features
+ module InviteMembersModalHelpers
+ def invite_member(names, role: 'Guest', expires_at: nil)
+ click_on 'Invite members'
+
+ page.within invite_modal_selector do
+ select_members(names)
+ choose_options(role, expires_at)
+ submit_invites
+ end
+
+ wait_for_requests
+ end
+
+ def invite_member_by_email(role)
+ click_on _('Invite members')
+
+ page.within invite_modal_selector do
+ choose_options(role, nil)
+ find(member_dropdown_selector).set('new_email@gitlab.com')
+ wait_for_requests
+
+ find('.dropdown-item', text: 'Invite "new_email@gitlab.com" by email').click
+
+ submit_invites
+
+ wait_for_requests
+ end
+ end
+
+ def input_invites(names)
+ click_on 'Invite members'
+
+ page.within invite_modal_selector do
+ select_members(names)
+ end
+ end
+
+ def select_members(names)
+ Array.wrap(names).each do |name|
+ find(member_dropdown_selector).set(name)
+
+ wait_for_requests
+ click_button name
+ end
+ end
+
+ def invite_group(name, role: 'Guest', expires_at: nil)
+ click_on 'Invite a group'
+
+ click_on 'Select a group'
+ wait_for_requests
+ click_button name
+ choose_options(role, expires_at)
+
+ submit_invites
+ end
+
+ def submit_invites
+ click_button 'Invite'
+ end
+
+ def choose_options(role, expires_at)
+ select role, from: 'Select a role'
+ fill_in 'YYYY-MM-DD', with: expires_at.strftime('%Y-%m-%d') if expires_at
+ end
+
+ def click_groups_tab
+ expect(page).to have_link 'Groups'
+ click_link "Groups"
+ end
+
+ def group_dropdown_selector
+ '[data-testid="group-select-dropdown"]'
+ end
+
+ def member_dropdown_selector
+ '[data-testid="members-token-select-input"]'
+ end
+
+ def invite_modal_selector
+ '[data-testid="invite-modal"]'
+ end
+
+ def member_token_error_selector(id)
+ "[data-testid='error-icon-#{id}']"
+ end
+
+ def member_token_avatar_selector
+ "[data-testid='token-avatar']"
+ end
+
+ def member_token_selector(id)
+ "[data-token-id='#{id}']"
+ end
+
+ def more_invite_errors_button_selector
+ "[data-testid='accordion-button']"
+ end
+
+ def limited_invite_error_selector
+ "[data-testid='errors-limited-item']"
+ end
+
+ def expanded_invite_error_selector
+ "[data-testid='errors-expanded-item']"
+ end
+
+ def remove_token(id)
+ page.within member_token_selector(id) do
+ find('[data-testid="close-icon"]').click
+ end
+ end
+
+ def expect_to_have_successful_invite_indicator(page, user)
+ expect(page).to have_selector("#{member_token_selector(user.id)} .gl-bg-green-100")
+ expect(page).not_to have_text("#{user.name}: ")
+ end
+
+ def expect_to_have_invalid_invite_indicator(page, user, message: true)
+ expect(page).to have_selector("#{member_token_selector(user.id)} .gl-bg-red-100")
+ expect(page).to have_selector(member_token_error_selector(user.id))
+ expect(page).to have_text("#{user.name}: Access level should be greater than or equal to") if message
+ end
+
+ def expect_to_have_normal_invite_indicator(page, user)
+ expect(page).to have_selector(member_token_selector(user.id))
+ expect(page).not_to have_selector("#{member_token_selector(user.id)} .gl-bg-red-100")
+ expect(page).not_to have_selector("#{member_token_selector(user.id)} .gl-bg-green-100")
+ expect(page).not_to have_text("#{user.name}: ")
+ end
+
+ def expect_to_have_invite_removed(page, user)
+ expect(page).not_to have_selector(member_token_selector(user.id))
+ expect(page).not_to have_text("#{user.name}: Access level should be greater than or equal to")
+ end
+
+ def expect_to_have_group(group)
+ expect(page).to have_selector("[entity-id='#{group.id}']")
+ end
+
+ def expect_not_to_have_group(group)
+ expect(page).not_to have_selector("[entity-id='#{group.id}']")
+ end
+ end
+end
diff --git a/spec/support/helpers/features/iteration_helpers.rb b/spec/support/helpers/features/iteration_helpers.rb
index 8e1d252f55f..fab373a547f 100644
--- a/spec/support/helpers/features/iteration_helpers.rb
+++ b/spec/support/helpers/features/iteration_helpers.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
-module IterationHelpers
- def iteration_period(iteration)
- "#{iteration.start_date.to_s(:medium)} - #{iteration.due_date.to_s(:medium)}"
+
+module Features
+ module IterationHelpers
+ def iteration_period(iteration)
+ "#{iteration.start_date.to_s(:medium)} - #{iteration.due_date.to_s(:medium)}"
+ end
end
end
diff --git a/spec/support/helpers/features/list_rows_helpers.rb b/spec/support/helpers/features/list_rows_helpers.rb
deleted file mode 100644
index 0626415361c..00000000000
--- a/spec/support/helpers/features/list_rows_helpers.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-# These helpers allow you to access rows in the list
-#
-# Usage:
-# describe "..." do
-# include Spec::Support::Helpers::Features::ListRowsHelpers
-# ...
-#
-# expect(first_row.text).to include("John Doe")
-# expect(second_row.text).to include("John Smith")
-#
-module Spec
- module Support
- module Helpers
- module Features
- module ListRowsHelpers
- def first_row
- page.all('ul.content-list > li')[0]
- end
-
- def second_row
- page.all('ul.content-list > li')[1]
- end
- end
- end
- end
- end
-end
diff --git a/spec/support/helpers/features/members_helpers.rb b/spec/support/helpers/features/members_helpers.rb
index 2d3f0902a3c..9882767cecf 100644
--- a/spec/support/helpers/features/members_helpers.rb
+++ b/spec/support/helpers/features/members_helpers.rb
@@ -1,78 +1,72 @@
# frozen_string_literal: true
-module Spec
- module Support
- module Helpers
- module Features
- module MembersHelpers
- def members_table
- page.find('[data-testid="members-table"]')
- end
+module Features
+ module MembersHelpers
+ def members_table
+ page.find('[data-testid="members-table"]')
+ end
- def all_rows
- page.within(members_table) do
- page.all('tbody > tr')
- end
- end
+ def all_rows
+ page.within(members_table) do
+ page.all('tbody > tr')
+ end
+ end
- def first_row
- all_rows[0]
- end
+ def first_row
+ all_rows[0]
+ end
- def second_row
- all_rows[1]
- end
+ def second_row
+ all_rows[1]
+ end
- def third_row
- all_rows[2]
- end
+ def third_row
+ all_rows[2]
+ end
- def find_row(name)
- page.within(members_table) do
- page.find('tbody > tr', text: name)
- end
- end
+ def find_row(name)
+ page.within(members_table) do
+ page.find('tbody > tr', text: name)
+ end
+ end
- def find_member_row(user)
- find_row(user.name)
- end
+ def find_member_row(user)
+ find_row(user.name)
+ end
- def find_username_row(user)
- find_row(user.username)
- end
+ def find_username_row(user)
+ find_row(user.username)
+ end
- def find_invited_member_row(email)
- find_row(email)
- end
+ def find_invited_member_row(email)
+ find_row(email)
+ end
- def find_group_row(group)
- find_row(group.full_name)
- end
+ def find_group_row(group)
+ find_row(group.full_name)
+ end
- def fill_in_filtered_search(label, with:)
- page.within '[data-testid="members-filtered-search-bar"]' do
- find_field(label).click
- find('input').native.send_keys(with)
- click_button 'Search'
- end
- end
+ def fill_in_filtered_search(label, with:)
+ page.within '[data-testid="members-filtered-search-bar"]' do
+ find_field(label).click
+ find('input').native.send_keys(with)
+ click_button 'Search'
+ end
+ end
- def user_action_dropdown
- '[data-testid="user-action-dropdown"]'
- end
+ def user_action_dropdown
+ '[data-testid="user-action-dropdown"]'
+ end
- def show_actions
- within user_action_dropdown do
- find('button').click
- end
- end
+ def show_actions
+ within user_action_dropdown do
+ find('button').click
+ end
+ end
- def show_actions_for_username(user)
- within find_username_row(user) do
- show_actions
- end
- end
- end
+ def show_actions_for_username(user)
+ within find_username_row(user) do
+ show_actions
end
end
end
diff --git a/spec/support/helpers/features/merge_request_helpers.rb b/spec/support/helpers/features/merge_request_helpers.rb
index 53896e1fe12..260a55487ea 100644
--- a/spec/support/helpers/features/merge_request_helpers.rb
+++ b/spec/support/helpers/features/merge_request_helpers.rb
@@ -1,25 +1,19 @@
# frozen_string_literal: true
-module Spec
- module Support
- module Helpers
- module Features
- module MergeRequestHelpers
- def preload_view_requirements(merge_request, note)
- # This will load the status fields of the author of the note and merge request
- # to avoid queries when rendering the view being tested.
- #
- merge_request.author.status
- note.author.status
- end
+module Features
+ module MergeRequestHelpers
+ def preload_view_requirements(merge_request, note)
+ # This will load the status fields of the author of the note and merge request
+ # to avoid queries when rendering the view being tested.
+ #
+ merge_request.author.status
+ note.author.status
+ end
- def serialize_issuable_sidebar(user, project, merge_request)
- MergeRequestSerializer
- .new(current_user: user, project: project)
- .represent(merge_request, serializer: 'sidebar')
- end
- end
- end
+ def serialize_issuable_sidebar(user, project, merge_request)
+ MergeRequestSerializer
+ .new(current_user: user, project: project)
+ .represent(merge_request, serializer: 'sidebar')
end
end
end
diff --git a/spec/support/helpers/features/mirroring_helpers.rb b/spec/support/helpers/features/mirroring_helpers.rb
new file mode 100644
index 00000000000..0c3006cd1d1
--- /dev/null
+++ b/spec/support/helpers/features/mirroring_helpers.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+# These helpers allow you to set up mirroring.
+#
+# Usage:
+# describe "..." do
+# include Features::MirroringHelpers
+# ...
+#
+# fill_and_wait_for_mirror_url_javascript("url", "ssh://user@localhost/project.git")
+# wait_for_mirror_field_javascript("protected", "0")
+#
+module Features
+ module MirroringHelpers
+ # input_identifier - identifier of the input field, passed to `fill_in` (can be an ID or a label).
+ # url - the URL to fill the input field with.
+ def fill_and_wait_for_mirror_url_javascript(input_identifier, url)
+ fill_in input_identifier, with: url
+ wait_for_mirror_field_javascript('url', url)
+ end
+
+ # attribute - can be `url` or `protected`. It's used in the `.js-mirror-<field>-hidden` selector.
+ # expected_value - the expected value of the hidden field.
+ def wait_for_mirror_field_javascript(attribute, expected_value)
+ expect(page).to have_css(".js-mirror-#{attribute}-hidden[value=\"#{expected_value}\"]", visible: :hidden)
+ end
+ end
+end
diff --git a/spec/support/helpers/features/notes_helpers.rb b/spec/support/helpers/features/notes_helpers.rb
index f8252254531..7973d541f9c 100644
--- a/spec/support/helpers/features/notes_helpers.rb
+++ b/spec/support/helpers/features/notes_helpers.rb
@@ -4,53 +4,47 @@
#
# Usage:
# describe "..." do
-# include Spec::Support::Helpers::Features::NotesHelpers
+# include Features::NotesHelpers
# ...
#
# add_note("Hello world!")
#
-module Spec
- module Support
- module Helpers
- module Features
- module NotesHelpers
- def add_note(text)
- perform_enqueued_jobs do
- page.within(".js-main-target-form") do
- fill_in("note[note]", with: text)
- find(".js-comment-submit-button").click
- end
- end
-
- wait_for_requests
- end
-
- def edit_note(note_text_to_edit, new_note_text)
- page.within('#notes-list li.note', text: note_text_to_edit) do
- find('.js-note-edit').click
- fill_in('note[note]', with: new_note_text)
- find('.js-comment-button').click
- end
-
- wait_for_requests
- end
-
- def preview_note(text)
- page.within('.js-main-target-form') do
- filled_text = fill_in('note[note]', with: text)
-
- # Wait for quick action prompt to load and then dismiss it with ESC
- # because it may block the Preview button
- wait_for_requests
- filled_text.send_keys(:escape)
-
- click_on('Preview')
-
- yield if block_given?
- end
- end
+module Features
+ module NotesHelpers
+ def add_note(text)
+ perform_enqueued_jobs do
+ page.within(".js-main-target-form") do
+ fill_in("note[note]", with: text)
+ find(".js-comment-submit-button").click
end
end
+
+ wait_for_requests
+ end
+
+ def edit_note(note_text_to_edit, new_note_text)
+ page.within('#notes-list li.note', text: note_text_to_edit) do
+ find('.js-note-edit').click
+ fill_in('note[note]', with: new_note_text)
+ find('.js-comment-button').click
+ end
+
+ wait_for_requests
+ end
+
+ def preview_note(text)
+ page.within('.js-main-target-form') do
+ filled_text = fill_in('note[note]', with: text)
+
+ # Wait for quick action prompt to load and then dismiss it with ESC
+ # because it may block the Preview button
+ wait_for_requests
+ filled_text.send_keys(:escape)
+
+ click_button("Preview")
+
+ yield if block_given?
+ end
end
end
end
diff --git a/spec/support/helpers/features/releases_helpers.rb b/spec/support/helpers/features/releases_helpers.rb
index 545e12341ef..d5846aad15d 100644
--- a/spec/support/helpers/features/releases_helpers.rb
+++ b/spec/support/helpers/features/releases_helpers.rb
@@ -4,80 +4,83 @@
#
# Usage:
# describe "..." do
-# include Spec::Support::Helpers::Features::ReleasesHelpers
+# include Features::ReleasesHelpers
# ...
#
# fill_tag_name("v1.0")
# select_create_from("my-feature-branch")
#
-module Spec
- module Support
- module Helpers
- module Features
- module ReleasesHelpers
- include ListboxHelpers
+module Features
+ module ReleasesHelpers
+ include ListboxHelpers
- def select_new_tag_name(tag_name)
- page.within '[data-testid="tag-name-field"]' do
- find('button').click
- wait_for_all_requests
+ def select_new_tag_name(tag_name)
+ open_tag_popover
- find('input[aria-label="Search or create tag"]').set(tag_name)
- wait_for_all_requests
+ page.within '[data-testid="tag-name-search"]' do
+ find('input[type="search"]').set(tag_name)
+ wait_for_all_requests
- click_button("Create tag #{tag_name}")
- click_button tag_name
- end
- end
-
- def select_create_from(branch_name)
- page.within '[data-testid="create-from-field"]' do
- find('button').click
+ click_button("Create tag #{tag_name}")
+ end
+ end
- wait_for_all_requests
+ def select_create_from(branch_name)
+ open_tag_popover
- find('input[aria-label="Search branches, tags, and commits"]').set(branch_name)
+ page.within '[data-testid="create-from-field"]' do
+ find('.ref-selector button').click
- wait_for_all_requests
+ wait_for_all_requests
- select_listbox_item(branch_name.to_s, exact_text: true)
- end
- end
+ find('input[aria-label="Search branches, tags, and commits"]').set(branch_name)
- def fill_release_title(release_title)
- fill_in('Release title', with: release_title)
- end
+ wait_for_all_requests
- def select_milestone(milestone_title)
- page.within '[data-testid="milestones-field"]' do
- find('button').click
+ select_listbox_item(branch_name.to_s, exact_text: true)
- wait_for_all_requests
+ click_button _('Save')
+ end
+ end
- find('input[aria-label="Search Milestones"]').set(milestone_title)
+ def fill_release_title(release_title)
+ fill_in('Release title', with: release_title)
+ end
- wait_for_all_requests
+ def select_milestone(milestone_title)
+ page.within '[data-testid="milestones-field"]' do
+ find('button').click
- find('button', text: milestone_title, match: :first).click
- end
- end
+ wait_for_all_requests
- def fill_release_notes(release_notes)
- fill_in('Release notes', with: release_notes)
- end
+ find('input[aria-label="Search Milestones"]').set(milestone_title)
- def fill_asset_link(link)
- all('input[name="asset-url"]').last.set(link[:url])
- all('input[name="asset-link-name"]').last.set(link[:title])
- all('select[name="asset-type"]').last.find("option[value=\"#{link[:type]}\"").select_option
- end
+ wait_for_all_requests
- # Click "Add another link" and tab back to the beginning of the new row
- def add_another_asset_link
- click_button('Add another link')
- end
- end
+ find('button', text: milestone_title, match: :first).click
end
end
+
+ def fill_release_notes(release_notes)
+ fill_in('Release notes', with: release_notes)
+ end
+
+ def fill_asset_link(link)
+ all('input[name="asset-url"]').last.set(link[:url])
+ all('input[name="asset-link-name"]').last.set(link[:title])
+ all('select[name="asset-type"]').last.find("option[value=\"#{link[:type]}\"").select_option
+ end
+
+ # Click "Add another link" and tab back to the beginning of the new row
+ def add_another_asset_link
+ click_button('Add another link')
+ end
+
+ def open_tag_popover(name = s_('Release|Search or create tag name'))
+ return if page.has_css? '.release-tag-selector'
+
+ click_button name
+ wait_for_all_requests
+ end
end
end
diff --git a/spec/support/helpers/features/responsive_table_helpers.rb b/spec/support/helpers/features/responsive_table_helpers.rb
index 7a175219fe9..980f09b7eea 100644
--- a/spec/support/helpers/features/responsive_table_helpers.rb
+++ b/spec/support/helpers/features/responsive_table_helpers.rb
@@ -3,7 +3,7 @@
#
# Usage:
# describe "..." do
-# include Spec::Support::Helpers::Features::ResponsiveTableHelpers
+# include Features::ResponsiveTableHelpers
# ...
#
# expect(first_row.text).to include("John Doe")
@@ -13,20 +13,14 @@
# index starts at 1 as index 0 is expected to be the table header
#
#
-module Spec
- module Support
- module Helpers
- module Features
- module ResponsiveTableHelpers
- def first_row
- page.all('.gl-responsive-table-row')[1]
- end
+module Features
+ module ResponsiveTableHelpers
+ def first_row
+ page.all('.gl-responsive-table-row')[1]
+ end
- def second_row
- page.all('.gl-responsive-table-row')[2]
- end
- end
- end
+ def second_row
+ page.all('.gl-responsive-table-row')[2]
end
end
end
diff --git a/spec/support/helpers/features/runners_helpers.rb b/spec/support/helpers/features/runners_helpers.rb
index c5d26108953..0504e883b82 100644
--- a/spec/support/helpers/features/runners_helpers.rb
+++ b/spec/support/helpers/features/runners_helpers.rb
@@ -1,68 +1,62 @@
# frozen_string_literal: true
-module Spec
- module Support
- module Helpers
- module Features
- module RunnersHelpers
- def within_runner_row(runner_id)
- within "[data-testid='runner-row-#{runner_id}']" do
- yield
- end
- end
-
- def search_bar_selector
- '[data-testid="runners-filtered-search"]'
- end
+module Features
+ module RunnersHelpers
+ def within_runner_row(runner_id)
+ within "[data-testid='runner-row-#{runner_id}']" do
+ yield
+ end
+ end
- # The filters must be clicked first to be able to receive events
- # See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1493
- def focus_filtered_search
- page.within(search_bar_selector) do
- page.find('.gl-filtered-search-term-token').click
- end
- end
+ def search_bar_selector
+ '[data-testid="runners-filtered-search"]'
+ end
- def input_filtered_search_keys(search_term)
- focus_filtered_search
+ # The filters must be clicked first to be able to receive events
+ # See: https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1493
+ def focus_filtered_search
+ page.within(search_bar_selector) do
+ page.find('.gl-filtered-search-term-token').click
+ end
+ end
- page.within(search_bar_selector) do
- page.find('input').send_keys(search_term)
- click_on 'Search'
- end
+ def input_filtered_search_keys(search_term)
+ focus_filtered_search
- wait_for_requests
- end
+ page.within(search_bar_selector) do
+ page.find('input').send_keys(search_term)
+ click_on 'Search'
+ end
- def open_filtered_search_suggestions(filter)
- focus_filtered_search
+ wait_for_requests
+ end
- page.within(search_bar_selector) do
- click_on filter
- end
+ def open_filtered_search_suggestions(filter)
+ focus_filtered_search
- wait_for_requests
- end
+ page.within(search_bar_selector) do
+ click_on filter
+ end
- def input_filtered_search_filter_is_only(filter, value)
- focus_filtered_search
+ wait_for_requests
+ end
- page.within(search_bar_selector) do
- click_on filter
+ def input_filtered_search_filter_is_only(filter, value)
+ focus_filtered_search
- # For OPERATORS_IS, clicking the filter
- # immediately preselects "=" operator
+ page.within(search_bar_selector) do
+ click_on filter
- page.find('input').send_keys(value)
- page.find('input').send_keys(:enter)
+ # For OPERATORS_IS, clicking the filter
+ # immediately preselects "=" operator
- click_on 'Search'
- end
+ page.find('input').send_keys(value)
+ page.find('input').send_keys(:enter)
- wait_for_requests
- end
- end
+ click_on 'Search'
end
+
+ wait_for_requests
end
end
end
diff --git a/spec/support/helpers/features/snippet_helpers.rb b/spec/support/helpers/features/snippet_helpers.rb
deleted file mode 100644
index 3e32b0e4c67..00000000000
--- a/spec/support/helpers/features/snippet_helpers.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: true
-
-# These helpers help you interact within the Source Editor (single-file editor, snippets, etc.).
-#
-
-require Rails.root.join("spec/support/helpers/features/source_editor_spec_helpers.rb")
-
-module Spec
- module Support
- module Helpers
- module Features
- module SnippetSpecHelpers
- include ActionView::Helpers::JavaScriptHelper
- include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
-
- def snippet_description_locator
- 'snippet-description'
- end
-
- def snippet_blob_path_locator
- 'snippet_file_name'
- end
-
- def snippet_description_view_selector
- '.snippet-header .snippet-description'
- end
-
- def snippet_description_field_collapsed
- find('.js-description-input').find('input,textarea')
- end
-
- def snippet_get_first_blob_path
- page.find_field('snippet_file_name', match: :first).value
- end
-
- def snippet_get_first_blob_value
- page.find('.gl-source-editor', match: :first)
- end
-
- def snippet_description_value
- page.find_field(snippet_description_locator).value
- end
-
- def snippet_fill_in_visibility(text)
- page.find('#visibility-level-setting').choose(text)
- end
-
- def snippet_fill_in_title(value)
- fill_in 'snippet-title', with: value
- end
-
- def snippet_fill_in_description(value)
- # Click placeholder first to expand full description field
- snippet_description_field_collapsed.click
- fill_in snippet_description_locator, with: value
- end
-
- def snippet_fill_in_content(value)
- page.within('.gl-source-editor') do
- el = find('.inputarea')
- el.send_keys value
- end
- end
-
- def snippet_fill_in_file_name(value)
- fill_in(snippet_blob_path_locator, match: :first, with: value)
- end
-
- def snippet_fill_in_form(title: nil, content: nil, file_name: nil, description: nil, visibility: nil)
- if content
- snippet_fill_in_content(content)
- # It takes some time after sending keys for the vue component to
- # update so let Capybara wait for the content before proceeding
- expect(page).to have_content(content)
- end
-
- snippet_fill_in_title(title) if title
-
- snippet_fill_in_description(description) if description
-
- snippet_fill_in_file_name(file_name) if file_name
-
- snippet_fill_in_visibility(visibility) if visibility
- end
- end
- end
- end
- end
-end
diff --git a/spec/support/helpers/features/snippet_spec_helpers.rb b/spec/support/helpers/features/snippet_spec_helpers.rb
new file mode 100644
index 00000000000..19393f6e438
--- /dev/null
+++ b/spec/support/helpers/features/snippet_spec_helpers.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+# These helpers help you interact within the Source Editor (single-file editor, snippets, etc.).
+#
+
+require Rails.root.join("spec/support/helpers/features/source_editor_spec_helpers.rb")
+
+module Features
+ module SnippetSpecHelpers
+ include ActionView::Helpers::JavaScriptHelper
+ include Features::SourceEditorSpecHelpers
+
+ def snippet_description_locator
+ 'snippet-description'
+ end
+
+ def snippet_blob_path_locator
+ 'snippet_file_name'
+ end
+
+ def snippet_description_view_selector
+ '.snippet-header .snippet-description'
+ end
+
+ def snippet_description_field_collapsed
+ find('.js-description-input').find('input,textarea')
+ end
+
+ def snippet_get_first_blob_path
+ page.find_field('snippet_file_name', match: :first).value
+ end
+
+ def snippet_get_first_blob_value
+ page.find('.gl-source-editor', match: :first)
+ end
+
+ def snippet_description_value
+ page.find_field(snippet_description_locator).value
+ end
+
+ def snippet_fill_in_visibility(text)
+ page.find('#visibility-level-setting').choose(text)
+ end
+
+ def snippet_fill_in_title(value)
+ fill_in 'snippet-title', with: value
+ end
+
+ def snippet_fill_in_description(value)
+ # Click placeholder first to expand full description field
+ snippet_description_field_collapsed.click
+ fill_in snippet_description_locator, with: value
+ end
+
+ def snippet_fill_in_content(value)
+ page.within('.gl-source-editor') do
+ el = find('.inputarea')
+ el.send_keys value
+ end
+ end
+
+ def snippet_fill_in_file_name(value)
+ fill_in(snippet_blob_path_locator, match: :first, with: value)
+ end
+
+ def snippet_fill_in_form(title: nil, content: nil, file_name: nil, description: nil, visibility: nil)
+ if content
+ snippet_fill_in_content(content)
+ # It takes some time after sending keys for the vue component to
+ # update so let Capybara wait for the content before proceeding
+ expect(page).to have_content(content)
+ end
+
+ snippet_fill_in_title(title) if title
+
+ snippet_fill_in_description(description) if description
+
+ snippet_fill_in_file_name(file_name) if file_name
+
+ snippet_fill_in_visibility(visibility) if visibility
+ end
+ end
+end
diff --git a/spec/support/helpers/features/sorting_helpers.rb b/spec/support/helpers/features/sorting_helpers.rb
index 504a9b764cf..8dda16af625 100644
--- a/spec/support/helpers/features/sorting_helpers.rb
+++ b/spec/support/helpers/features/sorting_helpers.rb
@@ -4,33 +4,27 @@
#
# Usage:
# describe "..." do
-# include Spec::Support::Helpers::Features::SortingHelpers
+# include Features::SortingHelpers
# ...
#
# sort_by("Last updated")
#
-module Spec
- module Support
- module Helpers
- module Features
- module SortingHelpers
- def sort_by(value)
- find('.filter-dropdown-container .dropdown').click
+module Features
+ module SortingHelpers
+ def sort_by(value)
+ find('.filter-dropdown-container .dropdown').click
- page.within('ul.dropdown-menu.dropdown-menu-right li') do
- click_link(value)
- end
- end
-
- # pajamas_sort_by is used to sort new pajamas dropdowns. When
- # all of the dropdowns are converted, pajamas_sort_by can be renamed to sort_by
- # https://gitlab.com/groups/gitlab-org/-/epics/7551
- def pajamas_sort_by(value)
- find('.filter-dropdown-container .gl-new-dropdown').click
- find('.gl-new-dropdown-item', text: value).click
- end
- end
+ page.within('ul.dropdown-menu.dropdown-menu-right li') do
+ click_link(value)
end
end
+
+ # pajamas_sort_by is used to sort new pajamas dropdowns. When
+ # all of the dropdowns are converted, pajamas_sort_by can be renamed to sort_by
+ # https://gitlab.com/groups/gitlab-org/-/epics/7551
+ def pajamas_sort_by(value)
+ find('.filter-dropdown-container .gl-new-dropdown').click
+ find('.gl-new-dropdown-item', text: value).click
+ end
end
end
diff --git a/spec/support/helpers/features/source_editor_spec_helpers.rb b/spec/support/helpers/features/source_editor_spec_helpers.rb
index f7eb2a52507..e20ded60b01 100644
--- a/spec/support/helpers/features/source_editor_spec_helpers.rb
+++ b/spec/support/helpers/features/source_editor_spec_helpers.rb
@@ -2,24 +2,18 @@
# These helpers help you interact within the Source Editor (single-file editor, snippets, etc.).
#
-module Spec
- module Support
- module Helpers
- module Features
- module SourceEditorSpecHelpers
- include ActionView::Helpers::JavaScriptHelper
+module Features
+ module SourceEditorSpecHelpers
+ include ActionView::Helpers::JavaScriptHelper
- def editor_set_value(value)
- editor = find('.monaco-editor')
- uri = editor['data-uri']
- execute_script("localMonaco.getModel('#{uri}').setValue('#{escape_javascript(value)}')")
+ def editor_set_value(value)
+ editor = find('.monaco-editor')
+ uri = editor['data-uri']
+ execute_script("localMonaco.getModel('#{uri}').setValue('#{escape_javascript(value)}')")
- # We only check that the first line is present because when the content is long,
- # only a part of the text will be rendered in the DOM due to scrolling
- page.has_selector?('.gl-source-editor .view-lines', text: value.lines.first)
- end
- end
- end
+ # We only check that the first line is present because when the content is long,
+ # only a part of the text will be rendered in the DOM due to scrolling
+ page.has_selector?('.gl-source-editor .view-lines', text: value.lines.first)
end
end
end
diff --git a/spec/support/helpers/features/top_nav_spec_helpers.rb b/spec/support/helpers/features/top_nav_spec_helpers.rb
index de495eceabc..ecc05189fb4 100644
--- a/spec/support/helpers/features/top_nav_spec_helpers.rb
+++ b/spec/support/helpers/features/top_nav_spec_helpers.rb
@@ -2,37 +2,31 @@
# These helpers help you interact within the Source Editor (single-file editor, snippets, etc.).
#
-module Spec
- module Support
- module Helpers
- module Features
- module TopNavSpecHelpers
- def open_top_nav
- find('.js-top-nav-dropdown-toggle').click
- end
+module Features
+ module TopNavSpecHelpers
+ def open_top_nav
+ find('.js-top-nav-dropdown-toggle').click
+ end
- def within_top_nav
- within('.js-top-nav-dropdown-menu') do
- yield
- end
- end
+ def within_top_nav
+ within('.js-top-nav-dropdown-menu') do
+ yield
+ end
+ end
- def open_top_nav_projects
- open_top_nav
+ def open_top_nav_projects
+ open_top_nav
- within_top_nav do
- click_button('Projects')
- end
- end
+ within_top_nav do
+ click_button('Projects')
+ end
+ end
- def open_top_nav_groups
- open_top_nav
+ def open_top_nav_groups
+ open_top_nav
- within_top_nav do
- click_button('Groups')
- end
- end
- end
+ within_top_nav do
+ click_button('Groups')
end
end
end
diff --git a/spec/support/helpers/features/two_factor_helpers.rb b/spec/support/helpers/features/two_factor_helpers.rb
index 08a7665201f..e0469091d96 100644
--- a/spec/support/helpers/features/two_factor_helpers.rb
+++ b/spec/support/helpers/features/two_factor_helpers.rb
@@ -4,71 +4,86 @@
#
# Usage:
# describe "..." do
-# include Spec::Support::Helpers::Features::TwoFactorHelpers
+# include Features::TwoFactorHelpers
# ...
#
# manage_two_factor_authentication
#
-module Spec
- module Support
- module Helpers
- module Features
- module TwoFactorHelpers
- def manage_two_factor_authentication
- click_on 'Manage two-factor authentication'
- expect(page).to have_content("Set up new device")
- wait_for_requests
- end
-
- def register_u2f_device(u2f_device = nil, name: 'My device')
- u2f_device ||= FakeU2fDevice.new(page, name)
- u2f_device.respond_to_u2f_registration
- click_on 'Set up new device'
- expect(page).to have_content('Your device was successfully set up')
- fill_in "Pick a name", with: name
- click_on 'Register device'
- u2f_device
- end
+module Features
+ module TwoFactorHelpers
+ def copy_recovery_codes
+ click_on _('Copy codes')
+ click_on _('Proceed')
+ end
- # Registers webauthn device via UI
- def register_webauthn_device(webauthn_device = nil, name: 'My device')
- webauthn_device ||= FakeWebauthnDevice.new(page, name)
- webauthn_device.respond_to_webauthn_registration
- click_on 'Set up new device'
- expect(page).to have_content('Your device was successfully set up')
- fill_in 'Pick a name', with: name
- click_on 'Register device'
- webauthn_device
- end
+ def enable_two_factor_authentication
+ click_on _('Enable two-factor authentication')
+ expect(page).to have_content(_('Set up new device'))
+ wait_for_requests
+ end
- # Adds webauthn device directly via database
- def add_webauthn_device(app_id, user, fake_device = nil, name: 'My device')
- fake_device ||= WebAuthn::FakeClient.new(app_id)
+ def manage_two_factor_authentication
+ click_on 'Manage two-factor authentication'
+ expect(page).to have_content("Set up new device")
+ wait_for_requests
+ end
- options_for_create = WebAuthn::Credential.options_for_create(
- user: { id: user.webauthn_xid, name: user.username },
- authenticator_selection: { user_verification: 'discouraged' },
- rp: { name: 'GitLab' }
- )
- challenge = options_for_create.challenge
+ # Registers webauthn device via UI
+ # Remove after `webauthn_without_totp` feature flag is deleted.
+ def register_webauthn_device(webauthn_device = nil, name: 'My device')
+ webauthn_device ||= FakeWebauthnDevice.new(page, name)
+ webauthn_device.respond_to_webauthn_registration
+ click_on 'Set up new device'
+ expect(page).to have_content('Your device was successfully set up')
+ fill_in 'Pick a name', with: name
+ click_on 'Register device'
+ webauthn_device
+ end
- device_response = fake_device.create(challenge: challenge).to_json # rubocop:disable Rails/SaveBang
- device_registration_params = { device_response: device_response,
- name: name }
+ def webauthn_device_registration(webauthn_device: nil, name: 'My device', password: 'fake')
+ webauthn_device ||= FakeWebauthnDevice.new(page, name)
+ webauthn_device.respond_to_webauthn_registration
+ click_on _('Set up new device')
+ webauthn_fill_form_and_submit(name: name, password: password)
+ webauthn_device
+ end
- Webauthn::RegisterService.new(
- user, device_registration_params, challenge).execute
- FakeWebauthnDevice.new(page, name, fake_device)
- end
+ def webauthn_fill_form_and_submit(name: 'My device', password: 'fake')
+ content = _('Your device was successfully set up! Give it a name and register it with the GitLab server.')
+ expect(page).to have_content(content)
- def assert_fallback_ui(page)
- expect(page).to have_button('Verify code')
- expect(page).to have_css('#user_otp_attempt')
- expect(page).not_to have_link('Sign in via 2FA code')
- expect(page).not_to have_css("#js-authenticate-token-2fa")
- end
- end
+ within '[data-testid="create-webauthn"]' do
+ fill_in _('Device name'), with: name
+ fill_in _('Current password'), with: password
+ click_on _('Register device')
end
end
+
+ # Adds webauthn device directly via database
+ def add_webauthn_device(app_id, user, fake_device = nil, name: 'My device')
+ fake_device ||= WebAuthn::FakeClient.new(app_id)
+
+ options_for_create = WebAuthn::Credential.options_for_create(
+ user: { id: user.webauthn_xid, name: user.username },
+ authenticator_selection: { user_verification: 'discouraged' },
+ rp: { name: 'GitLab' }
+ )
+ challenge = options_for_create.challenge
+
+ device_response = fake_device.create(challenge: challenge).to_json # rubocop:disable Rails/SaveBang
+ device_registration_params = { device_response: device_response,
+ name: name }
+
+ Webauthn::RegisterService.new(
+ user, device_registration_params, challenge).execute
+ FakeWebauthnDevice.new(page, name, fake_device)
+ end
+
+ def assert_fallback_ui(page)
+ expect(page).to have_button('Verify code')
+ expect(page).to have_css('#user_otp_attempt')
+ expect(page).not_to have_link('Sign in via 2FA code')
+ expect(page).not_to have_css("#js-authenticate-token-2fa")
+ end
end
end
diff --git a/spec/support/helpers/features/web_ide_spec_helpers.rb b/spec/support/helpers/features/web_ide_spec_helpers.rb
index 4793c9479fe..c51116b55b2 100644
--- a/spec/support/helpers/features/web_ide_spec_helpers.rb
+++ b/spec/support/helpers/features/web_ide_spec_helpers.rb
@@ -4,119 +4,120 @@
#
# Usage:
# describe "..." do
-# include WebIdeSpecHelpers
+# include Features::WebIdeSpecHelpers
# ...
#
# ide_visit(project)
# ide_commit
-#
-module WebIdeSpecHelpers
- include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
-
- # Open the IDE from anywhere by first visiting the given project's page
- def ide_visit(project)
- visit project_path(project)
-
- ide_visit_from_link
- end
+module Features
+ module WebIdeSpecHelpers
+ include Features::SourceEditorSpecHelpers
- # Open the IDE from the current page by clicking the Web IDE link
- def ide_visit_from_link(link_sel = 'Web IDE')
- new_tab = window_opened_by { click_link(link_sel) }
+ # Open the IDE from anywhere by first visiting the given project's page
+ def ide_visit(project)
+ visit project_path(project)
- switch_to_window new_tab
- end
+ ide_visit_from_link
+ end
- def ide_tree_body
- page.find('.ide-tree-body')
- end
+ # Open the IDE from the current page by clicking the Web IDE link
+ def ide_visit_from_link(link_sel = 'Web IDE')
+ new_tab = window_opened_by { click_link(link_sel) }
- def ide_tree_actions
- page.find('.ide-tree-actions')
- end
+ switch_to_window new_tab
+ end
- def ide_tab_selector(mode)
- ".js-ide-#{mode}-mode"
- end
+ def ide_tree_body
+ page.find('.ide-tree-body')
+ end
- def ide_folder_row_open?(row)
- row.matches_css?('.folder.is-open')
- end
+ def ide_tree_actions
+ page.find('.ide-tree-actions')
+ end
- # Deletes a file by traversing to `path`
- # then clicking the 'Delete' action.
- #
- # - Throws an error if the file is not found
- def ide_delete_file(path)
- container = ide_traverse_to_file(path)
+ def ide_tab_selector(mode)
+ ".js-ide-#{mode}-mode"
+ end
- click_file_action(container, 'Delete')
- end
+ def ide_folder_row_open?(row)
+ row.matches_css?('.folder.is-open')
+ end
- # Opens parent directories until the file at `path`
- # is exposed.
- #
- # - Returns a reference to the file row at `path`
- # - Throws an error if the file is not found
- def ide_traverse_to_file(path)
- paths = path.split('/')
- container = nil
+ # Deletes a file by traversing to `path`
+ # then clicking the 'Delete' action.
+ #
+ # - Throws an error if the file is not found
+ def ide_delete_file(path)
+ container = ide_traverse_to_file(path)
- paths.each_with_index do |path, index|
- ide_open_file_row(container) if container
- container = find_file_child(container, path, level: index)
+ click_file_action(container, 'Delete')
end
- container
- end
+ # Opens parent directories until the file at `path`
+ # is exposed.
+ #
+ # - Returns a reference to the file row at `path`
+ # - Throws an error if the file is not found
+ def ide_traverse_to_file(path)
+ paths = path.split('/')
+ container = nil
+
+ paths.each_with_index do |path, index|
+ ide_open_file_row(container) if container
+ container = find_file_child(container, path, level: index)
+ end
+
+ container
+ end
- def ide_open_file_row(row)
- return if ide_folder_row_open?(row)
+ def ide_open_file_row(row)
+ return if ide_folder_row_open?(row)
- row.click
- end
+ row.click
+ end
- def ide_set_editor_value(value)
- editor_set_value(value)
- end
+ def ide_set_editor_value(value)
+ editor_set_value(value)
+ end
- def ide_commit_tab_selector
- ide_tab_selector('commit')
- end
+ def ide_commit_tab_selector
+ ide_tab_selector('commit')
+ end
- def ide_commit
- find(ide_commit_tab_selector).click
+ def ide_commit
+ find(ide_commit_tab_selector).click
- commit_to_current_branch
- end
+ commit_to_current_branch
+ end
- private
+ private
- def file_row_container(row)
- row ? row.find(:xpath, '..') : ide_tree_body
- end
+ def file_row_container(row)
+ row ? row.find(:xpath, '..') : ide_tree_body
+ end
- def find_file_child(row, name, level: nil)
- container = file_row_container(row)
- container.find(".file-row[data-level=\"#{level}\"]", text: name)
- end
+ def find_file_child(row, name, level: nil)
+ container = file_row_container(row)
+ container.find(".file-row[data-level=\"#{level}\"]", text: name)
+ end
- def click_file_action(row, text)
- row.hover
- dropdown = row.find('.ide-new-btn')
- dropdown.find('button').click
- dropdown.find('button', text: text).click
- end
+ def click_file_action(row, text)
+ row.hover
+ dropdown = row.find('.ide-new-btn')
+ dropdown.find('button').click
+ dropdown.find('button', text: text).click
+ end
- def commit_to_current_branch(option: 'Commit to master branch', message: '')
- within '.multi-file-commit-form' do
- fill_in('commit-message', with: message) if message
+ def commit_to_current_branch(option: 'Commit to master branch', message: '')
+ within '.multi-file-commit-form' do
+ fill_in('commit-message', with: message) if message
- choose(option)
+ choose(option)
- click_button('Commit')
+ click_button('Commit')
- wait_for_requests
+ wait_for_requests
+ end
end
end
end
diff --git a/spec/support/helpers/filtered_search_helpers.rb b/spec/support/helpers/filtered_search_helpers.rb
index 677cea7b804..60638eb06cd 100644
--- a/spec/support/helpers/filtered_search_helpers.rb
+++ b/spec/support/helpers/filtered_search_helpers.rb
@@ -69,12 +69,6 @@ module FilteredSearchHelpers
filtered_search.send_keys(:enter)
end
- def init_label_search
- filtered_search.set('label:=')
- # This ensures the dropdown is shown
- expect(find('#js-dropdown-label')).not_to have_css('.filter-dropdown-loading')
- end
-
def expect_filtered_search_input_empty
expect(find('.filtered-search').value).to eq('')
end
@@ -190,9 +184,9 @@ module FilteredSearchHelpers
##
# For use with gl-filtered-search
- def select_tokens(*args, submit: false)
+ def select_tokens(*args, submit: false, input_text: 'Search')
within '[data-testid="filtered-search-input"]' do
- find_field('Search').click
+ find_field(input_text).click
args.each do |token|
# Move mouse away to prevent invoking tooltips on usernames, which blocks the search input
@@ -219,7 +213,7 @@ module FilteredSearchHelpers
def submit_search_term(value)
click_filtered_search_bar
- send_keys(value, :enter)
+ send_keys(value, :enter, :enter)
end
def click_filtered_search_bar
@@ -230,6 +224,13 @@ module FilteredSearchHelpers
find('.gl-filtered-search-token-segment', text: value).click
end
+ def toggle_sort_direction
+ page.within('.vue-filtered-search-bar-container .sort-dropdown-container') do
+ page.find("button[title^='Sort direction']").click
+ wait_for_requests
+ end
+ end
+
def expect_visible_suggestions_list
expect(page).to have_css('.gl-filtered-search-suggestion-list')
end
diff --git a/spec/support/helpers/fixture_helpers.rb b/spec/support/helpers/fixture_helpers.rb
index 7b3b8ae5f7a..eafdecb2e3d 100644
--- a/spec/support/helpers/fixture_helpers.rb
+++ b/spec/support/helpers/fixture_helpers.rb
@@ -8,6 +8,6 @@ module FixtureHelpers
end
def expand_fixture_path(filename, dir: '')
- File.expand_path(Rails.root.join(dir, 'spec', 'fixtures', filename))
+ File.expand_path(rails_root_join(dir, 'spec', 'fixtures', filename))
end
end
diff --git a/spec/support/helpers/gitaly_setup.rb b/spec/support/helpers/gitaly_setup.rb
index 398a2a20f2f..7db9e0aaf09 100644
--- a/spec/support/helpers/gitaly_setup.rb
+++ b/spec/support/helpers/gitaly_setup.rb
@@ -10,7 +10,6 @@ require 'securerandom'
require 'socket'
require 'logger'
require 'fileutils'
-require 'bundler'
require_relative '../../../lib/gitlab/utils'
@@ -50,51 +49,18 @@ module GitalySetup
expand_path('.gitlab_shell_secret')
end
- def gemfile
- File.join(tmp_tests_gitaly_dir, 'ruby', 'Gemfile')
- end
-
- def gemfile_dir
- File.dirname(gemfile)
- end
-
def gitlab_shell_secret_file
File.join(tmp_tests_gitlab_shell_dir, '.gitlab_shell_secret')
end
def env
{
- 'GEM_PATH' => Gem.path.join(':'),
- 'BUNDLER_SETUP' => nil,
- 'BUNDLE_INSTALL_FLAGS' => nil,
- 'BUNDLE_IGNORE_CONFIG' => '1',
- 'BUNDLE_PATH' => bundle_path,
- 'BUNDLE_GEMFILE' => gemfile,
- 'BUNDLE_JOBS' => '4',
- 'BUNDLE_RETRY' => '3',
- 'RUBYOPT' => nil,
-
# Git hooks can't run during tests as the internal API is not running.
'GITALY_TESTING_NO_GIT_HOOKS' => "1",
'GITALY_TESTING_ENABLE_ALL_FEATURE_FLAGS' => "true"
}
end
- def bundle_path
- # Allow the user to override BUNDLE_PATH if they need to
- return ENV['GITALY_TEST_BUNDLE_PATH'] if ENV['GITALY_TEST_BUNDLE_PATH']
-
- if ENV['CI']
- expand_path('vendor/gitaly-ruby')
- else
- explicit_path = Bundler.configured_bundle_path.explicit_path
-
- return unless explicit_path
-
- expand_path(explicit_path)
- end
- end
-
def config_path(service)
case service
when :gitaly
@@ -125,10 +91,6 @@ module GitalySetup
system(env, *cmd, exception: true, chdir: tmp_tests_gitaly_dir)
end
- def install_gitaly_gems
- run_command(%W[make #{tmp_tests_gitaly_dir}/.ruby-bundle], env: env)
- end
-
def build_gitaly
run_command(%w[make all WITH_BUNDLED_GIT=YesPlease], env: env.merge('GIT_VERSION' => nil))
end
@@ -188,20 +150,6 @@ module GitalySetup
end
end
- def check_gitaly_config!
- LOGGER.debug "Checking gitaly-ruby Gemfile...\n"
-
- unless File.exist?(gemfile)
- message = "#{gemfile} does not exist."
- message += "\n\nThis might have happened if the CI artifacts for this build were destroyed." if ENV['CI']
- abort message
- end
-
- LOGGER.debug "Checking gitaly-ruby bundle...\n"
- out = ENV['CI'] ? $stdout : '/dev/null'
- abort 'bundle check failed' unless system(env, 'bundle', 'check', out: out, chdir: gemfile_dir)
- end
-
def connect_proc(toml)
# This code needs to work in an environment where we cannot use bundler,
# so we cannot easily use the toml-rb gem. This ad-hoc parser should be
@@ -343,8 +291,6 @@ module GitalySetup
end
def spawn_gitaly(toml = nil)
- check_gitaly_config!
-
pids = []
if toml
diff --git a/spec/support/google_api/cloud_platform_helpers.rb b/spec/support/helpers/google_api/cloud_platform_helpers.rb
index b9752577c76..3d4ffe88da9 100644
--- a/spec/support/google_api/cloud_platform_helpers.rb
+++ b/spec/support/helpers/google_api/cloud_platform_helpers.rb
@@ -88,66 +88,68 @@ module GoogleApi
# rubocop:disable Metrics/PerceivedComplexity
def cloud_platform_cluster_body(options)
{
- "name": options[:name] || 'string',
- "description": options[:description] || 'string',
- "initialNodeCount": options[:initialNodeCount] || 'number',
- "masterAuth": {
- "username": options[:username] || 'string',
- "password": options[:password] || 'string',
- "clusterCaCertificate": options[:clusterCaCertificate] || load_sample_cert,
- "clientCertificate": options[:clientCertificate] || 'string',
- "clientKey": options[:clientKey] || 'string'
+ name: options[:name] || 'string',
+ description: options[:description] || 'string',
+ initialNodeCount: options[:initialNodeCount] || 'number',
+ masterAuth: {
+ username: options[:username] || 'string',
+ password: options[:password] || 'string',
+ clusterCaCertificate: options[:clusterCaCertificate] || load_sample_cert,
+ clientCertificate: options[:clientCertificate] || 'string',
+ clientKey: options[:clientKey] || 'string'
},
- "loggingService": options[:loggingService] || 'string',
- "monitoringService": options[:monitoringService] || 'string',
- "network": options[:network] || 'string',
- "clusterIpv4Cidr": options[:clusterIpv4Cidr] || 'string',
- "subnetwork": options[:subnetwork] || 'string',
- "enableKubernetesAlpha": options[:enableKubernetesAlpha] || 'boolean',
- "labelFingerprint": options[:labelFingerprint] || 'string',
- "selfLink": options[:selfLink] || 'string',
- "zone": options[:zone] || 'string',
- "endpoint": options[:endpoint] || 'string',
- "initialClusterVersion": options[:initialClusterVersion] || 'string',
- "currentMasterVersion": options[:currentMasterVersion] || 'string',
- "currentNodeVersion": options[:currentNodeVersion] || 'string',
- "createTime": options[:createTime] || 'string',
- "status": options[:status] || 'RUNNING',
- "statusMessage": options[:statusMessage] || 'string',
- "nodeIpv4CidrSize": options[:nodeIpv4CidrSize] || 'number',
- "servicesIpv4Cidr": options[:servicesIpv4Cidr] || 'string',
- "currentNodeCount": options[:currentNodeCount] || 'number',
- "expireTime": options[:expireTime] || 'string'
+ loggingService: options[:loggingService] || 'string',
+ monitoringService: options[:monitoringService] || 'string',
+ network: options[:network] || 'string',
+ clusterIpv4Cidr: options[:clusterIpv4Cidr] || 'string',
+ subnetwork: options[:subnetwork] || 'string',
+ enableKubernetesAlpha: options[:enableKubernetesAlpha] || 'boolean',
+ labelFingerprint: options[:labelFingerprint] || 'string',
+ selfLink: options[:selfLink] || 'string',
+ zone: options[:zone] || 'string',
+ endpoint: options[:endpoint] || 'string',
+ initialClusterVersion: options[:initialClusterVersion] || 'string',
+ currentMasterVersion: options[:currentMasterVersion] || 'string',
+ currentNodeVersion: options[:currentNodeVersion] || 'string',
+ createTime: options[:createTime] || 'string',
+ status: options[:status] || 'RUNNING',
+ statusMessage: options[:statusMessage] || 'string',
+ nodeIpv4CidrSize: options[:nodeIpv4CidrSize] || 'number',
+ servicesIpv4Cidr: options[:servicesIpv4Cidr] || 'string',
+ currentNodeCount: options[:currentNodeCount] || 'number',
+ expireTime: options[:expireTime] || 'string'
}
end
+ # rubocop:enable Metrics/CyclomaticComplexity
+ # rubocop:enable Metrics/PerceivedComplexity
def cloud_platform_operation_body(options)
{
- "name": options[:name] || 'operation-1234567891234-1234567',
- "zone": options[:zone] || 'us-central1-a',
- "operationType": options[:operationType] || 'CREATE_CLUSTER',
- "status": options[:status] || 'PENDING',
- "detail": options[:detail] || 'detail',
- "statusMessage": options[:statusMessage] || '',
- "selfLink": options[:selfLink] || 'https://container.googleapis.com/v1/projects/123456789101/zones/us-central1-a/operations/operation-1234567891234-1234567',
- "targetLink": options[:targetLink] || 'https://container.googleapis.com/v1/projects/123456789101/zones/us-central1-a/clusters/test-cluster',
- "startTime": options[:startTime] || '2017-09-13T16:49:13.055601589Z',
- "endTime": options[:endTime] || ''
+ name: options[:name] || 'operation-1234567891234-1234567',
+ zone: options[:zone] || 'us-central1-a',
+ operationType: options[:operationType] || 'CREATE_CLUSTER',
+ status: options[:status] || 'PENDING',
+ detail: options[:detail] || 'detail',
+ statusMessage: options[:statusMessage] || '',
+ selfLink: options[:selfLink] || 'https://container.googleapis.com/v1/projects/123456789101/zones/us-central1-a/operations/operation-1234567891234-1234567',
+ targetLink: options[:targetLink] || 'https://container.googleapis.com/v1/projects/123456789101/zones/us-central1-a/clusters/test-cluster',
+ startTime: options[:startTime] || '2017-09-13T16:49:13.055601589Z',
+ endTime: options[:endTime] || ''
}
end
def cloud_platform_projects_body(options)
{
- "projects": [
+ projects: [
{
- "projectNumber": options[:project_number] || "1234",
- "projectId": options[:project_id] || "test-project-1234",
- "lifecycleState": "ACTIVE",
- "name": options[:name] || "test-project",
- "createTime": "2017-12-16T01:48:29.129Z",
- "parent": {
- "type": "organization",
- "id": "12345"
+ projectNumber: options[:project_number] || "1234",
+ projectId: options[:project_id] || "test-project-1234",
+ lifecycleState: "ACTIVE",
+ name: options[:name] || "test-project",
+ createTime: "2017-12-16T01:48:29.129Z",
+ parent: {
+ type: "organization",
+ id: "12345"
}
}
]
@@ -156,10 +158,10 @@ module GoogleApi
def cloud_platform_projects_billing_info_body(project_id, billing_enabled)
{
- "name": "projects/#{project_id}/billingInfo",
- "projectId": project_id.to_s,
- "billingAccountName": "account-name",
- "billingEnabled": billing_enabled
+ name: "projects/#{project_id}/billingInfo",
+ projectId: project_id.to_s,
+ billingAccountName: "account-name",
+ billingEnabled: billing_enabled
}
end
end
diff --git a/spec/support/graphql/arguments.rb b/spec/support/helpers/graphql/arguments.rb
index 478a460a0f6..478a460a0f6 100644
--- a/spec/support/graphql/arguments.rb
+++ b/spec/support/helpers/graphql/arguments.rb
diff --git a/spec/support/graphql/fake_query_type.rb b/spec/support/helpers/graphql/fake_query_type.rb
index 18cf2cf3e82..bdf30908532 100644
--- a/spec/support/graphql/fake_query_type.rb
+++ b/spec/support/helpers/graphql/fake_query_type.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
require 'graphql'
module Graphql
diff --git a/spec/support/graphql/fake_tracer.rb b/spec/support/helpers/graphql/fake_tracer.rb
index 58688c9abd0..58688c9abd0 100644
--- a/spec/support/graphql/fake_tracer.rb
+++ b/spec/support/helpers/graphql/fake_tracer.rb
diff --git a/spec/support/graphql/field_inspection.rb b/spec/support/helpers/graphql/field_inspection.rb
index 8730f82b893..8730f82b893 100644
--- a/spec/support/graphql/field_inspection.rb
+++ b/spec/support/helpers/graphql/field_inspection.rb
diff --git a/spec/support/graphql/field_selection.rb b/spec/support/helpers/graphql/field_selection.rb
index 432340cfdb5..432340cfdb5 100644
--- a/spec/support/graphql/field_selection.rb
+++ b/spec/support/helpers/graphql/field_selection.rb
diff --git a/spec/support/graphql/resolver_factories.rb b/spec/support/helpers/graphql/resolver_factories.rb
index 76df4b58943..76df4b58943 100644
--- a/spec/support/graphql/resolver_factories.rb
+++ b/spec/support/helpers/graphql/resolver_factories.rb
diff --git a/spec/support/graphql/subscriptions/action_cable/mock_action_cable.rb b/spec/support/helpers/graphql/subscriptions/action_cable/mock_action_cable.rb
index 5467564a79e..2ccc62a8729 100644
--- a/spec/support/graphql/subscriptions/action_cable/mock_action_cable.rb
+++ b/spec/support/helpers/graphql/subscriptions/action_cable/mock_action_cable.rb
@@ -13,7 +13,7 @@ module Graphql
attr_reader :mock_broadcasted_messages
- def stream_from(stream_name, coder: nil, &block)
+ def stream_from(stream_name, coder: nil, &block) # rubocop:disable Lint/UnusedMethodArgument
# Rails uses `coder`, we don't
block ||= ->(msg) { @mock_broadcasted_messages << msg }
MockActionCable.mock_stream_for(stream_name).add_mock_channel(self, block)
@@ -30,7 +30,7 @@ module Graphql
end
def mock_broadcast(message)
- @mock_channels.each do |channel, handler|
+ @mock_channels.each_value do |handler|
handler && handler.call(message)
end
end
diff --git a/spec/support/graphql/subscriptions/action_cable/mock_gitlab_schema.rb b/spec/support/helpers/graphql/subscriptions/action_cable/mock_gitlab_schema.rb
index cd5d78cc78b..cd5d78cc78b 100644
--- a/spec/support/graphql/subscriptions/action_cable/mock_gitlab_schema.rb
+++ b/spec/support/helpers/graphql/subscriptions/action_cable/mock_gitlab_schema.rb
diff --git a/spec/support/graphql/subscriptions/notes/helper.rb b/spec/support/helpers/graphql/subscriptions/notes/helper.rb
index 9a552f9879e..9a552f9879e 100644
--- a/spec/support/graphql/subscriptions/notes/helper.rb
+++ b/spec/support/helpers/graphql/subscriptions/notes/helper.rb
diff --git a/spec/support/graphql/var.rb b/spec/support/helpers/graphql/var.rb
index 4f2c774e898..4f2c774e898 100644
--- a/spec/support/graphql/var.rb
+++ b/spec/support/helpers/graphql/var.rb
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index 2176a477371..a9ad853b028 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -89,13 +89,16 @@ module GraphqlHelpers
# All mutations accept a single `:input` argument. Wrap arguments here.
args = { input: args } if resolver_class <= ::Mutations::BaseMutation && !args.key?(:input)
- resolve_field(field, obj,
- args: args,
- ctx: ctx,
- schema: schema,
- object_type: resolver_parent,
- extras: { parent: parent, lookahead: lookahead },
- arg_style: arg_style)
+ resolve_field(
+ field,
+ obj,
+ args: args,
+ ctx: ctx,
+ schema: schema,
+ object_type: resolver_parent,
+ extras: { parent: parent, lookahead: lookahead },
+ arg_style: arg_style
+ )
end
# Resolve the value of a field on an object.
@@ -310,7 +313,7 @@ module GraphqlHelpers
"{ #{q} }"
end
- def graphql_mutation(name, input, fields = nil, &block)
+ def graphql_mutation(name, input, fields = nil, excluded = [], &block)
raise ArgumentError, 'Please pass either `fields` parameter or a block to `#graphql_mutation`, but not both.' if fields.present? && block
name = name.graphql_name if name.respond_to?(:graphql_name)
@@ -319,7 +322,7 @@ module GraphqlHelpers
mutation_field = GitlabSchema.mutation.fields[mutation_name]
fields = yield if block
- fields ||= all_graphql_fields_for(mutation_field.type.to_type_signature)
+ fields ||= all_graphql_fields_for(mutation_field.type.to_type_signature, excluded: excluded)
query = <<~MUTATION
mutation(#{input_variable_name}: #{mutation_field.arguments['input'].type.to_type_signature}) {
@@ -513,20 +516,23 @@ module GraphqlHelpers
end
def post_graphql_mutation(mutation, current_user: nil, token: {})
- post_graphql(mutation.query,
- current_user: current_user,
- variables: mutation.variables,
- token: token)
+ post_graphql(
+ mutation.query,
+ current_user: current_user,
+ variables: mutation.variables,
+ token: token
+ )
end
def post_graphql_mutation_with_uploads(mutation, current_user: nil)
file_paths = file_paths_in_mutation(mutation)
params = mutation_to_apollo_uploads_param(mutation, files: file_paths)
- workhorse_post_with_file(api('/', current_user, version: 'graphql'),
- params: params,
- file_key: '1'
- )
+ workhorse_post_with_file(
+ api('/', current_user, version: 'graphql'),
+ params: params,
+ file_key: '1'
+ )
end
def file_paths_in_mutation(mutation)
@@ -633,7 +639,11 @@ module GraphqlHelpers
end
def expect_graphql_errors_to_be_empty
- expect(flattened_errors).to be_empty
+ # TODO: using eq([]) instead of be_empty makes it print out the full error message including the
+ # raisedAt key which contains the full stacktrace. This is necessary to know where the
+ # unexpected error occurred during tests.
+ # This or an equivalent fix should be added in a separate MR on master.
+ expect(flattened_errors).to eq([])
end
# Helps migrate to the new GraphQL interpreter,
diff --git a/spec/support/http_io/http_io_helpers.rb b/spec/support/helpers/http_io_helpers.rb
index 0193db81fa9..638d780cdc2 100644
--- a/spec/support/http_io/http_io_helpers.rb
+++ b/spec/support/helpers/http_io_helpers.rb
@@ -31,9 +31,7 @@ module HttpIOHelpers
def remote_url_response_headers(response_status, from, to, size)
{ 'Content-Type' => 'text/plain' }.tap do |headers|
- if response_status == 206
- headers.merge('Content-Range' => "bytes #{from}-#{to}/#{size}")
- end
+ headers.merge('Content-Range' => "bytes #{from}-#{to}/#{size}") if response_status == 206
end
end
diff --git a/spec/support/helpers/jira_integration_helpers.rb b/spec/support/helpers/jira_integration_helpers.rb
index 66940314589..098f2968b0b 100644
--- a/spec/support/helpers/jira_integration_helpers.rb
+++ b/spec/support/helpers/jira_integration_helpers.rb
@@ -11,7 +11,7 @@ module JiraIntegrationHelpers
jira_issue_transition_id = '1'
jira_tracker.update!(
- url: url, username: username, password: password,
+ url: url, username: username, password: password, jira_auth_type: 0,
jira_issue_transition_id: jira_issue_transition_id, active: true
)
end
diff --git a/spec/support/helpers/keyset_pagination_helpers.rb b/spec/support/helpers/keyset_pagination_helpers.rb
new file mode 100644
index 00000000000..4a476c47fda
--- /dev/null
+++ b/spec/support/helpers/keyset_pagination_helpers.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module KeysetPaginationHelpers
+ def pagination_links(response)
+ link = response.headers['LINK']
+ return unless link
+
+ link.split(',').filter_map do |link|
+ match = link.match(/<(?<url>.*)>; rel="(?<rel>\w+)"/)
+ next unless match
+
+ { url: match[:url], rel: match[:rel] }
+ end
+ end
+
+ def pagination_params_from_next_url(response)
+ next_link = pagination_links(response).find { |link| link[:rel] == 'next' }
+ next_url = next_link&.fetch(:url)
+ return unless next_url
+
+ Rack::Utils.parse_query(URI.parse(next_url).query)
+ end
+end
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 5fde80e6dc9..67315b9d81e 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -51,7 +51,7 @@ module LoginHelpers
def gitlab_enable_admin_mode_sign_in(user)
visit new_admin_session_path
fill_in 'user_password', with: user.password
- click_button 'Enter Admin Mode'
+ click_button 'Enter admin mode'
wait_for_requests
end
@@ -82,7 +82,7 @@ module LoginHelpers
open_top_nav
within_top_nav do
- click_on 'Leave Admin Mode'
+ click_on 'Leave admin mode'
end
end
@@ -94,8 +94,8 @@ module LoginHelpers
# remember - Whether or not to check "Remember me" (default: false)
# two_factor_auth - If two-factor authentication is enabled (default: false)
# password - password to attempt to login with (default: user.password)
- def gitlab_sign_in_with(user, remember: false, two_factor_auth: false, password: nil)
- visit new_user_session_path
+ def gitlab_sign_in_with(user, remember: false, two_factor_auth: false, password: nil, visit: true)
+ visit new_user_session_path if visit
fill_in "user_login", with: user.email
fill_in "user_password", with: (password || user.password)
@@ -114,9 +114,9 @@ module LoginHelpers
def login_via(provider, user, uid, remember_me: false, additional_info: {})
mock_auth_hash(provider, uid, user.email, additional_info: additional_info)
visit new_user_session_path
- expect(page).to have_content('Sign in with')
+ expect(page).to have_css('.omniauth-container')
- check 'remember_me' if remember_me
+ check 'remember_me_omniauth' if remember_me
click_button "oauth-login-#{provider}"
end
@@ -139,11 +139,6 @@ module LoginHelpers
click_link_or_button "oauth-login-#{provider}"
end
- def fake_successful_u2f_authentication
- allow(U2fRegistration).to receive(:authenticate).and_return(true)
- FakeU2fDevice.new(page, nil).fake_u2f_authentication
- end
-
def fake_successful_webauthn_authentication
allow_any_instance_of(Webauthn::AuthenticateService).to receive(:execute).and_return(true)
FakeWebauthnDevice.new(page, nil).fake_webauthn_authentication
@@ -218,6 +213,15 @@ module LoginHelpers
config
end
+ def prepare_provider_route(provider_name)
+ routes = Rails.application.routes
+ routes.disable_clear_and_finalize = true
+ routes.formatter.clear
+ routes.draw do
+ post "/users/auth/#{provider_name}" => "omniauth_callbacks##{provider_name}"
+ end
+ end
+
def stub_omniauth_provider(provider, context: Rails.application)
env = env_from_context(context)
diff --git a/spec/support/helpers/markdown_feature.rb b/spec/support/helpers/markdown_feature.rb
index 0cb2863dc2c..5d9ef557ae6 100644
--- a/spec/support/helpers/markdown_feature.rb
+++ b/spec/support/helpers/markdown_feature.rb
@@ -48,6 +48,10 @@ class MarkdownFeature
@issue ||= create(:issue, project: project)
end
+ def work_item
+ @issue ||= create(:work_item, project: project)
+ end
+
def merge_request
@merge_request ||= create(:merge_request, :simple, source_project: project)
end
@@ -106,6 +110,10 @@ class MarkdownFeature
@xissue ||= create(:issue, project: xproject)
end
+ def xwork_item
+ @xwork_item ||= create(:work_item, project: xproject)
+ end
+
def xmerge_request
@xmerge_request ||= create(:merge_request, :simple, source_project: xproject)
end
diff --git a/spec/support/helpers/metrics_dashboard_helpers.rb b/spec/support/helpers/metrics_dashboard_helpers.rb
index a384e44f428..417baeda33a 100644
--- a/spec/support/helpers/metrics_dashboard_helpers.rb
+++ b/spec/support/helpers/metrics_dashboard_helpers.rb
@@ -49,8 +49,4 @@ module MetricsDashboardHelpers
def business_metric_title
Enums::PrometheusMetric.group_details[:business][:group_title]
end
-
- def self_monitoring_dashboard_path
- Metrics::Dashboard::SelfMonitoringDashboardService::DASHBOARD_PATH
- end
end
diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb
index 6fc5904fc83..1b8c3388051 100644
--- a/spec/support/helpers/migrations_helpers.rb
+++ b/spec/support/helpers/migrations_helpers.rb
@@ -92,7 +92,7 @@ module MigrationsHelpers
end
def reset_column_information(klass)
- klass.reset_column_information
+ klass.reset_column_information if klass.instance_variable_get(:@table_name)
end
# In some migration tests, we're using factories to create records,
diff --git a/spec/support/migrations_helpers/cluster_helpers.rb b/spec/support/helpers/migrations_helpers/cluster_helpers.rb
index 03104e22bcf..03104e22bcf 100644
--- a/spec/support/migrations_helpers/cluster_helpers.rb
+++ b/spec/support/helpers/migrations_helpers/cluster_helpers.rb
diff --git a/spec/support/helpers/migrations_helpers/namespaces_helper.rb b/spec/support/helpers/migrations_helpers/namespaces_helper.rb
new file mode 100644
index 00000000000..d9a4e0d1731
--- /dev/null
+++ b/spec/support/helpers/migrations_helpers/namespaces_helper.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module MigrationHelpers
+ module NamespacesHelpers
+ def create_namespace(name, visibility, options = {})
+ table(:namespaces).create!(
+ {
+ name: name,
+ path: name,
+ type: 'Group',
+ visibility_level: visibility
+ }.merge(options))
+ end
+ end
+end
diff --git a/spec/support/migrations_helpers/schema_version_finder.rb b/spec/support/helpers/migrations_helpers/schema_version_finder.rb
index b677db7ea26..69469959ce5 100644
--- a/spec/support/migrations_helpers/schema_version_finder.rb
+++ b/spec/support/helpers/migrations_helpers/schema_version_finder.rb
@@ -7,7 +7,8 @@
# to find and use schema prior to specified one.
#
# @example
-# RSpec.describe CleanupThings, :migration, schema: MigrationHelpers::SchemaVersionFinder.migration_prior(AddNotNullConstraint) do ...
+# RSpec.describe CleanupThings, :migration,
+# schema: MigrationHelpers::SchemaVersionFinder.migration_prior(AddNotNullConstraint) do ...
#
# SchemaVersionFinder returns schema version prior to the one specified, which allows to then add
# invalid records to the database, which in return allows to properly test data migration.
diff --git a/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb b/spec/support/helpers/migrations_helpers/vulnerabilities_findings_helper.rb
index 9a5313c3fa4..1f8505978f5 100644
--- a/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
+++ b/spec/support/helpers/migrations_helpers/vulnerabilities_findings_helper.rb
@@ -7,7 +7,7 @@ module MigrationHelpers
{
project_fingerprint: SecureRandom.hex(20),
- location_fingerprint: Digest::SHA1.hexdigest(SecureRandom.hex(10)),
+ location_fingerprint: Digest::SHA1.hexdigest(SecureRandom.hex(10)), # rubocop:disable Fips/SHA1
uuid: uuid,
name: "Vulnerability Finding #{uuid}",
metadata_version: '1.3',
@@ -20,7 +20,7 @@ module MigrationHelpers
"description" => "The cipher does not provide data integrity update 1",
"message" => "The cipher does not provide data integrity",
"cve" => "818bf5dacb291e15d9e6dc3c5ac32178:CIPHER",
- "solution" => "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
+ "solution" => "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.", # rubocop:disable Layout/LineLength
"location" => {
"file" => "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line" => 29,
@@ -49,8 +49,8 @@ module MigrationHelpers
"body" => nil,
"headers" => [
{
- "name" => "Accept",
- "value" => "*/*"
+ "name" => "Accept",
+ "value" => "*/*"
}
]
},
@@ -60,8 +60,8 @@ module MigrationHelpers
"body" => nil,
"headers" => [
{
- "name" => "Content-Length",
- "value" => "0"
+ "name" => "Content-Length",
+ "value" => "0"
}
]
},
diff --git a/spec/support/models/ci/partitioning_testing/cascade_check.rb b/spec/support/helpers/models/ci/partitioning_testing/cascade_check.rb
index bcfc9675476..81c2d2cb225 100644
--- a/spec/support/models/ci/partitioning_testing/cascade_check.rb
+++ b/spec/support/helpers/models/ci/partitioning_testing/cascade_check.rb
@@ -19,7 +19,7 @@ module PartitioningTesting
class_methods do
# Allowing partition callback to be used with BulkInsertSafe
def _bulk_insert_callback_allowed?(name, args)
- super || args.first == :after && args.second == :check_partition_cascade_value
+ super || (args.first == :after && args.second == :check_partition_cascade_value)
end
end
end
diff --git a/spec/support/models/ci/partitioning_testing/partition_identifiers.rb b/spec/support/helpers/models/ci/partitioning_testing/partition_identifiers.rb
index aa091095fb6..aa091095fb6 100644
--- a/spec/support/models/ci/partitioning_testing/partition_identifiers.rb
+++ b/spec/support/helpers/models/ci/partitioning_testing/partition_identifiers.rb
diff --git a/spec/support/models/ci/partitioning_testing/rspec_hooks.rb b/spec/support/helpers/models/ci/partitioning_testing/rspec_hooks.rb
index 39b15ba8721..3f0a2bb7f3b 100644
--- a/spec/support/models/ci/partitioning_testing/rspec_hooks.rb
+++ b/spec/support/helpers/models/ci/partitioning_testing/rspec_hooks.rb
@@ -4,6 +4,10 @@ RSpec.configure do |config|
config.include Ci::PartitioningTesting::PartitionIdentifiers
config.around(:each, :ci_partitionable) do |example|
+ unless Ci::Build.table_name.to_s.starts_with?('p_')
+ skip 'Skipping partitioning tests until `ci_builds` is partitioned'
+ end
+
Ci::PartitioningTesting::SchemaHelpers.with_routing_tables do
example.run
end
diff --git a/spec/support/models/ci/partitioning_testing/schema_helpers.rb b/spec/support/helpers/models/ci/partitioning_testing/schema_helpers.rb
index 4107bbcb976..4107bbcb976 100644
--- a/spec/support/models/ci/partitioning_testing/schema_helpers.rb
+++ b/spec/support/helpers/models/ci/partitioning_testing/schema_helpers.rb
diff --git a/spec/support/models/merge_request_without_merge_request_diff.rb b/spec/support/helpers/models/merge_request_without_merge_request_diff.rb
index 5cdf1feb7a5..e9f97a2c95a 100644
--- a/spec/support/models/merge_request_without_merge_request_diff.rb
+++ b/spec/support/helpers/models/merge_request_without_merge_request_diff.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class MergeRequestWithoutMergeRequestDiff < ::MergeRequest
+class MergeRequestWithoutMergeRequestDiff < ::MergeRequest # rubocop:disable Gitlab/NamespacedClass
self.inheritance_column = :_type_disabled
def ensure_merge_request_diff; end
diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb
index 48c6e590e1b..9a6af5fb8ae 100644
--- a/spec/support/helpers/navbar_structure_helper.rb
+++ b/spec/support/helpers/navbar_structure_helper.rb
@@ -73,7 +73,7 @@ module NavbarStructureHelper
insert_after_sub_nav_item(
_('Package Registry'),
within: _('Packages and registries'),
- new_sub_nav_item_name: _('Infrastructure Registry')
+ new_sub_nav_item_name: _('Terraform modules')
)
end
@@ -100,12 +100,28 @@ module NavbarStructureHelper
def insert_infrastructure_google_cloud_nav
insert_after_sub_nav_item(
- _('Terraform'),
+ s_('Terraform|Terraform states'),
within: _('Infrastructure'),
new_sub_nav_item_name: _('Google Cloud')
)
end
+ def insert_infrastructure_aws_nav
+ insert_after_sub_nav_item(
+ _('Google Cloud'),
+ within: _('Infrastructure'),
+ new_sub_nav_item_name: _('AWS')
+ )
+ end
+
+ def insert_model_experiments_nav(within)
+ insert_after_sub_nav_item(
+ within,
+ within: _('Packages and registries'),
+ new_sub_nav_item_name: _('Model experiments')
+ )
+ end
+
def project_analytics_sub_nav_item
[
_('Value stream'),
diff --git a/spec/support/helpers/note_interaction_helpers.rb b/spec/support/helpers/note_interaction_helpers.rb
index fa2705a64fa..40f1f6fe6f3 100644
--- a/spec/support/helpers/note_interaction_helpers.rb
+++ b/spec/support/helpers/note_interaction_helpers.rb
@@ -7,6 +7,6 @@ module NoteInteractionHelpers
note_element = find_by_scrolling("#note_#{note.id}")
note_element.find('.more-actions-toggle').click
- note_element.find('.more-actions .dropdown-menu li', match: :first)
+ note_element.find('.more-actions li', match: :first)
end
end
diff --git a/spec/support/helpers/project_template_test_helper.rb b/spec/support/helpers/project_template_test_helper.rb
index bedbb8601e8..35e40faeea7 100644
--- a/spec/support/helpers/project_template_test_helper.rb
+++ b/spec/support/helpers/project_template_test_helper.rb
@@ -4,12 +4,12 @@ module ProjectTemplateTestHelper
def all_templates
%w[
rails spring express iosswift dotnetcore android
- gomicro gatsby hugo jekyll plainhtml gitbook
+ gomicro gatsby hugo jekyll plainhtml
hexo middleman gitpod_spring_petclinic nfhugo
nfjekyll nfplainhtml nfgitbook nfhexo salesforcedx
serverless_framework tencent_serverless_framework
jsonnet cluster_management kotlin_native_linux
- pelican bridgetown typo3_distribution
+ pelican bridgetown typo3_distribution laravel
]
end
end
diff --git a/spec/support/prometheus/metric_builders.rb b/spec/support/helpers/prometheus/metric_builders.rb
index 512e32a44d0..53329ee8dce 100644
--- a/spec/support/prometheus/metric_builders.rb
+++ b/spec/support/helpers/prometheus/metric_builders.rb
@@ -16,9 +16,9 @@ module Prometheus
def simple_metrics(added_metric_name: 'metric_a')
[
- simple_metric(required_metrics: %W(#{added_metric_name} metric_b), queries: simple_queries),
+ simple_metric(required_metrics: %W[#{added_metric_name} metric_b], queries: simple_queries),
simple_metric(required_metrics: [added_metric_name], queries: [simple_query('empty')]),
- simple_metric(required_metrics: %w{metric_c})
+ simple_metric(required_metrics: %w[metric_c])
]
end
diff --git a/spec/support/helpers/query_recorder.rb b/spec/support/helpers/query_recorder.rb
index 5be9ba9ae1e..e8fa73a1b95 100644
--- a/spec/support/helpers/query_recorder.rb
+++ b/spec/support/helpers/query_recorder.rb
@@ -102,6 +102,10 @@ module ActiveRecord
@occurrences ||= @log.group_by(&:to_s).transform_values(&:count)
end
+ def occurrences_starting_with(str)
+ occurrences.select { |query, _count| query.starts_with?(str) }
+ end
+
def ignorable?(values)
return true if skip_schema_queries && values[:name]&.include?("SCHEMA")
return true if values[:name]&.match(/License Load/)
diff --git a/spec/support/redis/redis_helpers.rb b/spec/support/helpers/redis_helpers.rb
index 2c5ceb2f09e..2c5ceb2f09e 100644
--- a/spec/support/redis/redis_helpers.rb
+++ b/spec/support/helpers/redis_helpers.rb
diff --git a/spec/support/helpers/repo_helpers.rb b/spec/support/helpers/repo_helpers.rb
index 9f37cf61cc9..45467fb7099 100644
--- a/spec/support/helpers/repo_helpers.rb
+++ b/spec/support/helpers/repo_helpers.rb
@@ -41,6 +41,7 @@ eos
line_code: '2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14',
line_code_path: 'files/ruby/popen.rb',
del_line_code: '2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13',
+ referenced_by: [],
message: <<eos
Change some files
Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
@@ -56,6 +57,7 @@ eos
author_full_name: "Sytse Sijbrandij",
author_email: "sytse@gitlab.com",
files_changed_count: 1,
+ referenced_by: [],
message: <<eos
Add directory structure for tree_helper spec
@@ -74,6 +76,7 @@ eos
sha: "913c66a37b4a45b9769037c55c2d238bd0942d2e",
author_full_name: "Dmitriy Zaporozhets",
author_email: "dmitriy.zaporozhets@gmail.com",
+ referenced_by: [],
message: <<eos
Files, encoding and much more
Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
@@ -89,6 +92,7 @@ eos
author_email: "dmitriy.zaporozhets@gmail.com",
old_blob_id: '33f3729a45c02fc67d00adb1b8bca394b0e761d9',
new_blob_id: '2f63565e7aac07bcdadb654e253078b727143ec4',
+ referenced_by: [],
message: <<eos
Modified image
Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
diff --git a/spec/support/helpers/search_helpers.rb b/spec/support/helpers/search_helpers.rb
index eab30be9243..75853371c0f 100644
--- a/spec/support/helpers/search_helpers.rb
+++ b/spec/support/helpers/search_helpers.rb
@@ -2,9 +2,6 @@
module SearchHelpers
def fill_in_search(text)
- # Once the `new_header_search` feature flag has been removed
- # We can remove the `.search-input-wrap` selector
- # https://gitlab.com/gitlab-org/gitlab/-/issues/339348
page.within('.header-search-new') do
find('#search').click
fill_in 'search', with: text
@@ -14,10 +11,7 @@ module SearchHelpers
end
def submit_search(query)
- # Once the `new_header_search` feature flag has been removed
- # We can remove the `.search-form` selector
- # https://gitlab.com/gitlab-org/gitlab/-/issues/339348
- page.within('.header-search, .search-form, .search-page-form') do
+ page.within('.header-search, .search-page-form') do
field = find_field('search')
field.click
field.fill_in(with: query)
diff --git a/spec/support/helpers/session_helpers.rb b/spec/support/helpers/session_helpers.rb
index 394a401afca..5554695a27d 100644
--- a/spec/support/helpers/session_helpers.rb
+++ b/spec/support/helpers/session_helpers.rb
@@ -39,4 +39,10 @@ module SessionHelpers
def get_ttl(key)
Gitlab::Redis::Sessions.with { |redis| redis.ttl(key) }
end
+
+ def expire_session
+ get_session_keys.each do |key|
+ ::Gitlab::Redis::Sessions.with { |redis| redis.expire(key, -1) }
+ end
+ end
end
diff --git a/spec/support/helpers/snowplow_helpers.rb b/spec/support/helpers/snowplow_helpers.rb
index 265e1c38b09..a04e5d46df9 100644
--- a/spec/support/helpers/snowplow_helpers.rb
+++ b/spec/support/helpers/snowplow_helpers.rb
@@ -46,7 +46,7 @@ module SnowplowHelpers
# }
# ]
# )
- def expect_snowplow_event(category:, action:, context: nil, **kwargs)
+ def expect_snowplow_event(category:, action:, context: nil, tracking_method: :event, **kwargs)
if context
if context.is_a?(Array)
kwargs[:context] = []
@@ -60,7 +60,7 @@ module SnowplowHelpers
end
end
- expect(Gitlab::Tracking).to have_received(:event) # rubocop:disable RSpec/ExpectGitlabTracking
+ expect(Gitlab::Tracking).to have_received(tracking_method) # rubocop:disable RSpec/ExpectGitlabTracking
.with(category, action, **kwargs).at_least(:once)
end
@@ -79,11 +79,11 @@ module SnowplowHelpers
# expect_no_snowplow_event
# end
# end
- def expect_no_snowplow_event(category: nil, action: nil, **kwargs)
+ def expect_no_snowplow_event(category: nil, action: nil, tracking_method: :event, **kwargs)
if category && action
- expect(Gitlab::Tracking).not_to have_received(:event).with(category, action, **kwargs) # rubocop:disable RSpec/ExpectGitlabTracking
+ expect(Gitlab::Tracking).not_to have_received(tracking_method).with(category, action, **kwargs) # rubocop:disable RSpec/ExpectGitlabTracking
else
- expect(Gitlab::Tracking).not_to have_received(:event) # rubocop:disable RSpec/ExpectGitlabTracking
+ expect(Gitlab::Tracking).not_to have_received(tracking_method) # rubocop:disable RSpec/ExpectGitlabTracking
end
end
end
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index 4ca8f26be9e..4c997aceeee 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -102,7 +102,7 @@ module StubConfiguration
messages[storage_name] = Gitlab::GitalyClient::StorageSettings.new(storage_hash.to_h)
end
- allow(Gitlab.config.repositories).to receive(:storages).and_return(Settingslogic.new(messages))
+ allow(Gitlab.config.repositories).to receive(:storages).and_return(::GitlabSettings::Options.build(messages))
end
def stub_sentry_settings(enabled: true)
@@ -175,11 +175,11 @@ module StubConfiguration
end
end
- # Support nested hashes by converting all values into Settingslogic objects
+ # Support nested hashes by converting all values into GitlabSettings::Objects objects
def to_settings(hash)
hash.transform_values do |value|
if value.is_a? Hash
- Settingslogic.new(value.deep_stringify_keys)
+ ::GitlabSettings::Options.build(value)
else
value
end
diff --git a/spec/support/helpers/stub_gitlab_calls.rb b/spec/support/helpers/stub_gitlab_calls.rb
index e5c30769531..748ea525e40 100644
--- a/spec/support/helpers/stub_gitlab_calls.rb
+++ b/spec/support/helpers/stub_gitlab_calls.rb
@@ -94,10 +94,10 @@ module StubGitlabCalls
end
def stub_commonmark_sourcepos_disabled
- render_options = Banzai::Filter::MarkdownEngines::CommonMark::RENDER_OPTIONS
+ engine = Banzai::Filter::MarkdownFilter.render_engine(nil)
- allow_next_instance_of(Banzai::Filter::MarkdownEngines::CommonMark) do |instance|
- allow(instance).to receive(:render_options).and_return(render_options)
+ allow_next_instance_of(engine) do |instance|
+ allow(instance).to receive(:sourcepos_disabled?).and_return(true)
end
end
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index 6b633856228..e7587e59a91 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -2,9 +2,11 @@
module StubObjectStorage
def stub_dependency_proxy_object_storage(**params)
- stub_object_storage_uploader(config: ::Gitlab.config.dependency_proxy.object_store,
- uploader: ::DependencyProxy::FileUploader,
- **params)
+ stub_object_storage_uploader(
+ config: ::Gitlab.config.dependency_proxy.object_store,
+ uploader: ::DependencyProxy::FileUploader,
+ **params
+ )
end
def stub_object_storage_uploader(
@@ -15,7 +17,7 @@ module StubObjectStorage
direct_upload: false,
cdn: {}
)
- old_config = Settingslogic.new(config.deep_stringify_keys)
+ old_config = ::GitlabSettings::Options.build(config.to_h.deep_stringify_keys)
new_config = config.to_h.deep_symbolize_keys.merge({
enabled: enabled,
proxy_download: proxy_download,
@@ -30,14 +32,16 @@ module StubObjectStorage
allow(config).to receive(:proxy_download) { proxy_download }
allow(config).to receive(:direct_upload) { direct_upload }
- uploader_config = Settingslogic.new(new_config.deep_stringify_keys)
+ uploader_config = ::GitlabSettings::Options.build(new_config.to_h.deep_stringify_keys)
allow(uploader).to receive(:object_store_options).and_return(uploader_config)
allow(uploader.options).to receive(:object_store).and_return(uploader_config)
return unless enabled
- stub_object_storage(connection_params: uploader.object_store_credentials,
- remote_directory: old_config.remote_directory)
+ stub_object_storage(
+ connection_params: uploader.object_store_credentials,
+ remote_directory: old_config.remote_directory
+ )
end
def stub_object_storage(connection_params:, remote_directory:)
@@ -55,63 +59,99 @@ module StubObjectStorage
end
def stub_artifacts_object_storage(uploader = JobArtifactUploader, **params)
- stub_object_storage_uploader(config: Gitlab.config.artifacts.object_store,
- uploader: uploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.artifacts.object_store,
+ uploader: uploader,
+ **params
+ )
end
def stub_external_diffs_object_storage(uploader = described_class, **params)
- stub_object_storage_uploader(config: Gitlab.config.external_diffs.object_store,
- uploader: uploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.external_diffs.object_store,
+ uploader: uploader,
+ **params
+ )
end
def stub_lfs_object_storage(**params)
- stub_object_storage_uploader(config: Gitlab.config.lfs.object_store,
- uploader: LfsObjectUploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.lfs.object_store,
+ uploader: LfsObjectUploader,
+ **params
+ )
end
def stub_package_file_object_storage(**params)
- stub_object_storage_uploader(config: Gitlab.config.packages.object_store,
- uploader: ::Packages::PackageFileUploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.packages.object_store,
+ uploader: ::Packages::PackageFileUploader,
+ **params
+ )
end
def stub_rpm_repository_file_object_storage(**params)
- stub_object_storage_uploader(config: Gitlab.config.packages.object_store,
- uploader: ::Packages::Rpm::RepositoryFileUploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.packages.object_store,
+ uploader: ::Packages::Rpm::RepositoryFileUploader,
+ **params
+ )
end
def stub_composer_cache_object_storage(**params)
- stub_object_storage_uploader(config: Gitlab.config.packages.object_store,
- uploader: ::Packages::Composer::CacheUploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.packages.object_store,
+ uploader: ::Packages::Composer::CacheUploader,
+ **params
+ )
+ end
+
+ def debian_component_file_object_storage(**params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.packages.object_store,
+ uploader: ::Packages::Debian::ComponentFileUploader,
+ **params
+ )
+ end
+
+ def debian_distribution_release_file_object_storage(**params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.packages.object_store,
+ uploader: ::Packages::Debian::DistributionReleaseFileUploader,
+ **params
+ )
end
def stub_uploads_object_storage(uploader = described_class, **params)
- stub_object_storage_uploader(config: Gitlab.config.uploads.object_store,
- uploader: uploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.uploads.object_store,
+ uploader: uploader,
+ **params
+ )
end
def stub_ci_secure_file_object_storage(**params)
- stub_object_storage_uploader(config: Gitlab.config.ci_secure_files.object_store,
- uploader: Ci::SecureFileUploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.ci_secure_files.object_store,
+ uploader: Ci::SecureFileUploader,
+ **params
+ )
end
def stub_terraform_state_object_storage(**params)
- stub_object_storage_uploader(config: Gitlab.config.terraform_state.object_store,
- uploader: Terraform::StateUploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.terraform_state.object_store,
+ uploader: Terraform::StateUploader,
+ **params
+ )
end
def stub_pages_object_storage(uploader = described_class, **params)
- stub_object_storage_uploader(config: Gitlab.config.pages.object_store,
- uploader: uploader,
- **params)
+ stub_object_storage_uploader(
+ config: Gitlab.config.pages.object_store,
+ uploader: uploader,
+ **params
+ )
end
def stub_object_storage_multipart_init(endpoint, upload_id = "upload_id")
@@ -125,4 +165,16 @@ module StubObjectStorage
</InitiateMultipartUploadResult>
EOS
end
+
+ def stub_object_storage_multipart_init_with_final_store_path(full_path, upload_id = "upload_id")
+ stub_request(:post, %r{\A#{full_path}\?uploads\z})
+ .to_return status: 200, body: <<-EOS.strip_heredoc
+ <?xml version="1.0" encoding="UTF-8"?>
+ <InitiateMultipartUploadResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
+ <Bucket>example-bucket</Bucket>
+ <Key>example-object</Key>
+ <UploadId>#{upload_id}</UploadId>
+ </InitiateMultipartUploadResult>
+ EOS
+ end
end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 3403064bf0b..ceb567e54c4 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -2,6 +2,7 @@
require 'parallel'
require_relative 'gitaly_setup'
+require_relative '../../../lib/gitlab/setup_helper'
module TestEnv
extend self
@@ -233,7 +234,7 @@ module TestEnv
end
def workhorse_dir
- @workhorse_path ||= File.join('tmp', 'tests', 'gitlab-workhorse')
+ @workhorse_path ||= Rails.root.join('tmp', 'tests', 'gitlab-workhorse')
end
def with_workhorse(host, port, upstream, &blk)
@@ -371,6 +372,7 @@ module TestEnv
def seed_db
Gitlab::DatabaseImporters::WorkItems::BaseTypeImporter.upsert_types
Gitlab::DatabaseImporters::WorkItems::HierarchyRestrictionsImporter.upsert_restrictions
+ FactoryBot.create(:organization, :default)
end
private
diff --git a/spec/support/test_reports/test_reports_helper.rb b/spec/support/helpers/test_reports_helper.rb
index 85483062958..4c5a1cf3c74 100644
--- a/spec/support/test_reports/test_reports_helper.rb
+++ b/spec/support/helpers/test_reports_helper.rb
@@ -43,7 +43,7 @@ module TestReportsHelper
end
def sample_rspec_failed_message
- <<-EOF.strip_heredoc
+ <<-TEST_REPORT_MESSAGE.strip_heredoc
Failure/Error: is_expected.to eq(3)
expected: 3
@@ -51,7 +51,7 @@ module TestReportsHelper
(compared using ==)
./spec/test_spec.rb:12:in `block (4 levels) in &lt;top (required)&gt;&apos;
- EOF
+ TEST_REPORT_MESSAGE
end
def create_test_case_java_success(name = 'addTest')
@@ -92,12 +92,12 @@ module TestReportsHelper
end
def sample_java_failed_message
- <<-EOF.strip_heredoc
+ <<-TEST_REPORT_MESSAGE.strip_heredoc
junit.framework.AssertionFailedError: expected:&lt;1&gt; but was:&lt;3&gt;
at CalculatorTest.subtractExpression(Unknown Source)
at java.base/jdk.internal.database.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.database.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.database.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
- EOF
+ TEST_REPORT_MESSAGE
end
end
diff --git a/spec/support/trace/trace_helpers.rb b/spec/support/helpers/trace_helpers.rb
index 9255715ff71..9255715ff71 100644
--- a/spec/support/trace/trace_helpers.rb
+++ b/spec/support/helpers/trace_helpers.rb
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 2bec945fbc8..a1c25338312 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -7,7 +7,6 @@ module UsageDataHelpers
ci_external_pipelines
ci_pipeline_config_auto_devops
ci_pipeline_config_repository
- ci_runners
ci_triggers
ci_pipeline_schedules
auto_devops_enabled
@@ -54,8 +53,6 @@ module UsageDataHelpers
projects_asana_active
projects_jenkins_active
projects_jira_active
- projects_jira_server_active
- projects_jira_cloud_active
projects_jira_dvcs_cloud_active
projects_jira_dvcs_server_active
projects_slack_active
@@ -86,15 +83,9 @@ module UsageDataHelpers
).freeze
USAGE_DATA_KEYS = %i(
- active_user_count
counts
counts_monthly
recorded_at
- edition
- version
- installation_type
- uuid
- hostname
mattermost_enabled
signup_enabled
ldap_enabled
@@ -122,14 +113,14 @@ module UsageDataHelpers
end
def stub_prometheus_queries
- stub_request(:get, %r{^https?://::1:9090/-/ready})
+ stub_request(:get, %r{^https?://.*:9090/-/ready})
.to_return(
status: 200,
body: [{}].to_json,
headers: { 'Content-Type' => 'application/json' }
)
- stub_request(:get, %r{^https?://::1:9090/api/v1/query\?query=.*})
+ stub_request(:get, %r{^https?://.*:9090/api/v1/query\?query=.*})
.to_return(
status: 200,
body: [{}].to_json,
diff --git a/spec/support/helpers/user_login_helper.rb b/spec/support/helpers/user_login_helper.rb
index 47e858cb68c..d8368a94ad7 100644
--- a/spec/support/helpers/user_login_helper.rb
+++ b/spec/support/helpers/user_login_helper.rb
@@ -30,4 +30,20 @@ module UserLoginHelper
def ensure_one_active_pane
expect(page).to have_selector('.tab-pane.active', count: 1)
end
+
+ def ensure_remember_me_in_tab(tab_name)
+ find_link(tab_name).click
+
+ within '.tab-pane.active' do
+ expect(page).to have_content _('Remember me')
+ end
+ end
+
+ def ensure_remember_me_not_in_tab(tab_name)
+ find_link(tab_name).click
+
+ within '.tab-pane.active' do
+ expect(page).not_to have_content _('Remember me')
+ end
+ end
end
diff --git a/spec/support/helpers/wait_for_requests.rb b/spec/support/helpers/wait_for_requests.rb
index 8fd9bb47053..5e2e8ad53e0 100644
--- a/spec/support/helpers/wait_for_requests.rb
+++ b/spec/support/helpers/wait_for_requests.rb
@@ -1,6 +1,9 @@
# frozen_string_literal: true
+require_relative 'wait_helpers'
+
module WaitForRequests
+ include WaitHelpers
extend self
# This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests
diff --git a/spec/support/helpers/workhorse_helpers.rb b/spec/support/helpers/workhorse_helpers.rb
index f894aff373c..f3b1d3af501 100644
--- a/spec/support/helpers/workhorse_helpers.rb
+++ b/spec/support/helpers/workhorse_helpers.rb
@@ -29,31 +29,48 @@ module WorkhorseHelpers
# workhorse_form_with_file will transform file_key inside params as if it was disk accelerated by workhorse
def workhorse_form_with_file(url, file_key:, params:, method: :post)
- workhorse_request_with_file(method, url,
- file_key: file_key,
- params: params,
- env: { 'CONTENT_TYPE' => 'multipart/form-data' },
- send_rewritten_field: true
+ workhorse_request_with_file(
+ method, url,
+ file_key: file_key,
+ params: params,
+ env: { 'CONTENT_TYPE' => 'multipart/form-data' },
+ send_rewritten_field: true
)
end
# workhorse_finalize will transform file_key inside params as if it was the finalize call of an inline object storage upload.
# note that based on the content of the params it can simulate a disc acceleration or an object storage upload
def workhorse_finalize(url, file_key:, params:, method: :post, headers: {}, send_rewritten_field: false)
- workhorse_finalize_with_multiple_files(url, method: method, file_keys: file_key, params: params, headers: headers, send_rewritten_field: send_rewritten_field)
+ workhorse_finalize_with_multiple_files(
+ url,
+ method: method,
+ file_keys: file_key,
+ params: params,
+ headers: headers,
+ send_rewritten_field: send_rewritten_field
+ )
end
def workhorse_finalize_with_multiple_files(url, file_keys:, params:, method: :post, headers: {}, send_rewritten_field: false)
- workhorse_request_with_multiple_files(method, url,
- file_keys: file_keys,
- params: params,
- extra_headers: headers,
- send_rewritten_field: send_rewritten_field
+ workhorse_request_with_multiple_files(
+ method, url,
+ file_keys: file_keys,
+ params: params,
+ extra_headers: headers,
+ send_rewritten_field: send_rewritten_field
)
end
def workhorse_request_with_file(method, url, file_key:, params:, send_rewritten_field:, env: {}, extra_headers: {})
- workhorse_request_with_multiple_files(method, url, file_keys: file_key, params: params, env: env, extra_headers: extra_headers, send_rewritten_field: send_rewritten_field)
+ workhorse_request_with_multiple_files(
+ method,
+ url,
+ file_keys: file_key,
+ params: params,
+ env: env,
+ extra_headers: extra_headers,
+ send_rewritten_field: send_rewritten_field
+ )
end
def workhorse_request_with_multiple_files(method, url, file_keys:, params:, send_rewritten_field:, env: {}, extra_headers: {})
@@ -118,14 +135,15 @@ module WorkhorseHelpers
end
end
- def fog_to_uploaded_file(file, sha256: nil)
- filename = File.basename(file.key)
+ def fog_to_uploaded_file(file, filename: nil, sha256: nil, remote_id: nil)
+ filename ||= File.basename(file.key)
- UploadedFile.new(nil,
- filename: filename,
- remote_id: filename,
- size: file.content_length,
- sha256: sha256
- )
+ UploadedFile.new(
+ nil,
+ filename: filename,
+ remote_id: remote_id || filename,
+ size: file.content_length,
+ sha256: sha256
+ )
end
end
diff --git a/spec/support/import_export/common_util.rb b/spec/support/import_export/common_util.rb
index f8f32fa59d1..53e943dc3bc 100644
--- a/spec/support/import_export/common_util.rb
+++ b/spec/support/import_export/common_util.rb
@@ -18,14 +18,8 @@ module ImportExport
allow(Gitlab::ImportExport).to receive(:export_path) { export_path }
end
- def setup_reader(reader)
- if reader == :ndjson_reader && Feature.enabled?(:project_import_ndjson)
- allow_any_instance_of(Gitlab::ImportExport::Json::LegacyReader::File).to receive(:exist?).and_return(false)
- allow_any_instance_of(Gitlab::ImportExport::Json::NdjsonReader).to receive(:exist?).and_return(true)
- else
- allow_any_instance_of(Gitlab::ImportExport::Json::LegacyReader::File).to receive(:exist?).and_return(true)
- allow_any_instance_of(Gitlab::ImportExport::Json::NdjsonReader).to receive(:exist?).and_return(false)
- end
+ def setup_reader
+ allow_any_instance_of(Gitlab::ImportExport::Json::NdjsonReader).to receive(:exist?).and_return(true)
end
def fixtures_path
@@ -36,19 +30,12 @@ module ImportExport
"tmp/tests/gitlab-test/import_export"
end
- def get_json(path, exportable_path, key, ndjson_enabled)
- if ndjson_enabled
- json = if key == :projects
- consume_attributes(path, exportable_path)
- else
- consume_relations(path, exportable_path, key)
- end
+ def get_json(path, exportable_path, key)
+ if key == :projects
+ consume_attributes(path, exportable_path)
else
- json = project_json(path)
- json = json[key.to_s] unless key == :projects
+ consume_relations(path, exportable_path, key)
end
-
- json
end
def restore_then_save_project(project, user, import_path:, export_path:)
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index 9a26f50903f..ee1b4a3c33a 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -21,21 +21,25 @@ module ExportFileHelper
create(:label_link, label: label, target: issue)
- ci_pipeline = create(:ci_pipeline,
- project: project,
- sha: merge_request.diff_head_sha,
- ref: merge_request.source_branch,
- statuses: [commit_status])
+ ci_pipeline = create(
+ :ci_pipeline,
+ project: project,
+ sha: merge_request.diff_head_sha,
+ ref: merge_request.source_branch,
+ statuses: [commit_status]
+ )
create(:ci_build, pipeline: ci_pipeline, project: project)
create(:milestone, project: project)
create(:note, noteable: issue, project: project)
create(:note, noteable: merge_request, project: project)
create(:note, noteable: snippet, project: project)
- create(:note_on_commit,
- author: user,
- project: project,
- commit_id: ci_pipeline.sha)
+ create(
+ :note_on_commit,
+ author: user,
+ project: project,
+ commit_id: ci_pipeline.sha
+ )
event = create(:event, :created, target: milestone, project: project, author: user, action: 5)
create(:push_event_payload, event: event)
diff --git a/spec/support/matchers/background_migrations_matchers.rb b/spec/support/matchers/background_migrations_matchers.rb
index 9f39f576b95..97993b158c8 100644
--- a/spec/support/matchers/background_migrations_matchers.rb
+++ b/spec/support/matchers/background_migrations_matchers.rb
@@ -100,3 +100,17 @@ RSpec::Matchers.define :be_finalize_background_migration_of do |migration|
end
end
end
+
+RSpec::Matchers.define :ensure_batched_background_migration_is_finished_for do |migration_arguments|
+ define_method :matches? do |klass|
+ expect_next_instance_of(klass) do |instance|
+ expect(instance).to receive(:ensure_batched_background_migration_is_finished).with(migration_arguments)
+ end
+ end
+
+ define_method :does_not_match? do |klass|
+ expect_next_instance_of(klass) do |instance|
+ expect(instance).not_to receive(:ensure_batched_background_migration_is_finished).with(migration_arguments)
+ end
+ end
+end
diff --git a/spec/support/matchers/be_a_foreign_key_column_of.rb b/spec/support/matchers/be_a_foreign_key_column_of.rb
new file mode 100644
index 00000000000..af190991216
--- /dev/null
+++ b/spec/support/matchers/be_a_foreign_key_column_of.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# Assert all the given id columns are one of the foreign key columns:
+#
+# ```
+# id_columns = ['partition_id']
+# composite_keys = [['partition_id', 'build_id', 'name']]
+# expect(id_columns).to be_a_foreign_key_column_of(composite_keys)
+# ```
+#
+RSpec::Matchers.define :be_a_foreign_key_column_of do |composite_keys|
+ match do |id_columns|
+ id_columns.all? do |id_column|
+ composite_keys.any? do |composite_key|
+ composite_key.include?(id_column)
+ end
+ end
+ end
+end
diff --git a/spec/support/matchers/be_indexed_by.rb b/spec/support/matchers/be_indexed_by.rb
new file mode 100644
index 00000000000..ae955624ae9
--- /dev/null
+++ b/spec/support/matchers/be_indexed_by.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+# Assert all the given foreign keys are indexed:
+#
+# ```
+# composite_foreign_keys = [['build_id', 'partition_id']]
+# indexed_columns = [['build_id', 'name', 'partition_id'], ['partition_id', 'build_id', 'name']]
+# expect(composite_foreign_keys).to be_indexed_by(indexed_columns)
+# ```
+#
+RSpec::Matchers.define :be_indexed_by do |indexed_columns|
+ match do |composite_foreign_keys|
+ composite_foreign_keys.all? do |composite_foreign_key|
+ indexed_columns.any? do |columns|
+ # for example, [build_id, partition_id] should be covered by indexes e.g.
+ # - [build_id, partition_id, name]
+ # - [partition_id, build_id, name]
+ # but not by [build_id, name, partition_id]
+ # therefore, we just need to take the first few columns (same length as composite key)
+ # e.g. [partition_id, build_id] of [partition_id, build_id, name]
+ # and compare with [build_id, partition_id]
+ (composite_foreign_key - columns.first(composite_foreign_key.length)).blank?
+ end
+ end
+ end
+end
diff --git a/spec/support/matchers/exceed_redis_call_limit.rb b/spec/support/matchers/exceed_redis_call_limit.rb
new file mode 100644
index 00000000000..2b1e1ebad23
--- /dev/null
+++ b/spec/support/matchers/exceed_redis_call_limit.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module ExceedRedisCallLimitHelpers
+ def build_recorder(block)
+ return block if block.is_a?(RedisCommands::Recorder)
+
+ RedisCommands::Recorder.new(&block)
+ end
+
+ def verify_count(expected, block)
+ @actual = build_recorder(block).count
+
+ @actual > expected
+ end
+
+ def verify_commands_count(command, expected, block)
+ @actual = build_recorder(block).by_command(command).count
+
+ @actual > expected
+ end
+end
+
+RSpec::Matchers.define :exceed_redis_calls_limit do |expected|
+ supports_block_expectations
+
+ include ExceedRedisCallLimitHelpers
+
+ match do |block|
+ verify_count(expected, block)
+ end
+
+ failure_message do
+ "Expected at least #{expected} calls, but got #{actual}"
+ end
+
+ failure_message_when_negated do
+ "Expected a maximum of #{expected} calls, but got #{actual}"
+ end
+end
+
+RSpec::Matchers.define :exceed_redis_command_calls_limit do |command, expected|
+ supports_block_expectations
+
+ include ExceedRedisCallLimitHelpers
+
+ match do |block|
+ verify_commands_count(command, expected, block)
+ end
+
+ failure_message do
+ "Expected at least #{expected} calls to '#{command}', but got #{actual}"
+ end
+
+ failure_message_when_negated do
+ "Expected a maximum of #{expected} calls to '#{command}', but got #{actual}"
+ end
+end
diff --git a/spec/support/matchers/have_plain_text_content.rb b/spec/support/matchers/have_plain_text_content.rb
new file mode 100644
index 00000000000..94f65ce3771
--- /dev/null
+++ b/spec/support/matchers/have_plain_text_content.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# can be replaced with https://github.com/email-spec/email-spec/pull/196 in the future
+RSpec::Matchers.define :have_plain_text_content do |expected_text|
+ match do |actual_email|
+ plain_text_body(actual_email).include? expected_text
+ end
+
+ failure_message do |actual_email|
+ "Expected email\n#{plain_text_body(actual_email).indent(2)}\nto contain\n#{expected_text.indent(2)}"
+ end
+
+ def plain_text_body(email)
+ email.text_part.body.to_s
+ end
+end
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index a80c269f915..8fdece7b26d 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -115,7 +115,16 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-issue', count: 6)
+ expect(actual).to have_selector('a.gfm.gfm-issue', count: 9)
+ end
+ end
+
+ # WorkItemReferenceFilter
+ matcher :reference_work_items do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('a.gfm.gfm-work_item', count: 2)
end
end
@@ -202,7 +211,7 @@ module MarkdownMatchers
match do |actual|
expect(actual).to have_selector('[data-math-style="inline"]', count: 4)
- expect(actual).to have_selector('[data-math-style="display"]', count: 4)
+ expect(actual).to have_selector('[data-math-style="display"]', count: 6)
end
end
diff --git a/spec/support/matchers/request_urgency_matcher.rb b/spec/support/matchers/request_urgency_matcher.rb
new file mode 100644
index 00000000000..d3c5093719e
--- /dev/null
+++ b/spec/support/matchers/request_urgency_matcher.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+module RequestUrgencyMatcher
+ RSpec::Matchers.define :have_request_urgency do |request_urgency|
+ match do |_actual|
+ if controller_instance = request.env["action_controller.instance"]
+ controller_instance.urgency.name == request_urgency
+ elsif endpoint = request.env['api.endpoint']
+ urgency = endpoint.options[:for].try(:urgency_for_app, endpoint)
+ urgency.name == request_urgency
+ else
+ raise 'neither a controller nor a request spec'
+ end
+ end
+
+ failure_message do |_actual|
+ if controller_instance = request.env["action_controller.instance"]
+ "request urgency #{controller_instance.urgency.name} is set, \
+ but expected to be #{request_urgency}".squish
+ elsif endpoint = request.env['api.endpoint']
+ urgency = endpoint.options[:for].try(:urgency_for_app, endpoint)
+ "request urgency #{urgency.name} is set, \
+ but expected to be #{request_urgency}".squish
+ end
+ end
+ end
+end
diff --git a/spec/support/matchers/snapshot_matcher.rb b/spec/support/matchers/snapshot_matcher.rb
new file mode 100644
index 00000000000..ec1e9cb0815
--- /dev/null
+++ b/spec/support/matchers/snapshot_matcher.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define :have_snapshot do |date, expected_states|
+ match do |actual_snapshots|
+ snapshot = actual_snapshots.find { |snapshot| snapshot[:date] == date }
+
+ @snapshot_not_found = snapshot.nil?
+ @item_states_not_found = []
+ @not_eq_error = nil
+
+ break false if @snapshot_not_found
+
+ expected_states.each do |expected_state|
+ actual_state = snapshot[:item_states].find { |state| state[:item_id] == expected_state[:item_id] }
+
+ if actual_state.nil?
+ @item_states_not_found << expected_state[:issue_id]
+ else
+ default_state = {
+ weight: 0,
+ start_state: ResourceStateEvent.states[:opened],
+ end_state: ResourceStateEvent.states[:opened],
+ parent_id: nil,
+ children_ids: Set.new
+ }
+ begin
+ expect(actual_state).to eq(default_state.merge(expected_state))
+ rescue RSpec::Expectations::ExpectationNotMetError => e
+ @error_item_title = WorkItem.find(expected_state[:item_id]).title
+ @not_eq_error = e
+
+ raise
+ end
+ end
+ end
+ end
+
+ failure_message do |_|
+ break "No snapshot found for the given date #{date}" if @snapshot_not_found
+
+ messages = []
+
+ messages << <<~MESSAGE
+ Expected the snapshot on #{date} to match the expected snapshot.
+
+ Errors:
+ MESSAGE
+
+ messages << "Item states not found for: #{@item_states_not_found.join(', ')}" unless @item_states_not_found.empty?
+
+ messages << "`#{@error_item_title}` does not have the expected states.\n#{@not_eq_error}" if @not_eq_error
+
+ messages.join("\n")
+ end
+end
diff --git a/spec/support/migrations_helpers/namespaces_helper.rb b/spec/support/migrations_helpers/namespaces_helper.rb
deleted file mode 100644
index c62ef6a4620..00000000000
--- a/spec/support/migrations_helpers/namespaces_helper.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-module MigrationHelpers
- module NamespacesHelpers
- def create_namespace(name, visibility, options = {})
- table(:namespaces).create!({
- name: name,
- path: name,
- type: 'Group',
- visibility_level: visibility
- }.merge(options))
- end
- end
-end
diff --git a/spec/support/permissions_check.rb b/spec/support/permissions_check.rb
new file mode 100644
index 00000000000..efe0ecb530b
--- /dev/null
+++ b/spec/support/permissions_check.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module Support
+ module PermissionsCheck
+ def self.inject(mod)
+ mod.prepend PermissionsExtension if Gitlab::Utils.to_boolean(ENV['GITLAB_DEBUG_POLICIES'])
+ end
+
+ module PermissionsExtension
+ def before_check(policy, ability, _user, _subject, _opts)
+ puts(
+ "POLICY CHECK DEBUG -> " \
+ "policy: #{policy.class.name}, ability: #{ability}, called_from: #{caller_locations(2, 5)}"
+ )
+ end
+ end
+ end
+end
diff --git a/spec/support/protected_branch_helpers.rb b/spec/support/protected_branch_helpers.rb
index b34b9ec4641..d983d03fd2e 100644
--- a/spec/support/protected_branch_helpers.rb
+++ b/spec/support/protected_branch_helpers.rb
@@ -2,18 +2,10 @@
module ProtectedBranchHelpers
def set_allowed_to(operation, option = 'Maintainers', form: '.js-new-protected-branch')
- within form do
- select_elem = find(".js-allowed-to-#{operation}")
- select_elem.click
-
- wait_for_requests
-
- within('.dropdown-content') do
+ within(form) do
+ within_select(".js-allowed-to-#{operation}") do
Array(option).each { |opt| click_on(opt) }
end
-
- # Enhanced select is used in EE, therefore an extra click is needed.
- select_elem.click if select_elem['aria-expanded'] == 'true'
end
end
@@ -32,4 +24,15 @@ module ProtectedBranchHelpers
click_on "Protect"
wait_for_requests
end
+
+ def within_select(selector, &block)
+ select_input = find(selector)
+ select_input.click
+ wait_for_requests
+
+ within('.dropdown.show .dropdown-menu', &block)
+
+ # Enhanced select is used in EE, therefore an extra click is needed.
+ select_input.click if select_input['aria-expanded'] == 'true'
+ end
end
diff --git a/spec/support/protected_tags/access_control_ce_shared_examples.rb b/spec/support/protected_tags/access_control_ce_shared_examples.rb
deleted file mode 100644
index 8666c19481c..00000000000
--- a/spec/support/protected_tags/access_control_ce_shared_examples.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples "protected tags > access control > CE" do
- ProtectedRefAccess::HUMAN_ACCESS_LEVELS.each do |(access_type_id, access_type_name)|
- it "allows creating protected tags that #{access_type_name} can create" do
- visit project_protected_tags_path(project)
-
- set_protected_tag_name('master')
-
- within('.js-new-protected-tag') do
- allowed_to_create_button = find(".js-allowed-to-create")
-
- unless allowed_to_create_button.text == access_type_name
- allowed_to_create_button.click
- find('.create_access_levels-container .dropdown-menu li', match: :first)
- within('.create_access_levels-container .dropdown-menu') { click_on access_type_name }
- end
- end
-
- click_on "Protect"
-
- expect(ProtectedTag.count).to eq(1)
- expect(ProtectedTag.last.create_access_levels.map(&:access_level)).to eq([access_type_id])
- end
-
- it "allows updating protected tags so that #{access_type_name} can create them" do
- visit project_protected_tags_path(project)
-
- set_protected_tag_name('master')
-
- click_on "Protect"
-
- expect(ProtectedTag.count).to eq(1)
-
- within(".protected-tags-list") do
- find(".js-allowed-to-create").click
-
- within('.js-allowed-to-create-container') do
- expect(first("li")).to have_content("Roles")
- click_on access_type_name
- end
- end
-
- wait_for_requests
-
- expect(ProtectedTag.last.create_access_levels.map(&:access_level)).to include(access_type_id)
- end
- end
-end
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
index ff0b5bebe33..94c43669173 100644
--- a/spec/support/rspec.rb
+++ b/spec/support/rspec.rb
@@ -19,6 +19,13 @@ RSpec.configure do |config|
# Re-run failures locally with `--only-failures`
config.example_status_persistence_file_path = ENV.fetch('RSPEC_LAST_RUN_RESULTS_FILE', './spec/examples.txt')
+ # Makes diffs show entire non-truncated values.
+ config.before(:each, :unlimited_max_formatted_output_length) do
+ config.expect_with :rspec do |c|
+ c.max_formatted_output_length = nil
+ end
+ end
+
unless ENV['CI']
# Allow running `:focus` examples locally,
# falling back to all tests when there is no `:focus` example.
@@ -43,7 +50,10 @@ RSpec.configure do |config|
# Add warning for example missing feature_category
config.before do |example|
if warn_missing_feature_category && example.metadata[:feature_category].blank? && !ENV['CI']
- warn "Missing metadata feature_category: #{example.location} See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#feature-category-metadata"
+ location =
+ example.metadata[:shared_group_inclusion_backtrace].last&.formatted_inclusion_location ||
+ example.location
+ warn "Missing metadata feature_category: #{location} See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#feature-category-metadata"
end
end
end
diff --git a/spec/support/rspec_order.rb b/spec/support/rspec_order.rb
index c128e18b38e..0305ae7241d 100644
--- a/spec/support/rspec_order.rb
+++ b/spec/support/rspec_order.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'yaml'
+
module Support
module RspecOrder
TODO_YAML = File.join(__dir__, 'rspec_order_todo.yml')
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index 7aa7d8e8abd..82dc6659dbf 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -34,7 +34,6 @@
- './ee/spec/controllers/concerns/geo_instrumentation_spec.rb'
- './ee/spec/controllers/concerns/gitlab_subscriptions/seat_count_alert_spec.rb'
- './ee/spec/controllers/concerns/internal_redirect_spec.rb'
-- './ee/spec/controllers/concerns/registrations/verification_spec.rb'
- './ee/spec/controllers/concerns/routable_actions_spec.rb'
- './ee/spec/controllers/countries_controller_spec.rb'
- './ee/spec/controllers/country_states_controller_spec.rb'
@@ -62,7 +61,6 @@
- './ee/spec/controllers/groups/analytics/cycle_analytics_controller_spec.rb'
- './ee/spec/controllers/groups/analytics/cycle_analytics/stages_controller_spec.rb'
- './ee/spec/controllers/groups/analytics/cycle_analytics/summary_controller_spec.rb'
-- './ee/spec/controllers/groups/analytics/cycle_analytics/value_streams_controller_spec.rb'
- './ee/spec/controllers/groups/analytics/productivity_analytics_controller_spec.rb'
- './ee/spec/controllers/groups/analytics/repository_analytics_controller_spec.rb'
- './ee/spec/controllers/groups/analytics/tasks_by_type_controller_spec.rb'
@@ -123,7 +121,6 @@
- './ee/spec/controllers/projects/branches_controller_spec.rb'
- './ee/spec/controllers/projects/clusters_controller_spec.rb'
- './ee/spec/controllers/projects_controller_spec.rb'
-- './ee/spec/controllers/projects/dependencies_controller_spec.rb'
- './ee/spec/controllers/projects/deploy_keys_controller_spec.rb'
- './ee/spec/controllers/projects/environments_controller_spec.rb'
- './ee/spec/controllers/projects/feature_flag_issues_controller_spec.rb'
@@ -226,7 +223,6 @@
- './ee/spec/features/analytics/code_analytics_spec.rb'
- './ee/spec/features/analytics/group_analytics_spec.rb'
- './ee/spec/features/billings/billing_plans_spec.rb'
-- './ee/spec/features/billings/extend_reactivate_trial_spec.rb'
- './ee/spec/features/billings/qrtly_reconciliation_alert_spec.rb'
- './ee/spec/features/boards/board_filters_spec.rb'
- './ee/spec/features/boards/boards_licensed_features_spec.rb'
@@ -286,7 +282,6 @@
- './ee/spec/features/groups/audit_events_spec.rb'
- './ee/spec/features/groups/billing_spec.rb'
- './ee/spec/features/groups/contribution_analytics_spec.rb'
-- './ee/spec/features/groups/feature_discovery_moments_spec.rb'
- './ee/spec/features/groups/group_overview_spec.rb'
- './ee/spec/features/groups/group_page_with_external_authorization_service_spec.rb'
- './ee/spec/features/groups/group_projects_spec.rb'
@@ -330,7 +325,6 @@
- './ee/spec/features/groups/wiki/user_views_wiki_empty_spec.rb'
- './ee/spec/features/ide/user_opens_ide_spec.rb'
- './ee/spec/features/integrations/jira/jira_issues_list_spec.rb'
-- './ee/spec/features/invites_spec.rb'
- './ee/spec/features/issues/blocking_issues_spec.rb'
- './ee/spec/features/issues/epic_in_issue_sidebar_spec.rb'
- './ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb'
@@ -497,9 +491,7 @@
- './ee/spec/features/trial_registrations/company_information_spec.rb'
- './ee/spec/features/trial_registrations/signin_spec.rb'
- './ee/spec/features/trial_registrations/signup_spec.rb'
-- './ee/spec/features/trials/select_namespace_spec.rb'
- './ee/spec/features/trials/show_trial_banner_spec.rb'
-- './ee/spec/features/users/arkose_labs_csp_spec.rb'
- './ee/spec/features/users/login_spec.rb'
- './ee/spec/features/users/signup_spec.rb'
- './ee/spec/features/user_unsubscribes_from_admin_notifications_spec.rb'
@@ -529,7 +521,6 @@
- './ee/spec/finders/ee/alert_management/http_integrations_finder_spec.rb'
- './ee/spec/finders/ee/autocomplete/users_finder_spec.rb'
- './ee/spec/finders/ee/ci/daily_build_group_report_results_finder_spec.rb'
-- './ee/spec/finders/ee/clusters/agent_authorizations_finder_spec.rb'
- './ee/spec/finders/ee/clusters/agents_finder_spec.rb'
- './ee/spec/finders/ee/fork_targets_finder_spec.rb'
- './ee/spec/finders/ee/group_members_finder_spec.rb'
@@ -564,7 +555,6 @@
- './ee/spec/finders/license_template_finder_spec.rb'
- './ee/spec/finders/merge_requests/by_approvers_finder_spec.rb'
- './ee/spec/finders/merge_requests_finder_spec.rb'
-- './ee/spec/finders/merge_trains_finder_spec.rb'
- './ee/spec/finders/notes_finder_spec.rb'
- './ee/spec/finders/productivity_analytics_finder_spec.rb'
- './ee/spec/finders/projects/integrations/jira/by_ids_finder_spec.rb'
@@ -602,7 +592,6 @@
- './ee/spec/frontend/fixtures/merge_requests.rb'
- './ee/spec/frontend/fixtures/on_demand_dast_scans.rb'
- './ee/spec/frontend/fixtures/project_quality_summary.rb'
-- './ee/spec/frontend/fixtures/projects.rb'
- './ee/spec/frontend/fixtures/runner.rb'
- './ee/spec/frontend/fixtures/saml_providers.rb'
- './ee/spec/frontend/fixtures/search.rb'
@@ -957,7 +946,6 @@
- './ee/spec/helpers/ee/geo_helper_spec.rb'
- './ee/spec/helpers/ee/gitlab_routing_helper_spec.rb'
- './ee/spec/helpers/ee/graph_helper_spec.rb'
-- './ee/spec/helpers/ee/groups/analytics/cycle_analytics_helper_spec.rb'
- './ee/spec/helpers/ee/groups/group_members_helper_spec.rb'
- './ee/spec/helpers/ee/groups_helper_spec.rb'
- './ee/spec/helpers/ee/groups/settings_helper_spec.rb'
@@ -984,14 +972,11 @@
- './ee/spec/helpers/ee/subscribable_banner_helper_spec.rb'
- './ee/spec/helpers/ee/system_note_helper_spec.rb'
- './ee/spec/helpers/ee/todos_helper_spec.rb'
-- './ee/spec/helpers/ee/trial_helper_spec.rb'
-- './ee/spec/helpers/ee/trial_registration_helper_spec.rb'
- './ee/spec/helpers/ee/users/callouts_helper_spec.rb'
- './ee/spec/helpers/ee/version_check_helper_spec.rb'
- './ee/spec/helpers/ee/wiki_helper_spec.rb'
- './ee/spec/helpers/epics_helper_spec.rb'
- './ee/spec/helpers/gitlab_subscriptions/upcoming_reconciliation_helper_spec.rb'
-- './ee/spec/helpers/groups/feature_discovery_moments_helper_spec.rb'
- './ee/spec/helpers/groups/ldap_sync_helper_spec.rb'
- './ee/spec/helpers/groups/security_features_helper_spec.rb'
- './ee/spec/helpers/groups/sso_helper_spec.rb'
@@ -1122,19 +1107,13 @@
- './ee/spec/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/create_security_setting_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/delete_invalid_epic_issues_spec.rb'
-- './ee/spec/lib/ee/gitlab/background_migration/drop_invalid_remediations_spec.rb'
-- './ee/spec/lib/ee/gitlab/background_migration/fix_incorrect_max_seats_used_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_in_batch_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/migrate_shared_vulnerability_scanners_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids_spec.rb'
-- './ee/spec/lib/ee/gitlab/background_migration/populate_namespace_statistics_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column_spec.rb'
-- './ee/spec/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings_spec.rb'
- './ee/spec/lib/ee/gitlab/background_migration/purge_stale_security_scans_spec.rb'
-- './ee/spec/lib/ee/gitlab/background_migration/recalculate_vulnerability_finding_signatures_for_findings_spec.rb'
-- './ee/spec/lib/ee/gitlab/background_migration/update_vulnerability_occurrences_location_spec.rb'
- './ee/spec/lib/ee/gitlab/checks/push_rule_check_spec.rb'
- './ee/spec/lib/ee/gitlab/checks/push_rules/branch_check_spec.rb'
- './ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb'
@@ -1309,8 +1288,6 @@
- './ee/spec/lib/gitlab/auth/smartcard/session_enforcer_spec.rb'
- './ee/spec/lib/gitlab/auth/smartcard/session_spec.rb'
- './ee/spec/lib/gitlab/background_migration/migrate_requirements_to_work_items_spec.rb'
-- './ee/spec/lib/gitlab/background_migration/populate_test_reports_issue_id_spec.rb'
-- './ee/spec/lib/gitlab/background_migration/remove_all_trace_expiration_dates_spec.rb'
- './ee/spec/lib/gitlab/bullet/exclusions_spec.rb'
- './ee/spec/lib/gitlab/cache_spec.rb'
- './ee/spec/lib/gitlab/checks/changes_access_spec.rb'
@@ -1323,7 +1300,6 @@
- './ee/spec/lib/gitlab/ci/config/required/processor_spec.rb'
- './ee/spec/lib/gitlab/ci/config/security_orchestration_policies/processor_spec.rb'
- './ee/spec/lib/gitlab/cidr_spec.rb'
-- './ee/spec/lib/gitlab/ci/minutes/build_consumption_spec.rb'
- './ee/spec/lib/gitlab/ci/minutes/cached_quota_spec.rb'
- './ee/spec/lib/gitlab/ci/minutes/cost_factor_spec.rb'
- './ee/spec/lib/gitlab/ci/minutes/gitlab_contribution_cost_factor_spec.rb'
@@ -1371,7 +1347,6 @@
- './ee/spec/lib/gitlab/ci/templates/Jobs/browser_performance_testing_gitlab_ci_yaml_spec.rb'
- './ee/spec/lib/gitlab/ci/templates/Jobs/dast_default_branch_gitlab_ci_yaml_spec.rb'
- './ee/spec/lib/gitlab/ci/templates/Jobs/load_performance_testing_gitlab_ci_yaml_spec.rb'
-- './ee/spec/lib/gitlab/ci/templates/license_scanning_gitlab_ci_yaml_spec.rb'
- './ee/spec/lib/gitlab/ci/templates/sast_gitlab_ci_yaml_spec.rb'
- './ee/spec/lib/gitlab/ci/templates/sast_iac_gitlab_ci_yaml_spec.rb'
- './ee/spec/lib/gitlab/ci/templates/sast_latest_gitlab_ci_yaml_spec.rb'
@@ -1577,13 +1552,10 @@
- './ee/spec/lib/omni_auth/strategies/group_saml_spec.rb'
- './ee/spec/lib/omni_auth/strategies/kerberos_spec.rb'
- './ee/spec/lib/peek/views/elasticsearch_spec.rb'
-- './ee/spec/lib/sidebars/groups/menus/administration_menu_spec.rb'
- './ee/spec/lib/sidebars/groups/menus/analytics_menu_spec.rb'
- './ee/spec/lib/sidebars/groups/menus/epics_menu_spec.rb'
- './ee/spec/lib/sidebars/groups/menus/security_compliance_menu_spec.rb'
-- './ee/spec/lib/sidebars/groups/menus/trial_experiment_menu_spec.rb'
- './ee/spec/lib/sidebars/groups/menus/wiki_menu_spec.rb'
-- './ee/spec/lib/sidebars/projects/menus/trial_experiment_menu_spec.rb'
- './ee/spec/lib/system_check/app/search_check_spec.rb'
- './ee/spec/lib/system_check/geo/authorized_keys_check_spec.rb'
- './ee/spec/lib/system_check/geo/authorized_keys_flag_check_spec.rb'
@@ -1613,21 +1585,12 @@
- './ee/spec/mailers/notify_spec.rb'
- './ee/spec/migrations/20220411173544_cleanup_orphans_approval_project_rules_spec.rb'
- './ee/spec/migrations/20220517144749_remove_vulnerability_approval_rules_spec.rb'
-- './ee/spec/migrations/add_non_null_constraint_for_escalation_rule_on_pending_alert_escalations_spec.rb'
-- './ee/spec/migrations/async_build_trace_expire_at_index_spec.rb'
- './ee/spec/migrations/backfill_delayed_group_deletion_spec.rb'
-- './ee/spec/migrations/drop_invalid_remediations_spec.rb'
- './ee/spec/migrations/geo/fix_state_column_in_file_registry_spec.rb'
- './ee/spec/migrations/geo/fix_state_column_in_lfs_object_registry_spec.rb'
- './ee/spec/migrations/geo/migrate_ci_job_artifacts_to_separate_registry_spec.rb'
- './ee/spec/migrations/geo/migrate_lfs_objects_to_separate_registry_spec.rb'
- './ee/spec/migrations/geo/set_resync_flag_for_retried_projects_spec.rb'
-- './ee/spec/migrations/remove_schedule_and_status_null_constraints_from_pending_escalations_alert_spec.rb'
-- './ee/spec/migrations/schedule_delete_invalid_epic_issues_revised_spec.rb'
-- './ee/spec/migrations/schedule_populate_test_reports_issue_id_spec.rb'
-- './ee/spec/migrations/schedule_requirements_migration_spec.rb'
-- './ee/spec/migrations/schedule_trace_expiry_removal_spec.rb'
-- './ee/spec/migrations/update_vulnerability_occurrences_location_spec.rb'
- './ee/spec/models/alert_management/alert_payload_field_spec.rb'
- './ee/spec/models/allowed_email_domain_spec.rb'
- './ee/spec/models/analytics/cycle_analytics/aggregation_context_spec.rb'
@@ -1662,7 +1625,6 @@
- './ee/spec/models/boards/epic_user_preference_spec.rb'
- './ee/spec/models/board_spec.rb'
- './ee/spec/models/board_user_preference_spec.rb'
-- './ee/spec/models/broadcast_message_spec.rb'
- './ee/spec/models/burndown_spec.rb'
- './ee/spec/models/ci/bridge_spec.rb'
- './ee/spec/models/ci/daily_build_group_report_result_spec.rb'
@@ -1683,7 +1645,6 @@
- './ee/spec/models/concerns/approver_migrate_hook_spec.rb'
- './ee/spec/models/concerns/auditable_spec.rb'
- './ee/spec/models/concerns/deprecated_approvals_before_merge_spec.rb'
-- './ee/spec/models/concerns/ee/clusters/agents/authorization_config_scopes_spec.rb'
- './ee/spec/models/concerns/ee/issuable_spec.rb'
- './ee/spec/models/concerns/ee/mentionable_spec.rb'
- './ee/spec/models/concerns/ee/milestoneable_spec.rb'
@@ -1733,7 +1694,6 @@
- './ee/spec/models/dora/lead_time_for_changes_metric_spec.rb'
- './ee/spec/models/dora/time_to_restore_service_metric_spec.rb'
- './ee/spec/models/ee/alert_management/alert_spec.rb'
-- './ee/spec/models/ee/analytics/cycle_analytics/stage_event_hash_spec.rb'
- './ee/spec/models/ee/analytics/usage_trends/measurement_spec.rb'
- './ee/spec/models/ee/appearance_spec.rb'
- './ee/spec/models/ee/audit_event_spec.rb'
@@ -1754,7 +1714,6 @@
- './ee/spec/models/ee/integrations/jira_spec.rb'
- './ee/spec/models/ee/integration_spec.rb'
- './ee/spec/models/ee/iterations/cadence_spec.rb'
-- './ee/spec/models/ee/iteration_spec.rb'
- './ee/spec/models/ee/key_spec.rb'
- './ee/spec/models/ee/label_spec.rb'
- './ee/spec/models/ee/lfs_object_spec.rb'
@@ -1791,7 +1750,6 @@
- './ee/spec/models/ee/user_spec.rb'
- './ee/spec/models/ee/users_statistics_spec.rb'
- './ee/spec/models/ee/vulnerability_spec.rb'
-- './ee/spec/models/ee/work_items/type_spec.rb'
- './ee/spec/models/elastic/index_setting_spec.rb'
- './ee/spec/models/elastic/migration_record_spec.rb'
- './ee/spec/models/elastic/reindexing_slice_spec.rb'
@@ -1874,7 +1832,6 @@
- './ee/spec/models/merge_requests/external_status_check_spec.rb'
- './ee/spec/models/merge_request_spec.rb'
- './ee/spec/models/merge_requests/status_check_response_spec.rb'
-- './ee/spec/models/merge_train_spec.rb'
- './ee/spec/models/milestone_release_spec.rb'
- './ee/spec/models/milestone_spec.rb'
- './ee/spec/models/namespace_limit_spec.rb'
@@ -1934,7 +1891,6 @@
- './ee/spec/models/storage_shard_spec.rb'
- './ee/spec/models/uploads/local_spec.rb'
- './ee/spec/models/upload_spec.rb'
-- './ee/spec/models/user_detail_spec.rb'
- './ee/spec/models/user_permission_export_upload_spec.rb'
- './ee/spec/models/user_preference_spec.rb'
- './ee/spec/models/users_security_dashboard_project_spec.rb'
@@ -2330,7 +2286,6 @@
- './ee/spec/requests/groups_controller_spec.rb'
- './ee/spec/requests/groups/epics/epic_links_controller_spec.rb'
- './ee/spec/requests/groups/epics/related_epic_links_controller_spec.rb'
-- './ee/spec/requests/groups/feature_discovery_moments_spec.rb'
- './ee/spec/requests/groups/group_members_controller_spec.rb'
- './ee/spec/requests/groups/hook_logs_controller_spec.rb'
- './ee/spec/requests/groups/labels_spec.rb'
@@ -2471,10 +2426,7 @@
- './ee/spec/services/analytics/cycle_analytics/aggregator_service_spec.rb'
- './ee/spec/services/analytics/cycle_analytics/consistency_check_service_spec.rb'
- './ee/spec/services/analytics/cycle_analytics/data_loader_service_spec.rb'
-- './ee/spec/services/analytics/cycle_analytics/stages/create_service_spec.rb'
-- './ee/spec/services/analytics/cycle_analytics/stages/delete_service_spec.rb'
- './ee/spec/services/analytics/cycle_analytics/stages/list_service_spec.rb'
-- './ee/spec/services/analytics/cycle_analytics/stages/update_service_spec.rb'
- './ee/spec/services/analytics/cycle_analytics/value_streams/create_service_spec.rb'
- './ee/spec/services/analytics/cycle_analytics/value_streams/update_service_spec.rb'
- './ee/spec/services/analytics/devops_adoption/enabled_namespaces/bulk_delete_service_spec.rb'
@@ -2534,7 +2486,6 @@
- './ee/spec/services/audit_events/release_associate_milestone_audit_event_service_spec.rb'
- './ee/spec/services/audit_events/release_created_audit_event_service_spec.rb'
- './ee/spec/services/audit_events/release_updated_audit_event_service_spec.rb'
-- './ee/spec/services/audit_events/repository_download_started_audit_event_service_spec.rb'
- './ee/spec/services/audit_events/runner_custom_audit_event_service_spec.rb'
- './ee/spec/services/audit_events/runners_token_audit_event_service_spec.rb'
- './ee/spec/services/audit_events/streaming/headers/base_spec.rb'
@@ -2587,7 +2538,6 @@
- './ee/spec/services/ci/external_pull_requests/process_github_event_service_spec.rb'
- './ee/spec/services/ci/minutes/additional_packs/change_namespace_service_spec.rb'
- './ee/spec/services/ci/minutes/additional_packs/create_service_spec.rb'
-- './ee/spec/services/ci/minutes/batch_reset_service_spec.rb'
- './ee/spec/services/ci/minutes/email_notification_service_spec.rb'
- './ee/spec/services/ci/minutes/refresh_cached_data_service_spec.rb'
- './ee/spec/services/ci/minutes/reset_usage_service_spec.rb'
@@ -2668,9 +2618,7 @@
- './ee/spec/services/ee/issuable/destroy_service_spec.rb'
- './ee/spec/services/ee/issue_links/create_service_spec.rb'
- './ee/spec/services/ee/issues/after_create_service_spec.rb'
-- './ee/spec/services/ee/issues/build_from_vulnerability_service_spec.rb'
- './ee/spec/services/ee/issues/clone_service_spec.rb'
-- './ee/spec/services/ee/issues/create_from_vulnerability_data_service_spec.rb'
- './ee/spec/services/ee/issues/create_service_spec.rb'
- './ee/spec/services/ee/issues/move_service_spec.rb'
- './ee/spec/services/ee/issues/update_service_spec.rb'
@@ -2761,7 +2709,6 @@
- './ee/spec/services/epics/related_epic_links/list_service_spec.rb'
- './ee/spec/services/epics/reopen_service_spec.rb'
- './ee/spec/services/epics/transfer_service_spec.rb'
-- './ee/spec/services/epics/tree_reorder_service_spec.rb'
- './ee/spec/services/epics/update_dates_service_spec.rb'
- './ee/spec/services/epics/update_service_spec.rb'
- './ee/spec/services/external_status_checks/create_service_spec.rb'
@@ -2815,10 +2762,8 @@
- './ee/spec/services/gitlab_subscriptions/create_hand_raise_lead_service_spec.rb'
- './ee/spec/services/gitlab_subscriptions/create_service_spec.rb'
- './ee/spec/services/gitlab_subscriptions/create_trial_or_lead_service_spec.rb'
-- './ee/spec/services/gitlab_subscriptions/extend_reactivate_trial_service_spec.rb'
- './ee/spec/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service_spec.rb'
- './ee/spec/services/gitlab_subscriptions/fetch_subscription_plans_service_spec.rb'
-- './ee/spec/services/gitlab_subscriptions/notify_seats_exceeded_service_spec.rb'
- './ee/spec/services/gitlab_subscriptions/plan_upgrade_service_spec.rb'
- './ee/spec/services/gitlab_subscriptions/preview_billable_user_change_service_spec.rb'
- './ee/spec/services/gitlab_subscriptions/reconciliations/calculate_seat_count_data_service_spec.rb'
@@ -2869,7 +2814,6 @@
- './ee/spec/services/licenses/destroy_service_spec.rb'
- './ee/spec/services/members/activate_service_spec.rb'
- './ee/spec/services/members/await_service_spec.rb'
-- './ee/spec/services/merge_commits/export_csv_service_spec.rb'
- './ee/spec/services/merge_request_approval_settings/update_service_spec.rb'
- './ee/spec/services/merge_requests/approval_service_spec.rb'
- './ee/spec/services/merge_requests/build_service_spec.rb'
@@ -2946,7 +2890,6 @@
- './ee/spec/services/repositories/housekeeping_service_spec.rb'
- './ee/spec/services/requirements_management/export_csv_service_spec.rb'
- './ee/spec/services/requirements_management/import_csv_service_spec.rb'
-- './ee/spec/services/requirements_management/map_export_fields_service_spec.rb'
- './ee/spec/services/requirements_management/prepare_import_csv_service_spec.rb'
- './ee/spec/services/requirements_management/process_test_reports_service_spec.rb'
- './ee/spec/services/resource_access_tokens/create_service_spec.rb'
@@ -3017,7 +2960,6 @@
- './ee/spec/services/security/update_training_service_spec.rb'
- './ee/spec/services/security/vulnerability_counting_service_spec.rb'
- './ee/spec/services/sitemap/create_service_spec.rb'
-- './ee/spec/services/slash_commands/global_slack_handler_spec.rb'
- './ee/spec/services/software_license_policies/create_service_spec.rb'
- './ee/spec/services/software_license_policies/update_service_spec.rb'
- './ee/spec/services/start_pull_mirroring_service_spec.rb'
@@ -3106,7 +3048,6 @@
- './ee/spec/views/groups/_compliance_frameworks.html.haml_spec.rb'
- './ee/spec/views/groups/compliance_frameworks/new.html.haml_spec.rb'
- './ee/spec/views/groups/edit.html.haml_spec.rb'
-- './ee/spec/views/groups/feature_discovery_moments/advanced_features_dashboard.html.haml_spec.rb'
- './ee/spec/views/groups/hook_logs/show.html.haml_spec.rb'
- './ee/spec/views/groups/hooks/edit.html.haml_spec.rb'
- './ee/spec/views/groups/security/discover/show.html.haml_spec.rb'
@@ -3116,13 +3057,11 @@
- './ee/spec/views/layouts/checkout.html.haml_spec.rb'
- './ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb'
- './ee/spec/views/layouts/header/_ee_subscribable_banner.html.haml_spec.rb'
-- './ee/spec/views/layouts/header/help_dropdown/_cross_stage_fdm.html.haml_spec.rb'
- './ee/spec/views/layouts/header/_read_only_banner.html.haml_spec.rb'
- './ee/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb'
- './ee/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb'
- './ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
- './ee/spec/views/layouts/nav/sidebar/_push_rules_link.html.haml_spec.rb'
-- './ee/spec/views/layouts/_search.html.haml_spec.rb'
- './ee/spec/views/operations/environments.html.haml_spec.rb'
- './ee/spec/views/operations/index.html.haml_spec.rb'
- './ee/spec/views/profiles/preferences/show.html.haml_spec.rb'
@@ -3141,7 +3080,6 @@
- './ee/spec/views/projects/security/sast_configuration/show.html.haml_spec.rb'
- './ee/spec/views/projects/settings/subscriptions/_index.html.haml_spec.rb'
- './ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
-- './ee/spec/views/search/_category.html.haml_spec.rb'
- './ee/spec/views/shared/billings/_billing_plan_actions.html.haml_spec.rb'
- './ee/spec/views/shared/billings/_billing_plan.html.haml_spec.rb'
- './ee/spec/views/shared/billings/_billing_plans.html.haml_spec.rb'
@@ -3201,7 +3139,6 @@
- './ee/spec/workers/concerns/elastic/indexing_control_spec.rb'
- './ee/spec/workers/concerns/elastic/migration_obsolete_spec.rb'
- './ee/spec/workers/concerns/elastic/migration_options_spec.rb'
-- './ee/spec/workers/concerns/geo_queue_spec.rb'
- './ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb'
- './ee/spec/workers/create_github_webhook_worker_spec.rb'
- './ee/spec/workers/deployments/auto_rollback_worker_spec.rb'
@@ -3265,7 +3202,6 @@
- './ee/spec/workers/geo/verification_state_backfill_worker_spec.rb'
- './ee/spec/workers/geo/verification_timeout_worker_spec.rb'
- './ee/spec/workers/geo/verification_worker_spec.rb'
-- './ee/spec/workers/gitlab_subscriptions/notify_seats_exceeded_worker_spec.rb'
- './ee/spec/workers/group_saml_group_sync_worker_spec.rb'
- './ee/spec/workers/groups/create_event_worker_spec.rb'
- './ee/spec/workers/groups/export_memberships_worker_spec.rb'
@@ -3314,7 +3250,6 @@
- './ee/spec/workers/sync_seat_link_worker_spec.rb'
- './ee/spec/workers/todos_destroyer/confidential_epic_worker_spec.rb'
- './ee/spec/workers/update_all_mirrors_worker_spec.rb'
-- './ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb'
- './ee/spec/workers/vulnerabilities/historical_statistics/deletion_worker_spec.rb'
- './ee/spec/workers/vulnerabilities/statistics/adjustment_worker_spec.rb'
- './ee/spec/workers/vulnerabilities/statistics/schedule_worker_spec.rb'
@@ -3323,7 +3258,6 @@
- './spec/bin/feature_flag_spec.rb'
- './spec/bin/sidekiq_cluster_spec.rb'
- './spec/channels/application_cable/connection_spec.rb'
-- './spec/channels/awareness_channel_spec.rb'
- './spec/commands/metrics_server/metrics_server_spec.rb'
- './spec/commands/sidekiq_cluster/cli_spec.rb'
- './spec/components/diffs/overflow_warning_component_spec.rb'
@@ -3368,7 +3302,6 @@
- './spec/controllers/admin/jobs_controller_spec.rb'
- './spec/controllers/admin/plan_limits_controller_spec.rb'
- './spec/controllers/admin/projects_controller_spec.rb'
-- './spec/controllers/admin/runner_projects_controller_spec.rb'
- './spec/controllers/admin/runners_controller_spec.rb'
- './spec/controllers/admin/sessions_controller_spec.rb'
- './spec/controllers/admin/spam_logs_controller_spec.rb'
@@ -3458,7 +3391,6 @@
- './spec/controllers/import/github_controller_spec.rb'
- './spec/controllers/import/gitlab_controller_spec.rb'
- './spec/controllers/import/manifest_controller_spec.rb'
-- './spec/controllers/import/phabricator_controller_spec.rb'
- './spec/controllers/invites_controller_spec.rb'
- './spec/controllers/jira_connect/app_descriptor_controller_spec.rb'
- './spec/controllers/jira_connect/branches_controller_spec.rb'
@@ -3618,13 +3550,10 @@
- './spec/experiments/force_company_trial_experiment_spec.rb'
- './spec/experiments/in_product_guidance_environments_webide_experiment_spec.rb'
- './spec/experiments/ios_specific_templates_experiment_spec.rb'
-- './spec/experiments/require_verification_for_namespace_creation_experiment_spec.rb'
-- './spec/experiments/security_reports_mr_widget_prompt_experiment_spec.rb'
- './spec/features/abuse_report_spec.rb'
- './spec/features/action_cable_logging_spec.rb'
- './spec/features/admin/admin_abuse_reports_spec.rb'
- './spec/features/admin/admin_appearance_spec.rb'
-- './spec/features/admin/admin_broadcast_messages_spec.rb'
- './spec/features/admin/admin_browse_spam_logs_spec.rb'
- './spec/features/admin/admin_deploy_keys_spec.rb'
- './spec/features/admin/admin_dev_ops_reports_spec.rb'
@@ -3814,12 +3743,10 @@
- './spec/features/ics/dashboard_issues_spec.rb'
- './spec/features/ics/group_issues_spec.rb'
- './spec/features/ics/project_issues_spec.rb'
-- './spec/features/ide/clientside_preview_csp_spec.rb'
- './spec/features/ide_spec.rb'
- './spec/features/ide/static_object_external_storage_csp_spec.rb'
- './spec/features/ide/user_opens_merge_request_spec.rb'
- './spec/features/import/manifest_import_spec.rb'
-- './spec/features/invites_spec.rb'
- './spec/features/issuables/issuable_list_spec.rb'
- './spec/features/issuables/markdown_references/internal_references_spec.rb'
- './spec/features/issuables/markdown_references/jira_spec.rb'
@@ -3980,7 +3907,6 @@
- './spec/features/merge_request/user_sees_suggest_pipeline_spec.rb'
- './spec/features/merge_request/user_sees_system_notes_spec.rb'
- './spec/features/merge_request/user_sees_versions_spec.rb'
-- './spec/features/merge_request/user_sees_wip_help_message_spec.rb'
- './spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb'
- './spec/features/merge_request/user_squashes_merge_request_spec.rb'
- './spec/features/merge_request/user_suggests_changes_on_diff_spec.rb'
@@ -4108,7 +4034,6 @@
- './spec/features/projects/files/project_owner_creates_license_file_spec.rb'
- './spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb'
- './spec/features/projects/files/template_selector_menu_spec.rb'
-- './spec/features/projects/files/template_type_dropdown_spec.rb'
- './spec/features/projects/files/undo_template_spec.rb'
- './spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb'
- './spec/features/projects/files/user_browses_files_spec.rb'
@@ -4349,7 +4274,6 @@
- './spec/features/task_lists_spec.rb'
- './spec/features/topic_show_spec.rb'
- './spec/features/triggers_spec.rb'
-- './spec/features/u2f_spec.rb'
- './spec/features/unsubscribe_links_spec.rb'
- './spec/features/uploads/user_uploads_avatar_to_group_spec.rb'
- './spec/features/uploads/user_uploads_avatar_to_profile_spec.rb'
@@ -4377,10 +4301,8 @@
- './spec/features/users/snippets_spec.rb'
- './spec/features/users/terms_spec.rb'
- './spec/features/users/user_browses_projects_on_user_page_spec.rb'
-- './spec/features/users/zuora_csp_spec.rb'
- './spec/features/webauthn_spec.rb'
- './spec/features/whats_new_spec.rb'
-- './spec/features/work_items/work_item_children_spec.rb'
- './spec/finders/abuse_reports_finder_spec.rb'
- './spec/finders/access_requests_finder_spec.rb'
- './spec/finders/admin/plans_finder_spec.rb'
@@ -4414,7 +4336,6 @@
- './spec/finders/ci/runners_finder_spec.rb'
- './spec/finders/ci/variables_finder_spec.rb'
- './spec/finders/cluster_ancestors_finder_spec.rb'
-- './spec/finders/clusters/agent_authorizations_finder_spec.rb'
- './spec/finders/clusters/agents_finder_spec.rb'
- './spec/finders/clusters_finder_spec.rb'
- './spec/finders/clusters/knative_services_finder_spec.rb'
@@ -4515,7 +4436,6 @@
- './spec/finders/security/license_compliance_jobs_finder_spec.rb'
- './spec/finders/security/security_jobs_finder_spec.rb'
- './spec/finders/sentry_issue_finder_spec.rb'
-- './spec/finders/serverless_domain_finder_spec.rb'
- './spec/finders/snippets_finder_spec.rb'
- './spec/finders/starred_projects_finder_spec.rb'
- './spec/finders/tags_finder_spec.rb'
@@ -4571,7 +4491,6 @@
- './spec/frontend/fixtures/tags.rb'
- './spec/frontend/fixtures/timezones.rb'
- './spec/frontend/fixtures/todos.rb'
-- './spec/frontend/fixtures/u2f.rb'
- './spec/frontend/fixtures/webauthn.rb'
- './spec/graphql/features/authorization_spec.rb'
- './spec/graphql/gitlab_schema_spec.rb'
@@ -4603,7 +4522,6 @@
- './spec/graphql/mutations/clusters/agent_tokens/create_spec.rb'
- './spec/graphql/mutations/clusters/agent_tokens/revoke_spec.rb'
- './spec/graphql/mutations/commits/create_spec.rb'
-- './spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb'
- './spec/graphql/mutations/concerns/mutations/resolves_group_spec.rb'
- './spec/graphql/mutations/concerns/mutations/resolves_issuable_spec.rb'
- './spec/graphql/mutations/container_expiration_policies/update_spec.rb'
@@ -5084,7 +5002,6 @@
- './spec/helpers/admin/deploy_key_helper_spec.rb'
- './spec/helpers/admin/identities_helper_spec.rb'
- './spec/helpers/admin/user_actions_helper_spec.rb'
-- './spec/helpers/analytics/cycle_analytics_helper_spec.rb'
- './spec/helpers/appearances_helper_spec.rb'
- './spec/helpers/application_helper_spec.rb'
- './spec/helpers/application_settings_helper_spec.rb'
@@ -5099,14 +5016,12 @@
- './spec/helpers/boards_helper_spec.rb'
- './spec/helpers/branches_helper_spec.rb'
- './spec/helpers/breadcrumbs_helper_spec.rb'
-- './spec/helpers/broadcast_messages_helper_spec.rb'
- './spec/helpers/button_helper_spec.rb'
- './spec/helpers/calendar_helper_spec.rb'
- './spec/helpers/ci/builds_helper_spec.rb'
- './spec/helpers/ci/jobs_helper_spec.rb'
- './spec/helpers/ci/pipeline_editor_helper_spec.rb'
- './spec/helpers/ci/pipelines_helper_spec.rb'
-- './spec/helpers/ci/runners_helper_spec.rb'
- './spec/helpers/ci/secure_files_helper_spec.rb'
- './spec/helpers/ci/status_helper_spec.rb'
- './spec/helpers/ci/triggers_helper_spec.rb'
@@ -5142,7 +5057,6 @@
- './spec/helpers/groups/settings_helper_spec.rb'
- './spec/helpers/hooks_helper_spec.rb'
- './spec/helpers/icons_helper_spec.rb'
-- './spec/helpers/ide_helper_spec.rb'
- './spec/helpers/import_helper_spec.rb'
- './spec/helpers/instance_configuration_helper_spec.rb'
- './spec/helpers/integrations_helper_spec.rb'
@@ -5158,7 +5072,6 @@
- './spec/helpers/members_helper_spec.rb'
- './spec/helpers/merge_requests_helper_spec.rb'
- './spec/helpers/namespaces_helper_spec.rb'
-- './spec/helpers/nav_helper_spec.rb'
- './spec/helpers/nav/new_dropdown_helper_spec.rb'
- './spec/helpers/nav/top_nav_helper_spec.rb'
- './spec/helpers/notes_helper_spec.rb'
@@ -5201,7 +5114,6 @@
- './spec/helpers/tab_helper_spec.rb'
- './spec/helpers/terms_helper_spec.rb'
- './spec/helpers/timeboxes_helper_spec.rb'
-- './spec/helpers/timeboxes_routing_helper_spec.rb'
- './spec/helpers/time_helper_spec.rb'
- './spec/helpers/time_zone_helper_spec.rb'
- './spec/helpers/todos_helper_spec.rb'
@@ -5280,7 +5192,6 @@
- './spec/lib/api/entities/ci/job_request/port_spec.rb'
- './spec/lib/api/entities/ci/job_request/service_spec.rb'
- './spec/lib/api/entities/ci/pipeline_spec.rb'
-- './spec/lib/api/entities/clusters/agent_authorization_spec.rb'
- './spec/lib/api/entities/clusters/agent_spec.rb'
- './spec/lib/api/entities/deploy_key_spec.rb'
- './spec/lib/api/entities/deploy_keys_project_spec.rb'
@@ -5674,25 +5585,19 @@
- './spec/lib/gitlab/auth/saml/user_spec.rb'
- './spec/lib/gitlab/auth_spec.rb'
- './spec/lib/gitlab/auth/two_factor_auth_verifier_spec.rb'
-- './spec/lib/gitlab/auth/u2f_webauthn_converter_spec.rb'
- './spec/lib/gitlab/auth/unique_ips_limiter_spec.rb'
- './spec/lib/gitlab/auth/user_access_denied_reason_spec.rb'
- './spec/lib/gitlab/avatar_cache_spec.rb'
-- './spec/lib/gitlab/background_migration/backfill_ci_queuing_tables_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_group_features_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb'
-- './spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_issue_search_data_spec.rb'
-- './spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_member_namespace_for_group_members_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_namespace_id_for_namespace_route_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_namespace_id_for_project_route_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_namespace_id_of_vulnerability_reads_spec.rb'
-- './spec/lib/gitlab/background_migration/backfill_namespace_traversal_ids_children_spec.rb'
-- './spec/lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_note_discussion_id_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_project_feature_package_registry_access_level_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb'
@@ -5701,8 +5606,6 @@
- './spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_topics_title_spec.rb'
-- './spec/lib/gitlab/background_migration/backfill_upvotes_count_on_issues_spec.rb'
-- './spec/lib/gitlab/background_migration/backfill_user_namespace_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb'
- './spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb'
- './spec/lib/gitlab/background_migration/base_job_spec.rb'
@@ -5714,48 +5617,26 @@
- './spec/lib/gitlab/background_migration/batching_strategies/loose_index_scan_batching_strategy_spec.rb'
- './spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb'
- './spec/lib/gitlab/background_migration/cleanup_draft_data_from_faulty_regex_spec.rb'
-- './spec/lib/gitlab/background_migration/cleanup_orphaned_lfs_objects_projects_spec.rb'
- './spec/lib/gitlab/background_migration/cleanup_orphaned_routes_spec.rb'
- './spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb'
-- './spec/lib/gitlab/background_migration/delete_orphaned_deployments_spec.rb'
- './spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb'
-- './spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb'
- './spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb'
- './spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_inactive_public_projects_spec.rb'
- './spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb'
- './spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_one_member_no_repo_projects_spec.rb'
-- './spec/lib/gitlab/background_migration/drop_invalid_security_findings_spec.rb'
-- './spec/lib/gitlab/background_migration/drop_invalid_vulnerabilities_spec.rb'
-- './spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb'
-- './spec/lib/gitlab/background_migration/encrypt_static_object_token_spec.rb'
- './spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb'
-- './spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb'
- './spec/lib/gitlab/background_migration/fix_duplicate_project_name_and_path_spec.rb'
-- './spec/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at_spec.rb'
-- './spec/lib/gitlab/background_migration/fix_merge_request_diff_commit_users_spec.rb'
- './spec/lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata_spec.rb'
- './spec/lib/gitlab/background_migration/job_coordinator_spec.rb'
- './spec/lib/gitlab/background_migration/legacy_upload_mover_spec.rb'
- './spec/lib/gitlab/background_migration/legacy_uploads_migrator_spec.rb'
- './spec/lib/gitlab/background_migration/mailers/unconfirm_mailer_spec.rb'
-- './spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb'
-- './spec/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users_spec.rb'
- './spec/lib/gitlab/background_migration/migrate_personal_namespace_project_maintainer_to_owner_spec.rb'
-- './spec/lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics_spec.rb'
- './spec/lib/gitlab/background_migration/migrate_shimo_confluence_integration_category_spec.rb'
-- './spec/lib/gitlab/background_migration/migrate_u2f_webauthn_spec.rb'
-- './spec/lib/gitlab/background_migration/move_container_registry_enabled_to_project_feature_spec.rb'
- './spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb'
- './spec/lib/gitlab/background_migration/populate_container_repository_migration_plan_spec.rb'
-- './spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb'
- './spec/lib/gitlab/background_migration/populate_operation_visibility_permissions_from_operations_spec.rb'
-- './spec/lib/gitlab/background_migration/populate_topics_non_private_projects_count_spec.rb'
-- './spec/lib/gitlab/background_migration/populate_topics_total_projects_count_cache_spec.rb'
-- './spec/lib/gitlab/background_migration/populate_vulnerability_reads_spec.rb'
- './spec/lib/gitlab/background_migration/project_namespaces/backfill_project_namespaces_spec.rb'
-- './spec/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid_spec.rb'
-- './spec/lib/gitlab/background_migration/remove_all_trace_expiration_dates_spec.rb'
-- './spec/lib/gitlab/background_migration/remove_duplicate_vulnerabilities_findings_spec.rb'
- './spec/lib/gitlab/background_migration/remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb'
- './spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb'
- './spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb'
@@ -5765,16 +5646,10 @@
- './spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb'
- './spec/lib/gitlab/background_migration/set_legacy_open_source_license_available_for_non_public_projects_spec.rb'
- './spec/lib/gitlab/background_migration_spec.rb'
-- './spec/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users_spec.rb'
- './spec/lib/gitlab/background_migration/update_delayed_project_removal_to_null_for_user_namespaces_spec.rb'
- './spec/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url_spec.rb'
-- './spec/lib/gitlab/background_migration/update_timelogs_null_spent_at_spec.rb'
-- './spec/lib/gitlab/background_migration/update_timelogs_project_id_spec.rb'
-- './spec/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group_spec.rb'
- './spec/lib/gitlab/background_task_spec.rb'
- './spec/lib/gitlab/backtrace_cleaner_spec.rb'
-- './spec/lib/gitlab/bare_repository_import/importer_spec.rb'
-- './spec/lib/gitlab/bare_repository_import/repository_spec.rb'
- './spec/lib/gitlab/batch_worker_context_spec.rb'
- './spec/lib/gitlab/bitbucket_import/importer_spec.rb'
- './spec/lib/gitlab/bitbucket_import/project_creator_spec.rb'
@@ -5973,7 +5848,6 @@
- './spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb'
- './spec/lib/gitlab/ci/pipeline/chain/command_spec.rb'
- './spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb'
-- './spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb'
- './spec/lib/gitlab/ci/pipeline/chain/create_spec.rb'
- './spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb'
- './spec/lib/gitlab/ci/pipeline/chain/ensure_resource_groups_spec.rb'
@@ -6042,7 +5916,6 @@
- './spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb'
- './spec/lib/gitlab/ci/reports/security/scanner_spec.rb'
- './spec/lib/gitlab/ci/reports/security/scan_spec.rb'
-- './spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb'
- './spec/lib/gitlab/ci/reports/terraform_reports_spec.rb'
- './spec/lib/gitlab/ci/reports/test_case_spec.rb'
- './spec/lib/gitlab/ci/reports/test_failure_history_spec.rb'
@@ -6235,8 +6108,6 @@
- './spec/lib/gitlab/database_importers/common_metrics/importer_spec.rb'
- './spec/lib/gitlab/database_importers/common_metrics/prometheus_metric_spec.rb'
- './spec/lib/gitlab/database_importers/instance_administrators/create_group_spec.rb'
-- './spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb'
-- './spec/lib/gitlab/database_importers/self_monitoring/project/delete_service_spec.rb'
- './spec/lib/gitlab/database_importers/work_items/base_type_importer_spec.rb'
- './spec/lib/gitlab/database/load_balancing/action_cable_callbacks_spec.rb'
- './spec/lib/gitlab/database/load_balancing/configuration_spec.rb'
@@ -6280,7 +6151,6 @@
- './spec/lib/gitlab/database/migrations/test_background_runner_spec.rb'
- './spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb'
- './spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb'
-- './spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb'
- './spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb'
- './spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb'
- './spec/lib/gitlab/database/partitioning_migration_helpers/foreign_key_helpers_spec.rb'
@@ -6418,7 +6288,6 @@
- './spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb'
- './spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb'
- './spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb'
-- './spec/lib/gitlab/email/hook/validate_addresses_interceptor_spec.rb'
- './spec/lib/gitlab/email/message/build_ios_app_guide_spec.rb'
- './spec/lib/gitlab/email/message/in_product_marketing/admin_verify_spec.rb'
- './spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb'
@@ -6449,7 +6318,6 @@
- './spec/lib/gitlab/error_tracking/processor/sanitize_error_message_processor_spec.rb'
- './spec/lib/gitlab/error_tracking/processor/sanitizer_processor_spec.rb'
- './spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb'
-- './spec/lib/gitlab/error_tracking_spec.rb'
- './spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb'
- './spec/lib/gitlab/etag_caching/middleware_spec.rb'
- './spec/lib/gitlab/etag_caching/router/graphql_spec.rb'
@@ -6507,7 +6375,6 @@
- './spec/lib/gitlab/gitaly_client/ref_service_spec.rb'
- './spec/lib/gitlab/gitaly_client/remote_service_spec.rb'
- './spec/lib/gitlab/gitaly_client/repository_service_spec.rb'
-- './spec/lib/gitlab/gitaly_client/server_service_spec.rb'
- './spec/lib/gitlab/gitaly_client_spec.rb'
- './spec/lib/gitlab/gitaly_client/storage_settings_spec.rb'
- './spec/lib/gitlab/gitaly_client/util_spec.rb'
@@ -6593,9 +6460,6 @@
- './spec/lib/gitlab/github_import_spec.rb'
- './spec/lib/gitlab/github_import/user_finder_spec.rb'
- './spec/lib/gitlab/git/keep_around_spec.rb'
-- './spec/lib/gitlab/gitlab_import/client_spec.rb'
-- './spec/lib/gitlab/gitlab_import/importer_spec.rb'
-- './spec/lib/gitlab/gitlab_import/project_creator_spec.rb'
- './spec/lib/gitlab/git/lfs_changes_spec.rb'
- './spec/lib/gitlab/git/lfs_pointer_file_spec.rb'
- './spec/lib/gitlab/git/merge_base_spec.rb'
@@ -6641,7 +6505,6 @@
- './spec/lib/gitlab/graphql/batch_key_spec.rb'
- './spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb'
- './spec/lib/gitlab/graphql/copy_field_description_spec.rb'
-- './spec/lib/gitlab/graphql/deprecation_spec.rb'
- './spec/lib/gitlab/graphql/generic_tracing_spec.rb'
- './spec/lib/gitlab/graphql/known_operations_spec.rb'
- './spec/lib/gitlab/graphql/lazy_spec.rb'
@@ -6729,7 +6592,6 @@
- './spec/lib/gitlab/import_export/error_spec.rb'
- './spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb'
- './spec/lib/gitlab/import_export/file_importer_spec.rb'
-- './spec/lib/gitlab/import_export/fork_spec.rb'
- './spec/lib/gitlab/import_export/group/object_builder_spec.rb'
- './spec/lib/gitlab/import_export/group/relation_factory_spec.rb'
- './spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb'
@@ -6737,13 +6599,9 @@
- './spec/lib/gitlab/import_export/group/tree_saver_spec.rb'
- './spec/lib/gitlab/import_export/hash_util_spec.rb'
- './spec/lib/gitlab/import_export/importer_spec.rb'
-- './spec/lib/gitlab/import_export/import_export_equivalence_spec.rb'
- './spec/lib/gitlab/import_export/import_export_spec.rb'
- './spec/lib/gitlab/import_export/import_failure_service_spec.rb'
- './spec/lib/gitlab/import_export/import_test_coverage_spec.rb'
-- './spec/lib/gitlab/import_export/json/legacy_reader/file_spec.rb'
-- './spec/lib/gitlab/import_export/json/legacy_reader/hash_spec.rb'
-- './spec/lib/gitlab/import_export/json/legacy_writer_spec.rb'
- './spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb'
- './spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb'
- './spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb'
@@ -6790,7 +6648,6 @@
- './spec/lib/gitlab/import/set_async_jid_spec.rb'
- './spec/lib/gitlab/import_sources_spec.rb'
- './spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb'
-- './spec/lib/gitlab/incoming_email_spec.rb'
- './spec/lib/gitlab/insecure_key_fingerprint_spec.rb'
- './spec/lib/gitlab/instrumentation_helper_spec.rb'
- './spec/lib/gitlab/instrumentation/rate_limiting_gates_spec.rb'
@@ -6829,19 +6686,6 @@
- './spec/lib/gitlab/kubernetes/default_namespace_spec.rb'
- './spec/lib/gitlab/kubernetes/deployment_spec.rb'
- './spec/lib/gitlab/kubernetes/generic_secret_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/api_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/pod_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v2/base_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v2/delete_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v2/init_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v2/install_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v2/patch_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v2/reset_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v3/base_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v3/delete_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v3/install_command_spec.rb'
-- './spec/lib/gitlab/kubernetes/helm/v3/patch_command_spec.rb'
- './spec/lib/gitlab/kubernetes/ingress_spec.rb'
- './spec/lib/gitlab/kubernetes/kube_client_spec.rb'
- './spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb'
@@ -7031,21 +6875,6 @@
- './spec/lib/gitlab/performance_bar_spec.rb'
- './spec/lib/gitlab/performance_bar/stats_spec.rb'
- './spec/lib/gitlab/performance_bar/with_top_level_warnings_spec.rb'
-- './spec/lib/gitlab/phabricator_import/cache/map_spec.rb'
-- './spec/lib/gitlab/phabricator_import/conduit/client_spec.rb'
-- './spec/lib/gitlab/phabricator_import/conduit/maniphest_spec.rb'
-- './spec/lib/gitlab/phabricator_import/conduit/response_spec.rb'
-- './spec/lib/gitlab/phabricator_import/conduit/tasks_response_spec.rb'
-- './spec/lib/gitlab/phabricator_import/conduit/user_spec.rb'
-- './spec/lib/gitlab/phabricator_import/conduit/users_response_spec.rb'
-- './spec/lib/gitlab/phabricator_import/importer_spec.rb'
-- './spec/lib/gitlab/phabricator_import/issues/importer_spec.rb'
-- './spec/lib/gitlab/phabricator_import/issues/task_importer_spec.rb'
-- './spec/lib/gitlab/phabricator_import/project_creator_spec.rb'
-- './spec/lib/gitlab/phabricator_import/representation/task_spec.rb'
-- './spec/lib/gitlab/phabricator_import/representation/user_spec.rb'
-- './spec/lib/gitlab/phabricator_import/user_finder_spec.rb'
-- './spec/lib/gitlab/phabricator_import/worker_state_spec.rb'
- './spec/lib/gitlab/pipeline_scope_counts_spec.rb'
- './spec/lib/gitlab/polling_interval_spec.rb'
- './spec/lib/gitlab/popen/runner_spec.rb'
@@ -7068,7 +6897,6 @@
- './spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb'
- './spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb'
- './spec/lib/gitlab/prometheus/queries/deployment_query_spec.rb'
-- './spec/lib/gitlab/prometheus/queries/knative_invocation_query_spec.rb'
- './spec/lib/gitlab/prometheus/queries/matched_metric_query_spec.rb'
- './spec/lib/gitlab/prometheus/queries/validate_query_spec.rb'
- './spec/lib/gitlab/prometheus/query_variables_spec.rb'
@@ -7085,14 +6913,12 @@
- './spec/lib/gitlab/quick_actions/spend_time_and_date_separator_spec.rb'
- './spec/lib/gitlab/quick_actions/substitution_definition_spec.rb'
- './spec/lib/gitlab/quick_actions/users_extractor_spec.rb'
-- './spec/lib/gitlab/rack_attack/instrumented_cache_store_spec.rb'
- './spec/lib/gitlab/rack_attack/request_spec.rb'
- './spec/lib/gitlab/rack_attack_spec.rb'
- './spec/lib/gitlab/rack_attack/user_allowlist_spec.rb'
- './spec/lib/gitlab/reactive_cache_set_cache_spec.rb'
- './spec/lib/gitlab/redis/boolean_spec.rb'
- './spec/lib/gitlab/redis/cache_spec.rb'
-- './spec/lib/gitlab/redis/duplicate_jobs_spec.rb'
- './spec/lib/gitlab/redis/hll_spec.rb'
- './spec/lib/gitlab/redis/multi_store_spec.rb'
- './spec/lib/gitlab/redis/queues_spec.rb'
@@ -7154,8 +6980,6 @@
- './spec/lib/gitlab/seeder_spec.rb'
- './spec/lib/gitlab/serializer/ci/variables_spec.rb'
- './spec/lib/gitlab/serializer/pagination_spec.rb'
-- './spec/lib/gitlab/serverless/service_spec.rb'
-- './spec/lib/gitlab/service_desk_email_spec.rb'
- './spec/lib/gitlab/service_desk_spec.rb'
- './spec/lib/gitlab/session_spec.rb'
- './spec/lib/gitlab/setup_helper/praefect_spec.rb'
@@ -7225,7 +7049,6 @@
- './spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb'
- './spec/lib/gitlab/slash_commands/presenters/run_spec.rb'
- './spec/lib/gitlab/slash_commands/run_spec.rb'
-- './spec/lib/gitlab/slug/environment_spec.rb'
- './spec/lib/gitlab/snippet_search_results_spec.rb'
- './spec/lib/gitlab/sourcegraph_spec.rb'
- './spec/lib/gitlab/spamcheck/client_spec.rb'
@@ -7306,7 +7129,6 @@
- './spec/lib/gitlab/usage_data_counters/snippet_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/source_code_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters_spec.rb'
-- './spec/lib/gitlab/usage_data_counters/track_unique_events_spec.rb'
- './spec/lib/gitlab/usage_data_counters/vscode_extension_activity_unique_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb'
- './spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb'
@@ -7445,7 +7267,6 @@
- './spec/lib/sidebars/concerns/link_with_html_options_spec.rb'
- './spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb'
- './spec/lib/sidebars/groups/menus/group_information_menu_spec.rb'
-- './spec/lib/sidebars/groups/menus/invite_team_members_menu_spec.rb'
- './spec/lib/sidebars/groups/menus/issues_menu_spec.rb'
- './spec/lib/sidebars/groups/menus/kubernetes_menu_spec.rb'
- './spec/lib/sidebars/groups/menus/merge_requests_menu_spec.rb'
@@ -7463,7 +7284,6 @@
- './spec/lib/sidebars/projects/menus/external_wiki_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/hidden_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb'
-- './spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/issues_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb'
- './spec/lib/sidebars/projects/menus/monitor_menu_spec.rb'
@@ -7490,7 +7310,6 @@
- './spec/lib/system_check/sidekiq_check_spec.rb'
- './spec/lib/system_check/simple_executor_spec.rb'
- './spec/lib/system_check_spec.rb'
-- './spec/lib/tasks/gitlab/metrics_exporter_task_spec.rb'
- './spec/lib/unnested_in_filters/dsl_spec.rb'
- './spec/lib/unnested_in_filters/rewriter_spec.rb'
- './spec/lib/uploaded_file_spec.rb'
@@ -7513,72 +7332,6 @@
- './spec/mailers/notify_spec.rb'
- './spec/mailers/repository_check_mailer_spec.rb'
- './spec/metrics_server/metrics_server_spec.rb'
-- './spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb'
-- './spec/migrations/20210610153556_delete_legacy_operations_feature_flags_spec.rb'
-- './spec/migrations/2021061716138_cascade_delete_freeze_periods_spec.rb'
-- './spec/migrations/20210708130419_reschedule_merge_request_diff_users_background_migration_spec.rb'
-- './spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb'
-- './spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb'
-- './spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb'
-- './spec/migrations/20210804150320_create_base_work_item_types_spec.rb'
-- './spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb'
-- './spec/migrations/20210811122206_update_external_project_bots_spec.rb'
-- './spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb'
-- './spec/migrations/20210819145000_drop_temporary_columns_and_triggers_for_ci_builds_runner_session_spec.rb'
-- './spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb'
-- './spec/migrations/20210902144144_drop_temporary_columns_and_triggers_for_ci_build_needs_spec.rb'
-- './spec/migrations/20210906100316_drop_temporary_columns_and_triggers_for_ci_build_trace_chunks_spec.rb'
-- './spec/migrations/20210906130643_drop_temporary_columns_and_triggers_for_taggings_spec.rb'
-- './spec/migrations/20210907013944_cleanup_bigint_conversion_for_ci_builds_metadata_spec.rb'
-- './spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb'
-- './spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb'
-- './spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb'
-- './spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb'
-- './spec/migrations/20210918201050_remove_old_pending_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb'
-- './spec/migrations/20210922021816_drop_int4_columns_for_ci_job_artifacts_spec.rb'
-- './spec/migrations/20210922025631_drop_int4_column_for_ci_sources_pipelines_spec.rb'
-- './spec/migrations/20210922082019_drop_int4_column_for_events_spec.rb'
-- './spec/migrations/20210922091402_drop_int4_column_for_push_event_payloads_spec.rb'
-- './spec/migrations/20211006060436_schedule_populate_topics_total_projects_count_cache_spec.rb'
-- './spec/migrations/20211012134316_clean_up_migrate_merge_request_diff_commit_users_spec.rb'
-- './spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb'
-- './spec/migrations/20211028155449_schedule_fix_merge_request_diff_commit_users_migration_spec.rb'
-- './spec/migrations/20211101222614_consume_remaining_user_namespace_jobs_spec.rb'
-- './spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb'
-- './spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb'
-- './spec/migrations/20211116091751_change_namespace_type_default_to_user_spec.rb'
-- './spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb'
-- './spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb'
-- './spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb'
-- './spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb'
-- './spec/migrations/20211130165043_backfill_sequence_column_for_sprints_table_spec.rb'
-- './spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb'
-- './spec/migrations/20211207125331_remove_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb'
-- './spec/migrations/20211207135331_schedule_recalculate_uuid_on_vulnerabilities_occurrences4_spec.rb'
-- './spec/migrations/20211210140629_encrypt_static_object_token_spec.rb'
-- './spec/migrations/20211217174331_mark_recalculate_finding_signatures_as_completed_spec.rb'
-- './spec/migrations/20220106111958_add_insert_or_update_vulnerability_reads_trigger_spec.rb'
-- './spec/migrations/20220106112043_add_update_vulnerability_reads_trigger_spec.rb'
-- './spec/migrations/20220106112085_add_update_vulnerability_reads_location_trigger_spec.rb'
-- './spec/migrations/20220106163326_add_has_issues_on_vulnerability_reads_trigger_spec.rb'
-- './spec/migrations/20220107064845_populate_vulnerability_reads_spec.rb'
-- './spec/migrations/20220120094340_drop_position_from_security_findings_spec.rb'
-- './spec/migrations/20220124130028_dedup_runner_projects_spec.rb'
-- './spec/migrations/20220128155251_remove_dangling_running_builds_spec.rb'
-- './spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb'
-- './spec/migrations/20220202105733_delete_service_template_records_spec.rb'
-- './spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb'
-- './spec/migrations/20220204194347_encrypt_integration_properties_spec.rb'
-- './spec/migrations/20220208080921_schedule_migrate_personal_namespace_project_maintainer_to_owner_spec.rb'
-- './spec/migrations/20220211214605_update_integrations_trigger_type_new_on_insert_null_safe_spec.rb'
-- './spec/migrations/20220213103859_remove_integrations_type_spec.rb'
-- './spec/migrations/20220222192524_create_not_null_constraint_releases_tag_spec.rb'
-- './spec/migrations/20220222192525_remove_null_releases_spec.rb'
-- './spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb'
-- './spec/migrations/20220305223212_add_security_training_providers_spec.rb'
-- './spec/migrations/20220307192610_remove_duplicate_project_tag_releases_spec.rb'
-- './spec/migrations/20220309084954_remove_leftover_external_pull_request_deletions_spec.rb'
-- './spec/migrations/20220310141349_remove_dependency_list_usage_data_from_redis_spec.rb'
- './spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb'
- './spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb'
- './spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb'
@@ -7621,28 +7374,11 @@
- './spec/migrations/20220802114351_reschedule_backfill_container_registry_size_into_project_statistics_spec.rb'
- './spec/migrations/20220802204737_remove_deactivated_user_highest_role_stats_spec.rb'
- './spec/migrations/active_record/schema_spec.rb'
-- './spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb'
- './spec/migrations/add_epics_relative_position_spec.rb'
-- './spec/migrations/add_open_source_plan_spec.rb'
-- './spec/migrations/add_premium_and_ultimate_plan_limits_spec.rb'
-- './spec/migrations/add_triggers_to_integrations_type_new_spec.rb'
-- './spec/migrations/add_upvotes_count_index_to_issues_spec.rb'
- './spec/migrations/add_web_hook_calls_to_plan_limits_paid_tiers_spec.rb'
-- './spec/migrations/associate_existing_dast_builds_with_variables_spec.rb'
-- './spec/migrations/backfill_all_project_namespaces_spec.rb'
-- './spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb'
-- './spec/migrations/backfill_cycle_analytics_aggregations_spec.rb'
-- './spec/migrations/backfill_group_features_spec.rb'
- './spec/migrations/backfill_integrations_enable_ssl_verification_spec.rb'
-- './spec/migrations/backfill_integrations_type_new_spec.rb'
-- './spec/migrations/backfill_issues_upvotes_count_spec.rb'
-- './spec/migrations/backfill_member_namespace_id_for_group_members_spec.rb'
-- './spec/migrations/backfill_namespace_id_for_namespace_routes_spec.rb'
- './spec/migrations/backfill_namespace_id_for_project_routes_spec.rb'
- './spec/migrations/backfill_project_import_level_spec.rb'
-- './spec/migrations/backfill_project_namespaces_for_group_spec.rb'
-- './spec/migrations/backfill_stage_event_hash_spec.rb'
-- './spec/migrations/backfill_user_namespace_spec.rb'
- './spec/migrations/bulk_insert_cluster_enabled_grants_spec.rb'
- './spec/migrations/change_public_projects_cost_factor_spec.rb'
- './spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb'
@@ -7650,60 +7386,21 @@
- './spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb'
- './spec/migrations/cleanup_mr_attention_request_todos_spec.rb'
- './spec/migrations/cleanup_orphaned_routes_spec.rb'
-- './spec/migrations/cleanup_remaining_orphan_invites_spec.rb'
-- './spec/migrations/confirm_security_bot_spec.rb'
-- './spec/migrations/disable_expiration_policies_linked_to_no_container_images_spec.rb'
-- './spec/migrations/disable_job_token_scope_when_unused_spec.rb'
- './spec/migrations/finalize_orphaned_routes_cleanup_spec.rb'
- './spec/migrations/finalize_project_namespaces_backfill_spec.rb'
- './spec/migrations/finalize_routes_backfilling_for_projects_spec.rb'
-- './spec/migrations/finalize_traversal_ids_background_migrations_spec.rb'
- './spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb'
-- './spec/migrations/fix_batched_migrations_old_format_job_arguments_spec.rb'
-- './spec/migrations/generate_customers_dot_jwt_signing_key_spec.rb'
-- './spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb'
-- './spec/migrations/orphaned_invite_tokens_cleanup_spec.rb'
-- './spec/migrations/populate_audit_event_streaming_verification_token_spec.rb'
- './spec/migrations/populate_operation_visibility_permissions_spec.rb'
- './spec/migrations/queue_backfill_project_feature_package_registry_access_level_spec.rb'
-- './spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb'
-- './spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb'
-- './spec/migrations/remove_duplicate_dast_site_tokens_spec.rb'
-- './spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb'
- './spec/migrations/remove_invalid_integrations_spec.rb'
-- './spec/migrations/remove_not_null_contraint_on_title_from_sprints_spec.rb'
-- './spec/migrations/remove_schedule_and_status_from_pending_alert_escalations_spec.rb'
- './spec/migrations/remove_wiki_notes_spec.rb'
-- './spec/migrations/rename_services_to_integrations_spec.rb'
-- './spec/migrations/replace_external_wiki_triggers_spec.rb'
- './spec/migrations/reschedule_backfill_imported_issue_search_data_spec.rb'
-- './spec/migrations/reschedule_delete_orphaned_deployments_spec.rb'
-- './spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb'
-- './spec/migrations/reset_job_token_scope_enabled_again_spec.rb'
-- './spec/migrations/reset_job_token_scope_enabled_spec.rb'
-- './spec/migrations/reset_severity_levels_to_new_default_spec.rb'
-- './spec/migrations/retry_backfill_traversal_ids_spec.rb'
- './spec/migrations/schedule_backfill_draft_status_on_merge_requests_corrected_regex_spec.rb'
- './spec/migrations/schedule_backfilling_the_namespace_id_for_vulnerability_reads_spec.rb'
-- './spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb'
-- './spec/migrations/schedule_fix_incorrect_max_seats_used2_spec.rb'
-- './spec/migrations/schedule_fix_incorrect_max_seats_used_spec.rb'
- './spec/migrations/schedule_populate_requirements_issue_id_spec.rb'
- './spec/migrations/schedule_purging_stale_security_scans_spec.rb'
-- './spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb'
-- './spec/migrations/schedule_security_setting_creation_spec.rb'
- './spec/migrations/schedule_set_correct_vulnerability_state_spec.rb'
-- './spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb'
-- './spec/migrations/set_default_job_token_scope_true_spec.rb'
-- './spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb'
-- './spec/migrations/start_backfill_ci_queuing_tables_spec.rb'
-- './spec/migrations/steal_merge_request_diff_commit_users_migration_spec.rb'
- './spec/migrations/toggle_vsa_aggregations_enable_spec.rb'
-- './spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb'
-- './spec/migrations/update_application_settings_protected_paths_spec.rb'
-- './spec/migrations/update_default_scan_method_of_dast_site_profile_spec.rb'
-- './spec/migrations/update_integrations_trigger_type_new_on_insert_spec.rb'
-- './spec/migrations/update_invalid_member_states_spec.rb'
- './spec/models/ability_spec.rb'
- './spec/models/abuse_report_spec.rb'
- './spec/models/active_session_spec.rb'
@@ -7718,7 +7415,6 @@
- './spec/models/analytics/cycle_analytics/aggregation_spec.rb'
- './spec/models/analytics/cycle_analytics/issue_stage_event_spec.rb'
- './spec/models/analytics/cycle_analytics/merge_request_stage_event_spec.rb'
-- './spec/models/analytics/cycle_analytics/project_value_stream_spec.rb'
- './spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb'
- './spec/models/analytics/usage_trends/measurement_spec.rb'
- './spec/models/appearance_spec.rb'
@@ -7730,7 +7426,6 @@
- './spec/models/audit_event_spec.rb'
- './spec/models/authentication_event_spec.rb'
- './spec/models/award_emoji_spec.rb'
-- './spec/models/awareness_session_spec.rb'
- './spec/models/aws/role_spec.rb'
- './spec/models/badges/group_badge_spec.rb'
- './spec/models/badge_spec.rb'
@@ -7754,7 +7449,6 @@
- './spec/models/board_group_recent_visit_spec.rb'
- './spec/models/board_project_recent_visit_spec.rb'
- './spec/models/board_spec.rb'
-- './spec/models/broadcast_message_spec.rb'
- './spec/models/bulk_imports/configuration_spec.rb'
- './spec/models/bulk_imports/entity_spec.rb'
- './spec/models/bulk_imports/export_spec.rb'
@@ -7824,20 +7518,8 @@
- './spec/models/ci/unit_test_spec.rb'
- './spec/models/ci/variable_spec.rb'
- './spec/models/clusters/agents/activity_event_spec.rb'
-- './spec/models/clusters/agents/group_authorization_spec.rb'
-- './spec/models/clusters/agents/implicit_authorization_spec.rb'
- './spec/models/clusters/agent_spec.rb'
-- './spec/models/clusters/agents/project_authorization_spec.rb'
- './spec/models/clusters/agent_token_spec.rb'
-- './spec/models/clusters/applications/cert_manager_spec.rb'
-- './spec/models/clusters/applications/cilium_spec.rb'
-- './spec/models/clusters/applications/crossplane_spec.rb'
-- './spec/models/clusters/applications/helm_spec.rb'
-- './spec/models/clusters/applications/ingress_spec.rb'
-- './spec/models/clusters/applications/jupyter_spec.rb'
-- './spec/models/clusters/applications/knative_spec.rb'
-- './spec/models/clusters/applications/prometheus_spec.rb'
-- './spec/models/clusters/applications/runner_spec.rb'
- './spec/models/clusters/cluster_enabled_grant_spec.rb'
- './spec/models/clusters/clusters_hierarchy_spec.rb'
- './spec/models/clusters/cluster_spec.rb'
@@ -7862,7 +7544,6 @@
- './spec/models/concerns/atomic_internal_id_spec.rb'
- './spec/models/concerns/avatarable_spec.rb'
- './spec/models/concerns/awardable_spec.rb'
-- './spec/models/concerns/awareness_spec.rb'
- './spec/models/concerns/batch_destroy_dependent_associations_spec.rb'
- './spec/models/concerns/batch_nullify_dependent_associations_spec.rb'
- './spec/models/concerns/blob_language_from_git_attributes_spec.rb'
@@ -7880,7 +7561,6 @@
- './spec/models/concerns/ci/has_status_spec.rb'
- './spec/models/concerns/ci/has_variable_spec.rb'
- './spec/models/concerns/ci/maskable_spec.rb'
-- './spec/models/concerns/clusters/agents/authorization_config_scopes_spec.rb'
- './spec/models/concerns/counter_attribute_spec.rb'
- './spec/models/concerns/cron_schedulable_spec.rb'
- './spec/models/concerns/cross_database_modification_spec.rb'
@@ -7956,7 +7636,6 @@
- './spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb'
- './spec/models/concerns/transactions_spec.rb'
- './spec/models/concerns/triggerable_hooks_spec.rb'
-- './spec/models/concerns/uniquify_spec.rb'
- './spec/models/concerns/usage_statistics_spec.rb'
- './spec/models/concerns/vulnerability_finding_helpers_spec.rb'
- './spec/models/concerns/vulnerability_finding_signature_helpers_spec.rb'
@@ -8015,7 +7694,6 @@
- './spec/models/exported_protected_branch_spec.rb'
- './spec/models/external_issue_spec.rb'
- './spec/models/external_pull_request_spec.rb'
-- './spec/models/factories_spec.rb'
- './spec/models/fork_network_member_spec.rb'
- './spec/models/fork_network_spec.rb'
- './spec/models/generic_commit_status_spec.rb'
@@ -8131,7 +7809,6 @@
- './spec/models/loose_foreign_keys/modification_tracker_spec.rb'
- './spec/models/members/group_member_spec.rb'
- './spec/models/members/last_group_owner_assigner_spec.rb'
-- './spec/models/members/member_role_spec.rb'
- './spec/models/members/member_task_spec.rb'
- './spec/models/member_spec.rb'
- './spec/models/members/project_member_spec.rb'
@@ -8239,7 +7916,6 @@
- './spec/models/preloaders/merge_request_diff_preloader_spec.rb'
- './spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb'
- './spec/models/preloaders/user_max_access_level_in_projects_preloader_spec.rb'
-- './spec/models/preloaders/users_max_access_level_in_projects_preloader_spec.rb'
- './spec/models/product_analytics_event_spec.rb'
- './spec/models/programming_language_spec.rb'
- './spec/models/project_authorization_spec.rb'
@@ -8300,9 +7976,6 @@
- './spec/models/route_spec.rb'
- './spec/models/sent_notification_spec.rb'
- './spec/models/sentry_issue_spec.rb'
-- './spec/models/serverless/domain_cluster_spec.rb'
-- './spec/models/serverless/domain_spec.rb'
-- './spec/models/serverless/function_spec.rb'
- './spec/models/service_desk_setting_spec.rb'
- './spec/models/shard_spec.rb'
- './spec/models/snippet_blob_spec.rb'
@@ -8566,7 +8239,6 @@
- './spec/requests/api/environments_spec.rb'
- './spec/requests/api/error_tracking/client_keys_spec.rb'
- './spec/requests/api/error_tracking/collector_spec.rb'
-- './spec/requests/api/error_tracking/project_settings_spec.rb'
- './spec/requests/api/events_spec.rb'
- './spec/requests/api/feature_flags_spec.rb'
- './spec/requests/api/feature_flags_user_lists_spec.rb'
@@ -8612,7 +8284,6 @@
- './spec/requests/api/graphql/group/dependency_proxy_manifests_spec.rb'
- './spec/requests/api/graphql/group/group_members_spec.rb'
- './spec/requests/api/graphql/group/issues_spec.rb'
-- './spec/requests/api/graphql/group/labels_query_spec.rb'
- './spec/requests/api/graphql/group/merge_requests_spec.rb'
- './spec/requests/api/graphql/group/milestones_spec.rb'
- './spec/requests/api/graphql/group/packages_spec.rb'
@@ -8650,12 +8321,8 @@
- './spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb'
- './spec/requests/api/graphql/mutations/boards/lists/update_spec.rb'
- './spec/requests/api/graphql/mutations/branches/create_spec.rb'
-- './spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb'
-- './spec/requests/api/graphql/mutations/ci/job_play_spec.rb'
-- './spec/requests/api/graphql/mutations/ci/job_retry_spec.rb'
- './spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb'
- './spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb'
-- './spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb'
- './spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb'
- './spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb'
- './spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb'
@@ -8956,7 +8623,6 @@
- './spec/requests/groups/settings/access_tokens_controller_spec.rb'
- './spec/requests/groups/settings/applications_controller_spec.rb'
- './spec/requests/health_controller_spec.rb'
-- './spec/requests/ide_controller_spec.rb'
- './spec/requests/import/gitlab_groups_controller_spec.rb'
- './spec/requests/import/gitlab_projects_controller_spec.rb'
- './spec/requests/import/url_controller_spec.rb'
@@ -8965,7 +8631,6 @@
- './spec/requests/jira_connect/oauth_application_ids_controller_spec.rb'
- './spec/requests/jira_connect/oauth_callbacks_controller_spec.rb'
- './spec/requests/jira_connect/subscriptions_controller_spec.rb'
-- './spec/requests/jira_connect/users_controller_spec.rb'
- './spec/requests/jira_routing_spec.rb'
- './spec/requests/jwks_controller_spec.rb'
- './spec/requests/jwt_controller_spec.rb'
@@ -9024,7 +8689,6 @@
- './spec/requests/runner_setup_controller_spec.rb'
- './spec/requests/sandbox_controller_spec.rb'
- './spec/requests/search_controller_spec.rb'
-- './spec/requests/self_monitoring_project_spec.rb'
- './spec/requests/sessions_spec.rb'
- './spec/requests/terraform/services_controller_spec.rb'
- './spec/requests/user_activity_spec.rb'
@@ -9086,7 +8750,6 @@
- './spec/serializers/ci/trigger_entity_spec.rb'
- './spec/serializers/ci/trigger_serializer_spec.rb'
- './spec/serializers/ci/variable_entity_spec.rb'
-- './spec/serializers/cluster_application_entity_spec.rb'
- './spec/serializers/cluster_entity_spec.rb'
- './spec/serializers/cluster_serializer_spec.rb'
- './spec/serializers/clusters/kubernetes_error_entity_spec.rb'
@@ -9407,7 +9070,6 @@
- './spec/services/clusters/agents/create_service_spec.rb'
- './spec/services/clusters/agents/delete_expired_events_service_spec.rb'
- './spec/services/clusters/agents/delete_service_spec.rb'
-- './spec/services/clusters/agents/refresh_authorization_service_spec.rb'
- './spec/services/clusters/agent_tokens/create_service_spec.rb'
- './spec/services/clusters/agent_tokens/track_usage_service_spec.rb'
- './spec/services/clusters/build_kubernetes_namespace_service_spec.rb'
@@ -9685,7 +9347,6 @@
- './spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb'
- './spec/services/metrics/dashboard/panel_preview_service_spec.rb'
- './spec/services/metrics/dashboard/pod_dashboard_service_spec.rb'
-- './spec/services/metrics/dashboard/self_monitoring_dashboard_service_spec.rb'
- './spec/services/metrics/dashboard/system_dashboard_service_spec.rb'
- './spec/services/metrics/dashboard/transient_embed_service_spec.rb'
- './spec/services/metrics/dashboard/update_dashboard_service_spec.rb'
@@ -9795,12 +9456,10 @@
- './spec/services/projects/alerting/notify_service_spec.rb'
- './spec/services/projects/all_issues_count_service_spec.rb'
- './spec/services/projects/all_merge_requests_count_service_spec.rb'
-- './spec/services/projects/android_target_platform_detector_service_spec.rb'
- './spec/services/projects/apple_target_platform_detector_service_spec.rb'
- './spec/services/projects/autocomplete_service_spec.rb'
- './spec/services/projects/auto_devops/disable_service_spec.rb'
- './spec/services/projects/batch_open_issues_count_service_spec.rb'
-- './spec/services/projects/blame_service_spec.rb'
- './spec/services/projects/branches_by_mode_service_spec.rb'
- './spec/services/projects/cleanup_service_spec.rb'
- './spec/services/projects/container_repository/cleanup_tags_service_spec.rb'
@@ -9911,7 +9570,6 @@
- './spec/services/security/ci_configuration/sast_parser_service_spec.rb'
- './spec/services/security/ci_configuration/secret_detection_create_service_spec.rb'
- './spec/services/security/merge_reports_service_spec.rb'
-- './spec/services/serverless/associate_domain_service_spec.rb'
- './spec/services/service_desk_settings/update_service_spec.rb'
- './spec/services/service_ping/submit_service_ping_service_spec.rb'
- './spec/services/service_response_spec.rb'
@@ -10057,7 +9715,6 @@
- './spec/tasks/cache/clear/redis_spec.rb'
- './spec/tasks/config_lint_spec.rb'
- './spec/tasks/dev_rake_spec.rb'
-- './spec/tasks/gettext_rake_spec.rb'
- './spec/tasks/gitlab/artifacts/check_rake_spec.rb'
- './spec/tasks/gitlab/artifacts/migrate_rake_spec.rb'
- './spec/tasks/gitlab/background_migrations_rake_spec.rb'
@@ -10106,7 +9763,7 @@
- './spec/tooling/danger/customer_success_spec.rb'
- './spec/tooling/danger/datateam_spec.rb'
- './spec/tooling/danger/feature_flag_spec.rb'
-- './spec/tooling/danger/product_intelligence_spec.rb'
+- './spec/tooling/danger/analytics_instrumentation_spec.rb'
- './spec/tooling/danger/project_helper_spec.rb'
- './spec/tooling/danger/sidekiq_queues_spec.rb'
- './spec/tooling/danger/specs_spec.rb'
@@ -10188,7 +9845,6 @@
- './spec/views/admin/application_settings/_repository_check.html.haml_spec.rb'
- './spec/views/admin/application_settings/repository.html.haml_spec.rb'
- './spec/views/admin/application_settings/_repository_storage.html.haml_spec.rb'
-- './spec/views/admin/broadcast_messages/index.html.haml_spec.rb'
- './spec/views/admin/dashboard/index.html.haml_spec.rb'
- './spec/views/admin/identities/index.html.haml_spec.rb'
- './spec/views/admin/sessions/new.html.haml_spec.rb'
@@ -10231,7 +9887,6 @@
- './spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
- './spec/views/layouts/profile.html.haml_spec.rb'
- './spec/views/layouts/_published_experiments.html.haml_spec.rb'
-- './spec/views/layouts/_search.html.haml_spec.rb'
- './spec/views/layouts/signup_onboarding.html.haml_spec.rb'
- './spec/views/layouts/simple_registration.html.haml_spec.rb'
- './spec/views/layouts/terms.html.haml_spec.rb'
@@ -10307,7 +9962,6 @@
- './spec/views/shared/projects/_inactive_project_deletion_alert.html.haml_spec.rb'
- './spec/views/shared/projects/_list.html.haml_spec.rb'
- './spec/views/shared/projects/_project.html.haml_spec.rb'
-- './spec/views/shared/runners/_runner_details.html.haml_spec.rb'
- './spec/views/shared/snippets/_snippet.html.haml_spec.rb'
- './spec/views/shared/wikis/_sidebar.html.haml_spec.rb'
- './spec/workers/admin_email_worker_spec.rb'
@@ -10343,7 +9997,6 @@
- './spec/workers/ci/build_schedule_worker_spec.rb'
- './spec/workers/ci/build_trace_chunk_flush_worker_spec.rb'
- './spec/workers/ci/cancel_pipeline_worker_spec.rb'
-- './spec/workers/ci/create_cross_project_pipeline_worker_spec.rb'
- './spec/workers/ci/create_downstream_pipeline_worker_spec.rb'
- './spec/workers/ci/daily_build_group_report_results_worker_spec.rb'
- './spec/workers/ci/delete_objects_worker_spec.rb'
@@ -10381,18 +10034,14 @@
- './spec/workers/clusters/integrations/check_prometheus_health_worker_spec.rb'
- './spec/workers/concerns/application_worker_spec.rb'
- './spec/workers/concerns/cluster_agent_queue_spec.rb'
-- './spec/workers/concerns/cluster_queue_spec.rb'
- './spec/workers/concerns/cronjob_queue_spec.rb'
- './spec/workers/concerns/gitlab/github_import/object_importer_spec.rb'
-- './spec/workers/concerns/gitlab/github_import/queue_spec.rb'
- './spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb'
- './spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb'
- './spec/workers/concerns/gitlab/notify_upon_death_spec.rb'
- './spec/workers/concerns/limited_capacity/job_tracker_spec.rb'
- './spec/workers/concerns/limited_capacity/worker_spec.rb'
- './spec/workers/concerns/packages/cleanup_artifact_worker_spec.rb'
-- './spec/workers/concerns/pipeline_background_queue_spec.rb'
-- './spec/workers/concerns/pipeline_queue_spec.rb'
- './spec/workers/concerns/project_import_options_spec.rb'
- './spec/workers/concerns/reenqueuer_spec.rb'
- './spec/workers/concerns/repository_check_queue_spec.rb'
@@ -10422,7 +10071,6 @@
- './spec/workers/dependency_proxy/cleanup_manifest_worker_spec.rb'
- './spec/workers/dependency_proxy/image_ttl_group_policy_worker_spec.rb'
- './spec/workers/deployments/archive_in_project_worker_spec.rb'
-- './spec/workers/deployments/drop_older_deployments_worker_spec.rb'
- './spec/workers/deployments/hooks_worker_spec.rb'
- './spec/workers/deployments/link_merge_request_worker_spec.rb'
- './spec/workers/deployments/update_environment_worker_spec.rb'
@@ -10475,8 +10123,6 @@
- './spec/workers/gitlab/jira_import/stage/start_import_worker_spec.rb'
- './spec/workers/gitlab/jira_import/stuck_jira_import_jobs_worker_spec.rb'
- './spec/workers/gitlab_performance_bar_stats_worker_spec.rb'
-- './spec/workers/gitlab/phabricator_import/base_worker_spec.rb'
-- './spec/workers/gitlab/phabricator_import/import_tasks_worker_spec.rb'
- './spec/workers/gitlab_service_ping_worker_spec.rb'
- './spec/workers/gitlab_shell_worker_spec.rb'
- './spec/workers/google_cloud/create_cloudsql_instance_worker_spec.rb'
@@ -10614,8 +10260,6 @@
- './spec/workers/run_pipeline_schedule_worker_spec.rb'
- './spec/workers/schedule_merge_request_cleanup_refs_worker_spec.rb'
- './spec/workers/schedule_migrate_external_diffs_worker_spec.rb'
-- './spec/workers/self_monitoring_project_create_worker_spec.rb'
-- './spec/workers/self_monitoring_project_delete_worker_spec.rb'
- './spec/workers/service_desk_email_receiver_worker_spec.rb'
- './spec/workers/snippets/schedule_bulk_repository_shard_moves_worker_spec.rb'
- './spec/workers/snippets/update_repository_storage_worker_spec.rb'
diff --git a/spec/support/shared_contexts/bulk_imports_requests_shared_context.rb b/spec/support/shared_contexts/bulk_imports_requests_shared_context.rb
index e8fc498cbf7..7074b073a0c 100644
--- a/spec/support/shared_contexts/bulk_imports_requests_shared_context.rb
+++ b/spec/support/shared_contexts/bulk_imports_requests_shared_context.rb
@@ -24,16 +24,18 @@ RSpec.shared_context 'bulk imports requests context' do |url|
stub_request(:get, "https://gitlab.example.com/api/v4/groups?min_access_level=50&page=1&per_page=20&private_token=demo-pat&search=test&top_level_only=true")
.with(headers: request_headers)
- .to_return(status: 200,
- body: [{
- id: 2595440,
- web_url: 'https://gitlab.com/groups/test',
- name: 'Test',
- path: 'stub-test-group',
- full_name: 'Test',
- full_path: 'stub-test-group'
- }].to_json,
- headers: page_response_headers)
+ .to_return(
+ status: 200,
+ body: [{
+ id: 2595440,
+ web_url: 'https://gitlab.com/groups/test',
+ name: 'Test',
+ path: 'stub-test-group',
+ full_name: 'Test',
+ full_path: 'stub-test-group'
+ }].to_json,
+ headers: page_response_headers
+ )
stub_request(:get, "%{url}/api/v4/groups?min_access_level=50&page=1&per_page=20&private_token=demo-pat&search=&top_level_only=true" % { url: url })
.to_return(
@@ -45,6 +47,7 @@ RSpec.shared_context 'bulk imports requests context' do |url|
full_name: 'Stub',
full_path: 'stub-group'
}].to_json,
- headers: page_response_headers)
+ headers: page_response_headers
+ )
end
end
diff --git a/spec/support/shared_contexts/design_management_shared_contexts.rb b/spec/support/shared_contexts/design_management_shared_contexts.rb
index d89bcada1df..32b66723c5d 100644
--- a/spec/support/shared_contexts/design_management_shared_contexts.rb
+++ b/spec/support/shared_contexts/design_management_shared_contexts.rb
@@ -13,24 +13,33 @@ RSpec.shared_context 'four designs in three versions' do
let_it_be(:design_d) { create(:design, issue: issue) }
let_it_be(:first_version) do
- create(:design_version, issue: issue,
- created_designs: [design_a],
- modified_designs: [],
- deleted_designs: [])
+ create(
+ :design_version,
+ issue: issue,
+ created_designs: [design_a],
+ modified_designs: [],
+ deleted_designs: []
+ )
end
let_it_be(:second_version) do
- create(:design_version, issue: issue,
- created_designs: [design_b, design_c, design_d],
- modified_designs: [design_a],
- deleted_designs: [])
+ create(
+ :design_version,
+ issue: issue,
+ created_designs: [design_b, design_c, design_d],
+ modified_designs: [design_a],
+ deleted_designs: []
+ )
end
let_it_be(:third_version) do
- create(:design_version, issue: issue,
- created_designs: [],
- modified_designs: [design_a],
- deleted_designs: [design_d])
+ create(
+ :design_version,
+ issue: issue,
+ created_designs: [],
+ modified_designs: [design_a],
+ deleted_designs: [design_d]
+ )
end
before do
diff --git a/spec/support/shared_contexts/features/integrations/instance_and_group_integrations_shared_context.rb b/spec/support/shared_contexts/features/integrations/instance_and_group_integrations_shared_context.rb
index 58ee341f71f..41b500c0d4d 100644
--- a/spec/support/shared_contexts/features/integrations/instance_and_group_integrations_shared_context.rb
+++ b/spec/support/shared_contexts/features/integrations/instance_and_group_integrations_shared_context.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_context 'instance and group integration activation' do
- include_context 'integration activation'
+ include_context 'with integration activation'
def click_save_integration
click_save_changes_button
diff --git a/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb b/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb
index 2c92ef64815..c1f7dd79c08 100644
--- a/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb
+++ b/spec/support/shared_contexts/features/integrations/integrations_shared_context.rb
@@ -1,120 +1,135 @@
# frozen_string_literal: true
-Integration.available_integration_names.each do |integration|
- RSpec.shared_context integration do
- include JiraIntegrationHelpers if integration == 'jira'
+# Shared context for all integrations
+#
+# The following let binding should be defined:
+# - `integration`: Integration name. See `Integration.available_integration_names`.
+RSpec.shared_context 'with integration' do
+ include JiraIntegrationHelpers
- let(:dashed_integration) { integration.dasherize }
- let(:integration_method) { Project.integration_association_name(integration) }
- let(:integration_klass) { Integration.integration_name_to_model(integration) }
- let(:integration_instance) { integration_klass.new }
+ let(:dashed_integration) { integration.dasherize }
+ let(:integration_method) { Project.integration_association_name(integration) }
+ let(:integration_klass) { Integration.integration_name_to_model(integration) }
+ let(:integration_instance) { integration_klass.new }
- # Build a list of all attributes that an integration supports.
- let(:integration_attrs_list) do
- integration_fields + integration_events + custom_attributes.fetch(integration.to_sym, [])
- end
+ # Build a list of all attributes that an integration supports.
+ let(:integration_attrs_list) do
+ integration_fields + integration_events + custom_attributes.fetch(integration.to_sym, [])
+ end
- # Attributes defined as fields.
- let(:integration_fields) do
- integration_instance.fields.map { _1[:name].to_sym }
- end
+ # Attributes defined as fields.
+ let(:integration_fields) do
+ integration_instance.fields.map { |field| field[:name].to_sym }
+ end
- # Attributes for configurable event triggers.
- let(:integration_events) do
- integration_instance.configurable_events.map { IntegrationsHelper.integration_event_field_name(_1).to_sym }
- end
+ # Attributes for configurable event triggers.
+ let(:integration_events) do
+ integration_instance.configurable_events
+ .map { |event| IntegrationsHelper.integration_event_field_name(event).to_sym }
+ end
- # Other special cases, this list might be incomplete.
- #
- # Some of these won't be needed anymore after we've converted them to use the field DSL
- # in https://gitlab.com/gitlab-org/gitlab/-/issues/354899.
- #
- # Others like `comment_on_event_disabled` are actual columns on `integrations`, maybe we should migrate
- # these to fields as well.
- let(:custom_attributes) do
- {
- jira: %i[comment_on_event_enabled jira_issue_transition_automatic jira_issue_transition_id project_key
- issues_enabled vulnerabilities_enabled vulnerabilities_issuetype]
- }
- end
+ # Other special cases, this list might be incomplete.
+ #
+ # Some of these won't be needed anymore after we've converted them to use the field DSL
+ # in https://gitlab.com/gitlab-org/gitlab/-/issues/354899.
+ #
+ # Others like `comment_on_event_disabled` are actual columns on `integrations`, maybe we should migrate
+ # these to fields as well.
+ let(:custom_attributes) do
+ {
+ jira: %i[
+ comment_on_event_enabled jira_issue_transition_automatic jira_issue_transition_id project_key
+ issues_enabled vulnerabilities_enabled vulnerabilities_issuetype
+ ]
+ }
+ end
- let(:integration_attrs) do
- integration_attrs_list.inject({}) do |hash, k|
- if k =~ /^(token*|.*_token|.*_key)/ && k =~ /^[^app_store]/
- hash.merge!(k => 'secrettoken')
- elsif integration == 'confluence' && k == :confluence_url
- hash.merge!(k => 'https://example.atlassian.net/wiki')
- elsif integration == 'datadog' && k == :datadog_site
- hash.merge!(k => 'datadoghq.com')
- elsif integration == 'datadog' && k == :datadog_tags
- hash.merge!(k => 'key:value')
- elsif integration == 'packagist' && k == :server
- hash.merge!(k => 'https://packagist.example.com')
- elsif k =~ /^(.*_url|url|webhook)/
- hash.merge!(k => "http://example.com")
- elsif integration_klass.method_defined?("#{k}?")
- hash.merge!(k => true)
- elsif integration == 'irker' && k == :recipients
- hash.merge!(k => 'irc://irc.network.net:666/#channel')
- elsif integration == 'irker' && k == :server_port
- hash.merge!(k => 1234)
- elsif integration == 'jira' && k == :jira_issue_transition_id
- hash.merge!(k => '1,2,3')
- elsif integration == 'jira' && k == :jira_issue_transition_automatic
- hash.merge!(k => true)
- elsif integration == 'emails_on_push' && k == :recipients
- hash.merge!(k => 'foo@bar.com')
- elsif (integration == 'slack' || integration == 'mattermost') && k == :labels_to_be_notified_behavior
- hash.merge!(k => "match_any")
- elsif integration == 'campfire' && k == :room
- hash.merge!(k => '1234')
- elsif integration == 'apple_app_store' && k == :app_store_issuer_id
- hash.merge!(k => 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee')
- elsif integration == 'apple_app_store' && k == :app_store_private_key
- hash.merge!(k => File.read('spec/fixtures/ssl_key.pem'))
- elsif integration == 'apple_app_store' && k == :app_store_key_id
- hash.merge!(k => 'ABC1')
- else
- hash.merge!(k => "someword")
- end
+ let(:integration_attrs) do
+ integration_attrs_list.inject({}) do |hash, k|
+ if k =~ /^(token*|.*_token|.*_key)/ && !integration.in?(%w[apple_app_store google_play])
+ hash.merge!(k => 'secrettoken')
+ elsif integration == 'confluence' && k == :confluence_url
+ hash.merge!(k => 'https://example.atlassian.net/wiki')
+ elsif integration == 'datadog' && k == :datadog_site
+ hash.merge!(k => 'datadoghq.com')
+ elsif integration == 'datadog' && k == :datadog_tags
+ hash.merge!(k => 'key:value')
+ elsif integration == 'packagist' && k == :server
+ hash.merge!(k => 'https://packagist.example.com')
+ elsif k =~ /^(.*_url|url|webhook)/
+ hash.merge!(k => "http://example.com")
+ elsif integration_klass.method_defined?("#{k}?")
+ hash.merge!(k => true)
+ elsif integration == 'irker' && k == :recipients
+ hash.merge!(k => 'irc://irc.network.net:666/#channel')
+ elsif integration == 'irker' && k == :server_port
+ hash.merge!(k => 1234)
+ elsif integration == 'jira' && k == :jira_issue_transition_id
+ hash.merge!(k => '1,2,3')
+ elsif integration == 'jira' && k == :jira_issue_transition_automatic # rubocop:disable Lint/DuplicateBranch
+ hash.merge!(k => true)
+ elsif integration == 'jira' && k == :jira_auth_type # rubocop:disable Lint/DuplicateBranch
+ hash.merge!(k => 0)
+ elsif integration == 'emails_on_push' && k == :recipients
+ hash.merge!(k => 'foo@bar.com')
+ elsif (integration == 'slack' || integration == 'mattermost') && k == :labels_to_be_notified_behavior
+ hash.merge!(k => "match_any")
+ elsif integration == 'campfire' && k == :room
+ hash.merge!(k => '1234')
+ elsif integration == 'apple_app_store' && k == :app_store_issuer_id
+ hash.merge!(k => 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee')
+ elsif integration == 'apple_app_store' && k == :app_store_private_key
+ hash.merge!(k => File.read('spec/fixtures/ssl_key.pem'))
+ elsif integration == 'apple_app_store' && k == :app_store_key_id
+ hash.merge!(k => 'ABC1')
+ elsif integration == 'apple_app_store' && k == :app_store_private_key_file_name
+ hash.merge!(k => 'ssl_key.pem')
+ elsif integration == 'google_play' && k == :package_name
+ hash.merge!(k => 'com.gitlab.foo.bar')
+ elsif integration == 'google_play' && k == :service_account_key
+ hash.merge!(k => File.read('spec/fixtures/service_account.json'))
+ elsif integration == 'google_play' && k == :service_account_key_file_name
+ hash.merge!(k => 'service_account.json')
+ else
+ hash.merge!(k => "someword")
end
end
+ end
- let(:licensed_features) do
- {
- 'github' => :github_integration
- }
- end
+ let(:licensed_features) do
+ {
+ 'github' => :github_integration
+ }
+ end
- before do
- enable_license_for_integration(integration)
- stub_jira_integration_test if integration == 'jira'
- end
+ before do
+ enable_license_for_integration(integration)
+ stub_jira_integration_test if integration == 'jira'
+ end
- def initialize_integration(integration, attrs = {})
- record = project.find_or_initialize_integration(integration)
- record.reset_updated_properties if integration == 'datadog'
- record.attributes = attrs
- record.properties = integration_attrs
- record.save!
- record
- end
+ def initialize_integration(integration, attrs = {})
+ record = project.find_or_initialize_integration(integration)
+ record.reset_updated_properties if integration == 'datadog'
+ record.attributes = attrs
+ record.properties = integration_attrs
+ record.save!
+ record
+ end
- private
+ private
- def enable_license_for_integration(integration)
- return unless respond_to?(:stub_licensed_features)
+ def enable_license_for_integration(integration)
+ return unless respond_to?(:stub_licensed_features)
- licensed_feature = licensed_features[integration]
- return unless licensed_feature
+ licensed_feature = licensed_features[integration]
+ return unless licensed_feature
- stub_licensed_features(licensed_feature => true)
- project.clear_memoization(:disabled_integrations)
- end
+ stub_licensed_features(licensed_feature => true)
+ project.clear_memoization(:disabled_integrations)
end
end
-RSpec.shared_context 'integration activation' do
+RSpec.shared_context 'with integration activation' do
def click_active_checkbox
find('label', text: 'Active').click
end
diff --git a/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb b/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb
index fadd46a7e12..f16d19e5858 100644
--- a/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb
+++ b/spec/support/shared_contexts/features/integrations/project_integrations_jira_context.rb
@@ -10,5 +10,6 @@ RSpec.shared_context 'project integration Jira context' do
fill_in 'service_url', with: url
fill_in 'service_username', with: 'username'
fill_in 'service_password', with: 'password'
+ select('Basic', from: 'service_jira_auth_type')
end
end
diff --git a/spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb b/spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb
index bac7bd00f46..a9b9a5246e6 100644
--- a/spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb
+++ b/spec/support/shared_contexts/features/integrations/project_integrations_shared_context.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_context 'project integration activation' do
- include_context 'integration activation'
+ include_context 'with integration activation'
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
diff --git a/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb
index afb3976e3b8..16d23f63fd0 100644
--- a/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb
@@ -12,47 +12,55 @@ RSpec.shared_context 'IssuesFinder context' do
let_it_be(:milestone) { create(:milestone, project: project1, releases: [release]) }
let_it_be(:label) { create(:label, project: project2) }
let_it_be(:label2) { create(:label, project: project2) }
- let_it_be(:item1, reload: true) do
- create(:issue,
- author: user,
- assignees: [user],
- project: project1,
- milestone: milestone,
- title: 'gitlab',
- created_at: 1.week.ago,
- updated_at: 1.week.ago)
+ let_it_be_with_reload(:item1) do
+ create(
+ :issue,
+ author: user,
+ assignees: [user],
+ project: project1,
+ milestone: milestone,
+ title: 'gitlab',
+ created_at: 1.week.ago,
+ updated_at: 1.week.ago
+ )
end
- let_it_be(:item2, reload: true) do
- create(:issue,
- author: user,
- assignees: [user],
- project: project2,
- description: 'gitlab',
- created_at: 1.week.from_now,
- updated_at: 1.week.from_now)
+ let_it_be_with_reload(:item2) do
+ create(
+ :issue,
+ author: user,
+ assignees: [user],
+ project: project2,
+ description: 'gitlab',
+ created_at: 1.week.from_now,
+ updated_at: 1.week.from_now
+ )
end
- let_it_be(:item3, reload: true) do
- create(:issue,
- author: user2,
- assignees: [user2],
- project: project2,
- title: 'tanuki',
- description: 'tanuki',
- created_at: 2.weeks.from_now,
- updated_at: 2.weeks.from_now)
+ let_it_be_with_reload(:item3) do
+ create(
+ :issue,
+ author: user2,
+ assignees: [user2],
+ project: project2,
+ title: 'tanuki',
+ description: 'tanuki',
+ created_at: 2.weeks.from_now,
+ updated_at: 2.weeks.from_now
+ )
end
- let_it_be(:item4, reload: true) { create(:issue, project: project3) }
- let_it_be(:item5, reload: true) do
- create(:issue,
- author: user,
- assignees: [user],
- project: project1,
- title: 'wotnot',
- created_at: 3.days.ago,
- updated_at: 3.days.ago)
+ let_it_be_with_reload(:item4) { create(:issue, project: project3) }
+ let_it_be_with_reload(:item5) do
+ create(
+ :issue,
+ author: user,
+ assignees: [user],
+ project: project1,
+ title: 'wotnot',
+ created_at: 3.days.ago,
+ updated_at: 3.days.ago
+ )
end
let_it_be(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: item1) }
diff --git a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
index 8a64efe9df5..507bcd44ee8 100644
--- a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
@@ -54,34 +54,44 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests
let_it_be(:label2) { create(:label, project: project1) }
let!(:merge_request1) do
- create(:merge_request, assignees: [user], author: user, reviewers: [user2],
- source_project: project2, target_project: project1,
- target_branch: 'merged-target')
+ create(
+ :merge_request, assignees: [user], author: user, reviewers: [user2],
+ source_project: project2, target_project: project1,
+ target_branch: 'merged-target'
+ )
end
let!(:merge_request2) do
- create(:merge_request, :conflict, assignees: [user], author: user, reviewers: [user2],
- source_project: project2, target_project: project1,
- state: 'closed')
+ create(
+ :merge_request, :conflict, assignees: [user], author: user, reviewers: [user2],
+ source_project: project2, target_project: project1,
+ state: 'closed'
+ )
end
let!(:merge_request3) do
- create(:merge_request, :simple, author: user, assignees: [user2], reviewers: [user],
- source_project: project2, target_project: project2,
- state: 'locked',
- title: 'thing WIP thing')
+ create(
+ :merge_request, :simple, author: user, assignees: [user2], reviewers: [user],
+ source_project: project2, target_project: project2,
+ state: 'locked',
+ title: 'thing WIP thing'
+ )
end
let!(:merge_request4) do
- create(:merge_request, :simple, author: user,
- source_project: project3, target_project: project3,
- title: 'WIP thing')
+ create(
+ :merge_request, :simple, author: user,
+ source_project: project3, target_project: project3,
+ title: 'WIP thing'
+ )
end
let_it_be(:merge_request5) do
- create(:merge_request, :simple, author: user,
- source_project: project4, target_project: project4,
- title: '[WIP]')
+ create(
+ :merge_request, :simple, author: user,
+ source_project: project4, target_project: project4,
+ title: '[WIP]'
+ )
end
let!(:label_link) { create(:label_link, label: label, target: merge_request2) }
diff --git a/spec/support/shared_contexts/finders/work_items_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/work_items_finder_shared_contexts.rb
index 8c5bc339db5..1118039d164 100644
--- a/spec/support/shared_contexts/finders/work_items_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/work_items_finder_shared_contexts.rb
@@ -12,47 +12,55 @@ RSpec.shared_context 'WorkItemsFinder context' do
let_it_be(:milestone) { create(:milestone, project: project1, releases: [release]) }
let_it_be(:label) { create(:label, project: project2) }
let_it_be(:label2) { create(:label, project: project2) }
- let_it_be(:item1, reload: true) do
- create(:work_item,
- author: user,
- assignees: [user],
- project: project1,
- milestone: milestone,
- title: 'gitlab',
- created_at: 1.week.ago,
- updated_at: 1.week.ago)
+ let_it_be_with_reload(:item1) do
+ create(
+ :work_item,
+ author: user,
+ assignees: [user],
+ project: project1,
+ milestone: milestone,
+ title: 'gitlab',
+ created_at: 1.week.ago,
+ updated_at: 1.week.ago
+ )
end
- let_it_be(:item2, reload: true) do
- create(:work_item,
- author: user,
- assignees: [user],
- project: project2,
- description: 'gitlab',
- created_at: 1.week.from_now,
- updated_at: 1.week.from_now)
+ let_it_be_with_reload(:item2) do
+ create(
+ :work_item,
+ author: user,
+ assignees: [user],
+ project: project2,
+ description: 'gitlab',
+ created_at: 1.week.from_now,
+ updated_at: 1.week.from_now
+ )
end
- let_it_be(:item3, reload: true) do
- create(:work_item,
- author: user2,
- assignees: [user2],
- project: project2,
- title: 'tanuki',
- description: 'tanuki',
- created_at: 2.weeks.from_now,
- updated_at: 2.weeks.from_now)
+ let_it_be_with_reload(:item3) do
+ create(
+ :work_item,
+ author: user2,
+ assignees: [user2],
+ project: project2,
+ title: 'tanuki',
+ description: 'tanuki',
+ created_at: 2.weeks.from_now,
+ updated_at: 2.weeks.from_now
+ )
end
- let_it_be(:item4, reload: true) { create(:work_item, project: project3) }
- let_it_be(:item5, reload: true) do
- create(:work_item,
- author: user,
- assignees: [user],
- project: project1,
- title: 'wotnot',
- created_at: 3.days.ago,
- updated_at: 3.days.ago)
+ let_it_be_with_reload(:item4) { create(:work_item, project: project3) }
+ let_it_be_with_reload(:item5) do
+ create(
+ :work_item,
+ author: user,
+ assignees: [user],
+ project: project1,
+ title: 'wotnot',
+ created_at: 3.days.ago,
+ updated_at: 3.days.ago
+ )
end
let_it_be(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: item1) }
diff --git a/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb b/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb
index 22b401bc841..83bf622af67 100644
--- a/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb
+++ b/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb
@@ -8,7 +8,7 @@ RSpec.shared_context 'with GLFM example snapshot fixtures' do
# NOTE: We hardcode the IDs on all fixtures to prevent variability in the
# rendered HTML/Prosemirror JSON, and to minimize the need for normalization:
# https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#normalization
- create(:project, :repository, creator: user, group: group, name: 'glfm_project', id: 77777)
+ create(:project, :repository, creator: user, group: group, path: 'glfm_project', id: 77777)
end
let_it_be(:project_snippet) { create(:project_snippet, title: 'glfm_project_snippet', id: 88888, project: project) }
diff --git a/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb b/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb
index 1585ef0e7fc..095c8639d15 100644
--- a/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb
+++ b/spec/support/shared_contexts/graphql/types/query_type_shared_context.rb
@@ -7,6 +7,7 @@ RSpec.shared_context 'with FOSS query type fields' do
:board_list,
:ci_application_settings,
:ci_config,
+ :ci_pipeline_stage,
:ci_variables,
:container_repository,
:current_user,
diff --git a/spec/support/shared_contexts/issuable/merge_request_shared_context.rb b/spec/support/shared_contexts/issuable/merge_request_shared_context.rb
index b9cde12c537..35c1511c96a 100644
--- a/spec/support/shared_contexts/issuable/merge_request_shared_context.rb
+++ b/spec/support/shared_contexts/issuable/merge_request_shared_context.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_context 'merge request show action' do
- include Spec::Support::Helpers::Features::MergeRequestHelpers
+ include Features::MergeRequestHelpers
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) }
diff --git a/spec/support/shared_contexts/lib/gitlab/database/load_balancing/wal_tracking_shared_context.rb b/spec/support/shared_contexts/lib/gitlab/database/load_balancing/wal_tracking_shared_context.rb
new file mode 100644
index 00000000000..cbbd3754108
--- /dev/null
+++ b/spec/support/shared_contexts/lib/gitlab/database/load_balancing/wal_tracking_shared_context.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'when tracking WAL location reference' do
+ let(:current_location) { '0/D525E3A8' }
+
+ around do |example|
+ Gitlab::Database::LoadBalancing::Session.clear_session
+ example.run
+ Gitlab::Database::LoadBalancing::Session.clear_session
+ end
+
+ def expect_tracked_locations_when_replicas_available
+ {}.tap do |locations|
+ Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
+ expect(lb.host).to receive(:database_replica_location).and_return(current_location)
+
+ locations[lb.name] = current_location
+ end
+ end
+ end
+
+ def expect_tracked_locations_when_no_replicas_available
+ {}.tap do |locations|
+ Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
+ expect(lb).to receive(:host).at_least(:once).and_return(nil)
+ expect(lb).to receive(:primary_write_location).and_return(current_location)
+
+ locations[lb.name] = current_location
+ end
+ end
+ end
+
+ def expect_tracked_locations_from_primary_only
+ {}.tap do |locations|
+ Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
+ expect(lb).to receive(:primary_write_location).and_return(current_location)
+
+ locations[lb.name] = current_location
+ end
+ end
+ end
+
+ def stub_load_balancing_disabled!
+ Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
+ allow(lb).to receive(:primary_only?).and_return(true)
+ end
+ end
+
+ def stub_load_balancing_enabled!
+ Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
+ allow(lb).to receive(:primary_only?).and_return(false)
+ end
+ end
+
+ def stub_no_writes_performed!
+ allow(Gitlab::Database::LoadBalancing::Session.current).to receive(:use_primary?).and_return(false)
+ end
+
+ def stub_write_performed!
+ allow(Gitlab::Database::LoadBalancing::Session.current).to receive(:use_primary?).and_return(true)
+ end
+
+ def stub_replica_available!(available)
+ ::Gitlab::Database::LoadBalancing.each_load_balancer do |lb|
+ allow(lb).to receive(:select_up_to_date_host).with(current_location).and_return(available)
+ end
+ end
+end
diff --git a/spec/support/shared_contexts/lib/gitlab/database/partitioning/list_partitioning_shared_context.rb b/spec/support/shared_contexts/lib/gitlab/database/partitioning/list_partitioning_shared_context.rb
new file mode 100644
index 00000000000..e9cd1bdbbf5
--- /dev/null
+++ b/spec/support/shared_contexts/lib/gitlab/database/partitioning/list_partitioning_shared_context.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with a table structure for converting a table to a list partition' do
+ let(:migration_context) do
+ Gitlab::Database::Migration[2.1].new.tap do |migration|
+ migration.extend Gitlab::Database::PartitioningMigrationHelpers::TableManagementHelpers
+ migration.extend Gitlab::Database::PartitioningMigrationHelpers::ForeignKeyHelpers
+ end
+ end
+
+ let(:connection) { migration_context.connection }
+ let(:table_name) { '_test_table_to_partition' }
+ let(:table_identifier) { "#{connection.current_schema}.#{table_name}" }
+ let(:partitioning_column) { :partition_number }
+ let(:partitioning_default) { 1 }
+ let(:referenced_table_name) { '_test_referenced_table' }
+ let(:other_referenced_table_name) { '_test_other_referenced_table' }
+ let(:referencing_table_name) { '_test_referencing_table' }
+ let(:other_referencing_table_name) { '_test_other_referencing_table' }
+ let(:parent_table_name) { "#{table_name}_parent" }
+ let(:parent_table_identifier) { "#{connection.current_schema}.#{parent_table_name}" }
+ let(:lock_tables) { [] }
+
+ let(:model) { define_batchable_model(table_name, connection: connection) }
+
+ let(:parent_model) { define_batchable_model(parent_table_name, connection: connection) }
+ let(:referencing_model) { define_batchable_model(referencing_table_name, connection: connection) }
+
+ before do
+ # Suppress printing migration progress
+ allow(migration_context).to receive(:puts)
+ allow(migration_context.connection).to receive(:transaction_open?).and_return(false)
+
+ connection.execute(<<~SQL)
+ create table #{referenced_table_name} (
+ id bigserial primary key not null
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ create table #{other_referenced_table_name} (
+ id bigserial primary key not null
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ insert into #{referenced_table_name} default values;
+ insert into #{other_referenced_table_name} default values;
+ SQL
+
+ connection.execute(<<~SQL)
+ create table #{table_name} (
+ id bigserial not null,
+ #{partitioning_column} bigint not null default #{partitioning_default},
+ referenced_id bigint not null references #{referenced_table_name} (id) on delete cascade,
+ other_referenced_id bigint not null references #{other_referenced_table_name} (id) on delete set null,
+ primary key (id, #{partitioning_column})
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ create table #{referencing_table_name} (
+ id bigserial primary key not null,
+ #{partitioning_column} bigint not null,
+ ref_id bigint not null,
+ constraint fk_referencing foreign key (#{partitioning_column}, ref_id) references #{table_name} (#{partitioning_column}, id) on delete cascade
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ create table #{other_referencing_table_name} (
+ id bigserial not null,
+ #{partitioning_column} bigint not null,
+ ref_id bigint not null,
+ primary key (#{partitioning_column}, id),
+ constraint fk_referencing_other foreign key (#{partitioning_column}, ref_id) references #{table_name} (#{partitioning_column}, id)
+ ) partition by hash(#{partitioning_column});
+
+ create table #{other_referencing_table_name}_1
+ partition of #{other_referencing_table_name} for values with (modulus 2, remainder 0);
+
+ create table #{other_referencing_table_name}_2
+ partition of #{other_referencing_table_name} for values with (modulus 2, remainder 1);
+ SQL
+
+ connection.execute(<<~SQL)
+ insert into #{table_name} (referenced_id, other_referenced_id)
+ select #{referenced_table_name}.id, #{other_referenced_table_name}.id
+ from #{referenced_table_name}, #{other_referenced_table_name};
+ SQL
+ end
+end
diff --git a/spec/support/shared_contexts/merge_request_create_shared_context.rb b/spec/support/shared_contexts/merge_request_create_shared_context.rb
index f2defa4eab9..fc9a3767365 100644
--- a/spec/support/shared_contexts/merge_request_create_shared_context.rb
+++ b/spec/support/shared_contexts/merge_request_create_shared_context.rb
@@ -15,12 +15,13 @@ RSpec.shared_context 'merge request create context' do
target_project.add_maintainer(user2)
sign_in(user)
- visit project_new_merge_request_path(target_project,
- merge_request: {
- source_project_id: source_project.id,
- target_project_id: target_project.id,
- source_branch: 'fix',
- target_branch: 'master'
- })
+ visit project_new_merge_request_path(
+ target_project,
+ merge_request: {
+ source_project_id: source_project.id,
+ target_project_id: target_project.id,
+ source_branch: 'fix',
+ target_branch: 'master'
+ })
end
end
diff --git a/spec/support/shared_contexts/merge_request_edit_shared_context.rb b/spec/support/shared_contexts/merge_request_edit_shared_context.rb
index d490d26adfb..f0e89b0c5f9 100644
--- a/spec/support/shared_contexts/merge_request_edit_shared_context.rb
+++ b/spec/support/shared_contexts/merge_request_edit_shared_context.rb
@@ -9,11 +9,13 @@ RSpec.shared_context 'merge request edit context' do
let(:target_project) { create(:project, :public, :repository) }
let(:source_project) { target_project }
let(:merge_request) do
- create(:merge_request,
- source_project: source_project,
- target_project: target_project,
- source_branch: 'fix',
- target_branch: 'master')
+ create(
+ :merge_request,
+ source_project: source_project,
+ target_project: target_project,
+ source_branch: 'fix',
+ target_branch: 'master'
+ )
end
before do
diff --git a/spec/support/shared_contexts/merge_requests_allowing_collaboration_shared_context.rb b/spec/support/shared_contexts/merge_requests_allowing_collaboration_shared_context.rb
index 5412a991b22..50761f94c10 100644
--- a/spec/support/shared_contexts/merge_requests_allowing_collaboration_shared_context.rb
+++ b/spec/support/shared_contexts/merge_requests_allowing_collaboration_shared_context.rb
@@ -8,10 +8,12 @@ RSpec.shared_context 'merge request allowing collaboration' do
before do
canonical.add_maintainer(user)
- create(:merge_request,
- target_project: canonical,
- source_project: forked_project,
- source_branch: 'feature',
- allow_collaboration: true)
+ create(
+ :merge_request,
+ target_project: canonical,
+ source_project: forked_project,
+ source_branch: 'feature',
+ allow_collaboration: true
+ )
end
end
diff --git a/spec/support/shared_contexts/models/distribution_shared_context.rb b/spec/support/shared_contexts/models/distribution_shared_context.rb
new file mode 100644
index 00000000000..30f6b750e22
--- /dev/null
+++ b/spec/support/shared_contexts/models/distribution_shared_context.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# This shared context requires:
+# - factory: either :debian_project_distribution or :debian_group_distribution
+# - can_freeze: whether to freeze the created object or not
+RSpec.shared_context 'for Debian Distribution' do |factory, can_freeze|
+ let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, :with_suite) }
+ let_it_be(:distribution_with_same_container, freeze: can_freeze) do
+ create(factory, container: distribution_with_suite.container)
+ end
+
+ let_it_be(:distribution_with_same_codename, freeze: can_freeze) do
+ create(factory, codename: distribution_with_suite.codename)
+ end
+
+ let_it_be(:distribution_with_same_suite, freeze: can_freeze) { create(factory, suite: distribution_with_suite.suite) }
+ let_it_be(:distribution_with_codename_and_suite_flipped, freeze: can_freeze) do
+ create(factory, codename: distribution_with_suite.suite, suite: distribution_with_suite.codename)
+ end
+
+ let_it_be_with_refind(:distribution) { create(factory, container: distribution_with_suite.container) }
+end
diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb
index b74819d2ac7..7b839594816 100644
--- a/spec/support/shared_contexts/navbar_structure_context.rb
+++ b/spec/support/shared_contexts/navbar_structure_context.rb
@@ -5,10 +5,10 @@ RSpec.shared_context 'project navbar structure' do
let(:security_and_compliance_nav_item) do
{
- nav_item: _('Security & Compliance'),
+ nav_item: _('Security and Compliance'),
nav_sub_items: [
(_('Audit events') if Gitlab.ee?),
- _('Configuration')
+ _('Security configuration')
]
}
end
@@ -34,10 +34,10 @@ RSpec.shared_context 'project navbar structure' do
_('Commits'),
_('Branches'),
_('Tags'),
- _('Contributors'),
+ _('Contributor statistics'),
_('Graph'),
- _('Compare'),
- (_('Locked Files') if Gitlab.ee?)
+ _('Compare revisions'),
+ (_('Locked files') if Gitlab.ee?)
]
},
{
@@ -68,7 +68,7 @@ RSpec.shared_context 'project navbar structure' do
nav_item: _('Deployments'),
nav_sub_items: [
_('Environments'),
- _('Feature Flags'),
+ s_('FeatureFlags|Feature flags'),
_('Releases')
]
},
@@ -76,7 +76,7 @@ RSpec.shared_context 'project navbar structure' do
nav_item: _('Infrastructure'),
nav_sub_items: [
_('Kubernetes clusters'),
- _('Terraform')
+ s_('Terraform|Terraform states')
]
},
{
@@ -85,8 +85,7 @@ RSpec.shared_context 'project navbar structure' do
_('Metrics'),
_('Error Tracking'),
_('Alerts'),
- _('Incidents'),
- _('Airflow')
+ _('Incidents')
]
},
{
@@ -141,6 +140,7 @@ RSpec.shared_context 'group navbar structure' do
_('CI/CD'),
_('Applications'),
_('Packages and registries'),
+ s_('UsageQuota|Usage Quotas'),
_('Domain Verification')
]
}
@@ -155,18 +155,9 @@ RSpec.shared_context 'group navbar structure' do
}
end
- let(:administration_nav_item) do
- {
- nav_item: _('Administration'),
- nav_sub_items: [
- s_('UsageQuota|Usage Quotas')
- ]
- }
- end
-
let(:security_and_compliance_nav_item) do
{
- nav_item: _('Security & Compliance'),
+ nav_item: _('Security and Compliance'),
nav_sub_items: [
_('Audit events')
]
@@ -268,3 +259,30 @@ RSpec.shared_context 'dashboard navbar structure' do
]
end
end
+
+RSpec.shared_context '"Explore" navbar structure' do
+ let(:structure) do
+ [
+ {
+ nav_item: "Explore",
+ nav_sub_items: []
+ },
+ {
+ nav_item: _("Projects"),
+ nav_sub_items: []
+ },
+ {
+ nav_item: _("Groups"),
+ nav_sub_items: []
+ },
+ {
+ nav_item: _("Topics"),
+ nav_sub_items: []
+ },
+ {
+ nav_item: _("Snippets"),
+ nav_sub_items: []
+ }
+ ]
+ end
+end
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index 4c081c8464e..111fd3dc7df 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -12,7 +12,7 @@ RSpec.shared_context 'GroupPolicy context' do
let(:public_permissions) do
%i[
- read_group read_counts read_achievement
+ read_group read_counts
read_label read_issue_board_list read_milestone read_issue_board
]
end
@@ -42,9 +42,7 @@ RSpec.shared_context 'GroupPolicy context' do
let(:developer_permissions) do
%i[
- create_metrics_dashboard_annotation
- delete_metrics_dashboard_annotation
- update_metrics_dashboard_annotation
+ admin_metrics_dashboard_annotation
create_custom_emoji
create_package
read_cluster
@@ -59,6 +57,7 @@ RSpec.shared_context 'GroupPolicy context' do
create_cluster update_cluster admin_cluster add_cluster
destroy_upload
admin_achievement
+ award_achievement
]
end
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index afc7fc8766f..5014a810f35 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -38,8 +38,8 @@ RSpec.shared_context 'ProjectPolicy context' do
read_commit_status read_confidential_issues read_container_image
read_harbor_registry read_deployment read_environment read_merge_request
read_metrics_dashboard_annotation read_pipeline read_prometheus
- read_sentry_issue update_issue create_merge_request_in read_external_emails
- read_internal_note
+ read_sentry_issue update_issue create_merge_request_in
+ read_external_emails read_internal_note export_work_items
]
end
@@ -52,12 +52,11 @@ RSpec.shared_context 'ProjectPolicy context' do
admin_merge_request admin_tag create_build
create_commit_status create_container_image create_deployment
create_environment create_merge_request_from
- create_metrics_dashboard_annotation create_pipeline create_release
- create_wiki delete_metrics_dashboard_annotation
- destroy_container_image push_code read_pod_logs read_terraform_state
- resolve_note update_build update_commit_status update_container_image
- update_deployment update_environment update_merge_request
- update_metrics_dashboard_annotation update_pipeline update_release destroy_release
+ admin_metrics_dashboard_annotation create_pipeline create_release
+ create_wiki destroy_container_image push_code read_pod_logs
+ read_terraform_state resolve_note update_build update_commit_status
+ update_container_image update_deployment update_environment
+ update_merge_request update_pipeline update_release destroy_release
read_resource_group update_resource_group update_escalation_status
]
end
diff --git a/spec/support/shared_contexts/rack_attack_shared_context.rb b/spec/support/shared_contexts/rack_attack_shared_context.rb
index 12625ead72b..4e8c6e9dec6 100644
--- a/spec/support/shared_contexts/rack_attack_shared_context.rb
+++ b/spec/support/shared_contexts/rack_attack_shared_context.rb
@@ -5,8 +5,7 @@ RSpec.shared_context 'rack attack cache store' do
# Instead of test environment's :null_store so the throttles can increment
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
- # Make time-dependent tests deterministic
- freeze_time { example.run }
+ example.run
Rack::Attack.cache.store = Rails.cache
end
diff --git a/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb b/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
index 57967fb9414..ad64e4d5be5 100644
--- a/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
@@ -58,6 +58,9 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] }
let(:architecture) { { private: private_architecture, public: public_architecture }[visibility_level] }
let(:component) { { private: private_component, public: public_component }[visibility_level] }
+ let(:component_file) { { private: private_component_file, public: public_component_file }[visibility_level] }
+ let(:component_file_sources) { { private: private_component_file_sources, public: public_component_file_sources }[visibility_level] }
+ let(:component_file_di) { { private: private_component_file_di, public: public_component_file_di }[visibility_level] }
let(:component_file_older_sha256) { { private: private_component_file_older_sha256, public: public_component_file_older_sha256 }[visibility_level] }
let(:component_file_sources_older_sha256) { { private: private_component_file_sources_older_sha256, public: public_component_file_sources_older_sha256 }[visibility_level] }
let(:component_file_di_older_sha256) { { private: private_component_file_di_older_sha256, public: public_component_file_di_older_sha256 }[visibility_level] }
diff --git a/spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb b/spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb
index 81076ea6fdc..e56cd82e369 100644
--- a/spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/graphql/releases_and_group_releases_shared_context.rb
@@ -13,40 +13,40 @@ RSpec.shared_context 'when releases and group releases shared context' do
let(:query) do
graphql_query_for(resource_type, { fullPath: resource.full_path },
- %(
- releases {
- count
- nodes {
- tagName
- tagPath
- name
- commit {
- sha
- }
- assets {
- count
- sources {
+ %(
+ releases {
+ count
+ nodes {
+ tagName
+ tagPath
+ name
+ commit {
+ sha
+ }
+ assets {
+ count
+ sources {
+ nodes {
+ url
+ }
+ }
+ }
+ evidences {
nodes {
- url
+ sha
}
}
- }
- evidences {
- nodes {
- sha
+ links {
+ selfUrl
+ openedMergeRequestsUrl
+ mergedMergeRequestsUrl
+ closedMergeRequestsUrl
+ openedIssuesUrl
+ closedIssuesUrl
}
}
- links {
- selfUrl
- openedMergeRequestsUrl
- mergedMergeRequestsUrl
- closedMergeRequestsUrl
- openedIssuesUrl
- closedIssuesUrl
- }
}
- }
- ))
+ ))
end
let(:params_for_issues_and_mrs) { { scope: 'all', state: 'opened', release_tag: release.tag } }
diff --git a/spec/support/shared_contexts/security_and_compliance_permissions_shared_context.rb b/spec/support/shared_contexts/security_and_compliance_permissions_shared_context.rb
index dc5195e4b01..1fa1a5c69f4 100644
--- a/spec/support/shared_contexts/security_and_compliance_permissions_shared_context.rb
+++ b/spec/support/shared_contexts/security_and_compliance_permissions_shared_context.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_context '"Security & Compliance" permissions' do
+RSpec.shared_context '"Security and Compliance" permissions' do
let(:project_instance) { an_instance_of(Project) }
let(:user_instance) { an_instance_of(User) }
let(:before_request_defined) { false }
@@ -18,7 +18,7 @@ RSpec.shared_context '"Security & Compliance" permissions' do
allow(Ability).to receive(:allowed?).with(user_instance, :access_security_and_compliance, project_instance).and_return(true)
end
- context 'when the "Security & Compliance" feature is disabled' do
+ context 'when the "Security and Compliance" feature is disabled' do
subject { response }
before do
diff --git a/spec/support/shared_contexts/services/clusters/create_service_shared_context.rb b/spec/support/shared_contexts/services/clusters/create_service_shared_context.rb
new file mode 100644
index 00000000000..393e90da1d3
--- /dev/null
+++ b/spec/support/shared_contexts/services/clusters/create_service_shared_context.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with valid cluster create params' do
+ let(:clusterable) { Clusters::Instance.new }
+ let(:params) do
+ {
+ name: 'test-cluster',
+ provider_type: :gcp,
+ provider_gcp_attributes: {
+ gcp_project_id: 'gcp-project',
+ zone: 'us-central1-a',
+ num_nodes: 1,
+ machine_type: 'machine_type-a',
+ legacy_abac: 'true'
+ },
+ clusterable: clusterable
+ }
+ end
+end
diff --git a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
index 7db479bcfd2..0cf026749ee 100644
--- a/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
+++ b/spec/support/shared_contexts/services/projects/container_repository/delete_tags_service_shared_context.rb
@@ -8,9 +8,11 @@ RSpec.shared_context 'container repository delete tags service shared context' d
let(:params) { { tags: tags } }
before do
- stub_container_registry_config(enabled: true,
- api_url: 'http://registry.gitlab',
- host_port: 'registry.gitlab')
+ stub_container_registry_config(
+ enabled: true,
+ api_url: 'http://registry.gitlab',
+ host_port: 'registry.gitlab'
+ )
stub_container_registry_tags(
repository: repository.path,
diff --git a/spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb b/spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb
index 9746d287440..cd792ccc4e3 100644
--- a/spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb
+++ b/spec/support/shared_contexts/services/service_ping/stubbed_service_ping_metrics_definitions_shared_context.rb
@@ -4,9 +4,10 @@ RSpec.shared_context 'stubbed service ping metrics definitions' do
include UsageDataHelpers
let(:metrics_definitions) { standard_metrics + subscription_metrics + operational_metrics + optional_metrics }
+ # ToDo: remove during https://gitlab.com/gitlab-org/gitlab/-/issues/396824 (license metrics migration)
+ let(:subscription_metrics) { [] }
let(:standard_metrics) do
[
- metric_attributes('uuid', 'standard'),
metric_attributes('recorded_at', 'standard'),
metric_attributes('settings.collected_data_categories', 'standard', 'object')
]
diff --git a/spec/support/shared_examples/analytics/cycle_analytics/flow_metrics_examples.rb b/spec/support/shared_examples/analytics/cycle_analytics/flow_metrics_examples.rb
new file mode 100644
index 00000000000..9c096c5a158
--- /dev/null
+++ b/spec/support/shared_examples/analytics/cycle_analytics/flow_metrics_examples.rb
@@ -0,0 +1,629 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'validation on Time arguments' do
+ context 'when `to` parameter is higher than `from`' do
+ let(:variables) do
+ {
+ path: full_path,
+ from: 1.day.ago.iso8601,
+ to: 2.days.ago.iso8601
+ }
+ end
+
+ it 'returns error' do
+ expect(result).to be_nil
+ expect(graphql_errors.first['message']).to include('`from` argument must be before `to` argument')
+ end
+ end
+
+ context 'when from and to parameter range is higher than 180 days' do
+ let(:variables) do
+ {
+ path: full_path,
+ from: Time.now,
+ to: 181.days.from_now
+ }
+ end
+
+ it 'returns error' do
+ expect(result).to be_nil
+ expect(graphql_errors.first['message']).to include('Max of 180 days timespan is allowed')
+ end
+ end
+end
+
+RSpec.shared_examples 'value stream analytics flow metrics issueCount examples' do
+ let_it_be(:milestone) { create(:milestone, group: group) }
+ let_it_be(:label) { create(:group_label, group: group) }
+
+ let_it_be(:author) { create(:user) }
+ let_it_be(:assignee) { create(:user) }
+
+ let_it_be(:issue1) { create(:issue, project: project1, author: author, created_at: 12.days.ago) }
+ let_it_be(:issue2) { create(:issue, project: project2, author: author, created_at: 13.days.ago) }
+
+ let_it_be(:issue3) do
+ create(:labeled_issue,
+ project: project1,
+ labels: [label],
+ author: author,
+ milestone: milestone,
+ assignees: [assignee],
+ created_at: 14.days.ago)
+ end
+
+ let_it_be(:issue4) do
+ create(:labeled_issue,
+ project: project2,
+ labels: [label],
+ assignees: [assignee],
+ created_at: 15.days.ago)
+ end
+
+ let_it_be(:issue_outside_of_the_range) { create(:issue, project: project2, author: author, created_at: 50.days.ago) }
+
+ let(:query) do
+ <<~QUERY
+ query($path: ID!, $assigneeUsernames: [String!], $authorUsername: String, $milestoneTitle: String, $labelNames: [String!], $from: Time!, $to: Time!) {
+ #{context}(fullPath: $path) {
+ flowMetrics {
+ issueCount(assigneeUsernames: $assigneeUsernames, authorUsername: $authorUsername, milestoneTitle: $milestoneTitle, labelNames: $labelNames, from: $from, to: $to) {
+ value
+ unit
+ identifier
+ title
+ }
+ }
+ }
+ }
+ QUERY
+ end
+
+ let(:variables) do
+ {
+ path: full_path,
+ from: 20.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ subject(:result) do
+ post_graphql(query, current_user: current_user, variables: variables)
+
+ graphql_data.dig(context.to_s, 'flowMetrics', 'issueCount')
+ end
+
+ it 'returns the correct count' do
+ expect(result).to eq({
+ 'identifier' => 'issues',
+ 'unit' => nil,
+ 'value' => 4,
+ 'title' => n_('New Issue', 'New Issues', 4)
+ })
+ end
+
+ context 'with partial filters' do
+ let(:variables) do
+ {
+ path: full_path,
+ assigneeUsernames: [assignee.username],
+ labelNames: [label.title],
+ from: 20.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ it 'returns filtered count' do
+ expect(result).to eq({
+ 'identifier' => 'issues',
+ 'unit' => nil,
+ 'value' => 2,
+ 'title' => n_('New Issue', 'New Issues', 2)
+ })
+ end
+ end
+
+ context 'with all filters' do
+ let(:variables) do
+ {
+ path: full_path,
+ assigneeUsernames: [assignee.username],
+ labelNames: [label.title],
+ authorUsername: author.username,
+ milestoneTitle: milestone.title,
+ from: 20.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ it 'returns filtered count' do
+ expect(result).to eq({
+ 'identifier' => 'issues',
+ 'unit' => nil,
+ 'value' => 1,
+ 'title' => n_('New Issue', 'New Issues', 1)
+ })
+ end
+ end
+
+ context 'when the user is not authorized' do
+ let(:current_user) { create(:user) }
+
+ it 'returns nil' do
+ expect(result).to eq(nil)
+ end
+ end
+
+ it_behaves_like 'validation on Time arguments'
+end
+
+RSpec.shared_examples 'value stream analytics flow metrics deploymentCount examples' do
+ let_it_be(:deployment1) do
+ create(:deployment, :success, environment: production_environment1, finished_at: 5.days.ago)
+ end
+
+ let_it_be(:deployment2) do
+ create(:deployment, :success, environment: production_environment2, finished_at: 10.days.ago)
+ end
+
+ let_it_be(:deployment3) do
+ create(:deployment, :success, environment: production_environment2, finished_at: 15.days.ago)
+ end
+
+ let(:variables) do
+ {
+ path: full_path,
+ from: 12.days.ago.iso8601,
+ to: 3.days.ago.iso8601
+ }
+ end
+
+ let(:query) do
+ <<~QUERY
+ query($path: ID!, $from: Time!, $to: Time!) {
+ #{context}(fullPath: $path) {
+ flowMetrics {
+ deploymentCount(from: $from, to: $to) {
+ value
+ unit
+ identifier
+ title
+ }
+ }
+ }
+ }
+ QUERY
+ end
+
+ subject(:result) do
+ post_graphql(query, current_user: current_user, variables: variables)
+
+ graphql_data.dig(context.to_s, 'flowMetrics', 'deploymentCount')
+ end
+
+ it 'returns the correct count' do
+ expect(result).to eq({
+ 'identifier' => 'deploys',
+ 'unit' => nil,
+ 'value' => 2,
+ 'title' => n_('Deploy', 'Deploys', 2)
+ })
+ end
+
+ context 'when the user is not authorized' do
+ let(:current_user) { create(:user) }
+
+ it 'returns nil' do
+ expect(result).to eq(nil)
+ end
+ end
+
+ context 'when outside of the date range' do
+ let(:variables) do
+ {
+ path: full_path,
+ from: 20.days.ago.iso8601,
+ to: 18.days.ago.iso8601
+ }
+ end
+
+ it 'returns 0 count' do
+ expect(result).to eq({
+ 'identifier' => 'deploys',
+ 'unit' => nil,
+ 'value' => 0,
+ 'title' => n_('Deploy', 'Deploys', 0)
+ })
+ end
+ end
+
+ it_behaves_like 'validation on Time arguments'
+end
+
+RSpec.shared_examples 'value stream analytics flow metrics leadTime examples' do
+ let_it_be(:milestone) { create(:milestone, group: group) }
+ let_it_be(:label) { create(:group_label, group: group) }
+
+ let_it_be(:author) { create(:user) }
+ let_it_be(:assignee) { create(:user) }
+
+ let_it_be(:issue1) do
+ create(:issue, project: project1, author: author, created_at: 17.days.ago, closed_at: 12.days.ago)
+ end
+
+ let_it_be(:issue2) do
+ create(:issue, project: project2, author: author, created_at: 16.days.ago, closed_at: 13.days.ago)
+ end
+
+ let_it_be(:issue3) do
+ create(:labeled_issue,
+ project: project1,
+ labels: [label],
+ author: author,
+ milestone: milestone,
+ assignees: [assignee],
+ created_at: 14.days.ago,
+ closed_at: 11.days.ago)
+ end
+
+ let_it_be(:issue4) do
+ create(:labeled_issue,
+ project: project2,
+ labels: [label],
+ assignees: [assignee],
+ created_at: 20.days.ago,
+ closed_at: 15.days.ago)
+ end
+
+ before do
+ Analytics::CycleAnalytics::DataLoaderService.new(group: group, model: Issue).execute
+ end
+
+ let(:query) do
+ <<~QUERY
+ query($path: ID!, $assigneeUsernames: [String!], $authorUsername: String, $milestoneTitle: String, $labelNames: [String!], $from: Time!, $to: Time!) {
+ #{context}(fullPath: $path) {
+ flowMetrics {
+ leadTime(assigneeUsernames: $assigneeUsernames, authorUsername: $authorUsername, milestoneTitle: $milestoneTitle, labelNames: $labelNames, from: $from, to: $to) {
+ value
+ unit
+ identifier
+ title
+ links {
+ label
+ url
+ }
+ }
+ }
+ }
+ }
+ QUERY
+ end
+
+ let(:variables) do
+ {
+ path: full_path,
+ from: 21.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ subject(:result) do
+ post_graphql(query, current_user: current_user, variables: variables)
+
+ graphql_data.dig(context.to_s, 'flowMetrics', 'leadTime')
+ end
+
+ it 'returns the correct value' do
+ expect(result).to match(a_hash_including({
+ 'identifier' => 'lead_time',
+ 'unit' => n_('day', 'days', 4),
+ 'value' => 4,
+ 'title' => _('Lead Time'),
+ 'links' => [
+ { 'label' => s_('ValueStreamAnalytics|Dashboard'), 'url' => match(/issues_analytics/) },
+ { 'label' => s_('ValueStreamAnalytics|Go to docs'), 'url' => match(/definitions/) }
+ ]
+ }))
+ end
+
+ context 'when the user is not authorized' do
+ let(:current_user) { create(:user) }
+
+ it 'returns nil' do
+ expect(result).to eq(nil)
+ end
+ end
+
+ context 'when outside of the date range' do
+ let(:variables) do
+ {
+ path: full_path,
+ from: 30.days.ago.iso8601,
+ to: 25.days.ago.iso8601
+ }
+ end
+
+ it 'returns 0 count' do
+ expect(result).to match(a_hash_including({ 'value' => nil }))
+ end
+ end
+
+ context 'with all filters' do
+ let(:variables) do
+ {
+ path: full_path,
+ assigneeUsernames: [assignee.username],
+ labelNames: [label.title],
+ authorUsername: author.username,
+ milestoneTitle: milestone.title,
+ from: 20.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ it 'returns filtered count' do
+ expect(result).to match(a_hash_including({ 'value' => 3 }))
+ end
+ end
+end
+
+RSpec.shared_examples 'value stream analytics flow metrics cycleTime examples' do
+ let_it_be(:milestone) { create(:milestone, group: group) }
+ let_it_be(:label) { create(:group_label, group: group) }
+
+ let_it_be(:author) { create(:user) }
+ let_it_be(:assignee) { create(:user) }
+
+ let_it_be(:issue1) do
+ create(:issue, project: project1, author: author, closed_at: 12.days.ago).tap do |issue|
+ issue.metrics.update!(first_mentioned_in_commit_at: 17.days.ago)
+ end
+ end
+
+ let_it_be(:issue2) do
+ create(:issue, project: project2, author: author, closed_at: 13.days.ago).tap do |issue|
+ issue.metrics.update!(first_mentioned_in_commit_at: 16.days.ago)
+ end
+ end
+
+ let_it_be(:issue3) do
+ create(:labeled_issue,
+ project: project1,
+ labels: [label],
+ author: author,
+ milestone: milestone,
+ assignees: [assignee],
+ closed_at: 11.days.ago).tap do |issue|
+ issue.metrics.update!(first_mentioned_in_commit_at: 14.days.ago)
+ end
+ end
+
+ let_it_be(:issue4) do
+ create(:labeled_issue,
+ project: project2,
+ labels: [label],
+ assignees: [assignee],
+ closed_at: 15.days.ago).tap do |issue|
+ issue.metrics.update!(first_mentioned_in_commit_at: 20.days.ago)
+ end
+ end
+
+ before do
+ Analytics::CycleAnalytics::DataLoaderService.new(group: group, model: Issue).execute
+ end
+
+ let(:query) do
+ <<~QUERY
+ query($path: ID!, $assigneeUsernames: [String!], $authorUsername: String, $milestoneTitle: String, $labelNames: [String!], $from: Time!, $to: Time!) {
+ #{context}(fullPath: $path) {
+ flowMetrics {
+ cycleTime(assigneeUsernames: $assigneeUsernames, authorUsername: $authorUsername, milestoneTitle: $milestoneTitle, labelNames: $labelNames, from: $from, to: $to) {
+ value
+ unit
+ identifier
+ title
+ links {
+ label
+ url
+ }
+ }
+ }
+ }
+ }
+ QUERY
+ end
+
+ let(:variables) do
+ {
+ path: full_path,
+ from: 21.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ subject(:result) do
+ post_graphql(query, current_user: current_user, variables: variables)
+
+ graphql_data.dig(context.to_s, 'flowMetrics', 'cycleTime')
+ end
+
+ it 'returns the correct value' do
+ expect(result).to eq({
+ 'identifier' => 'cycle_time',
+ 'unit' => n_('day', 'days', 4),
+ 'value' => 4,
+ 'title' => _('Cycle Time'),
+ 'links' => []
+ })
+ end
+
+ context 'when the user is not authorized' do
+ let(:current_user) { create(:user) }
+
+ it 'returns nil' do
+ expect(result).to eq(nil)
+ end
+ end
+
+ context 'when outside of the date range' do
+ let(:variables) do
+ {
+ path: full_path,
+ from: 30.days.ago.iso8601,
+ to: 25.days.ago.iso8601
+ }
+ end
+
+ it 'returns 0 count' do
+ expect(result).to match(a_hash_including({ 'value' => nil }))
+ end
+ end
+
+ context 'with all filters' do
+ let(:variables) do
+ {
+ path: full_path,
+ assigneeUsernames: [assignee.username],
+ labelNames: [label.title],
+ authorUsername: author.username,
+ milestoneTitle: milestone.title,
+ from: 20.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ it 'returns filtered count' do
+ expect(result).to match(a_hash_including({ 'value' => 3 }))
+ end
+ end
+end
+
+RSpec.shared_examples 'value stream analytics flow metrics issuesCompleted examples' do
+ let_it_be(:milestone) { create(:milestone, group: group) }
+ let_it_be(:label) { create(:group_label, group: group) }
+
+ let_it_be(:author) { create(:user) }
+ let_it_be(:assignee) { create(:user) }
+
+ # we don't care about opened date, only closed date.
+ let_it_be(:issue1) do
+ create(:issue, project: project1, author: author, created_at: 17.days.ago, closed_at: 12.days.ago)
+ end
+
+ let_it_be(:issue2) do
+ create(:issue, project: project2, author: author, created_at: 16.days.ago, closed_at: 13.days.ago)
+ end
+
+ let_it_be(:issue3) do
+ create(:labeled_issue,
+ project: project1,
+ labels: [label],
+ author: author,
+ milestone: milestone,
+ assignees: [assignee],
+ created_at: 14.days.ago,
+ closed_at: 11.days.ago)
+ end
+
+ let_it_be(:issue4) do
+ create(:labeled_issue,
+ project: project2,
+ labels: [label],
+ assignees: [assignee],
+ created_at: 20.days.ago,
+ closed_at: 15.days.ago)
+ end
+
+ before do
+ Analytics::CycleAnalytics::DataLoaderService.new(group: group, model: Issue).execute
+ end
+
+ let(:query) do
+ <<~QUERY
+ query($path: ID!, $assigneeUsernames: [String!], $authorUsername: String, $milestoneTitle: String, $labelNames: [String!], $from: Time!, $to: Time!) {
+ #{context}(fullPath: $path) {
+ flowMetrics {
+ issuesCompletedCount(assigneeUsernames: $assigneeUsernames, authorUsername: $authorUsername, milestoneTitle: $milestoneTitle, labelNames: $labelNames, from: $from, to: $to) {
+ value
+ unit
+ identifier
+ title
+ links {
+ label
+ url
+ }
+ }
+ }
+ }
+ }
+ QUERY
+ end
+
+ let(:variables) do
+ {
+ path: full_path,
+ from: 21.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ subject(:result) do
+ post_graphql(query, current_user: current_user, variables: variables)
+
+ graphql_data.dig(context.to_s, 'flowMetrics', 'issuesCompletedCount')
+ end
+
+ it 'returns the correct value' do
+ expect(result).to match(a_hash_including({
+ 'identifier' => 'issues_completed',
+ 'unit' => n_('issue', 'issues', 4),
+ 'value' => 4,
+ 'title' => _('Issues Completed'),
+ 'links' => [
+ { 'label' => s_('ValueStreamAnalytics|Dashboard'), 'url' => match(/issues_analytics/) },
+ { 'label' => s_('ValueStreamAnalytics|Go to docs'), 'url' => match(/definitions/) }
+ ]
+ }))
+ end
+
+ context 'when the user is not authorized' do
+ let(:current_user) { create(:user) }
+
+ it 'returns nil' do
+ expect(result).to eq(nil)
+ end
+ end
+
+ context 'when outside of the date range' do
+ let(:variables) do
+ {
+ path: full_path,
+ from: 30.days.ago.iso8601,
+ to: 25.days.ago.iso8601
+ }
+ end
+
+ it 'returns 0 count' do
+ expect(result).to match(a_hash_including({ 'value' => 0.0 }))
+ end
+ end
+
+ context 'with all filters' do
+ let(:variables) do
+ {
+ path: full_path,
+ assigneeUsernames: [assignee.username],
+ labelNames: [label.title],
+ authorUsername: author.username,
+ milestoneTitle: milestone.title,
+ from: 20.days.ago.iso8601,
+ to: 10.days.ago.iso8601
+ }
+ end
+
+ it 'returns filtered count' do
+ expect(result).to match(a_hash_including({ 'value' => 1.0 }))
+ end
+ end
+end
diff --git a/spec/support/shared_examples/analytics/cycle_analytics/request_params_examples.rb b/spec/support/shared_examples/analytics/cycle_analytics/request_params_examples.rb
new file mode 100644
index 00000000000..ef9830fbce8
--- /dev/null
+++ b/spec/support/shared_examples/analytics/cycle_analytics/request_params_examples.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'unlicensed cycle analytics request params' do
+ let(:params) do
+ {
+ created_after: '2019-01-01',
+ created_before: '2019-03-01',
+ project_ids: [2, 3],
+ namespace: namespace,
+ current_user: user
+ }
+ end
+
+ subject { described_class.new(params) }
+
+ before do
+ root_group.add_owner(user)
+ end
+
+ describe 'validations' do
+ it 'is valid' do
+ expect(subject).to be_valid
+ end
+
+ context 'when `created_before` is missing' do
+ before do
+ params[:created_before] = nil
+ end
+
+ it 'is valid', time_travel_to: '2019-03-01' do
+ expect(subject).to be_valid
+ end
+ end
+
+ context 'when `created_before` is earlier than `created_after`' do
+ before do
+ params[:created_before] = '2015-01-01'
+ end
+
+ it 'is invalid' do
+ expect(subject).not_to be_valid
+ expect(subject.errors.messages[:created_before]).not_to be_empty
+ end
+ end
+
+ context 'when the date range exceeds 180 days' do
+ before do
+ params[:created_before] = '2019-07-15'
+ end
+
+ it 'is invalid' do
+ expect(subject).not_to be_valid
+ message = s_('CycleAnalytics|The given date range is larger than 180 days')
+ expect(subject.errors.messages[:created_after]).to include(message)
+ end
+ end
+ end
+
+ it 'casts `created_after` to `Time`' do
+ expect(subject.created_after).to be_a_kind_of(Time)
+ end
+
+ it 'casts `created_before` to `Time`' do
+ expect(subject.created_before).to be_a_kind_of(Time)
+ end
+
+ describe 'optional `value_stream`' do
+ context 'when `value_stream` is not empty' do
+ let(:value_stream) { instance_double('Analytics::CycleAnalytics::ValueStream') }
+
+ before do
+ params[:value_stream] = value_stream
+ end
+
+ it { expect(subject.value_stream).to eq(value_stream) }
+ end
+
+ context 'when `value_stream` is nil' do
+ before do
+ params[:value_stream] = nil
+ end
+
+ it { expect(subject.value_stream).to eq(nil) }
+ end
+ end
+
+ describe 'sorting params' do
+ before do
+ params.merge!(sort: 'duration', direction: 'asc')
+ end
+
+ it 'converts sorting params to symbol when passing it to data collector' do
+ data_collector_params = subject.to_data_collector_params
+
+ expect(data_collector_params[:sort]).to eq(:duration)
+ expect(data_collector_params[:direction]).to eq(:asc)
+ end
+
+ it 'adds sorting params to data attributes' do
+ data_attributes = subject.to_data_attributes
+
+ expect(data_attributes[:sort]).to eq('duration')
+ expect(data_attributes[:direction]).to eq('asc')
+ end
+ end
+
+ describe 'aggregation params' do
+ context 'when not licensed' do
+ it 'returns nil' do
+ data_collector_params = subject.to_data_attributes
+ expect(data_collector_params[:aggregation]).to eq(nil)
+ end
+ end
+ end
+
+ describe 'use_aggregated_data_collector param' do
+ subject(:value) { described_class.new(params).to_data_collector_params[:use_aggregated_data_collector] }
+
+ it { is_expected.to eq(false) }
+ end
+
+ describe 'feature availablity data attributes' do
+ subject(:value) { described_class.new(params).to_data_attributes }
+
+ it 'disables all paid features' do
+ is_expected.to match(a_hash_including(enable_tasks_by_type_chart: 'false',
+ enable_customizable_stages: 'false',
+ enable_projects_filter: 'false'))
+ end
+ end
+end
diff --git a/spec/support/shared_examples/banzai/filters/filter_timeout_shared_examples.rb b/spec/support/shared_examples/banzai/filters/filter_timeout_shared_examples.rb
new file mode 100644
index 00000000000..618be53cb3b
--- /dev/null
+++ b/spec/support/shared_examples/banzai/filters/filter_timeout_shared_examples.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+# These shared_examples require the following variables:
+# - text: The text to be run through the filter
+#
+# Usage:
+#
+# it_behaves_like 'html filter timeout' do
+# let(:text) { 'some text' }
+# end
+RSpec.shared_examples 'html filter timeout' do
+ context 'when rendering takes too long' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:context) { { project: project } }
+
+ it 'times out' do
+ stub_const("Banzai::Filter::TimeoutHtmlPipelineFilter::RENDER_TIMEOUT", 0.1)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:call_with_timeout) do
+ sleep(0.2)
+ text
+ end
+ end
+
+ expect(Gitlab::RenderTimeout).to receive(:timeout).and_call_original
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(Timeout::Error),
+ project_id: context[:project].id,
+ class_name: described_class.name.demodulize
+ )
+
+ result = filter(text)
+
+ expect(result.to_html).to eq text
+ end
+ end
+end
+
+# Usage:
+#
+# it_behaves_like 'text html filter timeout' do
+# let(:text) { 'some text' }
+# end
+RSpec.shared_examples 'text filter timeout' do
+ context 'when rendering takes too long' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:context) { { project: project } }
+
+ it 'times out' do
+ stub_const("Banzai::Filter::TimeoutTextPipelineFilter::RENDER_TIMEOUT", 0.1)
+ allow_next_instance_of(described_class) do |instance|
+ allow(instance).to receive(:call_with_timeout) do
+ sleep(0.2)
+ text
+ end
+ end
+
+ expect(Gitlab::RenderTimeout).to receive(:timeout).and_call_original
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ instance_of(Timeout::Error),
+ project_id: context[:project].id,
+ class_name: described_class.name.demodulize
+ )
+
+ result = filter(text)
+
+ expect(result).to eq text
+ end
+ end
+end
diff --git a/spec/support/shared_examples/banzai/filters/inline_embeds_shared_examples.rb b/spec/support/shared_examples/banzai/filters/inline_embeds_shared_examples.rb
index 599161abbfe..8f2f3f89914 100644
--- a/spec/support/shared_examples/banzai/filters/inline_embeds_shared_examples.rb
+++ b/spec/support/shared_examples/banzai/filters/inline_embeds_shared_examples.rb
@@ -7,6 +7,10 @@ RSpec.shared_examples 'a metrics embed filter' do
let(:input) { %(<a href="#{url}">example</a>) }
let(:doc) { filter(input) }
+ before do
+ stub_feature_flags(remove_monitor_metrics: false)
+ end
+
context 'when the document has an external link' do
let(:url) { 'https://foo.com' }
@@ -38,6 +42,18 @@ RSpec.shared_examples 'a metrics embed filter' do
expect(doc.at_css('.js-render-metrics')).to be_present
end
end
+
+ context 'when metrics dashboard feature is unavailable' do
+ before do
+ stub_feature_flags(remove_monitor_metrics: true)
+ end
+
+ it 'does not append a metrics chart placeholder' do
+ node = doc.at_css('.js-render-metrics')
+
+ expect(node).not_to be_present
+ end
+ end
end
# Nokogiri escapes the URLs, but we don't care about that
diff --git a/spec/support/banzai/reference_filter_shared_examples.rb b/spec/support/shared_examples/banzai/filters/reference_filter_shared_examples.rb
index 0046d931e7d..6912bcaee34 100644
--- a/spec/support/banzai/reference_filter_shared_examples.rb
+++ b/spec/support/shared_examples/banzai/filters/reference_filter_shared_examples.rb
@@ -6,7 +6,7 @@
# let(:reference) { '#42' }
RSpec.shared_examples 'a reference containing an element node' do
let(:inner_html) { 'element <code>node</code> inside' }
- let(:reference_with_element) { %{<a href="#{reference}">#{inner_html}</a>} }
+ let(:reference_with_element) { %(<a href="#{reference}">#{inner_html}</a>) }
it 'does not escape inner html' do
doc = reference_filter(reference_with_element)
@@ -29,7 +29,7 @@ RSpec.shared_examples 'user reference or project reference' do
end
end
- context 'mentioning a resource' do
+ context 'when mentioning a resource' do
it_behaves_like 'a reference containing an element node'
it_behaves_like 'it contains a data- attribute'
@@ -66,12 +66,12 @@ RSpec.shared_examples 'user reference or project reference' do
doc = reference_filter("Hey #{reference}", only_path: true)
link = doc.css('a').first.attr('href')
- expect(link).not_to match %r(https?://)
+ expect(link).not_to match %r{https?://}
expect(link).to eq urls.send "#{subject_name}_path", subject
end
- context 'referencing a resource in a link href' do
- let(:reference) { %Q{<a href="#{get_reference(subject)}">Some text</a>} }
+ describe 'referencing a resource in a link href' do
+ let(:reference) { %(<a href="#{get_reference(subject)}">Some text</a>) }
it_behaves_like 'it contains a data- attribute'
diff --git a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
index 7df4b7635d3..ddd3bbd636a 100644
--- a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
+++ b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
@@ -80,7 +80,7 @@ RSpec.shared_examples 'multiple issue boards' do
click_button 'Select a label'
- page.choose(planning.title)
+ find('label', text: planning.title).click
click_button 'Add to board'
diff --git a/spec/support/shared_examples/bulk_imports/visibility_level_examples.rb b/spec/support/shared_examples/bulk_imports/visibility_level_examples.rb
index 40e9726f89c..02eae250e6a 100644
--- a/spec/support/shared_examples/bulk_imports/visibility_level_examples.rb
+++ b/spec/support/shared_examples/bulk_imports/visibility_level_examples.rb
@@ -27,14 +27,6 @@ RSpec.shared_examples 'visibility level settings' do
expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
-
- context 'when destination is blank' do
- let(:destination_namespace) { '' }
-
- it 'sets visibility level to public' do
- expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PUBLIC)
- end
- end
end
context 'when internal' do
@@ -63,27 +55,6 @@ RSpec.shared_examples 'visibility level settings' do
expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
-
- context 'when destination is blank' do
- let(:destination_namespace) { '' }
-
- it 'sets visibility level to internal' do
- expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::INTERNAL)
- end
-
- context 'when visibility level is restricted' do
- it 'sets visibility level to private' do
- stub_application_setting(
- restricted_visibility_levels: [
- Gitlab::VisibilityLevel::INTERNAL,
- Gitlab::VisibilityLevel::PUBLIC
- ]
- )
-
- expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
- end
- end
- end
end
context 'when private' do
@@ -112,13 +83,5 @@ RSpec.shared_examples 'visibility level settings' do
expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
end
end
-
- context 'when destination is blank' do
- let(:destination_namespace) { '' }
-
- it 'sets visibility level to private' do
- expect(transformed_data[:visibility_level]).to eq(Gitlab::VisibilityLevel::PRIVATE)
- end
- end
end
end
diff --git a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
index de38d1ff9f8..af1843bae28 100644
--- a/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb
@@ -138,6 +138,19 @@ RSpec.shared_examples 'a GitHub-ish import controller: GET status' do
.not_to exceed_all_query_limit(control_count)
end
+ context 'when user is not allowed to import projects' do
+ let(:user) { create(:user) }
+ let!(:group) { create(:group).tap { |group| group.add_developer(user) } }
+
+ it 'returns 404' do
+ expect(stub_client(repos: [], orgs: [])).to receive(:repos)
+
+ get :status, params: { namespace_id: group.id }, format: :html
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
context 'when filtering' do
let(:repo_2) { repo_fake.new(login: 'emacs', full_name: 'asd/emacs', name: 'emacs', owner: { login: 'owner' }) }
let(:project) { create(:project, import_type: provider, namespace: user.namespace, import_status: :finished, import_source: 'example/repo') }
diff --git a/spec/support/shared_examples/controllers/import_controller_status_shared_examples.rb b/spec/support/shared_examples/controllers/import_controller_status_shared_examples.rb
index 44baadaaade..e94f063399d 100644
--- a/spec/support/shared_examples/controllers/import_controller_status_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/import_controller_status_shared_examples.rb
@@ -19,4 +19,26 @@ RSpec.shared_examples 'import controller status' do
expect(json_response.dig("imported_projects", 0, "id")).to eq(project.id)
expect(json_response.dig("provider_repos", 0, "id")).to eq(repo_id)
end
+
+ context 'when format is html' do
+ context 'when namespace_id is present' do
+ let!(:developer_group) { create(:group).tap { |g| g.add_developer(user) } }
+
+ context 'when user cannot import projects' do
+ it 'returns 404' do
+ get :status, params: { namespace_id: developer_group.id }, format: :html
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when user can import projects' do
+ it 'returns 200' do
+ get :status, params: { namespace_id: group.id }, format: :html
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+ end
+ end
+ end
end
diff --git a/spec/support/controllers/project_import_rate_limiter_shared_examples.rb b/spec/support/shared_examples/controllers/project_import_rate_limiter_shared_examples.rb
index 66d753a4010..66d753a4010 100644
--- a/spec/support/controllers/project_import_rate_limiter_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/project_import_rate_limiter_shared_examples.rb
diff --git a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
index cc28a79b4ca..e75188f8249 100644
--- a/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/repositories/git_http_controller_shared_examples.rb
@@ -60,19 +60,6 @@ RSpec.shared_examples Repositories::GitHttpController do
expect(response).to have_gitlab_http_status(:ok)
end
- it 'updates the user activity' do
- activity_project = container.is_a?(PersonalSnippet) ? nil : project
-
- activity_service = instance_double(Users::ActivityService)
-
- args = { author: user, project: activity_project, namespace: activity_project&.namespace }
- expect(Users::ActivityService).to receive(:new).with(args).and_return(activity_service)
-
- expect(activity_service).to receive(:execute)
-
- get :info_refs, params: params
- end
-
include_context 'parsed logs' do
it 'adds user info to the logs' do
get :info_refs, params: params
@@ -87,14 +74,20 @@ RSpec.shared_examples Repositories::GitHttpController do
end
describe 'POST #git_upload_pack' do
- before do
+ it 'returns 200' do
allow(controller).to receive(:verify_workhorse_api!).and_return(true)
- end
- it 'returns 200' do
post :git_upload_pack, params: params
expect(response).to have_gitlab_http_status(:ok)
end
+
+ context 'when JWT token is not provided' do
+ it 'returns 403' do
+ post :git_upload_pack, params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
end
end
diff --git a/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb b/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
index 112b9cbb204..f658cfac0f5 100644
--- a/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
@@ -15,8 +15,9 @@ RSpec.shared_examples 'snippets sort order' do
context 'when no sort param is provided' do
it 'calls SnippetsFinder with updated_at sort option' do
- expect(SnippetsFinder).to receive(:new).with(user,
- hash_including(sort: 'updated_desc')).and_call_original
+ expect(SnippetsFinder).to receive(:new)
+ .with(user, hash_including(sort: 'updated_desc'))
+ .and_call_original
subject
end
@@ -27,8 +28,9 @@ RSpec.shared_examples 'snippets sort order' do
let(:sort_argument) { { sort: order } }
it 'calls SnippetsFinder with the given sort param' do
- expect(SnippetsFinder).to receive(:new).with(user,
- hash_including(sort: order)).and_call_original
+ expect(SnippetsFinder).to receive(:new)
+ .with(user, hash_including(sort: order))
+ .and_call_original
subject
end
diff --git a/spec/support/shared_examples/controllers/unique_hll_events_examples.rb b/spec/support/shared_examples/controllers/unique_hll_events_examples.rb
index 38c3157e898..b5528afa0b5 100644
--- a/spec/support/shared_examples/controllers/unique_hll_events_examples.rb
+++ b/spec/support/shared_examples/controllers/unique_hll_events_examples.rb
@@ -7,6 +7,9 @@
RSpec.shared_examples 'tracking unique hll events' do
it 'tracks unique event' do
+ # Allow any event tracking before we expect the specific event we want to check below
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).and_call_original
+
expect(Gitlab::UsageDataCounters::HLLRedisCounter).to(
receive(:track_event)
.with(target_event, values: expected_value)
diff --git a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
index 5d77ed5fdfc..32aa566c27e 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -15,20 +15,33 @@ RSpec.shared_examples 'wiki controller actions' do
sign_in(user)
end
- shared_examples 'recovers from git timeout' do
+ shared_examples 'recovers from git errors' do
let(:method_name) { :page }
- context 'when we encounter git command errors' do
+ context 'when we encounter CommandTimedOut error' do
it 'renders the appropriate template', :aggregate_failures do
- expect(controller).to receive(method_name) do
- raise ::Gitlab::Git::CommandTimedOut, 'Deadline Exceeded'
- end
+ expect(controller)
+ .to receive(method_name)
+ .and_raise(::Gitlab::Git::CommandTimedOut, 'Deadline Exceeded')
request
expect(response).to render_template('shared/wikis/git_error')
end
end
+
+ context 'when we encounter a NoRepository error' do
+ it 'renders the appropriate template', :aggregate_failures do
+ expect(controller)
+ .to receive(method_name)
+ .and_raise(Gitlab::Git::Repository::NoRepository)
+
+ request
+
+ expect(response).to render_template('shared/wikis/empty')
+ expect(assigns(:error)).to eq('Could not access the Wiki Repository at this time.')
+ end
+ end
end
describe 'GET #new' do
@@ -65,7 +78,7 @@ RSpec.shared_examples 'wiki controller actions' do
get :pages, params: routing_params.merge(id: wiki_title)
end
- it_behaves_like 'recovers from git timeout' do
+ it_behaves_like 'recovers from git errors' do
subject(:request) { get :pages, params: routing_params.merge(id: wiki_title) }
let(:method_name) { :wiki_pages }
@@ -122,7 +135,7 @@ RSpec.shared_examples 'wiki controller actions' do
end
end
- it_behaves_like 'recovers from git timeout' do
+ it_behaves_like 'recovers from git errors' do
subject(:request) { get :history, params: routing_params.merge(id: wiki_title) }
let(:allow_read_wiki) { true }
@@ -170,7 +183,7 @@ RSpec.shared_examples 'wiki controller actions' do
end
end
- it_behaves_like 'recovers from git timeout' do
+ it_behaves_like 'recovers from git errors' do
subject(:request) { get :diff, params: routing_params.merge(id: wiki_title, version_id: wiki.repository.commit.id) }
end
end
@@ -185,7 +198,7 @@ RSpec.shared_examples 'wiki controller actions' do
context 'when page exists' do
let(:id) { wiki_title }
- it_behaves_like 'recovers from git timeout'
+ it_behaves_like 'recovers from git errors'
it 'renders the page' do
request
@@ -366,7 +379,7 @@ RSpec.shared_examples 'wiki controller actions' do
subject(:request) { get(:edit, params: routing_params.merge(id: id_param)) }
it_behaves_like 'edit action'
- it_behaves_like 'recovers from git timeout'
+ it_behaves_like 'recovers from git errors'
context 'when page content encoding is valid' do
render_views
@@ -386,11 +399,10 @@ RSpec.shared_examples 'wiki controller actions' do
let(:id_param) { wiki_title }
subject(:request) do
- patch(:update,
- params: routing_params.merge(
- id: id_param,
- wiki: { title: new_title, content: new_content }
- ))
+ patch(:update, params: routing_params.merge(
+ id: id_param,
+ wiki: { title: new_title, content: new_content }
+ ))
end
it_behaves_like 'edit action'
@@ -426,10 +438,9 @@ RSpec.shared_examples 'wiki controller actions' do
let(:new_content) { 'New content' }
subject(:request) do
- post(:create,
- params: routing_params.merge(
- wiki: { title: new_title, content: new_content }
- ))
+ post(:create, params: routing_params.merge(
+ wiki: { title: new_title, content: new_content }
+ ))
end
context 'when page is valid' do
@@ -463,10 +474,9 @@ RSpec.shared_examples 'wiki controller actions' do
let(:delete_user) { user }
subject(:request) do
- delete(:destroy,
- params: routing_params.merge(
- id: id_param
- ))
+ delete(:destroy, params: routing_params.merge(
+ id: id_param
+ ))
end
before do
diff --git a/spec/support/shared_examples/db/seeds/data_seeder_shared_examples.rb b/spec/support/shared_examples/db/seeds/data_seeder_shared_examples.rb
new file mode 100644
index 00000000000..4e8d65ac25e
--- /dev/null
+++ b/spec/support/shared_examples/db/seeds/data_seeder_shared_examples.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'raises an error when specifying an invalid factory' do
+ it 'raises an error' do
+ expect { parser.parse }.to raise_error(RuntimeError, /invalids.*to a valid registered Factory/)
+ end
+end
+
+RSpec.shared_examples 'specifying invalid traits to a factory' do
+ it 'raises an error', :aggregate_failures do
+ expect { parser.parse }.to raise_error do |error|
+ expect(error).to be_a(RuntimeError)
+ expect(error.message).to include('Trait not registered: \\"invalid\\"')
+ expect(error.message).to include('for Factory \\"issue\\"')
+ end
+ end
+end
+
+RSpec.shared_examples 'specifying invalid attributes to a factory' do
+ it 'raises an error' do
+ expect { parser.parse }.to raise_error(RuntimeError, /is not a valid attribute/)
+ end
+
+ it 'contains possible alternatives' do
+ expect { parser.parse }.to raise_error(RuntimeError, /Did you mean/)
+ end
+end
+
+RSpec.shared_examples 'an id already exists' do
+ it 'raises a validation error' do
+ expect { parser.parse }.to raise_error(/id `my_label` must be unique/)
+ end
+end
+
+RSpec.shared_examples 'name is not specified' do
+ it 'raises an error when name is not specified' do
+ expect { parser.parse }.to raise_error(/Seed file must specify a name/)
+ end
+end
+
+RSpec.shared_examples 'factory definitions' do
+ it 'has exactly two definitions' do
+ parser.parse
+
+ expect(parser.definitions.size).to eq(2)
+ end
+
+ it 'creates the group label' do
+ expect { parser.parse }.to change { GroupLabel.count }.by(1)
+ end
+
+ it 'creates the project' do
+ expect { parser.parse }.to change { Project.count }.by(1)
+ end
+end
+
+RSpec.shared_examples 'passes traits' do
+ it 'passes traits' do
+ expect_next_instance_of(Gitlab::DataSeeder::FactoryDefinitions::FactoryDefinition) do |instance|
+ # `described` trait will automaticaly generate a description
+ expect(instance.build(binding).description).to eq('Description of Test Label')
+ end
+
+ parser.parse
+ end
+end
+
+RSpec.shared_examples 'has a name' do
+ it 'has a name' do
+ parser.parse
+
+ expect(parser.name).to eq('Test')
+ end
+end
+
+RSpec.shared_examples 'definition has an id' do
+ it 'binds the object', :aggregate_failures do
+ parser.parse
+
+ expect(group_labels).to be_a(OpenStruct) # rubocop:disable Style/OpenStructUse
+ expect(group_labels.my_label).to be_a(GroupLabel)
+ expect(group_labels.my_label.title).to eq('My Label')
+ end
+end
+
+RSpec.shared_examples 'id has spaces' do
+ it 'binds to an underscored variable', :aggregate_failures do
+ parser.parse
+
+ expect(group_labels).to respond_to(:id_with_spaces)
+ expect(group_labels.id_with_spaces.title).to eq('With Spaces')
+ end
+
+ it 'renders a warning' do
+ expect { parser.parse }.to output(%(parsing id "id with spaces" as "id_with_spaces"\n)).to_stderr
+ end
+end
+
+RSpec.shared_examples 'definition does not have an id' do
+ it 'does not bind the object' do
+ parser.parse
+
+ expect(group_labels.to_h).to be_empty
+ end
+end
+
+RSpec.shared_examples 'invalid id' do |message|
+ it 'raises an error' do
+ expect { parser.parse }.to raise_error(message)
+ end
+end
diff --git a/spec/support/shared_examples/features/2fa_shared_examples.rb b/spec/support/shared_examples/features/2fa_shared_examples.rb
index 44f30c32472..6c4e98c9989 100644
--- a/spec/support/shared_examples/features/2fa_shared_examples.rb
+++ b/spec/support/shared_examples/features/2fa_shared_examples.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
RSpec.shared_examples 'hardware device for 2fa' do |device_type|
- include Spec::Support::Helpers::Features::TwoFactorHelpers
+ include Features::TwoFactorHelpers
include Spec::Support::Helpers::ModalHelpers
def register_device(device_type, **kwargs)
case device_type.downcase
- when "u2f"
- register_u2f_device(**kwargs)
when "webauthn"
register_webauthn_device(**kwargs)
else
@@ -98,9 +96,7 @@ RSpec.shared_examples 'hardware device for 2fa' do |device_type|
end
it 'provides a button that shows the fallback otp code UI' do
- expect(page).to have_link('Sign in via 2FA code')
-
- click_link('Sign in via 2FA code')
+ click_button(_('Sign in via 2FA code'))
assert_fallback_ui(page)
end
diff --git a/spec/support/shared_examples/features/abuse_report_shared_examples.rb b/spec/support/shared_examples/features/abuse_report_shared_examples.rb
new file mode 100644
index 00000000000..ea9b4e9f4b2
--- /dev/null
+++ b/spec/support/shared_examples/features/abuse_report_shared_examples.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'reports the user with an abuse category' do
+ it 'creates abuse report' do
+ click_button 'Report abuse'
+ choose "They're posting spam."
+ click_button 'Next'
+
+ page.attach_file('spec/fixtures/dk.png') do
+ click_button "Choose file"
+ end
+
+ fill_in 'abuse_report_message', with: 'This user sends spam'
+ click_button 'Send report'
+
+ expect(page).to have_content 'Thank you for your report'
+ end
+end
diff --git a/spec/support/shared_examples/features/access_tokens_shared_examples.rb b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
index 32a7b32ac72..3c78869ffaa 100644
--- a/spec/support/shared_examples/features/access_tokens_shared_examples.rb
+++ b/spec/support/shared_examples/features/access_tokens_shared_examples.rb
@@ -9,7 +9,7 @@ RSpec.shared_examples 'resource access tokens missing access rights' do
end
RSpec.shared_examples 'resource access tokens creation' do |resource_type|
- include Spec::Support::Helpers::AccessTokenHelpers
+ include Features::AccessTokenHelpers
it 'allows creation of an access token', :aggregate_failures do
name = 'My access token'
diff --git a/spec/support/shared_examples/features/confidential_notes_shared_examples.rb b/spec/support/shared_examples/features/confidential_notes_shared_examples.rb
index 289da025af6..cd0e8f94934 100644
--- a/spec/support/shared_examples/features/confidential_notes_shared_examples.rb
+++ b/spec/support/shared_examples/features/confidential_notes_shared_examples.rb
@@ -3,7 +3,7 @@
require "spec_helper"
RSpec.shared_examples 'confidential notes on issuables' do
- include Spec::Support::Helpers::Features::NotesHelpers
+ include Features::NotesHelpers
context 'when user does not have permissions' do
it 'does not show confidential note checkbox' do
diff --git a/spec/support/shared_examples/features/content_editor_shared_examples.rb b/spec/support/shared_examples/features/content_editor_shared_examples.rb
index 6cd9c4ce1c4..41114197ff5 100644
--- a/spec/support/shared_examples/features/content_editor_shared_examples.rb
+++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb
@@ -1,116 +1,310 @@
# frozen_string_literal: true
+require 'spec_helper'
+
RSpec.shared_examples 'edits content using the content editor' do
+ include ContentEditorHelpers
+
let(:content_editor_testid) { '[data-testid="content-editor"] [contenteditable].ProseMirror' }
- def switch_to_content_editor
- click_button _('View rich text')
- click_button _('Rich text')
- end
+ let(:is_mac) { page.evaluate_script('navigator.platform').include?('Mac') }
+ let(:modifier_key) { is_mac ? :command : :control }
- def type_in_content_editor(keys)
- find(content_editor_testid).send_keys keys
- end
+ it 'saves page content in local storage if the user navigates away' do
+ switch_to_content_editor
- def open_insert_media_dropdown
- page.find('svg[data-testid="media-icon"]').click
- end
+ expect(page).to have_css(content_editor_testid)
- def set_source_editor_content(content)
- find('.js-gfm-input').set content
- end
+ type_in_content_editor ' Typing text in the content editor'
- def expect_formatting_menu_to_be_visible
- expect(page).to have_css('[data-testid="formatting-bubble-menu"]')
- end
+ wait_until_hidden_field_is_updated /Typing text in the content editor/
- def expect_formatting_menu_to_be_hidden
- expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]')
- end
+ begin
+ refresh
+ rescue Selenium::WebDriver::Error::UnexpectedAlertOpenError
+ end
- def expect_media_bubble_menu_to_be_visible
- expect(page).to have_css('[data-testid="media-bubble-menu"]')
+ expect(page).to have_text('Typing text in the content editor')
end
- def upload_asset(fixture_name)
- attach_file('content_editor_image', Rails.root.join('spec', 'fixtures', fixture_name), make_visible: true)
- end
+ describe 'creating and editing links' do
+ before do
+ switch_to_content_editor
+ end
- def wait_until_hidden_field_is_updated(value)
- expect(page).to have_field('wiki[content]', with: value, type: 'hidden')
- end
+ context 'when clicking the link icon in the toolbar' do
+ it 'shows the link bubble menu' do
+ page.find('[data-testid="formatting-toolbar"] [data-testid="link"]').click
- it 'saves page content in local storage if the user navigates away' do
- switch_to_content_editor
+ expect(page).to have_css('[data-testid="link-bubble-menu"]')
+ end
- expect(page).to have_css(content_editor_testid)
+ context 'if no text is selected' do
+ before do
+ page.find('[data-testid="formatting-toolbar"] [data-testid="link"]').click
+ end
+
+ it 'opens an empty inline modal to create a link' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ expect(page).to have_field('link-text', with: '')
+ expect(page).to have_field('link-href', with: '')
+ end
+ end
+
+ context 'when the user clicks the apply button' do
+ it 'applies the changes to the document' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ fill_in 'link-text', with: 'Link to GitLab home page'
+ fill_in 'link-href', with: 'https://gitlab.com'
+
+ click_button 'Apply'
+ end
+
+ page.within content_editor_testid do
+ expect(page).to have_css('a[href="https://gitlab.com"]')
+ expect(page).to have_text('Link to GitLab home page')
+ end
+ end
+ end
+
+ context 'when the user clicks the cancel button' do
+ it 'does not apply the changes to the document' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ fill_in 'link-text', with: 'Link to GitLab home page'
+ fill_in 'link-href', with: 'https://gitlab.com'
+
+ click_button 'Cancel'
+ end
+
+ page.within content_editor_testid do
+ expect(page).not_to have_css('a')
+ end
+ end
+ end
+ end
- type_in_content_editor ' Typing text in the content editor'
+ context 'if text is selected' do
+ before do
+ type_in_content_editor 'The quick brown fox jumps over the lazy dog'
+ type_in_content_editor [:shift, :left]
+ type_in_content_editor [:shift, :left]
+ type_in_content_editor [:shift, :left]
+
+ page.find('[data-testid="formatting-toolbar"] [data-testid="link"]').click
+ end
+
+ it 'prefills inline modal to create a link' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ expect(page).to have_field('link-text', with: 'dog')
+ expect(page).to have_field('link-href', with: '')
+ end
+ end
+
+ context 'when the user clicks the apply button' do
+ it 'applies the changes to the document' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ fill_in 'link-text', with: 'new dog'
+ fill_in 'link-href', with: 'https://en.wikipedia.org/wiki/Shiba_Inu'
+
+ click_button 'Apply'
+ end
+
+ page.within content_editor_testid do
+ expect(page).to have_selector('a[href="https://en.wikipedia.org/wiki/Shiba_Inu"]',
+ text: 'new dog'
+ )
+ end
+ end
+ end
+ end
+ end
- wait_until_hidden_field_is_updated /Typing text in the content editor/
+ context 'if cursor is placed on an existing link' do
+ before do
+ type_in_content_editor 'Link to [GitLab home **page**](https://gitlab.com)'
+ type_in_content_editor :left
+ end
- refresh
+ it 'prefills inline modal to edit the link' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ page.find('[data-testid="edit-link"]').click
- expect(page).to have_text('Typing text in the content editor')
+ expect(page).to have_field('link-text', with: 'GitLab home page')
+ expect(page).to have_field('link-href', with: 'https://gitlab.com')
+ end
+ end
- refresh # also retained after second refresh
+ it 'updates the link attributes if text is not updated' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ page.find('[data-testid="edit-link"]').click
- expect(page).to have_text('Typing text in the content editor')
+ fill_in 'link-href', with: 'https://about.gitlab.com'
- click_link 'Cancel' # draft is deleted on cancel
+ click_button 'Apply'
+ end
- page.go_back
+ page.within content_editor_testid do
+ expect(page).to have_selector('a[href="https://about.gitlab.com"]')
+ expect(page.find('a')).to have_text('GitLab home page')
+ expect(page).to have_selector('strong', text: 'page')
+ end
+ end
- expect(page).not_to have_text('Typing text in the content editor')
- end
+ it 'updates the link attributes and text if text is updated' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ page.find('[data-testid="edit-link"]').click
- describe 'formatting bubble menu' do
- it 'shows a formatting bubble menu for a regular paragraph and headings' do
- switch_to_content_editor
+ fill_in 'link-text', with: 'GitLab about page'
+ fill_in 'link-href', with: 'https://about.gitlab.com'
- expect(page).to have_css(content_editor_testid)
+ click_button 'Apply'
+ end
- type_in_content_editor 'Typing text in the content editor'
- type_in_content_editor [:shift, :left]
+ page.within content_editor_testid do
+ expect(page).to have_selector('a[href="https://about.gitlab.com"]',
+ text: 'GitLab about page'
+ )
+ expect(page).not_to have_selector('strong')
+ end
+ end
- expect_formatting_menu_to_be_visible
+ it 'does nothing if Cancel is clicked' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ page.find('[data-testid="edit-link"]').click
- type_in_content_editor [:right, :right, :enter, '## Heading']
+ click_button 'Cancel'
+ end
- expect_formatting_menu_to_be_hidden
+ page.within content_editor_testid do
+ expect(page).to have_selector('a[href="https://gitlab.com"]',
+ text: 'GitLab home page'
+ )
+ expect(page).to have_selector('strong')
+ end
+ end
- type_in_content_editor [:shift, :left]
+ context 'when the user clicks the unlink button' do
+ it 'removes the link' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ page.find('[data-testid="remove-link"]').click
+ end
+
+ page.within content_editor_testid do
+ expect(page).not_to have_selector('a')
+ expect(page).to have_selector('strong', text: 'page')
+ end
+ end
+ end
+ end
+
+ context 'when selection spans more than a link' do
+ before do
+ type_in_content_editor 'a [b **c**](https://gitlab.com)'
+
+ type_in_content_editor [:shift, :left]
+ type_in_content_editor [:shift, :left]
+ type_in_content_editor [:shift, :left]
+ type_in_content_editor [:shift, :left]
+ type_in_content_editor [:shift, :left]
+
+ page.find('[data-testid="formatting-toolbar"] [data-testid="link"]').click
+ end
+
+ it 'prefills inline modal with the entire selection' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ expect(page).to have_field('link-text', with: 'a b c')
+ expect(page).to have_field('link-href', with: '')
+ end
+ end
- expect_formatting_menu_to_be_visible
+ it 'expands the link and updates the link attributes if text is not updated' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ fill_in 'link-href', with: 'https://about.gitlab.com'
+
+ click_button 'Apply'
+ end
+
+ page.within content_editor_testid do
+ expect(page).to have_selector('a[href="https://about.gitlab.com"]')
+ expect(page.find('a')).to have_text('a b c')
+ expect(page).to have_selector('strong', text: 'c')
+ end
+ end
+
+ it 'expands the link, updates the link attributes and text if text is updated' do
+ page.within '[data-testid="link-bubble-menu"]' do
+ fill_in 'link-text', with: 'new text'
+ fill_in 'link-href', with: 'https://about.gitlab.com'
+
+ click_button 'Apply'
+ end
+
+ page.within content_editor_testid do
+ expect(page).to have_selector('a[href="https://about.gitlab.com"]',
+ text: 'new text'
+ )
+ expect(page).not_to have_selector('strong')
+ end
+ end
end
end
- describe 'media elements bubble menu' do
+ describe 'selecting text' do
before do
switch_to_content_editor
- open_insert_media_dropdown
+ # delete all text first
+ type_in_content_editor [modifier_key, 'a']
+ type_in_content_editor :backspace
+
+ type_in_content_editor 'The quick **brown** fox _jumps_ over the lazy dog!'
+ type_in_content_editor :enter
+ type_in_content_editor '[Link](https://gitlab.com)'
+ type_in_content_editor :enter
+ type_in_content_editor 'Jackdaws love my ~~big~~ sphinx of quartz!'
+
+ # select all text
+ type_in_content_editor [modifier_key, 'a']
end
- def test_displays_media_bubble_menu(media_element_selector, fixture_file)
- upload_asset fixture_file
+ it 'renders selected text in a .content-editor-selection class' do
+ page.within content_editor_testid do
+ assert_selected 'The quick'
+ assert_selected 'brown'
+ assert_selected 'fox'
+ assert_selected 'jumps'
+ assert_selected 'over the lazy dog!'
- wait_for_requests
+ assert_selected 'Link'
- expect(page).to have_css(media_element_selector)
+ assert_selected 'Jackdaws love my'
+ assert_selected 'big'
+ assert_selected 'sphinx of quartz!'
+ end
+ end
- page.find(media_element_selector).click
+ def assert_selected(text)
+ expect(page).to have_selector('.content-editor-selection', text: text)
+ end
+ end
- expect_formatting_menu_to_be_hidden
- expect_media_bubble_menu_to_be_visible
+ describe 'media elements bubble menu' do
+ before do
+ switch_to_content_editor
+
+ click_attachment_button
end
it 'displays correct media bubble menu for images', :js do
- test_displays_media_bubble_menu '[data-testid="content_editor_editablebox"] img[src]', 'dk.png'
+ display_media_bubble_menu '[data-testid="content_editor_editablebox"] img[src]', 'dk.png'
+
+ expect_media_bubble_menu_to_be_visible
end
it 'displays correct media bubble menu for video', :js do
- test_displays_media_bubble_menu '[data-testid="content_editor_editablebox"] video', 'video_sample.mp4'
+ display_media_bubble_menu '[data-testid="content_editor_editablebox"] video', 'video_sample.mp4'
+
+ expect_media_bubble_menu_to_be_visible
end
end
@@ -150,7 +344,6 @@ RSpec.shared_examples 'edits content using the content editor' do
type_in_content_editor 'var a = 0'
type_in_content_editor [:shift, :left]
- expect_formatting_menu_to_be_hidden
expect(page).to have_css('[data-testid="code-block-bubble-menu"]')
end
@@ -187,8 +380,8 @@ RSpec.shared_examples 'edits content using the content editor' do
expect(iframe['src']).to include('/-/sandbox/mermaid')
within_frame(iframe) do
- expect(find('svg').text).to include('JohnDoe12')
- expect(find('svg').text).to include('HelloWorld34')
+ expect(find('svg .nodes').text).to include('JohnDoe12')
+ expect(find('svg .nodes').text).to include('HelloWorld34')
end
expect(iframe['height'].to_i).to be > 100
@@ -198,12 +391,13 @@ RSpec.shared_examples 'edits content using the content editor' do
within_frame(iframe) do
page.has_content?('JaneDoe34')
- expect(find('svg').text).to include('JaneDoe34')
- expect(find('svg').text).to include('HelloWorld56')
+ expect(find('svg .nodes').text).to include('JaneDoe34')
+ expect(find('svg .nodes').text).to include('HelloWorld56')
end
end
- it 'toggles the diagram when preview button is clicked' do
+ it 'toggles the diagram when preview button is clicked',
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/397682' do
find('[data-testid="preview-diagram"]').click
expect(find(content_editor_testid)).not_to have_selector('iframe')
@@ -213,8 +407,61 @@ RSpec.shared_examples 'edits content using the content editor' do
iframe = find(content_editor_testid).find('iframe')
within_frame(iframe) do
- expect(find('svg').text).to include('JohnDoe12')
- expect(find('svg').text).to include('HelloWorld34')
+ expect(find('svg .nodes').text).to include('JohnDoe12')
+ expect(find('svg .nodes').text).to include('HelloWorld34')
+ end
+ end
+ end
+
+ describe 'pasting text' do
+ before do
+ switch_to_content_editor
+
+ type_in_content_editor "Some **rich** _text_ ~~content~~ [link](https://gitlab.com)"
+
+ type_in_content_editor [modifier_key, 'a']
+ type_in_content_editor [modifier_key, 'x']
+ end
+
+ it 'pastes text with formatting if ctrl + v is pressed' do
+ type_in_content_editor [modifier_key, 'v']
+
+ page.within content_editor_testid do
+ expect(page).to have_selector('strong', text: 'rich')
+ expect(page).to have_selector('em', text: 'text')
+ expect(page).to have_selector('s', text: 'content')
+ expect(page).to have_selector('a[href="https://gitlab.com"]', text: 'link')
+ end
+ end
+
+ it 'pastes raw text without formatting if shift + ctrl + v is pressed' do
+ type_in_content_editor [modifier_key, :shift, 'v']
+
+ page.within content_editor_testid do
+ expect(page).to have_text('Some rich text content link')
+
+ expect(page).not_to have_selector('strong')
+ expect(page).not_to have_selector('em')
+ expect(page).not_to have_selector('s')
+ expect(page).not_to have_selector('a')
+ end
+ end
+
+ it 'pastes raw text without formatting, stripping whitespaces, if shift + ctrl + v is pressed' do
+ type_in_content_editor " Some **rich**"
+ type_in_content_editor :enter
+ type_in_content_editor " _text_"
+ type_in_content_editor :enter
+ type_in_content_editor " ~~content~~"
+ type_in_content_editor :enter
+ type_in_content_editor " [link](https://gitlab.com)"
+
+ type_in_content_editor [modifier_key, 'a']
+ type_in_content_editor [modifier_key, 'x']
+ type_in_content_editor [modifier_key, :shift, 'v']
+
+ page.within content_editor_testid do
+ expect(page).to have_text('Some rich text content link')
end
end
end
@@ -225,7 +472,7 @@ RSpec.shared_examples 'edits content using the content editor' do
before do
if defined?(project)
create(:issue, project: project, title: 'My Cool Linked Issue')
- create(:merge_request, source_project: project, title: 'My Cool Merge Request')
+ create(:merge_request, source_project: project, source_branch: 'branch-1', title: 'My Cool Merge Request')
create(:label, project: project, title: 'My Cool Label')
create(:milestone, project: project, title: 'My Cool Milestone')
@@ -234,7 +481,7 @@ RSpec.shared_examples 'edits content using the content editor' do
project = create(:project, group: group)
create(:issue, project: project, title: 'My Cool Linked Issue')
- create(:merge_request, source_project: project, title: 'My Cool Merge Request')
+ create(:merge_request, source_project: project, source_branch: 'branch-1', title: 'My Cool Merge Request')
create(:group_label, group: group, title: 'My Cool Label')
create(:milestone, group: group, title: 'My Cool Milestone')
@@ -251,7 +498,9 @@ RSpec.shared_examples 'edits content using the content editor' do
expect(find(suggestions_dropdown)).to have_text('abc123')
expect(find(suggestions_dropdown)).to have_text('all')
- expect(find(suggestions_dropdown)).to have_text('Group Members (2)')
+ expect(find(suggestions_dropdown)).to have_text('Group Members')
+
+ type_in_content_editor 'bc'
send_keys [:arrow_down, :enter]
@@ -332,3 +581,23 @@ RSpec.shared_examples 'edits content using the content editor' do
end
end
end
+
+RSpec.shared_examples 'inserts diagrams.net diagram using the content editor' do
+ include ContentEditorHelpers
+
+ before do
+ switch_to_content_editor
+
+ click_attachment_button
+ end
+
+ it 'displays correct media bubble menu with edit diagram button' do
+ display_media_bubble_menu '[data-testid="content_editor_editablebox"] img[src]', 'diagram.drawio.svg'
+
+ expect_media_bubble_menu_to_be_visible
+
+ click_edit_diagram_button
+
+ expect_drawio_editor_is_opened
+ end
+end
diff --git a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
index 96e57980c68..7e0e235698e 100644
--- a/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/creatable_merge_request_shared_examples.rb
@@ -5,20 +5,20 @@ RSpec.shared_examples 'a creatable merge request' do
include ListboxHelpers
it 'creates new merge request', :js do
- find('.js-assignee-search').click
+ find('[data-testid="assignee-ids-dropdown-toggle"]').click
page.within '.dropdown-menu-user' do
click_link user2.name
end
expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user2.id.to_s)
- page.within '.js-assignee-search' do
+ page.within '[data-testid="assignee-ids-dropdown-toggle"]' do
expect(page).to have_content user2.name
end
click_link 'Assign to me'
expect(find('input[name="merge_request[assignee_ids][]"]', visible: false).value).to match(user.id.to_s)
- page.within '.js-assignee-search' do
+ page.within '[data-testid="assignee-ids-dropdown-toggle"]' do
expect(page).to have_content user.name
end
diff --git a/spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb b/spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb
index efbd735c451..9b5d9d66890 100644
--- a/spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb
+++ b/spec/support/shared_examples/features/dashboard/sidebar_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples "a dashboard page with sidebar" do |page_path, menu_label|
+RSpec.shared_examples 'a "Your work" page with sidebar and breadcrumbs' do |page_path, menu_label|
before do
sign_in(user)
visit send(page_path)
@@ -18,4 +18,13 @@ RSpec.shared_examples "a dashboard page with sidebar" do |page_path, menu_label|
expect(page).to have_css(active_menu_item_css)
end
end
+
+ describe "breadcrumbs" do
+ it 'has "Your work" as its root breadcrumb' do
+ breadcrumbs = page.find('[data-testid="breadcrumb-links"]')
+ within breadcrumbs do
+ expect(page).to have_css("li:first-child a[href=\"#{root_path}\"]", text: "Your work")
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/features/deploy_token_shared_examples.rb b/spec/support/shared_examples/features/deploy_token_shared_examples.rb
index 9fe08e5c996..80f5f1d805c 100644
--- a/spec/support/shared_examples/features/deploy_token_shared_examples.rb
+++ b/spec/support/shared_examples/features/deploy_token_shared_examples.rb
@@ -17,9 +17,11 @@ RSpec.shared_examples 'a deploy token in settings' do
it 'add a new deploy token', :js do
visit page_path
- fill_in _('Name'), with: 'new_deploy_key'
- fill_in _('Expiration date (optional)'), with: (Date.today + 1.month).to_s
- fill_in _('Username (optional)'), with: 'deployer'
+ within('#js-deploy-tokens') do
+ fill_in _('Name'), with: 'new_deploy_key'
+ fill_in _('Expiration date (optional)'), with: (Date.today + 1.month).to_s
+ fill_in _('Username (optional)'), with: 'deployer'
+ end
check 'read_repository'
check 'read_registry'
click_button 'Create deploy token'
diff --git a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
index ea6d1655694..14e53dc8655 100644
--- a/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/features/editable_merge_request_shared_examples.rb
@@ -77,15 +77,21 @@ RSpec.shared_examples 'an editable merge request' do
expect(page).to have_selector('.js-quick-submit')
end
- it 'warns about version conflict' do
+ it 'warns about version conflict', :js do
merge_request.update!(title: "New title")
fill_in 'merge_request_title', with: 'bug 345'
fill_in 'merge_request_description', with: 'bug description'
- click_button 'Save changes'
+ click_button _('Save changes')
- expect(page).to have_content 'Someone edited the merge request the same time you did'
+ expect(page).to have_content(
+ format(
+ _("Someone edited this %{model_name} at the same time you did. Please check out the %{link_to_model} and make sure your changes will not unintentionally remove theirs."), # rubocop:disable Layout/LineLength
+ model_name: _('merge request'),
+ link_to_model: _('merge request')
+ )
+ )
end
it 'preserves description textarea height', :js do
@@ -104,8 +110,8 @@ RSpec.shared_examples 'an editable merge request' do
fill_in 'merge_request_description', with: long_description
height = get_textarea_height
- find('.js-md-preview-button').click
- find('.js-md-write-button').click
+ click_button("Preview")
+ click_button("Continue editing")
new_height = get_textarea_height
expect(height).to eq(new_height)
diff --git a/spec/support/shared_examples/features/explore/sidebar_shared_examples.rb b/spec/support/shared_examples/features/explore/sidebar_shared_examples.rb
new file mode 100644
index 00000000000..1754c8bf53d
--- /dev/null
+++ b/spec/support/shared_examples/features/explore/sidebar_shared_examples.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'an "Explore" page with sidebar and breadcrumbs' do |page_path, menu_label|
+ before do
+ visit send(page_path)
+ end
+
+ let(:sidebar_css) { 'aside.nav-sidebar[aria-label="Explore"]' }
+ let(:active_menu_item_css) { "li.active[data-track-label=\"#{menu_label}_menu\"]" }
+
+ it 'shows the "Explore" sidebar' do
+ expect(page).to have_css(sidebar_css)
+ end
+
+ it 'shows the correct sidebar menu item as active' do
+ within(sidebar_css) do
+ expect(page).to have_css(active_menu_item_css)
+ end
+ end
+
+ describe 'breadcrumbs' do
+ it 'has "Explore" as its root breadcrumb' do
+ within '.breadcrumbs-list' do
+ expect(page).to have_css("li:first a[href=\"#{explore_root_path}\"]", text: 'Explore')
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb b/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb
index dab125caa60..b8e42843e6f 100644
--- a/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb
+++ b/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'for each incident details route' do |example, tab_text:|
+RSpec.shared_examples 'for each incident details route' do |example, tab_text:, tab:|
before do
sign_in(user)
visit incident_path
@@ -25,4 +25,16 @@ RSpec.shared_examples 'for each incident details route' do |example, tab_text:|
it_behaves_like example
end
+
+ context "for /-/issues/incident/:id/#{tab} route" do
+ let(:incident_path) { incident_project_issues_path(project, incident, tab) }
+
+ it_behaves_like example
+ end
+
+ context "for /-/issues/:id/#{tab} route" do
+ let(:incident_path) { incident_issue_project_issue_path(project, incident, tab) }
+
+ it_behaves_like example
+ end
end
diff --git a/spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb b/spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb
index 4c312b42c0a..148ff2cfb54 100644
--- a/spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb
+++ b/spec/support/shared_examples/features/integrations/user_activates_mattermost_slash_command_integration_shared_examples.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'user activates the Mattermost Slash Command integration'
it 'shows a token placeholder' do
token_placeholder = find_field('service_token')['placeholder']
- expect(token_placeholder).to eq('XXxxXXxxXXxxXXxxXXxxXXxx')
+ expect(token_placeholder).to eq('')
end
it 'redirects to the integrations page after saving but not activating' do
diff --git a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
index b6f7094e422..b8c6b85adb2 100644
--- a/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
+++ b/spec/support/shared_examples/features/issuable_invite_members_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable invite members' do
- include Spec::Support::Helpers::Features::InviteMembersModalHelper
+ include Features::InviteMembersModalHelpers
context 'when a privileged user can invite' do
before do
@@ -17,8 +17,6 @@ RSpec.shared_examples 'issuable invite members' do
page.within '.dropdown-menu-user' do
expect(page).to have_link('Invite Members')
- expect(page).to have_selector('[data-track-action="click_invite_members"]')
- expect(page).to have_selector('[data-track-label="edit_assignee"]')
end
click_link 'Invite Members'
diff --git a/spec/support/shared_examples/features/manage_applications_shared_examples.rb b/spec/support/shared_examples/features/manage_applications_shared_examples.rb
index b59f3f1e27b..b8fd58e7efa 100644
--- a/spec/support/shared_examples/features/manage_applications_shared_examples.rb
+++ b/spec/support/shared_examples/features/manage_applications_shared_examples.rb
@@ -5,87 +5,40 @@ RSpec.shared_examples 'manage applications' do
let_it_be(:application_name_changed) { "#{application_name} changed" }
let_it_be(:application_redirect_uri) { 'https://foo.bar' }
- context 'when hash_oauth_secrets flag set' do
- before do
- stub_feature_flags(hash_oauth_secrets: true)
- end
-
- it 'allows user to manage applications', :js do
- visit new_application_path
+ it 'allows user to manage applications', :js do
+ visit new_application_path
- expect(page).to have_content 'Add new application'
+ expect(page).to have_content 'Add new application'
- fill_in :doorkeeper_application_name, with: application_name
- fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri
- check :doorkeeper_application_scopes_read_user
- click_on 'Save application'
+ fill_in :doorkeeper_application_name, with: application_name
+ fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri
+ check :doorkeeper_application_scopes_read_user
+ click_on 'Save application'
- validate_application(application_name, 'Yes')
- expect(page).to have_content _('This is the only time the secret is accessible. Copy the secret and store it securely')
- expect(page).to have_link('Continue', href: index_path)
+ validate_application(application_name, 'Yes')
+ expect(page).to have_content _('This is the only time the secret is accessible. Copy the secret and store it securely')
+ expect(page).to have_link('Continue', href: index_path)
- expect(page).to have_css("button[title=\"Copy secret\"]", text: 'Copy')
+ expect(page).to have_button(_('Copy secret'))
- click_on 'Edit'
+ click_on 'Edit'
- application_name_changed = "#{application_name} changed"
+ application_name_changed = "#{application_name} changed"
- fill_in :doorkeeper_application_name, with: application_name_changed
- uncheck :doorkeeper_application_confidential
- click_on 'Save application'
-
- validate_application(application_name_changed, 'No')
- expect(page).not_to have_link('Continue')
- expect(page).to have_content _('The secret is only available when you first create the application')
-
- visit_applications_path
-
- page.within '.oauth-applications' do
- click_on 'Destroy'
- end
- expect(page.find('.oauth-applications')).not_to have_content 'test_changed'
- end
- end
-
- context 'when hash_oauth_secrets flag not set' do
- before do
- stub_feature_flags(hash_oauth_secrets: false)
- end
-
- it 'allows user to manage applications', :js do
- visit new_application_path
-
- expect(page).to have_content 'Add new application'
-
- fill_in :doorkeeper_application_name, with: application_name
- fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri
- check :doorkeeper_application_scopes_read_user
- click_on 'Save application'
-
- validate_application(application_name, 'Yes')
- expect(page).to have_link('Continue', href: index_path)
-
- application = Doorkeeper::Application.find_by(name: application_name)
- expect(page).to have_css("button[title=\"Copy secret\"][data-clipboard-text=\"#{application.secret}\"]", text: 'Copy')
-
- click_on 'Edit'
-
- application_name_changed = "#{application_name} changed"
-
- fill_in :doorkeeper_application_name, with: application_name_changed
- uncheck :doorkeeper_application_confidential
- click_on 'Save application'
+ fill_in :doorkeeper_application_name, with: application_name_changed
+ uncheck :doorkeeper_application_confidential
+ click_on 'Save application'
- validate_application(application_name_changed, 'No')
- expect(page).not_to have_link('Continue')
+ validate_application(application_name_changed, 'No')
+ expect(page).not_to have_link('Continue')
+ expect(page).to have_content _('The secret is only available when you create the application or renew the secret.')
- visit_applications_path
+ visit_applications_path
- page.within '.oauth-applications' do
- click_on 'Destroy'
- end
- expect(page.find('.oauth-applications')).not_to have_content 'test_changed'
+ page.within '.oauth-applications' do
+ click_on 'Destroy'
end
+ expect(page.find('.oauth-applications')).not_to have_content 'test_changed'
end
context 'when scopes are blank' do
diff --git a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
index c2dc87b0fb0..6487e6a94c1 100644
--- a/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
+++ b/spec/support/shared_examples/features/master_manages_access_requests_shared_example.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'Maintainer manages access requests' do
- include Spec::Support::Helpers::Features::MembersHelpers
+ include Features::MembersHelpers
let(:user) { create(:user) }
let(:maintainer) { create(:user) }
diff --git a/spec/support/shared_examples/features/milestone_editing_shared_examples.rb b/spec/support/shared_examples/features/milestone_editing_shared_examples.rb
new file mode 100644
index 00000000000..d21bf62ecfa
--- /dev/null
+++ b/spec/support/shared_examples/features/milestone_editing_shared_examples.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'milestone handling version conflicts' do
+ it 'warns about version conflict when milestone has been updated in the background' do
+ # Update the milestone in the background in order to trigger a version conflict
+ milestone.update!(title: "New title")
+
+ fill_in _('Title'), with: 'Title for version conflict'
+ fill_in _('Description'), with: 'Description for version conflict'
+
+ click_button _('Save changes')
+
+ expect(page).to have_content(
+ format(
+ _("Someone edited this %{model_name} at the same time you did. Please check out the %{link_to_model} and make sure your changes will not unintentionally remove theirs."), # rubocop:disable Layout/LineLength
+ model_name: _('milestone'),
+ link_to_model: _('milestone')
+ )
+ )
+ end
+end
diff --git a/spec/support/shared_examples/features/packages_shared_examples.rb b/spec/support/shared_examples/features/packages_shared_examples.rb
index f09cf0613a1..5126e849c2e 100644
--- a/spec/support/shared_examples/features/packages_shared_examples.rb
+++ b/spec/support/shared_examples/features/packages_shared_examples.rb
@@ -9,7 +9,7 @@ RSpec.shared_examples 'packages list' do |check_project_name: false|
expect(package_row).to have_content(pkg.name)
expect(package_row).to have_content(pkg.version)
- expect(package_row).to have_content(pkg.project.name) if check_project_name
+ expect(package_row).to have_content(pkg.project.path) if check_project_name
end
end
@@ -18,7 +18,35 @@ RSpec.shared_examples 'packages list' do |check_project_name: false|
end
end
+RSpec.shared_examples 'pipelines on packages list' do
+ let_it_be(:pipelines) do
+ %w[c83d6e391c22777fca1ed3012fce84f633d7fed0
+ d83d6e391c22777fca1ed3012fce84f633d7fed0].map do |sha|
+ create(:ci_pipeline, project: project, sha: sha)
+ end
+ end
+
+ before do
+ pipelines.each do |pipeline|
+ create(:package_build_info, package: package, pipeline: pipeline)
+ end
+ end
+
+ it 'shows the latest pipeline' do
+ # Test after reload
+ page.evaluate_script 'window.location.reload()'
+
+ wait_for_requests
+
+ expect(page).to have_content('d83d6e39')
+ end
+end
+
RSpec.shared_examples 'package details link' do |property|
+ before do
+ stub_application_setting(npm_package_requests_forwarding: false)
+ end
+
it 'navigates to the correct url' do
page.within(packages_table_selector) do
click_link package.name
@@ -30,6 +58,45 @@ RSpec.shared_examples 'package details link' do |property|
expect(page).to have_content('Installation')
expect(page).to have_content('Registry setup')
+ expect(page).to have_content('Other versions 0')
+ end
+
+ context 'with other versions' do
+ let_it_be(:npm_package1) { create(:npm_package, project: project, name: 'zzz', version: '1.1.0') }
+ let_it_be(:npm_package2) { create(:npm_package, project: project, name: 'zzz', version: '1.2.0') }
+
+ before do
+ page.within(packages_table_selector) do
+ first(:link, package.name).click
+ end
+ end
+
+ it 'shows tab with count' do
+ expect(page).to have_content('Other versions 2')
+ end
+
+ it 'visiting tab shows total on page' do
+ click_link 'Other versions'
+
+ expect(page).to have_content('2 versions')
+ end
+
+ it 'deleting version updates count' do
+ click_link 'Other versions'
+
+ find('[data-testid="delete-dropdown"]', match: :first).click
+ find('[data-testid="action-delete"]', match: :first).click
+ click_button('Permanently delete')
+
+ expect(page).to have_content 'Package deleted successfully'
+
+ expect(page).to have_content('Other versions 1')
+ expect(page).to have_content('1 version')
+
+ expect(page).not_to have_content('1.0.0')
+ expect(page).to have_content('1.1.0')
+ expect(page).to have_content('1.2.0')
+ end
end
end
diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb
index 81d548e000a..2d3f1949716 100644
--- a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb
+++ b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb
@@ -1,126 +1,67 @@
# frozen_string_literal: true
RSpec.shared_examples "protected branches > access control > CE" do
- ProtectedRefAccess::HUMAN_ACCESS_LEVELS.each do |(access_type_id, access_type_name)|
+ let(:no_one) { ProtectedRef::AccessLevel.humanize(::Gitlab::Access::NO_ACCESS) }
+
+ ProtectedRef::AccessLevel.human_access_levels.each do |(access_type_id, access_type_name)|
it "allows creating protected branches that #{access_type_name} can push to" do
visit project_protected_branches_path(project)
set_protected_branch_name('master')
-
- find(".js-allowed-to-merge").click
- within('[data-testid="allowed-to-merge-dropdown"]') do
- expect(first("li")).to have_content("Roles")
- find(:link, 'No one').click
- end
-
- within('.js-new-protected-branch') do
- allowed_to_push_button = find(".js-allowed-to-push")
-
- unless allowed_to_push_button.text == access_type_name
- allowed_to_push_button.click
- within(".dropdown.show .dropdown-menu") { click_on access_type_name }
- end
- end
-
+ set_allowed_to('merge', no_one)
+ set_allowed_to('push', access_type_name)
click_on_protect
- wait_for_requests
expect(ProtectedBranch.count).to eq(1)
expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to eq([access_type_id])
end
- it "allows updating protected branches so that #{access_type_name} can push to them" do
+ it "allows creating protected branches that #{access_type_name} can merge to" do
visit project_protected_branches_path(project)
set_protected_branch_name('master')
-
- find(".js-allowed-to-merge").click
- within('[data-testid="allowed-to-merge-dropdown"]') do
- expect(first("li")).to have_content("Roles")
- find(:link, 'No one').click
- end
-
- find(".js-allowed-to-push").click
- within('[data-testid="allowed-to-push-dropdown"]') do
- expect(first("li")).to have_content("Roles")
- find(:link, 'No one').click
- end
-
+ set_allowed_to('merge', access_type_name)
+ set_allowed_to('push', no_one)
click_on_protect
expect(ProtectedBranch.count).to eq(1)
-
- within(".protected-branches-list") do
- find(".js-allowed-to-push").click
-
- within('.js-allowed-to-push-container') do
- expect(first("li")).to have_content("Roles")
- find(:link, access_type_name).click
- end
-
- find(".js-allowed-to-push").click
- end
-
- wait_for_requests
-
- expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to include(access_type_id)
+ expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to eq([access_type_id])
end
- end
- ProtectedRefAccess::HUMAN_ACCESS_LEVELS.each do |(access_type_id, access_type_name)|
- it "allows creating protected branches that #{access_type_name} can merge to" do
+ it "allows updating protected branches so that #{access_type_name} can push to them" do
visit project_protected_branches_path(project)
set_protected_branch_name('master')
+ set_allowed_to('merge', no_one)
+ set_allowed_to('push', no_one)
+ click_on_protect
- within('.js-new-protected-branch') do
- allowed_to_merge_button = find(".js-allowed-to-merge")
+ expect(ProtectedBranch.count).to eq(1)
- unless allowed_to_merge_button.text == access_type_name
- allowed_to_merge_button.click
- within(".dropdown.show .dropdown-menu") { click_on access_type_name }
+ within(".protected-branches-list") do
+ within_select(".js-allowed-to-push") do
+ click_on(access_type_name)
end
end
- find(".js-allowed-to-push").click
- within('[data-testid="allowed-to-push-dropdown"]') do
- expect(first("li")).to have_content("Roles")
- find(:link, 'No one').click
- end
-
- click_on_protect
+ wait_for_requests
- expect(ProtectedBranch.count).to eq(1)
- expect(ProtectedBranch.last.merge_access_levels.map(&:access_level)).to eq([access_type_id])
+ expect(ProtectedBranch.last.push_access_levels.map(&:access_level)).to include(access_type_id)
end
it "allows updating protected branches so that #{access_type_name} can merge to them" do
visit project_protected_branches_path(project)
set_protected_branch_name('master')
-
- find(".js-allowed-to-merge").click
- within('[data-testid="allowed-to-merge-dropdown"]') do
- expect(first("li")).to have_content("Roles")
- find(:link, 'No one').click
- end
-
- find(".js-allowed-to-push").click
- within('[data-testid="allowed-to-push-dropdown"]') do
- expect(first("li")).to have_content("Roles")
- find(:link, 'No one').click
- end
-
+ set_allowed_to('merge', no_one)
+ set_allowed_to('push', no_one)
click_on_protect
expect(ProtectedBranch.count).to eq(1)
within(".protected-branches-list") do
- find(".js-allowed-to-merge").click
-
- within('.js-allowed-to-merge-container') do
- expect(first("li")).to have_content("Roles")
- find(:link, access_type_name).click
+ within_select(".js-allowed-to-merge") do
+ click_on(access_type_name)
end
end
diff --git a/spec/support/shared_examples/features/protected_tags_with_deploy_keys_examples.rb b/spec/support/shared_examples/features/protected_tags_with_deploy_keys_examples.rb
new file mode 100644
index 00000000000..cc0984b6226
--- /dev/null
+++ b/spec/support/shared_examples/features/protected_tags_with_deploy_keys_examples.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'Deploy keys with protected tags' do
+ let(:dropdown_sections_minus_deploy_keys) { all_dropdown_sections - ['Deploy Keys'] }
+
+ context 'when deploy keys are enabled to this project' do
+ let!(:deploy_key_1) { create(:deploy_key, title: 'title 1', projects: [project]) }
+ let!(:deploy_key_2) { create(:deploy_key, title: 'title 2', projects: [project]) }
+
+ context 'when only one deploy key can push' do
+ before do
+ deploy_key_1.deploy_keys_projects.first.update!(can_push: true)
+ end
+
+ it "shows all dropdown sections in the 'Allowed to create' main dropdown, with only one deploy key" do
+ visit project_protected_tags_path(project)
+
+ find(".js-allowed-to-create").click
+ wait_for_requests
+
+ within('[data-testid="allowed-to-create-dropdown"]') do
+ dropdown_headers = page.all('.dropdown-header').map(&:text)
+
+ expect(dropdown_headers).to contain_exactly(*all_dropdown_sections)
+ expect(page).to have_content('title 1')
+ expect(page).not_to have_content('title 2')
+ end
+ end
+
+ it "shows all sections in the 'Allowed to create' update dropdown" do
+ create(:protected_tag, :no_one_can_create, project: project, name: 'v1.0.0')
+
+ visit project_protected_tags_path(project)
+
+ within(".js-protected-tag-edit-form") do
+ find(".js-allowed-to-create").click
+ wait_for_requests
+
+ dropdown_headers = page.all('.dropdown-header').map(&:text)
+
+ expect(dropdown_headers).to contain_exactly(*all_dropdown_sections)
+ end
+ end
+ end
+
+ context 'when no deploy key can push' do
+ it "just shows all sections but not deploy keys in the 'Allowed to create' dropdown" do
+ visit project_protected_tags_path(project)
+
+ find(".js-allowed-to-create").click
+ wait_for_requests
+
+ within('[data-testid="allowed-to-create-dropdown"]') do
+ dropdown_headers = page.all('.dropdown-header').map(&:text)
+
+ expect(dropdown_headers).to contain_exactly(*dropdown_sections_minus_deploy_keys)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/reportable_note_shared_examples.rb b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
index bb3fab5b23e..133da230bed 100644
--- a/spec/support/shared_examples/features/reportable_note_shared_examples.rb
+++ b/spec/support/shared_examples/features/reportable_note_shared_examples.rb
@@ -20,7 +20,7 @@ RSpec.shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- expect(dropdown).to have_button('Report abuse to administrator')
+ expect(dropdown).to have_button('Report abuse')
if type == 'issue' || type == 'merge_request'
expect(dropdown).to have_button('Delete comment')
@@ -33,7 +33,7 @@ RSpec.shared_examples 'reportable note' do |type|
dropdown = comment.find(more_actions_selector)
open_dropdown(dropdown)
- dropdown.click_button('Report abuse to administrator')
+ dropdown.click_button('Report abuse')
choose "They're posting spam."
click_button "Next"
@@ -48,6 +48,6 @@ RSpec.shared_examples 'reportable note' do |type|
restore_window_size
dropdown.find('.more-actions-toggle').click
- dropdown.find('.dropdown-menu li', match: :first)
+ dropdown.find('.more-actions li', match: :first)
end
end
diff --git a/spec/support/shared_examples/features/rss_shared_examples.rb b/spec/support/shared_examples/features/rss_shared_examples.rb
index ad865b084e1..f6566214e32 100644
--- a/spec/support/shared_examples/features/rss_shared_examples.rb
+++ b/spec/support/shared_examples/features/rss_shared_examples.rb
@@ -13,6 +13,12 @@ RSpec.shared_examples "it has an RSS button with current_user's feed token" do
end
end
+RSpec.shared_examples "it has an RSS link with current_user's feed token" do
+ it "shows the RSS link with current_user's feed token" do
+ expect(page).to have_link 'Subscribe to RSS feed', href: /feed_token=#{user.feed_token}/
+ end
+end
+
RSpec.shared_examples "an autodiscoverable RSS feed without a feed token" do
it "has an RSS autodiscovery link tag without a feed token" do
expect(page).to have_css("link[type*='atom+xml']:not([href*='feed_token'])", visible: false)
@@ -26,10 +32,18 @@ RSpec.shared_examples "it has an RSS button without a feed token" do
end
end
+RSpec.shared_examples "it has an RSS link without a feed token" do
+ it "shows the RSS link without a feed token" do
+ expect(page).to have_link 'Subscribe to RSS feed'
+ expect(page).not_to have_link 'Subscribe to RSS feed', href: /feed_token/
+ end
+end
+
RSpec.shared_examples "updates atom feed link" do |type|
it "for #{type}" do
sign_in(user)
visit path
+ click_button 'Actions', match: :first
link = find_link('Subscribe to RSS feed')
params = CGI.parse(URI.parse(link[:href]).query)
diff --git a/spec/support/shared_examples/features/runners_shared_examples.rb b/spec/support/shared_examples/features/runners_shared_examples.rb
index 63a0832117d..7edf306183e 100644
--- a/spec/support/shared_examples/features/runners_shared_examples.rb
+++ b/spec/support/shared_examples/features/runners_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'shows and resets runner registration token' do
- include Spec::Support::Helpers::Features::RunnersHelpers
+ include Features::RunnersHelpers
include Spec::Support::Helpers::ModalHelpers
before do
@@ -63,16 +63,15 @@ RSpec.shared_examples 'shows and resets runner registration token' do
end
RSpec.shared_examples 'shows no runners registered' do
- it 'shows total count with 0' do
+ it 'shows 0 count and the empty state' do
expect(find('[data-testid="runner-type-tabs"]')).to have_text "#{s_('Runners|All')} 0"
# No stats are shown
expect(page).not_to have_text s_('Runners|Online')
expect(page).not_to have_text s_('Runners|Offline')
expect(page).not_to have_text s_('Runners|Stale')
- end
- it 'shows "no runners" message' do
+ # "no runners" message
expect(page).to have_text s_('Runners|Get started with runners')
end
end
@@ -84,16 +83,14 @@ RSpec.shared_examples 'shows no runners found' do
end
RSpec.shared_examples 'shows runner in list' do
- it 'does not show empty state' do
- expect(page).not_to have_content s_('Runners|Get started with runners')
- end
-
- it 'shows runner row' do
+ it 'shows runner row and no empty state' do
within_runner_row(runner.id) do
expect(page).to have_text "##{runner.id}"
expect(page).to have_text runner.short_sha
expect(page).to have_text runner.description
end
+
+ expect(page).not_to have_content s_('Runners|Get started with runners')
end
end
@@ -229,3 +226,33 @@ RSpec.shared_examples 'submits edit runner form' do
end
end
end
+
+RSpec.shared_examples 'creates runner and shows register page' do
+ context 'when runner is saved' do
+ before do
+ fill_in s_('Runners|Runner description'), with: 'runner-foo'
+ fill_in s_('Runners|Tags'), with: 'tag1'
+ click_on _('Submit')
+ wait_for_requests
+ end
+
+ it 'navigates to registration page and opens install instructions drawer' do
+ expect(page.find('[data-testid="alert-success"]')).to have_content(s_('Runners|Runner created.'))
+ expect(current_url).to match(register_path_pattern)
+
+ click_on 'How do I install GitLab Runner?'
+ expect(page.find('[data-testid="runner-platforms-drawer"]')).to have_content('gitlab-runner install')
+ end
+
+ it 'warns from leaving page without finishing registration' do
+ click_on s_('Runners|Go to runners page')
+
+ alert = page.driver.browser.switch_to.alert
+
+ expect(alert).not_to be_nil
+ alert.dismiss
+
+ expect(current_url).to match(register_path_pattern)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb b/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb
index 4d242d0e719..cbd0ffbab21 100644
--- a/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb
+++ b/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb
@@ -48,14 +48,18 @@ RSpec.shared_examples 'a redacted search results' do
it 'redacts the inaccessible issue' do
expect(search_service.send(:logger))
.to receive(:error)
- .with(hash_including(
- message: "redacted_search_results",
- current_user_id: user.id,
- query: search,
- filtered: array_including(
- [
- { class_name: 'Issue', id: unreadable.id, ability: :read_issue }
- ])))
+ .with(
+ hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Issue', id: unreadable.id, ability: :read_issue }
+ ]
+ )
+ )
+ )
expect(result).to contain_exactly(readable)
end
@@ -95,16 +99,18 @@ RSpec.shared_examples 'a redacted search results' do
end
let(:unredacted_results) do
- ar_relation(Note,
- readable_note_on_commit,
- readable_diff_note,
- readable_note_on_mr,
- readable_diff_note_on_mr,
- readable_note_on_project_snippet,
- unreadable_note_on_commit,
- unreadable_diff_note,
- unreadable_note_on_mr,
- unreadable_note_on_project_snippet)
+ ar_relation(
+ Note,
+ readable_note_on_commit,
+ readable_diff_note,
+ readable_note_on_mr,
+ readable_diff_note_on_mr,
+ readable_note_on_project_snippet,
+ unreadable_note_on_commit,
+ unreadable_diff_note,
+ unreadable_note_on_mr,
+ unreadable_note_on_project_snippet
+ )
end
let(:scope) { 'notes' }
@@ -112,23 +118,29 @@ RSpec.shared_examples 'a redacted search results' do
it 'redacts the inaccessible notes' do
expect(search_service.send(:logger))
.to receive(:error)
- .with(hash_including(
- message: "redacted_search_results",
- current_user_id: user.id,
- query: search,
- filtered: array_including(
- [
- { class_name: 'Note', id: unreadable_note_on_commit.id, ability: :read_note },
- { class_name: 'DiffNote', id: unreadable_diff_note.id, ability: :read_note },
- { class_name: 'DiscussionNote', id: unreadable_note_on_mr.id, ability: :read_note },
- { class_name: 'Note', id: unreadable_note_on_project_snippet.id, ability: :read_note }
- ])))
-
- expect(result).to contain_exactly(readable_note_on_commit,
- readable_diff_note,
- readable_note_on_mr,
- readable_diff_note_on_mr,
- readable_note_on_project_snippet)
+ .with(
+ hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Note', id: unreadable_note_on_commit.id, ability: :read_note },
+ { class_name: 'DiffNote', id: unreadable_diff_note.id, ability: :read_note },
+ { class_name: 'DiscussionNote', id: unreadable_note_on_mr.id, ability: :read_note },
+ { class_name: 'Note', id: unreadable_note_on_project_snippet.id, ability: :read_note }
+ ]
+ )
+ )
+ )
+
+ expect(result).to contain_exactly(
+ readable_note_on_commit,
+ readable_diff_note,
+ readable_note_on_mr,
+ readable_diff_note_on_mr,
+ readable_note_on_project_snippet
+ )
end
end
@@ -141,14 +153,18 @@ RSpec.shared_examples 'a redacted search results' do
it 'redacts the inaccessible merge request' do
expect(search_service.send(:logger))
.to receive(:error)
- .with(hash_including(
- message: "redacted_search_results",
- current_user_id: user.id,
- query: search,
- filtered: array_including(
- [
- { class_name: 'MergeRequest', id: unreadable.id, ability: :read_merge_request }
- ])))
+ .with(
+ hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'MergeRequest', id: unreadable.id, ability: :read_merge_request }
+ ]
+ )
+ )
+ )
expect(result).to contain_exactly(readable)
end
@@ -169,14 +185,18 @@ RSpec.shared_examples 'a redacted search results' do
it 'redacts the inaccessible blob' do
expect(search_service.send(:logger))
.to receive(:error)
- .with(hash_including(
- message: "redacted_search_results",
- current_user_id: user.id,
- query: search,
- filtered: array_including(
- [
- { class_name: 'Gitlab::Search::FoundBlob', id: unreadable.id, ability: :read_blob }
- ])))
+ .with(
+ hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Gitlab::Search::FoundBlob', id: unreadable.id, ability: :read_blob }
+ ]
+ )
+ )
+ )
expect(result).to contain_exactly(readable)
end
@@ -191,14 +211,18 @@ RSpec.shared_examples 'a redacted search results' do
it 'redacts the inaccessible blob' do
expect(search_service.send(:logger))
.to receive(:error)
- .with(hash_including(
- message: "redacted_search_results",
- current_user_id: user.id,
- query: search,
- filtered: array_including(
- [
- { class_name: 'Gitlab::Search::FoundWikiPage', id: unreadable.id, ability: :read_wiki_page }
- ])))
+ .with(
+ hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Gitlab::Search::FoundWikiPage', id: unreadable.id, ability: :read_wiki_page }
+ ]
+ )
+ )
+ )
expect(result).to contain_exactly(readable)
end
@@ -213,14 +237,18 @@ RSpec.shared_examples 'a redacted search results' do
it 'redacts the inaccessible snippet' do
expect(search_service.send(:logger))
.to receive(:error)
- .with(hash_including(
- message: "redacted_search_results",
- current_user_id: user.id,
- query: search,
- filtered: array_including(
- [
- { class_name: 'ProjectSnippet', id: unreadable.id, ability: :read_snippet }
- ])))
+ .with(
+ hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'ProjectSnippet', id: unreadable.id, ability: :read_snippet }
+ ]
+ )
+ )
+ )
expect(result).to contain_exactly(readable)
end
@@ -239,14 +267,18 @@ RSpec.shared_examples 'a redacted search results' do
it 'redacts the inaccessible snippet' do
expect(search_service.send(:logger))
.to receive(:error)
- .with(hash_including(
- message: "redacted_search_results",
- current_user_id: user.id,
- query: search,
- filtered: array_including(
- [
- { class_name: 'PersonalSnippet', id: unreadable.id, ability: :read_snippet }
- ])))
+ .with(
+ hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'PersonalSnippet', id: unreadable.id, ability: :read_snippet }
+ ]
+ )
+ )
+ )
expect(result).to contain_exactly(readable)
end
@@ -265,14 +297,18 @@ RSpec.shared_examples 'a redacted search results' do
it 'redacts the inaccessible commit' do
expect(search_service.send(:logger))
.to receive(:error)
- .with(hash_including(
- message: "redacted_search_results",
- current_user_id: user.id,
- query: search,
- filtered: array_including(
- [
- { class_name: 'Commit', id: unreadable.id, ability: :read_commit }
- ])))
+ .with(
+ hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Commit', id: unreadable.id, ability: :read_commit }
+ ]
+ )
+ )
+ )
expect(result).to contain_exactly(readable)
end
diff --git a/spec/support/shared_examples/features/secure_oauth_authorizations_shared_examples.rb b/spec/support/shared_examples/features/secure_oauth_authorizations_shared_examples.rb
index 028e075c87a..231406289b4 100644
--- a/spec/support/shared_examples/features/secure_oauth_authorizations_shared_examples.rb
+++ b/spec/support/shared_examples/features/secure_oauth_authorizations_shared_examples.rb
@@ -10,7 +10,7 @@ RSpec.shared_examples 'Secure OAuth Authorizations' do
end
context 'when user is unconfirmed' do
- let(:user) { create(:user, confirmed_at: nil) }
+ let(:user) { create(:user, :unconfirmed) }
it 'displays an error' do
expect(page).to have_text I18n.t('doorkeeper.errors.messages.unconfirmed_email')
diff --git a/spec/support/shared_examples/features/trial_email_validation_shared_example.rb b/spec/support/shared_examples/features/trial_email_validation_shared_example.rb
index 8304a91af86..81c9ac1164b 100644
--- a/spec/support/shared_examples/features/trial_email_validation_shared_example.rb
+++ b/spec/support/shared_examples/features/trial_email_validation_shared_example.rb
@@ -1,59 +1,38 @@
# frozen_string_literal: true
RSpec.shared_examples 'user email validation' do
- let(:email_hint_message) { 'We recommend a work email address.' }
- let(:email_error_message) { 'Please provide a valid email address.' }
+ let(:email_hint_message) { _('We recommend a work email address.') }
+ let(:email_error_message) { _('Please provide a valid email address.') }
let(:email_warning_message) do
- 'This email address does not look right, are you sure you typed it correctly?'
+ _('This email address does not look right, are you sure you typed it correctly?')
end
- context 'with trial_email_validation flag enabled' do
- it 'shows an error message until a correct email is entered' do
- visit path
- expect(page).to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
+ it 'shows an error message until a correct email is entered' do
+ visit path
+ expect(page).to have_content(email_hint_message)
+ expect(page).not_to have_content(email_error_message)
+ expect(page).not_to have_content(email_warning_message)
- fill_in 'new_user_email', with: 'foo@'
- fill_in 'new_user_first_name', with: ''
+ fill_in 'new_user_email', with: 'foo@'
+ fill_in 'new_user_first_name', with: ''
- expect(page).not_to have_content(email_hint_message)
- expect(page).to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
+ expect(page).not_to have_content(email_hint_message)
+ expect(page).to have_content(email_error_message)
+ expect(page).not_to have_content(email_warning_message)
- fill_in 'new_user_email', with: 'foo@bar'
- fill_in 'new_user_first_name', with: ''
+ fill_in 'new_user_email', with: 'foo@bar'
+ fill_in 'new_user_first_name', with: ''
- expect(page).not_to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).to have_content(email_warning_message)
+ expect(page).not_to have_content(email_hint_message)
+ expect(page).not_to have_content(email_error_message)
+ expect(page).to have_content(email_warning_message)
- fill_in 'new_user_email', with: 'foo@gitlab.com'
- fill_in 'new_user_first_name', with: ''
+ fill_in 'new_user_email', with: 'foo@gitlab.com'
+ fill_in 'new_user_first_name', with: ''
- expect(page).not_to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
- end
- end
-
- context 'when trial_email_validation flag disabled' do
- before do
- stub_feature_flags trial_email_validation: false
- end
-
- it 'does not show an error message' do
- visit path
- expect(page).to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
-
- fill_in 'new_user_email', with: 'foo@'
-
- expect(page).to have_content(email_hint_message)
- expect(page).not_to have_content(email_error_message)
- expect(page).not_to have_content(email_warning_message)
- end
+ expect(page).not_to have_content(email_hint_message)
+ expect(page).not_to have_content(email_error_message)
+ expect(page).not_to have_content(email_warning_message)
end
end
diff --git a/spec/support/shared_examples/features/variable_list_pagination_shared_examples.rb b/spec/support/shared_examples/features/variable_list_pagination_shared_examples.rb
new file mode 100644
index 00000000000..0b0c9edcb42
--- /dev/null
+++ b/spec/support/shared_examples/features/variable_list_pagination_shared_examples.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'variable list pagination' do |variable_type|
+ first_page_count = 20
+
+ before do
+ first_page_count.times do |i|
+ case variable_type
+ when :ci_variable
+ create(variable_type, key: "test_key_#{i}", value: 'test_value', masked: true, project: project)
+ when :ci_group_variable
+ create(variable_type, key: "test_key_#{i}", value: 'test_value', masked: true, group: group)
+ else
+ create(variable_type, key: "test_key_#{i}", value: 'test_value', masked: true)
+ end
+ end
+
+ visit page_path
+ wait_for_requests
+ end
+
+ it 'can navigate between pages' do
+ page.within('[data-testid="ci-variable-table"]') do
+ expect(page.all('.js-ci-variable-row').length).to be(first_page_count)
+ end
+
+ click_button 'Next'
+ wait_for_requests
+
+ page.within('[data-testid="ci-variable-table"]') do
+ expect(page.all('.js-ci-variable-row').length).to be(1)
+ end
+
+ click_button 'Previous'
+ wait_for_requests
+
+ page.within('[data-testid="ci-variable-table"]') do
+ expect(page.all('.js-ci-variable-row').length).to be(first_page_count)
+ end
+ end
+
+ it 'sorts variables alphabetically in ASC and DESC order' do
+ page.within('[data-testid="ci-variable-table"]') do
+ expect(find('.js-ci-variable-row:nth-child(1) td[data-label="Key"]').text).to eq(variable.key)
+ expect(find('.js-ci-variable-row:nth-child(20) td[data-label="Key"]').text).to eq('test_key_8')
+ end
+
+ click_button 'Next'
+ wait_for_requests
+
+ page.within('[data-testid="ci-variable-table"]') do
+ expect(find('.js-ci-variable-row:nth-child(1) td[data-label="Key"]').text).to eq('test_key_9')
+ end
+
+ page.within('[data-testid="ci-variable-table"]') do
+ find('.b-table-sort-icon-left').click
+ end
+
+ wait_for_requests
+
+ page.within('[data-testid="ci-variable-table"]') do
+ expect(find('.js-ci-variable-row:nth-child(1) td[data-label="Key"]').text).to eq('test_key_9')
+ expect(find('.js-ci-variable-row:nth-child(20) td[data-label="Key"]').text).to eq('test_key_0')
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/variable_list_shared_examples.rb b/spec/support/shared_examples/features/variable_list_shared_examples.rb
index f0b72cfaee3..1211c9d19e6 100644
--- a/spec/support/shared_examples/features/variable_list_shared_examples.rb
+++ b/spec/support/shared_examples/features/variable_list_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'variable list' do |is_admin|
+RSpec.shared_examples 'variable list' do
it 'shows a list of variables' do
page.within('[data-testid="ci-variable-table"]') do
expect(find('.js-ci-variable-row:nth-child(1) td[data-label="Key"]').text).to eq(variable.key)
@@ -256,14 +256,6 @@ RSpec.shared_examples 'variable list' do |is_admin|
expect(find('[data-testid="ci-variable-protected-checkbox"]')).to be_checked
end
end
-
- it 'shows a message regarding the changed default' do
- if is_admin
- expect(page).to have_content 'Environment variables on this GitLab instance are configured to be protected by default'
- else
- expect(page).to have_content 'Environment variables are configured by your administrator to be protected by default'
- end
- end
end
context 'application setting is false' do
diff --git a/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb b/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
index 7a3b94ad81d..6451c531aec 100644
--- a/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/file_attachments_shared_examples.rb
@@ -62,7 +62,7 @@ RSpec.shared_examples 'wiki file attachments' do
attach_with_dropzone(true)
wait_for_requests
- find('.js-md-preview-button').click
+ click_button("Preview")
file_path = page.find('input[name="files[]"]', visible: :hidden).value
link = page.find('a.no-attachment-icon')['href']
img_link = page.find('a.no-attachment-icon img')['src']
diff --git a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
index 3e285bb8ad7..ca68df9a89b 100644
--- a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
@@ -78,7 +78,7 @@ RSpec.shared_examples 'User previews wiki changes' do
it_behaves_like 'relative links' do
before do
- click_on 'Preview'
+ click_button("Preview")
end
let(:element) { preview }
@@ -88,7 +88,7 @@ RSpec.shared_examples 'User previews wiki changes' do
# using two `\n` ensures we're sublist to it's own line due
# to list auto-continue
fill_in :wiki_content, with: "1. one\n\n - sublist\n"
- click_on "Preview"
+ click_button("Preview")
# the above generates two separate lists (not embedded) in CommonMark
expect(preview).to have_content("sublist")
@@ -102,7 +102,7 @@ RSpec.shared_examples 'User previews wiki changes' do
[[also_do_not_linkify]]
```
HEREDOC
- click_on "Preview"
+ click_button("Preview")
expect(preview).to have_content("do_not_linkify")
expect(preview).to have_content('[[do_not_linkify]]')
diff --git a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
index 0334187e4b1..c1e4185e058 100644
--- a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
@@ -150,6 +150,7 @@ RSpec.shared_examples 'User updates wiki page' do
end
it_behaves_like 'edits content using the content editor'
+ it_behaves_like 'inserts diagrams.net diagram using the content editor'
it_behaves_like 'autocompletes items'
end
diff --git a/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb
index a7c32932ba7..767caffd417 100644
--- a/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_views_wiki_page_shared_examples.rb
@@ -9,9 +9,11 @@ RSpec.shared_examples 'User views a wiki page' do
let(:path) { 'image.png' }
let(:wiki_page) do
- create(:wiki_page,
- wiki: wiki,
- title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})")
+ create(
+ :wiki_page,
+ wiki: wiki,
+ title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})"
+ )
end
before do
diff --git a/spec/support/shared_examples/features/wiki/user_views_wiki_sidebar_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_views_wiki_sidebar_shared_examples.rb
index 639eb3f2b99..21c7e2b6c75 100644
--- a/spec/support/shared_examples/features/wiki/user_views_wiki_sidebar_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_views_wiki_sidebar_shared_examples.rb
@@ -84,6 +84,44 @@ RSpec.shared_examples 'User views wiki sidebar' do
expect(page).not_to have_link('View All Pages')
end
+ it 'shows all collapse buttons in the sidebar' do
+ visit wiki_path(wiki)
+
+ within('.right-sidebar') do
+ expect(page.all("[data-testid='chevron-down-icon']").size).to eq(3)
+ end
+ end
+
+ it 'collapses/expands children when click collapse/expand button in the sidebar', :js do
+ visit wiki_path(wiki)
+
+ within('.right-sidebar') do
+ first("[data-testid='chevron-down-icon']").click
+ (11..15).each { |i| expect(page).not_to have_content("my page #{i}") }
+ expect(page.all("[data-testid='chevron-down-icon']").size).to eq(1)
+ expect(page.all("[data-testid='chevron-right-icon']").size).to eq(1)
+
+ first("[data-testid='chevron-right-icon']").click
+ (11..15).each { |i| expect(page).to have_content("my page #{i}") }
+ expect(page.all("[data-testid='chevron-down-icon']").size).to eq(3)
+ expect(page.all("[data-testid='chevron-right-icon']").size).to eq(0)
+ end
+ end
+
+ it 'shows create child page button when hover to the page title in the sidebar', :js do
+ visit wiki_path(wiki)
+
+ within('.right-sidebar') do
+ first_wiki_list = first("[data-testid='wiki-list']")
+ wiki_link = first("[data-testid='wiki-list'] a:last-of-type")['href']
+
+ first_wiki_list.hover
+ wiki_new_page_link = first("[data-testid='wiki-list'] a")['href']
+
+ expect(wiki_new_page_link).to eq "#{wiki_link}/%7Bnew_page_title%7D"
+ end
+ end
+
context 'when there are more than 15 existing pages' do
before do
create(:wiki_page, wiki: wiki, title: 'my page 16')
diff --git a/spec/support/shared_examples/features/work_items_shared_examples.rb b/spec/support/shared_examples/features/work_items_shared_examples.rb
index 4f3d957ad71..526a56e7dab 100644
--- a/spec/support/shared_examples/features/work_items_shared_examples.rb
+++ b/spec/support/shared_examples/features/work_items_shared_examples.rb
@@ -1,5 +1,20 @@
# frozen_string_literal: true
+RSpec.shared_examples 'work items title' do
+ let(:title_selector) { '[data-testid="work-item-title"]' }
+
+ it 'successfully shows and changes the title of the work item' do
+ expect(work_item.reload.title).to eq work_item.title
+
+ find(title_selector).set("Work item title")
+ find(title_selector).native.send_keys(:return)
+
+ wait_for_requests
+
+ expect(work_item.reload.title).to eq 'Work item title'
+ end
+end
+
RSpec.shared_examples 'work items status' do
let(:state_selector) { '[data-testid="work-item-state-select"]' }
@@ -15,18 +30,110 @@ RSpec.shared_examples 'work items status' do
end
end
-RSpec.shared_examples 'work items comments' do
+RSpec.shared_examples 'work items comments' do |type|
let(:form_selector) { '[data-testid="work-item-add-comment"]' }
+ let(:textarea_selector) { '[data-testid="work-item-add-comment"] #work-item-add-or-edit-comment' }
+ let(:is_mac) { page.evaluate_script('navigator.platform').include?('Mac') }
+ let(:modifier_key) { is_mac ? :command : :control }
+ let(:comment) { 'Test comment' }
+
+ def set_comment
+ find(form_selector).fill_in(with: comment)
+ end
it 'successfully creates and shows comments' do
- click_button 'Add a comment'
+ set_comment
- find(form_selector).fill_in(with: "Test comment")
click_button "Comment"
wait_for_requests
- expect(page).to have_content "Test comment"
+ page.within(".main-notes-list") do
+ expect(page).to have_content comment
+ end
+ end
+
+ context 'for work item note actions signed in user with developer role' do
+ it 'shows work item note actions' do
+ set_comment
+
+ click_button "Comment"
+
+ wait_for_requests
+
+ page.within(".main-notes-list") do
+ expect(page).to have_selector('[data-testid="work-item-note-actions"]')
+
+ find('[data-testid="work-item-note-actions"]', match: :first).click
+
+ expect(page).to have_selector('[data-testid="copy-link-action"]')
+ expect(page).not_to have_selector('[data-testid="assign-note-action"]')
+ end
+ end
+ end
+
+ it 'successfully posts comments using shortcut and checks if textarea is blank when reinitiated' do
+ set_comment
+
+ send_keys([modifier_key, :enter])
+
+ wait_for_requests
+
+ page.within(".main-notes-list") do
+ expect(page).to have_content comment
+ end
+
+ expect(find(textarea_selector)).to have_content ""
+ end
+
+ context 'when using quick actions' do
+ it 'autocompletes quick actions common to all work item types', :aggregate_failures do
+ click_reply_and_enter_slash
+
+ page.within('#at-view-commands') do
+ expect(page).to have_text("/title")
+ expect(page).to have_text("/shrug")
+ expect(page).to have_text("/tableflip")
+ expect(page).to have_text("/close")
+ expect(page).to have_text("/cc")
+ end
+ end
+
+ context 'when a widget is enabled' do
+ before do
+ WorkItems::Type.default_by_type(type).widget_definitions
+ .find_by_widget_type(:assignees).update!(disabled: false)
+ end
+
+ it 'autocompletes quick action for the enabled widget' do
+ click_reply_and_enter_slash
+
+ page.within('#at-view-commands') do
+ expect(page).to have_text("/assign")
+ end
+ end
+ end
+
+ context 'when a widget is disabled' do
+ before do
+ WorkItems::Type.default_by_type(type).widget_definitions
+ .find_by_widget_type(:assignees).update!(disabled: true)
+ end
+
+ it 'does not autocomplete quick action for the disabled widget' do
+ click_reply_and_enter_slash
+
+ page.within('#at-view-commands') do
+ expect(page).not_to have_text("/assign")
+ end
+ end
+ end
+
+ def click_reply_and_enter_slash
+ find(form_selector).fill_in(with: "/")
+
+ wait_for_all_requests
+ end
end
end
@@ -39,7 +146,6 @@ RSpec.shared_examples 'work items assignees' do
# submit and simulate blur to save
send_keys(:enter)
find("body").click
-
wait_for_requests
expect(work_item.assignees).to include(user)
@@ -47,6 +153,8 @@ RSpec.shared_examples 'work items assignees' do
end
RSpec.shared_examples 'work items labels' do
+ let(:label_title_selector) { '[data-testid="labels-title"]' }
+
it 'successfully assigns a label' do
label = create(:label, project: work_item.project, title: "testing-label")
@@ -55,8 +163,7 @@ RSpec.shared_examples 'work items labels' do
# submit and simulate blur to save
send_keys(:enter)
- find("body").click
-
+ find(label_title_selector).click
wait_for_requests
expect(work_item.labels).to include(label)
@@ -83,7 +190,7 @@ RSpec.shared_examples 'work items description' do
wait_for_requests
- page.within('.atwho-container') do
+ page.within('#at-view-commands') do
expect(page).to have_text("title")
expect(page).to have_text("shrug")
expect(page).to have_text("tableflip")
@@ -125,7 +232,7 @@ RSpec.shared_examples 'work items description' do
end
RSpec.shared_examples 'work items invite members' do
- include Spec::Support::Helpers::Features::InviteMembersModalHelper
+ include Features::InviteMembersModalHelpers
it 'successfully assigns the current user by searching' do
# The button is only when the mouse is over the input
@@ -139,3 +246,143 @@ RSpec.shared_examples 'work items invite members' do
end
end
end
+
+RSpec.shared_examples 'work items milestone' do
+ def set_milestone(milestone_dropdown, milestone_text)
+ milestone_dropdown.click
+
+ find('[data-testid="work-item-milestone-dropdown"] .gl-form-input', visible: true).send_keys "\"#{milestone_text}\""
+ wait_for_requests
+
+ click_button(milestone_text)
+ wait_for_requests
+ end
+
+ let(:milestone_dropdown_selector) { '[data-testid="work-item-milestone-dropdown"]' }
+
+ it 'searches and sets or removes milestone for the work item' do
+ set_milestone(find(milestone_dropdown_selector), milestone.title)
+
+ expect(page.find(milestone_dropdown_selector)).to have_text(milestone.title)
+
+ set_milestone(find(milestone_dropdown_selector), 'No milestone')
+
+ expect(page.find(milestone_dropdown_selector)).to have_text('Add to milestone')
+ end
+end
+
+RSpec.shared_examples 'work items comment actions for guest users' do
+ context 'for guest user' do
+ it 'hides other actions other than copy link' do
+ page.within(".main-notes-list") do
+ expect(page).to have_selector('[data-testid="work-item-note-actions"]')
+
+ find('[data-testid="work-item-note-actions"]', match: :first).click
+
+ expect(page).to have_selector('[data-testid="copy-link-action"]')
+ expect(page).not_to have_selector('[data-testid="assign-note-action"]')
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'work items notifications' do
+ let(:actions_dropdown_selector) { '[data-testid="work-item-actions-dropdown"]' }
+ let(:notifications_toggle_selector) { '[data-testid="notifications-toggle-action"] > button' }
+
+ it 'displays toast when notification is toggled' do
+ find(actions_dropdown_selector).click
+
+ page.within('[data-testid="notifications-toggle-form"]') do
+ expect(page).not_to have_css(".is-checked")
+
+ find(notifications_toggle_selector).click
+ wait_for_requests
+
+ expect(page).to have_css(".is-checked")
+ end
+
+ page.within('.gl-toast') do
+ expect(find('.toast-body')).to have_content(_('Notifications turned on.'))
+ end
+ end
+end
+
+RSpec.shared_examples 'work items todos' do
+ let(:todos_action_selector) { '[data-testid="work-item-todos-action"]' }
+ let(:todos_icon_selector) { '[data-testid="work-item-todos-icon"]' }
+ let(:header_section_selector) { '[data-testid="work-item-body"]' }
+
+ def toggle_todo_action
+ find(todos_action_selector).click
+ wait_for_requests
+ end
+
+ it 'adds item to the list' do
+ page.within(header_section_selector) do
+ expect(find(todos_action_selector)['aria-label']).to eq('Add a to do')
+
+ toggle_todo_action
+
+ expect(find(todos_action_selector)['aria-label']).to eq('Mark as done')
+ end
+
+ page.within ".header-content span[aria-label='#{_('Todos count')}']" do
+ expect(page).to have_content '1'
+ end
+ end
+
+ it 'marks a todo as done' do
+ page.within(header_section_selector) do
+ toggle_todo_action
+ toggle_todo_action
+ end
+
+ expect(find(todos_action_selector)['aria-label']).to eq('Add a to do')
+ expect(page).to have_selector(".header-content span[aria-label='#{_('Todos count')}']", visible: :hidden)
+ end
+end
+
+RSpec.shared_examples 'work items award emoji' do
+ let(:award_section_selector) { '[data-testid="work-item-award-list"]' }
+ let(:award_action_selector) { '[data-testid="award-button"]' }
+ let(:selected_award_action_selector) { '[data-testid="award-button"].selected' }
+ let(:emoji_picker_action_selector) { '[data-testid="emoji-picker"]' }
+ let(:basketball_emoji_selector) { 'gl-emoji[data-name="basketball"]' }
+
+ def select_emoji
+ first(award_action_selector).click
+
+ wait_for_requests
+ end
+
+ it 'adds award to the work item' do
+ within(award_section_selector) do
+ select_emoji
+
+ expect(page).to have_selector(selected_award_action_selector)
+ expect(first(award_action_selector)).to have_content '1'
+ end
+ end
+
+ it 'removes award from work item' do
+ within(award_section_selector) do
+ select_emoji
+
+ expect(first(award_action_selector)).to have_content '1'
+
+ select_emoji
+
+ expect(first(award_action_selector)).to have_content '0'
+ end
+ end
+
+ it 'add custom award to the work item' do
+ within(award_section_selector) do
+ find(emoji_picker_action_selector).click
+ find(basketball_emoji_selector).click
+
+ expect(page).to have_selector(basketball_emoji_selector)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
index 93f9e42241b..67fed00b5ca 100644
--- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
+++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
@@ -161,10 +161,12 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
let_it_be(:another_release) { create(:release, project: project1, tag: 'v2.0.0') }
let_it_be(:another_milestone) { create(:milestone, project: project1, releases: [another_release]) }
let_it_be(:another_item) do
- create(factory,
- project: project1,
- milestone: another_milestone,
- title: 'another item')
+ create(
+ factory,
+ project: project1,
+ milestone: another_milestone,
+ title: 'another item'
+ )
end
let(:params) { { not: { release_tag: release.tag, project_id: project1.id } } }
@@ -421,8 +423,11 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
let!(:created_items) do
milestones.map do |milestone|
- create(factory, project: milestone.project || project_in_group,
- milestone: milestone, author: user, assignees: [user])
+ create(
+ factory,
+ project: milestone.project || project_in_group,
+ milestone: milestone, author: user, assignees: [user]
+ )
end
end
@@ -593,7 +598,7 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
context 'filtering by no label' do
- let(:params) { { label_name: described_class::Params::FILTER_NONE } }
+ let(:params) { { label_name: IssuableFinder::Params::FILTER_NONE } }
it 'returns items with no labels' do
expect(items).to contain_exactly(item1, item4, item5)
@@ -601,7 +606,7 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
context 'filtering by any label' do
- let(:params) { { label_name: described_class::Params::FILTER_ANY } }
+ let(:params) { { label_name: IssuableFinder::Params::FILTER_ANY } }
it 'returns items that have one or more label' do
create_list(:label_link, 2, label: create(:label, project: project2), target: item3)
@@ -909,9 +914,9 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
context 'filtering by item type' do
- let_it_be(:incident_item) { create(factory, issue_type: :incident, project: project1) }
- let_it_be(:objective) { create(factory, issue_type: :objective, project: project1) }
- let_it_be(:key_result) { create(factory, issue_type: :key_result, project: project1) }
+ let_it_be(:incident_item) { create(factory, :incident, project: project1) }
+ let_it_be(:objective) { create(factory, :objective, project: project1) }
+ let_it_be(:key_result) { create(factory, :key_result, project: project1) }
context 'no type given' do
let(:params) { { issue_types: [] } }
@@ -983,9 +988,9 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
let_it_be(:root_group) { create(:group) }
let_it_be(:group) { create(:group, parent: root_group) }
let_it_be(:project_crm) { create(:project, :public, group: group) }
- let_it_be(:organization) { create(:organization, group: root_group) }
- let_it_be(:contact1) { create(:contact, group: root_group, organization: organization) }
- let_it_be(:contact2) { create(:contact, group: root_group, organization: organization) }
+ let_it_be(:crm_organization) { create(:crm_organization, group: root_group) }
+ let_it_be(:contact1) { create(:contact, group: root_group, organization: crm_organization) }
+ let_it_be(:contact2) { create(:contact, group: root_group, organization: crm_organization) }
let_it_be(:contact1_item1) { create(factory, project: project_crm) }
let_it_be(:contact1_item2) { create(factory, project: project_crm) }
@@ -1023,10 +1028,10 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
context 'filtering by crm organization' do
- let(:params) { { project_id: project_crm.id, crm_organization_id: organization.id } }
+ let(:params) { { project_id: project_crm.id, crm_organization_id: crm_organization.id } }
context 'when the user can read crm organization' do
- it 'returns for that organization' do
+ it 'returns for that crm organization' do
root_group.add_reporter(user)
expect(items).to contain_exactly(contact1_item1, contact1_item2, contact2_item1)
@@ -1034,7 +1039,7 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
end
context 'when the user can not read crm organization' do
- it 'does not filter by organization' do
+ it 'does not filter by crm organization' do
expect(items).to match_array(all_project_issues)
end
end
diff --git a/spec/support/shared_examples/graphql/members_shared_examples.rb b/spec/support/shared_examples/graphql/members_shared_examples.rb
index 5cba8baa829..5ab17f5a49d 100644
--- a/spec/support/shared_examples/graphql/members_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/members_shared_examples.rb
@@ -39,8 +39,10 @@ RSpec.shared_examples 'querying members with a group' do
let(:base_args) { { relations: described_class.arguments['relations'].default_value } }
subject do
- resolve(described_class, obj: resource, args: base_args.merge(args),
- ctx: { current_user: user_4 }, arg_style: :internal)
+ resolve(
+ described_class, obj: resource, args: base_args.merge(args),
+ ctx: { current_user: user_4 }, arg_style: :internal
+ )
end
describe '#resolve' do
@@ -83,8 +85,10 @@ RSpec.shared_examples 'querying members with a group' do
let_it_be(:other_user) { create(:user) }
subject do
- resolve(described_class, obj: resource, args: base_args.merge(args),
- ctx: { current_user: other_user }, arg_style: :internal)
+ resolve(
+ described_class, obj: resource, args: base_args.merge(args),
+ ctx: { current_user: other_user }, arg_style: :internal
+ )
end
it 'generates an error' do
diff --git a/spec/support/shared_examples/graphql/mutation_shared_examples.rb b/spec/support/shared_examples/graphql/mutation_shared_examples.rb
index dc590e23ace..808fb097f29 100644
--- a/spec/support/shared_examples/graphql/mutation_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutation_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'a mutation that returns top-level errors' do |errors: []|
expect(graphql_errors).to be_present
- error_messages = graphql_errors.map { |e| e['message'] }
+ error_messages = graphql_errors.pluck('message')
expect(error_messages).to match_errors
end
@@ -25,7 +25,7 @@ end
# the mutation.
RSpec.shared_examples 'a mutation that returns a top-level access error' do
include_examples 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
+ errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
end
RSpec.shared_examples 'an invalid argument to the mutation' do |argument_name:|
diff --git a/spec/support/shared_examples/graphql/mutations/members/bulk_update_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/members/bulk_update_shared_examples.rb
new file mode 100644
index 00000000000..e885b5d283e
--- /dev/null
+++ b/spec/support/shared_examples/graphql/mutations/members/bulk_update_shared_examples.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'members bulk update mutation' do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:user1) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+ let_it_be(:member1) { create(member_type, source: source, user: user1) }
+ let_it_be(:member2) { create(member_type, source: source, user: user2) }
+
+ let(:extra_params) { { expires_at: 10.days.from_now } }
+ let(:input_params) { input.merge(extra_params) }
+ let(:mutation) { graphql_mutation(mutation_name, input_params) }
+ let(:mutation_response) { graphql_mutation_response(mutation_name) }
+
+ let(:input) do
+ {
+ source_id_key => source.to_global_id.to_s,
+ 'user_ids' => [user1.to_global_id.to_s, user2.to_global_id.to_s],
+ 'access_level' => 'GUEST'
+ }
+ end
+
+ context 'when user is not logged-in' do
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when user is not an owner' do
+ before do
+ source.add_developer(current_user)
+ end
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when user is an owner' do
+ before do
+ source.add_owner(current_user)
+ end
+
+ shared_examples 'updates the user access role' do
+ specify do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ new_access_levels = mutation_response[response_member_field].map do |member|
+ member['accessLevel']['integerValue']
+ end
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['errors']).to be_empty
+ expect(new_access_levels).to all(be Gitlab::Access::GUEST)
+ end
+ end
+
+ it_behaves_like 'updates the user access role'
+
+ context 'when inherited members are passed' do
+ let(:input) do
+ {
+ source_id_key => source.to_global_id.to_s,
+ 'user_ids' => [user1.to_global_id.to_s, user2.to_global_id.to_s, parent_group_member.user.to_global_id.to_s],
+ 'access_level' => 'GUEST'
+ }
+ end
+
+ it 'does not update the members' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ error = Mutations::Members::BulkUpdateBase::INVALID_MEMBERS_ERROR
+ expect(json_response['errors'].first['message']).to include(error)
+ end
+ end
+
+ context 'when members count is more than the allowed limit' do
+ let(:max_members_update_limit) { 1 }
+
+ before do
+ stub_const('Mutations::Members::BulkUpdateBase::MAX_MEMBERS_UPDATE_LIMIT', max_members_update_limit)
+ end
+
+ it 'does not update the members' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ error = Mutations::Members::BulkUpdateBase::MAX_MEMBERS_UPDATE_ERROR
+ expect(json_response['errors'].first['message']).to include(error)
+ end
+ end
+
+ context 'when the update service raises access denied error' do
+ before do
+ allow_next_instance_of(Members::UpdateService) do |instance|
+ allow(instance).to receive(:execute).and_raise(Gitlab::Access::AccessDeniedError)
+ end
+ end
+
+ it 'does not update the members' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response[response_member_field]).to be_nil
+ expect(mutation_response['errors'])
+ .to contain_exactly("Unable to update members, please check user permissions.")
+ end
+ end
+
+ context 'when the update service returns an error message' do
+ before do
+ allow_next_instance_of(Members::UpdateService) do |instance|
+ error_result = {
+ message: 'Expires at cannot be a date in the past',
+ status: :error,
+ members: [member1]
+ }
+ allow(instance).to receive(:execute).and_return(error_result)
+ end
+ end
+
+ it 'will pass through the error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(mutation_response[response_member_field].first['id']).to eq(member1.to_global_id.to_s)
+ expect(mutation_response['errors']).to contain_exactly('Expires at cannot be a date in the past')
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb b/spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb
index 022e2308517..3b9dadf2e80 100644
--- a/spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/mutations/set_assignees_shared_examples.rb
@@ -16,10 +16,12 @@ RSpec.shared_examples 'an assignable resource' do
let(:mode) { described_class.arguments['operationMode'].default_value }
subject do
- mutation.resolve(project_path: resource.project.full_path,
- iid: resource.iid,
- operation_mode: mode,
- assignee_usernames: assignee_usernames)
+ mutation.resolve(
+ project_path: resource.project.full_path,
+ iid: resource.iid,
+ operation_mode: mode,
+ assignee_usernames: assignee_usernames
+ )
end
it 'raises an error if the resource is not accessible to the user' do
diff --git a/spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb b/spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb
index 0d2e9f6ec8c..99d122e8254 100644
--- a/spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/notes_on_noteables_shared_examples.rb
@@ -4,9 +4,11 @@ RSpec.shared_context 'exposing regular notes on a noteable in GraphQL' do
include GraphqlHelpers
let(:note) do
- create(:note,
- noteable: noteable,
- project: (noteable.project if noteable.respond_to?(:project)))
+ create(
+ :note,
+ noteable: noteable,
+ project: (noteable.project if noteable.respond_to?(:project))
+ )
end
let(:user) { note.author }
@@ -46,7 +48,7 @@ RSpec.shared_context 'exposing regular notes on a noteable in GraphQL' do
discussions {
edges {
node {
- #{all_graphql_fields_for('Discussion', max_depth: 4)}
+ #{all_graphql_fields_for('Discussion', max_depth: 4, excluded: ['productAnalyticsState'])}
}
}
}
diff --git a/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb b/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb
new file mode 100644
index 00000000000..52908c5b6df
--- /dev/null
+++ b/spec/support/shared_examples/graphql/notes_quick_actions_for_work_items_shared_examples.rb
@@ -0,0 +1,195 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'work item supports assignee widget updates via quick actions' do
+ let_it_be(:developer) { create(:user).tap { |user| project.add_developer(user) } }
+
+ context 'when assigning a user' do
+ let(:body) { "/assign @#{developer.username}" }
+
+ it 'updates the work item assignee' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.to change { noteable.assignee_ids }.from([]).to([developer.id])
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
+
+ context 'when unassigning a user' do
+ let(:body) { "/unassign @#{developer.username}" }
+
+ before do
+ noteable.update!(assignee_ids: [developer.id])
+ end
+
+ it 'updates the work item assignee' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.to change { noteable.assignee_ids }.from([developer.id]).to([])
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+ end
+end
+
+RSpec.shared_examples 'work item does not support assignee widget updates via quick actions' do
+ let(:developer) { create(:user).tap { |user| project.add_developer(user) } }
+ let(:body) { "Updating assignee.\n/assign @#{developer.username}" }
+
+ before do
+ WorkItems::Type.default_by_type(:task).widget_definitions
+ .find_by_widget_type(:assignees).update!(disabled: true)
+ end
+
+ it 'ignores the quick action' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.not_to change { noteable.assignee_ids }
+ end
+end
+
+RSpec.shared_examples 'work item supports labels widget updates via quick actions' do
+ shared_examples 'work item labels are updated' do
+ it do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.to change { noteable.labels.count }.to(expected_labels.count)
+
+ expect(noteable.labels).to match_array(expected_labels)
+ end
+ end
+
+ let_it_be(:existing_label) { create(:label, project: project) }
+ let_it_be(:label1) { create(:label, project: project) }
+ let_it_be(:label2) { create(:label, project: project) }
+
+ let(:add_label_ids) { [] }
+ let(:remove_label_ids) { [] }
+
+ before_all do
+ noteable.update!(labels: [existing_label])
+ end
+
+ context 'when only removing labels' do
+ let(:remove_label_ids) { [existing_label.to_gid.to_s] }
+ let(:expected_labels) { [] }
+ let(:body) { "/remove_label ~\"#{existing_label.name}\"" }
+
+ it_behaves_like 'work item labels are updated'
+ end
+
+ context 'when only adding labels' do
+ let(:add_label_ids) { [label1.to_gid.to_s, label2.to_gid.to_s] }
+ let(:expected_labels) { [label1, label2, existing_label] }
+ let(:body) { "/labels ~\"#{label1.name}\" ~\"#{label2.name}\"" }
+
+ it_behaves_like 'work item labels are updated'
+ end
+
+ context 'when adding and removing labels' do
+ let(:remove_label_ids) { [existing_label.to_gid.to_s] }
+ let(:add_label_ids) { [label1.to_gid.to_s, label2.to_gid.to_s] }
+ let(:expected_labels) { [label1, label2] }
+ let(:body) { "/label ~\"#{label1.name}\" ~\"#{label2.name}\"\n/remove_label ~\"#{existing_label.name}\"" }
+
+ it_behaves_like 'work item labels are updated'
+ end
+end
+
+RSpec.shared_examples 'work item does not support labels widget updates via quick actions' do
+ let(:label1) { create(:label, project: project) }
+ let(:body) { "Updating labels.\n/labels ~\"#{label1.name}\"" }
+
+ before do
+ WorkItems::Type.default_by_type(:task).widget_definitions
+ .find_by_widget_type(:labels).update!(disabled: true)
+ end
+
+ it 'ignores the quick action' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.not_to change { noteable.labels.count }
+
+ expect(noteable.labels).to be_empty
+ end
+end
+
+RSpec.shared_examples 'work item supports start and due date widget updates via quick actions' do
+ let(:due_date) { Date.today }
+ let(:body) { "/remove_due_date" }
+
+ before do
+ noteable.update!(due_date: due_date)
+ end
+
+ it 'updates start and due date' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.to not_change(noteable, :start_date).and(
+ change { noteable.due_date }.from(due_date).to(nil)
+ )
+ end
+end
+
+RSpec.shared_examples 'work item does not support start and due date widget updates via quick actions' do
+ let(:body) { "Updating due date.\n/due today" }
+
+ before do
+ WorkItems::Type.default_by_type(:task).widget_definitions
+ .find_by_widget_type(:start_and_due_date).update!(disabled: true)
+ end
+
+ it 'ignores the quick action' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.not_to change { noteable.due_date }
+ end
+end
+
+RSpec.shared_examples 'work item supports type change via quick actions' do
+ let_it_be(:assignee) { create(:user) }
+ let_it_be(:task_type) { WorkItems::Type.default_by_type(:task) }
+
+ let(:body) { "Updating type.\n/type Issue" }
+
+ before do
+ noteable.update!(work_item_type: task_type, issue_type: task_type.base_type)
+ end
+
+ it 'updates type' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.to change { noteable.work_item_type.base_type }.from('task').to('issue')
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
+
+ context 'when quick command for unsupported widget is present' do
+ let(:body) { "\n/type Issue\n/assign @#{assignee.username}" }
+
+ before do
+ WorkItems::Type.default_by_type(:issue).widget_definitions
+ .find_by_widget_type(:assignees).update!(disabled: true)
+ end
+
+ it 'updates only type' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ noteable.reload
+ end.to change { noteable.work_item_type.base_type }.from('task').to('issue')
+ .and change { noteable.assignees }.to([])
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['errors'])
+ .to include("Commands only Type changed successfully. Assigned @#{assignee.username}.")
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/resolvers/data_transfer_resolver_shared_examples.rb b/spec/support/shared_examples/graphql/resolvers/data_transfer_resolver_shared_examples.rb
new file mode 100644
index 00000000000..8551bd052ce
--- /dev/null
+++ b/spec/support/shared_examples/graphql/resolvers/data_transfer_resolver_shared_examples.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'Data transfer resolver' do
+ it 'returns mock data' do |_query_object|
+ mocked_data = ['mocked_data']
+
+ allow_next_instance_of(DataTransfer::MockedTransferFinder) do |instance|
+ allow(instance).to receive(:execute).and_return(mocked_data)
+ end
+
+ expect(resolve_egress[:egress_nodes]).to eq(mocked_data)
+ end
+
+ context 'when data_transfer_monitoring is disabled' do
+ before do
+ stub_feature_flags(data_transfer_monitoring: false)
+ end
+
+ it 'returns empty result' do
+ expect(resolve_egress).to eq(egress_nodes: [])
+ end
+ end
+end
diff --git a/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb b/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
index 4dc2ce61c4d..b346f35bdc9 100644
--- a/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/types/gitlab_style_deprecations_shared_examples.rb
@@ -65,7 +65,7 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
deprecable = subject(deprecated: { milestone: '1.10', reason: :alpha })
expect(deprecable.deprecation_reason).to eq(
- 'This feature is in Alpha. It can be changed or removed at any time. Introduced in 1.10.'
+ 'This feature is an Experiment. It can be changed or removed at any time. Introduced in 1.10.'
)
end
@@ -73,7 +73,7 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
deprecable = subject(alpha: { milestone: '1.10' })
expect(deprecable.deprecation_reason).to eq(
- 'This feature is in Alpha. It can be changed or removed at any time. Introduced in 1.10.'
+ 'This feature is an Experiment. It can be changed or removed at any time. Introduced in 1.10.'
)
end
@@ -82,7 +82,7 @@ RSpec.shared_examples 'Gitlab-style deprecations' do
subject(alpha: { milestone: '1.10' }, deprecated: { milestone: '1.10', reason: 'my reason' } )
end.to raise_error(
ArgumentError,
- eq("`alpha` and `deprecated` arguments cannot be passed at the same time")
+ eq("`experiment` and `deprecated` arguments cannot be passed at the same time")
)
end
diff --git a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
index bb33a7559dc..3dffc2066ae 100644
--- a/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/types/merge_request_interactions_type_shared_examples.rb
@@ -42,8 +42,13 @@ RSpec.shared_examples "a user type with merge request interaction type" do
profileEnableGitpodPath
savedReplies
savedReply
+ user_achievements
]
+ # TODO: 'workspaces' needs to be included, but only when this spec is run in EE context, to account for the
+ # ee-only extension in ee/app/graphql/ee/types/user_interface.rb. Not sure how else to handle this.
+ expected_fields << 'workspaces' if Gitlab.ee?
+
expect(described_class).to have_graphql_fields(*expected_fields)
end
diff --git a/spec/support/shared_examples/helpers/callouts_for_web_hooks.rb b/spec/support/shared_examples/helpers/callouts_for_web_hooks.rb
new file mode 100644
index 00000000000..b3d3000aa06
--- /dev/null
+++ b/spec/support/shared_examples/helpers/callouts_for_web_hooks.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'CalloutsHelper#web_hook_disabled_dismissed shared examples' do
+ context 'when the web-hook failure callout has never been dismissed' do
+ it 'is false' do
+ expect(helper).not_to be_web_hook_disabled_dismissed(container)
+ end
+ end
+
+ context 'when the web-hook failure callout has been dismissed', :freeze_time, :clean_gitlab_redis_shared_state do
+ before do
+ create(factory,
+ feature_name: Users::CalloutsHelper::WEB_HOOK_DISABLED,
+ user: user,
+ dismissed_at: 1.week.ago,
+ container_key => container)
+ end
+
+ it 'is true' do
+ expect(helper).to be_web_hook_disabled_dismissed(container)
+ end
+
+ it 'is true when passed as a presenter' do
+ skip "Does not apply to #{container.class}" unless container.is_a?(Presentable)
+
+ expect(helper).to be_web_hook_disabled_dismissed(container.present)
+ end
+
+ context 'when there was an older failure' do
+ before do
+ Gitlab::Redis::SharedState.with { |r| r.set(key, 1.month.ago.iso8601) }
+ end
+
+ it 'is true' do
+ expect(helper).to be_web_hook_disabled_dismissed(container)
+ end
+ end
+
+ context 'when there has been a more recent failure' do
+ before do
+ Gitlab::Redis::SharedState.with { |r| r.set(key, 1.day.ago.iso8601) }
+ end
+
+ it 'is false' do
+ expect(helper).not_to be_web_hook_disabled_dismissed(container)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/integrations/integration_settings_form.rb b/spec/support/shared_examples/integrations/integration_settings_form.rb
index aeb4e0feb12..c43bdfa53ff 100644
--- a/spec/support/shared_examples/integrations/integration_settings_form.rb
+++ b/spec/support/shared_examples/integrations/integration_settings_form.rb
@@ -2,12 +2,16 @@
RSpec.shared_examples 'integration settings form' do
include IntegrationsHelper
+
+ before do
+ stub_feature_flags(remove_monitor_metrics: false)
+ end
+
# Note: these specs don't validate channel fields
# which are present on a few integrations
it 'displays all the integrations', feature_category: :integrations do
aggregate_failures do
integrations.each do |integration|
- stub_feature_flags(integration_slack_app_notifications: false)
navigate_to_integration(integration)
page.within('form.integration-settings-form') do
diff --git a/spec/support/shared_examples/lib/api/ai_workhorse_shared_examples.rb b/spec/support/shared_examples/lib/api/ai_workhorse_shared_examples.rb
new file mode 100644
index 00000000000..7ace223723c
--- /dev/null
+++ b/spec/support/shared_examples/lib/api/ai_workhorse_shared_examples.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'delegates AI request to Workhorse' do |provider_flag|
+ context "when #{provider_flag} is disabled" do
+ before do
+ stub_feature_flags(provider_flag => false)
+ end
+
+ it 'responds as not found' do
+ post api(url, current_user), params: input_params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when ai_experimentation_api is disabled' do
+ before do
+ stub_feature_flags(ai_experimentation_api: false)
+ end
+
+ it 'responds as not found' do
+ post api(url, current_user), params: input_params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ it 'responds with Workhorse send-url headers' do
+ post api(url, current_user), params: input_params
+
+ expect(response.body).to eq('""')
+ expect(response).to have_gitlab_http_status(:ok)
+
+ send_url_prefix, encoded_data = response.headers['Gitlab-Workhorse-Send-Data'].split(':')
+ data = Gitlab::Json.parse(Base64.urlsafe_decode64(encoded_data))
+
+ expect(send_url_prefix).to eq('send-url')
+ expect(data).to eq({
+ 'AllowRedirects' => false,
+ 'Method' => 'POST'
+ }.merge(expected_params))
+ end
+end
diff --git a/spec/support/shared_examples/lib/api/terraform_state_enabled_shared_examples.rb b/spec/support/shared_examples/lib/api/terraform_state_enabled_shared_examples.rb
new file mode 100644
index 00000000000..b88eade7db2
--- /dev/null
+++ b/spec/support/shared_examples/lib/api/terraform_state_enabled_shared_examples.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'it depends on value of the `terraform_state.enabled` config' do |params = {}|
+ let(:expected_success_status) { params[:success_status] || :ok }
+
+ context 'when terraform_state.enabled=false' do
+ before do
+ stub_config(terraform_state: { enabled: false })
+ end
+
+ it 'returns `forbidden` response' do
+ request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'when terraform_state.enabled=true' do
+ before do
+ stub_config(terraform_state: { enabled: true })
+ end
+
+ it 'returns a successful response' do
+ request
+
+ expect(response).to have_gitlab_http_status(expected_success_status)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
index d471a758f3e..c8d62205c1e 100644
--- a/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb
@@ -1,14 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'deployment metrics examples' do
- def create_deployment(args)
- project = args[:project]
- environment = project.environments.production.first || create(:environment, :production, project: project)
- create(:deployment, :success, args.merge(environment: environment))
-
- # this is needed for the DORA API so we have aggregated data
- ::Dora::DailyMetrics::RefreshWorker.new.perform(environment.id, Time.current.to_date.to_s) if Gitlab.ee?
- end
+ include CycleAnalyticsHelpers
describe "#deploys" do
subject { stage_summary.third }
diff --git a/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb
index bce889b454d..5740adb3f0e 100644
--- a/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/cycle_analytics/event_shared_examples.rb
@@ -68,3 +68,64 @@ RSpec.shared_examples_for 'LEFT JOIN-able value stream analytics event' do
end
end
end
+
+RSpec.shared_examples_for 'value stream analytics first assignment event methods' do
+ let_it_be(:model1) { create(model_factory) } # rubocop: disable Rails/SaveBang
+ let_it_be(:model2) { create(model_factory) } # rubocop: disable Rails/SaveBang
+
+ let_it_be(:assignment_event1) do
+ create(event_factory, action: :add, created_at: 3.years.ago, model_factory => model1)
+ end
+
+ let_it_be(:assignment_event2) do
+ create(event_factory, action: :add, created_at: 2.years.ago, model_factory => model1)
+ end
+
+ let_it_be(:unassignment_event1) do
+ create(event_factory, action: :remove, created_at: 1.year.ago, model_factory => model1)
+ end
+
+ let(:query) { model1.class.where(id: [model1.id, model2.id]) }
+ let(:event) { described_class.new({}) }
+
+ describe '#apply_query_customization' do
+ subject(:records) { event.apply_query_customization(query).pluck(:id, *event.column_list).to_a }
+
+ it 'looks up the first assignment event timestamp' do
+ expect(records).to match_array([[model1.id, be_within(1.second).of(assignment_event1.created_at)]])
+ end
+ end
+
+ describe '#apply_negated_query_customization' do
+ subject(:records) { event.apply_negated_query_customization(query).pluck(:id).to_a }
+
+ it 'returns records where the event has not happened yet' do
+ expect(records).to eq([model2.id])
+ end
+ end
+
+ describe '#include_in' do
+ subject(:records) { event.include_in(query).pluck(:id, *event.column_list).to_a }
+
+ it 'returns both records' do
+ expect(records).to match_array([
+ [model1.id, be_within(1.second).of(assignment_event1.created_at)],
+ [model2.id, nil]
+ ])
+ end
+
+ context 'when invoked multiple times' do
+ subject(:records) do
+ scope = event.include_in(query)
+ event.include_in(scope).pluck(:id, *event.column_list).to_a
+ end
+
+ it 'returns both records' do
+ expect(records).to match_array([
+ [model1.id, be_within(1.second).of(assignment_event1.created_at)],
+ [model2.id, nil]
+ ])
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/database/async_constraints_validation_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/async_constraints_validation_shared_examples.rb
new file mode 100644
index 00000000000..b9d71183851
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/async_constraints_validation_shared_examples.rb
@@ -0,0 +1,131 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'async constraints validation' do
+ include ExclusiveLeaseHelpers
+
+ let!(:lease) { stub_exclusive_lease(lease_key, :uuid, timeout: lease_timeout) }
+ let(:lease_key) { "gitlab/database/asyncddl/actions/#{Gitlab::Database::PRIMARY_DATABASE_NAME}" }
+ let(:lease_timeout) { described_class::TIMEOUT_PER_ACTION }
+
+ let(:constraints_model) { Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValidation }
+ let(:table_name) { '_test_async_constraints' }
+ let(:constraint_name) { 'constraint_parent_id' }
+
+ let(:validation) do
+ create(:postgres_async_constraint_validation,
+ table_name: table_name,
+ name: constraint_name,
+ constraint_type: constraint_type)
+ end
+
+ let(:connection) { validation.connection }
+
+ subject { described_class.new(validation) }
+
+ it 'validates the constraint while controlling statement timeout' do
+ allow(connection).to receive(:execute).and_call_original
+ expect(connection).to receive(:execute)
+ .with("SET statement_timeout TO '43200s'").ordered.and_call_original
+ expect(connection).to receive(:execute)
+ .with(/ALTER TABLE "#{table_name}" VALIDATE CONSTRAINT "#{constraint_name}";/).ordered.and_call_original
+ expect(connection).to receive(:execute)
+ .with("RESET statement_timeout").ordered.and_call_original
+
+ subject.perform
+ end
+
+ it 'removes the constraint validation record from table' do
+ expect(validation).to receive(:destroy!).and_call_original
+
+ expect { subject.perform }.to change { constraints_model.count }.by(-1)
+ end
+
+ it 'skips logic if not able to acquire exclusive lease' do
+ expect(lease).to receive(:try_obtain).ordered.and_return(false)
+ expect(connection).not_to receive(:execute).with(/ALTER TABLE/)
+ expect(validation).not_to receive(:destroy!)
+
+ expect { subject.perform }.not_to change { constraints_model.count }
+ end
+
+ it 'logs messages around execution' do
+ allow(Gitlab::AppLogger).to receive(:info).and_call_original
+
+ subject.perform
+
+ expect(Gitlab::AppLogger)
+ .to have_received(:info)
+ .with(a_hash_including(message: 'Starting to validate constraint'))
+
+ expect(Gitlab::AppLogger)
+ .to have_received(:info)
+ .with(a_hash_including(message: 'Finished validating constraint'))
+ end
+
+ context 'when the constraint does not exist' do
+ before do
+ connection.create_table(table_name, force: true)
+ end
+
+ it 'skips validation and removes the record' do
+ expect(connection).not_to receive(:execute).with(/ALTER TABLE/)
+
+ expect { subject.perform }.to change { constraints_model.count }.by(-1)
+ end
+
+ it 'logs an appropriate message' do
+ expected_message = /Skipping #{constraint_name} validation since it does not exist/
+
+ allow(Gitlab::AppLogger).to receive(:info).and_call_original
+
+ subject.perform
+
+ expect(Gitlab::AppLogger)
+ .to have_received(:info)
+ .with(a_hash_including(message: expected_message))
+ end
+ end
+
+ context 'with error handling' do
+ before do
+ allow(connection).to receive(:execute).and_call_original
+
+ allow(connection).to receive(:execute)
+ .with(/ALTER TABLE "#{table_name}" VALIDATE CONSTRAINT "#{constraint_name}";/)
+ .and_raise(ActiveRecord::StatementInvalid)
+ end
+
+ context 'on production' do
+ before do
+ allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
+ end
+
+ it 'increases execution attempts' do
+ expect { subject.perform }.to change { validation.attempts }.by(1)
+
+ expect(validation.last_error).to be_present
+ expect(validation).not_to be_destroyed
+ end
+
+ it 'logs an error message including the constraint_name' do
+ expect(Gitlab::AppLogger)
+ .to receive(:error)
+ .with(a_hash_including(:message, :constraint_name))
+ .and_call_original
+
+ subject.perform
+ end
+ end
+
+ context 'on development' do
+ it 'also raises errors' do
+ expect { subject.perform }
+ .to raise_error(ActiveRecord::StatementInvalid)
+ .and change { validation.attempts }.by(1)
+
+ expect(validation.last_error).to be_present
+ expect(validation).not_to be_destroyed
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb
new file mode 100644
index 00000000000..6f0cede7130
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples "index validators" do |validator, expected_result|
+ let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') }
+ let(:database_indexes) do
+ [
+ ['wrong_index', 'CREATE UNIQUE INDEX wrong_index ON public.table_name (column_name)'],
+ ['extra_index', 'CREATE INDEX extra_index ON public.table_name (column_name)'],
+ ['index', 'CREATE UNIQUE INDEX "index" ON public.achievements USING btree (namespace_id, lower(name))']
+ ]
+ end
+
+ let(:inconsistency_type) { validator.name.demodulize.underscore }
+
+ let(:database_name) { 'main' }
+
+ let(:database_model) { Gitlab::Database.database_base_models[database_name] }
+
+ let(:connection) { database_model.connection }
+
+ let(:schema) { connection.current_schema }
+
+ let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) }
+ let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) }
+
+ subject(:result) { validator.new(structure_file, database).execute }
+
+ before do
+ allow(connection).to receive(:select_rows).and_return(database_indexes)
+ end
+
+ it 'returns index inconsistencies' do
+ expect(result.map(&:object_name)).to match_array(expected_result)
+ expect(result.map(&:type)).to all(eql inconsistency_type)
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb
new file mode 100644
index 00000000000..ec7a881f7ce
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/schema_objects_shared_examples.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples "schema objects assertions for" do |stmt_name|
+ let(:stmt) { PgQuery.parse(statement).tree.stmts.first.stmt }
+ let(:schema_object) { described_class.new(stmt.public_send(stmt_name)) }
+
+ describe '#name' do
+ it 'returns schema object name' do
+ expect(schema_object.name).to eq(name)
+ end
+ end
+
+ describe '#statement' do
+ it 'returns schema object statement' do
+ expect(schema_object.statement).to eq(statement)
+ end
+ end
+
+ describe '#table_name' do
+ it 'returns schema object table_name' do
+ expect(schema_object.table_name).to eq(table_name)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/database/table_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/table_validators_shared_examples.rb
new file mode 100644
index 00000000000..96e58294675
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/table_validators_shared_examples.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples "table validators" do |validator, expected_result|
+ subject(:result) { validator.new(structure_file, database).execute }
+
+ let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') }
+ let(:inconsistency_type) { validator.name.demodulize.underscore }
+ let(:database_model) { Gitlab::Database.database_base_models['main'] }
+ let(:connection) { database_model.connection }
+ let(:schema) { connection.current_schema }
+ let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) }
+ let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) }
+ let(:database_tables) do
+ [
+ {
+ 'table_name' => 'wrong_table',
+ 'column_name' => 'id',
+ 'not_null' => true,
+ 'data_type' => 'integer',
+ 'column_default' => "nextval('audit_events_id_seq'::regclass)"
+ },
+ {
+ 'table_name' => 'wrong_table',
+ 'column_name' => 'description',
+ 'not_null' => true,
+ 'data_type' => 'character varying',
+ 'column_default' => nil
+ },
+ {
+ 'table_name' => 'extra_table',
+ 'column_name' => 'id',
+ 'not_null' => true,
+ 'data_type' => 'integer',
+ 'column_default' => "nextval('audit_events_id_seq'::regclass)"
+ },
+ {
+ 'table_name' => 'extra_table',
+ 'column_name' => 'email',
+ 'not_null' => true,
+ 'data_type' => 'character varying',
+ 'column_default' => nil
+ },
+ {
+ 'table_name' => 'extra_table_columns',
+ 'column_name' => 'id',
+ 'not_null' => true,
+ 'data_type' => 'bigint',
+ 'column_default' => "nextval('audit_events_id_seq'::regclass)"
+ },
+ {
+ 'table_name' => 'extra_table_columns',
+ 'column_name' => 'name',
+ 'not_null' => true,
+ 'data_type' => 'character varying(255)',
+ 'column_default' => nil
+ },
+ {
+ 'table_name' => 'extra_table_columns',
+ 'column_name' => 'extra_column',
+ 'not_null' => true,
+ 'data_type' => 'character varying(255)',
+ 'column_default' => nil
+ },
+ {
+ 'table_name' => 'missing_table_columns',
+ 'column_name' => 'id',
+ 'not_null' => true,
+ 'data_type' => 'bigint',
+ 'column_default' => 'NOT NULL'
+ }
+ ]
+ end
+
+ before do
+ allow(connection).to receive(:exec_query).and_return(database_tables)
+ end
+
+ it 'returns table inconsistencies' do
+ expect(result.map(&:object_name)).to match_array(expected_result)
+ expect(result.map(&:type)).to all(eql inconsistency_type)
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb
new file mode 100644
index 00000000000..13a112275c2
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/database/trigger_validators_shared_examples.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'trigger validators' do |validator, expected_result|
+ subject(:result) { validator.new(structure_file, database).execute }
+
+ let(:structure_file_path) { Rails.root.join('spec/fixtures/structure.sql') }
+ let(:structure_file) { Gitlab::Database::SchemaValidation::StructureSql.new(structure_file_path, schema) }
+ let(:inconsistency_type) { validator.name.demodulize.underscore }
+ let(:database_name) { 'main' }
+ let(:schema) { 'public' }
+ let(:database_model) { Gitlab::Database.database_base_models[database_name] }
+ let(:connection) { database_model.connection }
+ let(:database) { Gitlab::Database::SchemaValidation::Database.new(connection) }
+
+ let(:database_triggers) do
+ [
+ ['trigger', 'CREATE TRIGGER trigger AFTER INSERT ON public.t1 FOR EACH ROW EXECUTE FUNCTION t1()'],
+ ['wrong_trigger', 'CREATE TRIGGER wrong_trigger BEFORE UPDATE ON public.t2 FOR EACH ROW EXECUTE FUNCTION t2()'],
+ ['extra_trigger', 'CREATE TRIGGER extra_trigger BEFORE INSERT ON public.t4 FOR EACH ROW EXECUTE FUNCTION t4()']
+ ]
+ end
+
+ before do
+ allow(connection).to receive(:select_rows).and_return(database_triggers)
+ end
+
+ it 'returns trigger inconsistencies' do
+ expect(result.map(&:object_name)).to match_array(expected_result)
+ expect(result.map(&:type)).to all(eql inconsistency_type)
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/gitaly_client_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/gitaly_client_shared_examples.rb
index f26b9a4a7bd..d388abb16c6 100644
--- a/spec/support/shared_examples/lib/gitlab/gitaly_client_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/gitaly_client_shared_examples.rb
@@ -1,10 +1,12 @@
# frozen_string_literal: true
def raw_repo_without_container(repository)
- Gitlab::Git::Repository.new(repository.shard,
- "#{repository.disk_path}.git",
- repository.repo_type.identifier_for_container(repository.container),
- repository.container.full_path)
+ Gitlab::Git::Repository.new(
+ repository.shard,
+ "#{repository.disk_path}.git",
+ repository.repo_type.identifier_for_container(repository.container),
+ repository.container.full_path
+ )
end
RSpec.shared_examples 'Gitaly feature flag actors are inferred from repository' do
diff --git a/spec/support/shared_examples/lib/gitlab/json_logger_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/json_logger_shared_examples.rb
new file mode 100644
index 00000000000..8a5e8397c3d
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/json_logger_shared_examples.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a json logger' do |extra_params|
+ let(:now) { Time.now }
+ let(:correlation_id) { Labkit::Correlation::CorrelationId.current_id }
+
+ it 'formats strings' do
+ output = subject.format_message('INFO', now, 'test', 'Hello world')
+ data = Gitlab::Json.parse(output)
+
+ expect(data['severity']).to eq('INFO')
+ expect(data['time']).to eq(now.utc.iso8601(3))
+ expect(data['message']).to eq('Hello world')
+ expect(data['correlation_id']).to eq(correlation_id)
+ expect(data).to include(extra_params)
+ end
+
+ it 'formats hashes' do
+ output = subject.format_message('INFO', now, 'test', { hello: 1 })
+ data = Gitlab::Json.parse(output)
+
+ expect(data['severity']).to eq('INFO')
+ expect(data['time']).to eq(now.utc.iso8601(3))
+ expect(data['hello']).to eq(1)
+ expect(data['message']).to be_nil
+ expect(data['correlation_id']).to eq(correlation_id)
+ expect(data).to include(extra_params)
+ end
+end
diff --git a/spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb
index 27ca27a9035..4b0e3234750 100644
--- a/spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/local_and_remote_storage_migration_shared_examples.rb
@@ -8,9 +8,9 @@ RSpec.shared_examples 'local and remote storage migration' do
where(:start_store, :end_store, :method) do
ObjectStorage::Store::LOCAL | ObjectStorage::Store::REMOTE | :migrate_to_remote_storage
- ObjectStorage::Store::REMOTE | ObjectStorage::Store::REMOTE | :migrate_to_remote_storage # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ ObjectStorage::Store::REMOTE | ObjectStorage::Store::REMOTE | :migrate_to_remote_storage
ObjectStorage::Store::REMOTE | ObjectStorage::Store::LOCAL | :migrate_to_local_storage
- ObjectStorage::Store::LOCAL | ObjectStorage::Store::LOCAL | :migrate_to_local_storage # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
+ ObjectStorage::Store::LOCAL | ObjectStorage::Store::LOCAL | :migrate_to_local_storage
end
with_them do
diff --git a/spec/support/shared_examples/lib/gitlab/project_search_results_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/project_search_results_shared_examples.rb
index f83fecee4ea..0016f1e670d 100644
--- a/spec/support/shared_examples/lib/gitlab/project_search_results_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/project_search_results_shared_examples.rb
@@ -38,8 +38,7 @@ RSpec.shared_examples 'access restricted confidential issues' do
let(:user) { author }
it 'lists project confidential issues' do
- expect(objects).to contain_exactly(issue,
- security_issue_1)
+ expect(objects).to contain_exactly(issue, security_issue_1)
expect(results.limited_issues_count).to eq 2
end
end
@@ -48,8 +47,7 @@ RSpec.shared_examples 'access restricted confidential issues' do
let(:user) { assignee }
it 'lists project confidential issues for assignee' do
- expect(objects).to contain_exactly(issue,
- security_issue_2)
+ expect(objects).to contain_exactly(issue, security_issue_2)
expect(results.limited_issues_count).to eq 2
end
end
@@ -60,9 +58,7 @@ RSpec.shared_examples 'access restricted confidential issues' do
end
it 'lists project confidential issues' do
- expect(objects).to contain_exactly(issue,
- security_issue_1,
- security_issue_2)
+ expect(objects).to contain_exactly(issue, security_issue_1, security_issue_2)
expect(results.limited_issues_count).to eq 3
end
end
@@ -72,9 +68,7 @@ RSpec.shared_examples 'access restricted confidential issues' do
context 'when admin mode is enabled', :enable_admin_mode do
it 'lists all project issues' do
- expect(objects).to contain_exactly(issue,
- security_issue_1,
- security_issue_2)
+ expect(objects).to contain_exactly(issue, security_issue_1, security_issue_2)
end
end
diff --git a/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb
index 025f0d5c7ea..c2898513424 100644
--- a/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/repo_type_shared_examples.rb
@@ -15,7 +15,7 @@ RSpec.shared_examples 'a repo type' do
describe '#repository_for' do
it 'finds the repository for the repo type' do
- expect(described_class.repository_for(expected_container)).to eq(expected_repository)
+ expect(described_class.repository_for(expected_repository_resolver)).to eq(expected_repository)
end
it 'returns nil when container is nil' do
diff --git a/spec/support/shared_examples/lib/gitlab/search_language_filter_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/search_language_filter_shared_examples.rb
index a3e4379f4d3..18545698c27 100644
--- a/spec/support/shared_examples/lib/gitlab/search_language_filter_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/search_language_filter_shared_examples.rb
@@ -26,29 +26,4 @@ RSpec.shared_examples 'search results filtered by language' do
expect(blob_results.size).to eq(5)
expect(paths).to match_array(expected_paths)
end
-
- context 'when the search_blobs_language_aggregation feature flag is disabled' do
- before do
- stub_feature_flags(search_blobs_language_aggregation: false)
- end
-
- it 'does not filter by language', :sidekiq_inline, :aggregate_failures do
- expected_paths = %w[
- CHANGELOG
- CONTRIBUTING.md
- bar/branch-test.txt
- custom-highlighting/test.gitlab-custom
- files/ruby/popen.rb
- files/ruby/regex.rb
- files/ruby/version_info.rb
- files/whitespace
- encoding/test.txt
- files/markdown/ruby-style-guide.md
- ]
-
- paths = blob_results.map { |blob| blob.binary_path }
- expect(blob_results.size).to eq(10)
- expect(paths).to match_array(expected_paths)
- end
- end
end
diff --git a/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb
index ff03051ed37..74570a4da5c 100644
--- a/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/sidekiq_middleware/strategy_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name|
instance_double(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob, duplicate_key_ttl: Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob::DEFAULT_DUPLICATE_KEY_TTL)
end
- let(:expected_message) { "dropped #{strategy_name.to_s.humanize.downcase}" }
+ let(:humanized_strategy_name) { strategy_name.to_s.humanize.downcase }
subject(:strategy) { Gitlab::SidekiqMiddleware::DuplicateJobs::Strategies.for(strategy_name).new(fake_duplicate_job) }
@@ -155,7 +155,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name|
fake_logger = instance_double(Gitlab::SidekiqLogging::DeduplicationLogger)
expect(Gitlab::SidekiqLogging::DeduplicationLogger).to receive(:instance).and_return(fake_logger)
- expect(fake_logger).to receive(:deduplicated_log).with(a_hash_including({ 'jid' => 'new jid' }), expected_message, {})
+ expect(fake_logger).to receive(:deduplicated_log).with(a_hash_including({ 'jid' => 'new jid' }), humanized_strategy_name, {})
strategy.schedule({ 'jid' => 'new jid' }) {}
end
@@ -165,7 +165,7 @@ RSpec.shared_examples 'deduplicating jobs when scheduling' do |strategy_name|
expect(Gitlab::SidekiqLogging::DeduplicationLogger).to receive(:instance).and_return(fake_logger)
allow(fake_duplicate_job).to receive(:options).and_return({ foo: :bar })
- expect(fake_logger).to receive(:deduplicated_log).with(a_hash_including({ 'jid' => 'new jid' }), expected_message, { foo: :bar })
+ expect(fake_logger).to receive(:deduplicated_log).with(a_hash_including({ 'jid' => 'new jid' }), humanized_strategy_name, { foo: :bar })
strategy.schedule({ 'jid' => 'new jid' }) {}
end
diff --git a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
index d4802a19202..169fceced7a 100644
--- a/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/usage_data_counters/issuable_activity_shared_examples.rb
@@ -1,11 +1,11 @@
# frozen_string_literal: true
-RSpec.shared_examples 'a daily tracked issuable snowplow and service ping events for given event params' do
+RSpec.shared_examples 'tracked issuable snowplow and service ping events for given event params' do
before do
stub_application_setting(usage_ping_enabled: true)
end
- def count_unique(date_from: 1.minute.ago, date_to: 1.minute.from_now)
+ def count_unique(date_from: Date.today.beginning_of_week, date_to: 1.week.from_now)
Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(event_names: action, start_date: date_from, end_date: date_to)
end
@@ -27,35 +27,23 @@ RSpec.shared_examples 'a daily tracked issuable snowplow and service ping events
expect_snowplow_event(**{ category: category, action: event_action, user: user1 }.merge(event_params))
end
-
- context 'with route_hll_to_snowplow_phase2 disabled' do
- before do
- stub_feature_flags(route_hll_to_snowplow_phase2: false)
- end
-
- it 'does not emit snowplow event' do
- track_action({ author: user1 }.merge(track_params))
-
- expect_no_snowplow_event
- end
- end
end
-RSpec.shared_examples 'daily tracked issuable snowplow and service ping events with project' do
- it_behaves_like 'a daily tracked issuable snowplow and service ping events for given event params' do
+RSpec.shared_examples 'tracked issuable snowplow and service ping events with project' do
+ it_behaves_like 'tracked issuable snowplow and service ping events for given event params' do
let(:context) do
Gitlab::Tracking::ServicePingContext
.new(data_source: :redis_hll, event: event_property)
.to_h
end
- let(:track_params) { { project: project } }
- let(:event_params) { track_params.merge(label: event_label, property: event_property, namespace: project.namespace, context: [context]) }
+ let(:track_params) { original_params || { project: project } }
+ let(:event_params) { { project: project }.merge(label: event_label, property: event_property, namespace: project.namespace, context: [context]) }
end
end
-RSpec.shared_examples 'a daily tracked issuable snowplow and service ping events with namespace' do
- it_behaves_like 'a daily tracked issuable snowplow and service ping events for given event params' do
+RSpec.shared_examples 'tracked issuable snowplow and service ping events with namespace' do
+ it_behaves_like 'tracked issuable snowplow and service ping events for given event params' do
let(:context) do
Gitlab::Tracking::ServicePingContext
.new(data_source: :redis_hll, event: event_property)
diff --git a/spec/support/shared_examples/lib/gitlab/utils/username_and_email_generator_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/utils/username_and_email_generator_shared_examples.rb
new file mode 100644
index 00000000000..a42d1450e4d
--- /dev/null
+++ b/spec/support/shared_examples/lib/gitlab/utils/username_and_email_generator_shared_examples.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'username and email pair is generated by Gitlab::Utils::UsernameAndEmailGenerator' do
+ let(:randomhex) { 'randomhex' }
+
+ it 'check email domain' do
+ expect(subject.email).to end_with("@#{email_domain}")
+ end
+
+ it 'contains SecureRandom part' do
+ allow(SecureRandom).to receive(:hex).at_least(:once).and_return(randomhex)
+
+ expect(subject.username).to include("_#{randomhex}")
+ expect(subject.email).to include("_#{randomhex}@")
+ end
+
+ it 'email name is the same as username' do
+ expect(subject.email).to include("#{subject.username}@")
+ end
+
+ context 'when conflicts' do
+ let(:reserved_username) { "#{username_prefix}_#{randomhex}" }
+ let(:reserved_email) { "#{reserved_username}@#{email_domain}" }
+
+ shared_examples 'uniquifies username and email' do
+ it 'uniquifies username and email' do
+ expect(subject.username).to eq("#{reserved_username}1")
+ expect(subject.email).to include("#{subject.username}@")
+ end
+ end
+
+ context 'when username is reserved' do
+ context 'when username is reserved by user' do
+ before do
+ create(:user, username: reserved_username)
+ allow(SecureRandom).to receive(:hex).at_least(:once).and_return(randomhex)
+ end
+
+ include_examples 'uniquifies username and email'
+ end
+
+ context 'when it conflicts with top-level group namespace' do
+ before do
+ create(:group, path: reserved_username)
+ allow(SecureRandom).to receive(:hex).at_least(:once).and_return(randomhex)
+ end
+
+ include_examples 'uniquifies username and email'
+ end
+
+ context 'when it conflicts with top-level group namespace that includes upcased characters' do
+ before do
+ create(:group, path: reserved_username.upcase)
+ allow(SecureRandom).to receive(:hex).at_least(:once).and_return(randomhex)
+ end
+
+ include_examples 'uniquifies username and email'
+ end
+ end
+
+ context 'when email is reserved' do
+ context 'when it conflicts with confirmed primary email' do
+ before do
+ create(:user, email: reserved_email)
+ allow(SecureRandom).to receive(:hex).at_least(:once).and_return(randomhex)
+ end
+
+ include_examples 'uniquifies username and email'
+ end
+
+ context 'when it conflicts with unconfirmed primary email' do
+ before do
+ create(:user, :unconfirmed, email: reserved_email)
+ allow(SecureRandom).to receive(:hex).at_least(:once).and_return(randomhex)
+ end
+
+ include_examples 'uniquifies username and email'
+ end
+
+ context 'when it conflicts with confirmed secondary email' do
+ before do
+ create(:email, :confirmed, email: reserved_email)
+ allow(SecureRandom).to receive(:hex).at_least(:once).and_return(randomhex)
+ end
+
+ include_examples 'uniquifies username and email'
+ end
+ end
+
+ context 'when email and username is reserved' do
+ before do
+ create(:user, email: reserved_email)
+ create(:user, username: "#{reserved_username}1")
+ allow(SecureRandom).to receive(:hex).at_least(:once).and_return(randomhex)
+ end
+
+ it 'uniquifies username and email' do
+ expect(subject.username).to eq("#{reserved_username}2")
+
+ expect(subject.email).to include("#{subject.username}@")
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/menus_shared_examples.rb b/spec/support/shared_examples/lib/menus_shared_examples.rb
index 2c2cb362b07..0aa98517444 100644
--- a/spec/support/shared_examples/lib/menus_shared_examples.rb
+++ b/spec/support/shared_examples/lib/menus_shared_examples.rb
@@ -37,3 +37,58 @@ RSpec.shared_examples_for 'pill_count formatted results' do
expect(pill_count).to eq('112.6k')
end
end
+
+RSpec.shared_examples_for 'serializable as super_sidebar_menu_args' do
+ let(:extra_attrs) { raise NotImplementedError }
+
+ it 'returns hash with provided attributes' do
+ expect(menu.serialize_as_menu_item_args).to eq({
+ title: menu.title,
+ link: menu.link,
+ active_routes: menu.active_routes,
+ container_html_options: menu.container_html_options,
+ **extra_attrs
+ })
+ end
+
+ it 'returns hash with an item_id' do
+ expect(menu.serialize_as_menu_item_args[:item_id]).not_to be_nil
+ end
+end
+
+RSpec.shared_examples_for 'not serializable as super_sidebar_menu_args' do
+ it 'returns nil' do
+ expect(menu.serialize_as_menu_item_args).to be_nil
+ end
+end
+
+RSpec.shared_examples_for 'a panel with uniquely identifiable menu items' do
+ let(:menu_items) do
+ subject.instance_variable_get(:@menus)
+ .flat_map { |menu| menu.instance_variable_get(:@items) }
+ end
+
+ it 'all menu_items have unique item_id' do
+ duplicated_ids = menu_items.group_by(&:item_id).reject { |_, v| (v.size < 2) }
+
+ expect(duplicated_ids).to eq({})
+ end
+
+ it 'all menu_items have an item_id' do
+ items_with_nil_id = menu_items.select { |item| item.item_id.nil? }
+
+ expect(items_with_nil_id).to match_array([])
+ end
+end
+
+RSpec.shared_examples_for 'a panel with all menu_items categorized' do
+ let(:uncategorized_menu) do
+ subject.instance_variable_get(:@menus)
+ .find { |menu| menu.instance_of?(::Sidebars::UncategorizedMenu) }
+ end
+
+ it 'has no uncategorized menu_items' do
+ uncategorized_menu_items = uncategorized_menu.instance_variable_get(:@items)
+ expect(uncategorized_menu_items).to eq([])
+ end
+end
diff --git a/spec/support/shared_examples/lib/sentry/client_shared_examples.rb b/spec/support/shared_examples/lib/sentry/client_shared_examples.rb
index e0b411e1e2a..fa3e9bf5340 100644
--- a/spec/support/shared_examples/lib/sentry/client_shared_examples.rb
+++ b/spec/support/shared_examples/lib/sentry/client_shared_examples.rb
@@ -90,7 +90,9 @@ RSpec.shared_examples 'Sentry API response size limit' do
end
it 'raises an exception when response is too large' do
- expect { subject }.to raise_error(ErrorTracking::SentryClient::ResponseInvalidSizeError,
- 'Sentry API response is too big. Limit is 1 MB.')
+ expect { subject }.to raise_error(
+ ErrorTracking::SentryClient::ResponseInvalidSizeError,
+ 'Sentry API response is too big. Limit is 1 MB.'
+ )
end
end
diff --git a/spec/support/shared_examples/lib/sidebars/admin/menus/admin_menus_shared_examples.rb b/spec/support/shared_examples/lib/sidebars/admin/menus/admin_menus_shared_examples.rb
new file mode 100644
index 00000000000..f913c6b8a9e
--- /dev/null
+++ b/spec/support/shared_examples/lib/sidebars/admin/menus/admin_menus_shared_examples.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'Admin menu' do |link:, title:, icon:, separated: false|
+ let_it_be(:user) { build(:user, :admin) }
+
+ before do
+ allow(user).to receive(:can_admin_all_resources?).and_return(true)
+ end
+
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'renders the correct link' do
+ expect(subject.link).to match link
+ end
+
+ it 'renders the correct title' do
+ expect(subject.title).to eq title
+ end
+
+ it 'renders the correct icon' do
+ expect(subject.sprite_icon).to be icon
+ end
+
+ it 'renders the separator if needed' do
+ expect(subject.separated?).to be separated
+ end
+
+ describe '#render?' do
+ context 'when user is admin' do
+ it 'renders' do
+ expect(subject.render?).to be true
+ end
+ end
+
+ context 'when user is not admin' do
+ it 'does not render' do
+ expect(described_class.new(Sidebars::Context.new(current_user: build(:user),
+ container: nil)).render?).to be false
+ end
+ end
+
+ context 'when user is not logged in' do
+ it 'does not render' do
+ expect(described_class.new(Sidebars::Context.new(current_user: nil, container: nil)).render?).to be false
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'Admin menu without sub menus' do |active_routes:|
+ let_it_be(:user) { build(:user, :admin) }
+
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'does not contain any sub menu(s)' do
+ expect(subject.has_items?).to be false
+ end
+
+ it 'defines correct active route' do
+ expect(subject.active_routes).to eq active_routes
+ end
+end
+
+RSpec.shared_examples 'Admin menu with sub menus' do
+ let_it_be(:user) { build(:user, :admin) }
+
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'contains submemus' do
+ expect(subject.has_items?).to be true
+ end
+end
diff --git a/spec/support/shared_examples/lib/sidebars/user_profile/user_profile_menus_shared_examples.rb b/spec/support/shared_examples/lib/sidebars/user_profile/user_profile_menus_shared_examples.rb
new file mode 100644
index 00000000000..5e8aebb4f29
--- /dev/null
+++ b/spec/support/shared_examples/lib/sidebars/user_profile/user_profile_menus_shared_examples.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'User profile menu' do |title:, icon:, active_route:|
+ let_it_be(:current_user) { build(:user) }
+ let_it_be(:user) { build(:user) }
+
+ let(:context) { Sidebars::Context.new(current_user: current_user, container: user) }
+
+ subject { described_class.new(context) }
+
+ it 'does not contain any sub menu' do
+ expect(subject.has_items?).to be false
+ end
+
+ it 'renders the correct link' do
+ expect(subject.link).to match link
+ end
+
+ it 'renders the correct title' do
+ expect(subject.title).to eq title
+ end
+
+ it 'renders the correct icon' do
+ expect(subject.sprite_icon).to eq icon
+ end
+
+ it 'defines correct active route' do
+ expect(subject.active_routes[:path]).to be active_route
+ end
+
+ it 'renders if user is logged in' do
+ expect(subject.render?).to be true
+ end
+
+ [:blocked, :banned].each do |trait|
+ context "when viewed user is #{trait}" do
+ let_it_be(:viewed_user) { build(:user, trait) }
+ let(:context) { Sidebars::Context.new(current_user: user, container: viewed_user) }
+
+ context 'when user is not logged in' do
+ it 'is not allowed to view the menu item' do
+ expect(described_class.new(Sidebars::Context.new(current_user: nil,
+ container: viewed_user)).render?).to be false
+ end
+ end
+
+ context 'when current user has permission' do
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_user_profile, viewed_user).and_return(true)
+ end
+
+ it 'is allowed to view the menu item' do
+ expect(described_class.new(context).render?).to be true
+ end
+ end
+
+ context 'when current user does not have permission' do
+ it 'is not allowed to view the menu item' do
+ expect(described_class.new(context).render?).to be false
+ end
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'Followers/followees counts' do |symbol|
+ let_it_be(:current_user) { build(:user) }
+ let_it_be(:user) { build(:user) }
+
+ let(:context) { Sidebars::Context.new(current_user: current_user, container: user) }
+
+ subject { described_class.new(context) }
+
+ context 'when there are items' do
+ before do
+ allow(user).to receive(symbol).and_return([1, 2])
+ end
+
+ it 'renders the pill' do
+ expect(subject.has_pill?).to be(true)
+ end
+
+ it 'returns the count' do
+ expect(subject.pill_count).to be(2)
+ end
+ end
+
+ context 'when there are no items' do
+ it 'does not render the pill' do
+ expect(subject.has_pill?).to be(false)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/lib/sidebars/user_settings/menus/user_settings_menus_shared_examples.rb b/spec/support/shared_examples/lib/sidebars/user_settings/menus/user_settings_menus_shared_examples.rb
new file mode 100644
index 00000000000..b91386d1935
--- /dev/null
+++ b/spec/support/shared_examples/lib/sidebars/user_settings/menus/user_settings_menus_shared_examples.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'User settings menu' do |link:, title:, icon:, active_routes:|
+ let_it_be(:user) { create(:user) }
+
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ subject { described_class.new(context) }
+
+ it 'does not contain any sub menu' do
+ expect(subject.has_items?).to be false
+ end
+
+ it 'renders the correct link' do
+ expect(subject.link).to match link
+ end
+
+ it 'renders the correct title' do
+ expect(subject.title).to eq title
+ end
+
+ it 'renders the correct icon' do
+ expect(subject.sprite_icon).to be icon
+ end
+
+ it 'defines correct active route' do
+ expect(subject.active_routes).to eq active_routes
+ end
+end
+
+RSpec.shared_examples 'User settings menu #render? method' do
+ describe '#render?' do
+ subject { described_class.new(context) }
+
+ context 'when user is logged in' do
+ let_it_be(:user) { build(:user) }
+ let(:context) { Sidebars::Context.new(current_user: user, container: nil) }
+
+ it 'renders' do
+ expect(subject.render?).to be true
+ end
+ end
+
+ context 'when user is not logged in' do
+ let(:context) { Sidebars::Context.new(current_user: nil, container: nil) }
+
+ it 'does not render' do
+ expect(subject.render?).to be false
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/mailers/export_csv_shared_examples.rb b/spec/support/shared_examples/mailers/export_csv_shared_examples.rb
new file mode 100644
index 00000000000..731d7c810f9
--- /dev/null
+++ b/spec/support/shared_examples/mailers/export_csv_shared_examples.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'export csv email' do |collection_type|
+ include_context 'gitlab email notification'
+
+ it 'attachment has csv mime type' do
+ expect(attachment.mime_type).to eq 'text/csv'
+ end
+
+ it 'generates a useful filename' do
+ expect(attachment.filename).to include(Date.today.year.to_s)
+ expect(attachment.filename).to include(collection_type)
+ expect(attachment.filename).to include('myproject')
+ expect(attachment.filename).to end_with('.csv')
+ end
+
+ it 'mentions number of objects and project name' do
+ expect(subject).to have_content '3'
+ expect(subject).to have_content empty_project.name
+ end
+
+ it "doesn't need to mention truncation by default" do
+ expect(subject).not_to have_content 'truncated'
+ end
+
+ context 'when truncated' do
+ let(:export_status) { { truncated: true, rows_expected: 12, rows_written: 10 } }
+
+ it 'mentions that the csv has been truncated' do
+ expect(subject).to have_content 'truncated'
+ end
+
+ it 'mentions the number of objects written and expected' do
+ expect(subject).to have_content "10 of 12 #{collection_type.humanize.downcase}"
+ end
+ end
+end
diff --git a/spec/support/shared_examples/mailers/notify_shared_examples.rb b/spec/support/shared_examples/mailers/notify_shared_examples.rb
index 2e182fb399d..cf1ab7697ab 100644
--- a/spec/support/shared_examples/mailers/notify_shared_examples.rb
+++ b/spec/support/shared_examples/mailers/notify_shared_examples.rb
@@ -59,7 +59,7 @@ end
RSpec.shared_examples 'an email with X-GitLab headers containing project details' do
it 'has X-GitLab-Project headers' do
aggregate_failures do
- full_path_as_domain = "#{project.name}.#{project.namespace.path}"
+ full_path_as_domain = "#{project.path}.#{project.namespace.path}"
is_expected.to have_header('X-GitLab-Project', /#{project.name}/)
is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/)
is_expected.to have_header('X-GitLab-Project-Path', /#{project.full_path}/)
@@ -294,3 +294,17 @@ RSpec.shared_examples 'does not render a manage notifications link' do
end
end
end
+
+RSpec.shared_examples 'email with default notification reason' do
+ it do
+ is_expected.to have_body_text("You're receiving this email because of your account")
+ is_expected.to have_plain_text_content("You're receiving this email because of your account")
+ end
+end
+
+RSpec.shared_examples 'email with link to issue' do
+ it do
+ is_expected.to have_body_text(%(<a href="#{project_issue_url(project, issue)}">view it on GitLab</a>))
+ is_expected.to have_plain_text_content("view it on GitLab: #{project_issue_url(project, issue)}")
+ end
+end
diff --git a/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb b/spec/support/shared_examples/metrics_instrumentation_shared_examples.rb
index cef9860fe25..cef9860fe25 100644
--- a/spec/support/gitlab/usage/metrics_instrumentation_shared_examples.rb
+++ b/spec/support/shared_examples/metrics_instrumentation_shared_examples.rb
diff --git a/spec/support/shared_examples/migrations/add_work_item_widget_shared_examples.rb b/spec/support/shared_examples/migrations/add_work_item_widget_shared_examples.rb
new file mode 100644
index 00000000000..28eac52256f
--- /dev/null
+++ b/spec/support/shared_examples/migrations/add_work_item_widget_shared_examples.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'migration that adds widget to work items definitions' do |widget_name:|
+ let(:migration) { described_class.new }
+ let(:work_item_definitions) { table(:work_item_widget_definitions) }
+
+ describe '#up' do
+ it "creates widget definition in all types" do
+ work_item_definitions.where(name: widget_name).delete_all
+
+ expect { migrate! }.to change { work_item_definitions.count }.by(7)
+ expect(work_item_definitions.all.pluck(:name)).to include(widget_name)
+ end
+
+ it 'logs a warning if the type is missing' do
+ allow(described_class::WorkItemType).to receive(:find_by_name_and_namespace_id).and_call_original
+ allow(described_class::WorkItemType).to receive(:find_by_name_and_namespace_id)
+ .with('Issue', nil).and_return(nil)
+
+ expect(Gitlab::AppLogger).to receive(:warn).with('type Issue is missing, not adding widget')
+ migrate!
+ end
+ end
+
+ describe '#down' do
+ it "removes definitions for widget" do
+ migrate!
+
+ expect { migration.down }.to change { work_item_definitions.count }.by(-7)
+ expect(work_item_definitions.all.pluck(:name)).not_to include(widget_name)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/active_record_enum_shared_examples.rb b/spec/support/shared_examples/models/active_record_enum_shared_examples.rb
index 3d765b6ca93..10f3263d4fc 100644
--- a/spec/support/shared_examples/models/active_record_enum_shared_examples.rb
+++ b/spec/support/shared_examples/models/active_record_enum_shared_examples.rb
@@ -10,3 +10,13 @@ RSpec.shared_examples 'having unique enum values' do
end
end
end
+
+RSpec.shared_examples 'having enum with nil value' do
+ it 'has enum with nil value' do
+ subject.public_send("#{attr_value}!")
+
+ expect(subject.public_send("#{attr}_for_database")).to be_nil
+ expect(subject.public_send("#{attr}?")).to eq(true)
+ expect(subject.class.public_send(attr_value)).to eq([subject])
+ end
+end
diff --git a/spec/support/shared_examples/models/chat_integration_shared_examples.rb b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
index 085fec6ff1e..addd37cde32 100644
--- a/spec/support/shared_examples/models/chat_integration_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
@@ -221,11 +221,13 @@ RSpec.shared_examples "chat integration" do |integration_name|
context "with commit comment" do
let_it_be(:note) do
- create(:note_on_commit,
- author: user,
- project: project,
- commit_id: project.repository.commit.id,
- note: "a comment on a commit")
+ create(
+ :note_on_commit,
+ author: user,
+ project: project,
+ commit_id: project.repository.commit.id,
+ note: "a comment on a commit"
+ )
end
it_behaves_like "triggered #{integration_name} integration"
@@ -261,9 +263,11 @@ RSpec.shared_examples "chat integration" do |integration_name|
context "with failed pipeline" do
let_it_be(:pipeline) do
- create(:ci_pipeline,
- project: project, status: "failed",
- sha: project.commit.sha, ref: project.default_branch)
+ create(
+ :ci_pipeline,
+ project: project, status: "failed",
+ sha: project.commit.sha, ref: project.default_branch
+ )
end
it_behaves_like "triggered #{integration_name} integration"
@@ -271,9 +275,11 @@ RSpec.shared_examples "chat integration" do |integration_name|
context "with succeeded pipeline" do
let_it_be(:pipeline) do
- create(:ci_pipeline,
- project: project, status: "success",
- sha: project.commit.sha, ref: project.default_branch)
+ create(
+ :ci_pipeline,
+ project: project, status: "success",
+ sha: project.commit.sha, ref: project.default_branch
+ )
end
context "with default notify_only_broken_pipelines" do
diff --git a/spec/support/shared_examples/models/ci/token_format_shared_examples.rb b/spec/support/shared_examples/models/ci/token_format_shared_examples.rb
new file mode 100644
index 00000000000..0272982e2d0
--- /dev/null
+++ b/spec/support/shared_examples/models/ci/token_format_shared_examples.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples_for 'ensures runners_token is prefixed' do |factory|
+ subject(:record) { FactoryBot.build(factory) }
+
+ describe '#runners_token', feature_category: :system_access do
+ let(:runners_prefix) { RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX }
+
+ it 'generates runners_token which starts with runner prefix' do
+ expect(record.runners_token).to match(a_string_starting_with(runners_prefix))
+ end
+
+ context 'when record has an invalid token' do
+ subject(:record) { FactoryBot.build(factory, runners_token: invalid_runners_token) }
+
+ let(:invalid_runners_token) { "not_start_with_runners_prefix" }
+
+ it 'generates runners_token which starts with runner prefix' do
+ expect(record.runners_token).to match(a_string_starting_with(runners_prefix))
+ end
+
+ it 'changes the attribute values for runners_token and runners_token_encrypted' do
+ expect { record.runners_token }
+ .to change { record[:runners_token] }.from(invalid_runners_token).to(nil)
+ .and change { record[:runners_token_encrypted] }.from(nil)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/clusters/prometheus_client_shared.rb b/spec/support/shared_examples/models/clusters/prometheus_client_shared.rb
index 8d6dcfef925..140968da272 100644
--- a/spec/support/shared_examples/models/clusters/prometheus_client_shared.rb
+++ b/spec/support/shared_examples/models/clusters/prometheus_client_shared.rb
@@ -41,10 +41,12 @@ RSpec.shared_examples '#prometheus_client shared' do
subject.cluster.platform_kubernetes.namespace = 'a-namespace'
stub_kubeclient_discover(cluster.platform_kubernetes.api_url)
- create(:cluster_kubernetes_namespace,
- cluster: cluster,
- cluster_project: cluster.cluster_project,
- project: cluster.cluster_project.project)
+ create(
+ :cluster_kubernetes_namespace,
+ cluster: cluster,
+ cluster_project: cluster.cluster_project,
+ project: cluster.cluster_project.project
+ )
end
it 'creates proxy prometheus_client' do
diff --git a/spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb b/spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb
index 122774a9028..a196b63585c 100644
--- a/spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/auto_disabling_hooks_shared_examples.rb
@@ -17,8 +17,12 @@ RSpec.shared_examples 'a hook that gets automatically disabled on failure' do
[4, 1.second.from_now], # Exceeded the grace period, set by #backoff!
[4, Time.current] # Exceeded the grace period, set by #backoff!, edge-case
].map do |(recent_failures, disabled_until)|
- create(hook_factory, **default_factory_arguments, recent_failures: recent_failures,
-disabled_until: disabled_until)
+ create(
+ hook_factory,
+ **default_factory_arguments,
+ recent_failures: recent_failures,
+ disabled_until: disabled_until
+ )
end
end
@@ -45,8 +49,12 @@ disabled_until: disabled_until)
[0, suspended],
[0, expired]
].map do |(recent_failures, disabled_until)|
- create(hook_factory, **default_factory_arguments, recent_failures: recent_failures,
-disabled_until: disabled_until)
+ create(
+ hook_factory,
+ **default_factory_arguments,
+ recent_failures: recent_failures,
+ disabled_until: disabled_until
+ )
end
end
@@ -61,6 +69,20 @@ disabled_until: disabled_until)
# Nothing is missing
expect(find_hooks.executable.to_a + find_hooks.disabled.to_a).to match_array(find_hooks.to_a)
end
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it 'causes all hooks to be considered executable' do
+ expect(find_hooks.executable.count).to eq(16)
+ end
+
+ it 'causes no hooks to be considered disabled' do
+ expect(find_hooks.disabled).to be_empty
+ end
+ end
end
describe '#executable?', :freeze_time do
@@ -108,6 +130,16 @@ disabled_until: disabled_until)
it 'has the correct state' do
expect(web_hook.executable?).to eq(executable)
end
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it 'is always executable' do
+ expect(web_hook).to be_executable
+ end
+ end
end
end
@@ -151,7 +183,7 @@ disabled_until: disabled_until)
context 'when we have exhausted the grace period' do
before do
- hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD)
+ hook.update!(recent_failures: WebHooks::AutoDisabling::FAILURE_THRESHOLD)
end
context 'when the hook is permanently disabled' do
@@ -172,6 +204,16 @@ disabled_until: disabled_until)
def run_expectation
expect { hook.backoff! }.to change { hook.backoff_count }.by(1)
end
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it 'does not increment backoff count' do
+ expect { hook.failed! }.not_to change { hook.backoff_count }
+ end
+ end
end
end
end
@@ -181,6 +223,16 @@ disabled_until: disabled_until)
def run_expectation
expect { hook.failed! }.to change { hook.recent_failures }.by(1)
end
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it 'does not increment recent failure count' do
+ expect { hook.failed! }.not_to change { hook.recent_failures }
+ end
+ end
end
end
@@ -189,6 +241,16 @@ disabled_until: disabled_until)
expect { hook.disable! }.to change { hook.executable? }.from(true).to(false)
end
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it 'does not disable the hook' do
+ expect { hook.disable! }.not_to change { hook.executable? }
+ end
+ end
+
it 'does nothing if the hook is already disabled' do
allow(hook).to receive(:permanently_disabled?).and_return(true)
@@ -210,7 +272,7 @@ disabled_until: disabled_until)
end
it 'allows FAILURE_THRESHOLD initial failures before we back-off' do
- WebHook::FAILURE_THRESHOLD.times do
+ WebHooks::AutoDisabling::FAILURE_THRESHOLD.times do
hook.backoff!
expect(hook).not_to be_temporarily_disabled
end
@@ -221,13 +283,23 @@ disabled_until: disabled_until)
context 'when hook has been told to back off' do
before do
- hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD)
+ hook.update!(recent_failures: WebHooks::AutoDisabling::FAILURE_THRESHOLD)
hook.backoff!
end
it 'is true' do
expect(hook).to be_temporarily_disabled
end
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it 'is false' do
+ expect(hook).not_to be_temporarily_disabled
+ end
+ end
end
end
@@ -244,6 +316,16 @@ disabled_until: disabled_until)
it 'is true' do
expect(hook).to be_permanently_disabled
end
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it 'is false' do
+ expect(hook).not_to be_permanently_disabled
+ end
+ end
end
end
@@ -258,15 +340,31 @@ disabled_until: disabled_until)
end
it { is_expected.to eq :disabled }
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it { is_expected.to eq(:executable) }
+ end
end
context 'when hook has been backed off' do
before do
- hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD + 1)
+ hook.update!(recent_failures: WebHooks::AutoDisabling::FAILURE_THRESHOLD + 1)
hook.disabled_until = 1.hour.from_now
end
it { is_expected.to eq :temporarily_disabled }
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(auto_disabling_web_hooks: false)
+ end
+
+ it { is_expected.to eq(:executable) }
+ end
end
end
end
diff --git a/spec/support/shared_examples/models/concerns/cascading_namespace_setting_shared_examples.rb b/spec/support/shared_examples/models/concerns/cascading_namespace_setting_shared_examples.rb
index a4db4e25db3..c51e4999e81 100644
--- a/spec/support/shared_examples/models/concerns/cascading_namespace_setting_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/cascading_namespace_setting_shared_examples.rb
@@ -112,9 +112,10 @@ RSpec.shared_examples 'a cascading namespace setting boolean attribute' do
it 'does not allow the local value to be saved' do
subgroup_settings.send("#{settings_attribute_name}=", nil)
- expect { subgroup_settings.save! }
- .to raise_error(ActiveRecord::RecordInvalid,
- /cannot be changed because it is locked by an ancestor/)
+ expect { subgroup_settings.save! }.to raise_error(
+ ActiveRecord::RecordInvalid,
+ /cannot be changed because it is locked by an ancestor/
+ )
end
end
@@ -171,15 +172,59 @@ RSpec.shared_examples 'a cascading namespace setting boolean attribute' do
end
describe "##{settings_attribute_name}=" do
- before do
- subgroup_settings.update!(settings_attribute_name => nil)
- group_settings.update!(settings_attribute_name => true)
+ using RSpec::Parameterized::TableSyntax
+
+ where(:parent_value, :current_subgroup_value, :new_subgroup_value, :expected_subgroup_value_after_update) do
+ true | nil | true | nil
+ true | nil | "true" | nil
+ true | false | true | true
+ true | false | "true" | true
+ true | true | false | false
+ true | true | "false" | false
+ false | nil | false | nil
+ false | nil | true | true
+ false | true | false | false
+ false | false | true | true
end
- it 'does not save the value locally when it matches the cascaded value' do
- subgroup_settings.update!(settings_attribute_name => true)
+ with_them do
+ before do
+ subgroup_settings.update!(settings_attribute_name => current_subgroup_value)
+ group_settings.update!(settings_attribute_name => parent_value)
+ end
- expect(subgroup_settings.read_attribute(settings_attribute_name)).to eq(nil)
+ it 'validates starting values from before block', :aggregate_failures do
+ expect(group_settings.reload.read_attribute(settings_attribute_name)).to eq(parent_value)
+ expect(subgroup_settings.reload.read_attribute(settings_attribute_name)).to eq(current_subgroup_value)
+ end
+
+ it 'does not save the value locally when it matches cascaded value', :aggregate_failures do
+ subgroup_settings.send("#{settings_attribute_name}=", new_subgroup_value)
+
+ # Verify dirty value
+ expect(subgroup_settings.read_attribute(settings_attribute_name)).to eq(expected_subgroup_value_after_update)
+
+ subgroup_settings.save!
+
+ # Verify persisted value
+ expect(subgroup_settings.reload.read_attribute(settings_attribute_name))
+ .to eq(expected_subgroup_value_after_update)
+ end
+
+ context 'when mass assigned' do
+ before do
+ subgroup_settings.attributes =
+ { settings_attribute_name => new_subgroup_value, "lock_#{settings_attribute_name}" => false }
+ end
+
+ it 'does not save the value locally when it matches cascaded value', :aggregate_failures do
+ subgroup_settings.save!
+
+ # Verify persisted value
+ expect(subgroup_settings.reload.read_attribute(settings_attribute_name))
+ .to eq(expected_subgroup_value_after_update)
+ end
+ end
end
end
@@ -277,9 +322,10 @@ RSpec.shared_examples 'a cascading namespace setting boolean attribute' do
it 'does not allow the attribute to be saved' do
subgroup_settings.send("lock_#{settings_attribute_name}=", true)
- expect { subgroup_settings.save! }
- .to raise_error(ActiveRecord::RecordInvalid,
- /cannot be changed because it is locked by an ancestor/)
+ expect { subgroup_settings.save! }.to raise_error(
+ ActiveRecord::RecordInvalid,
+ /cannot be changed because it is locked by an ancestor/
+ )
end
end
@@ -299,9 +345,10 @@ RSpec.shared_examples 'a cascading namespace setting boolean attribute' do
it 'does not allow the lock to be saved when the attribute is nil' do
subgroup_settings.send("#{settings_attribute_name}=", nil)
- expect { subgroup_settings.save! }
- .to raise_error(ActiveRecord::RecordInvalid,
- /cannot be nil when locking the attribute/)
+ expect { subgroup_settings.save! }.to raise_error(
+ ActiveRecord::RecordInvalid,
+ /cannot be nil when locking the attribute/
+ )
end
it 'copies the cascaded value when locking the attribute if the local value is nil', :aggregate_failures do
@@ -320,9 +367,10 @@ RSpec.shared_examples 'a cascading namespace setting boolean attribute' do
it 'does not allow the attribute to be saved' do
subgroup_settings.send("lock_#{settings_attribute_name}=", true)
- expect { subgroup_settings.save! }
- .to raise_error(ActiveRecord::RecordInvalid,
- /cannot be changed because it is locked by an ancestor/)
+ expect { subgroup_settings.save! }.to raise_error(
+ ActiveRecord::RecordInvalid,
+ /cannot be changed because it is locked by an ancestor/
+ )
end
end
diff --git a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
index 5755b9a56b1..9d189842b28 100644
--- a/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb
@@ -17,6 +17,11 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
let(:amount) { 10 }
let(:increment) { Gitlab::Counters::Increment.new(amount: amount, ref: 3) }
let(:counter_key) { model.counter(attribute).key }
+ let(:returns_current) do
+ model.class.counter_attributes
+ .find { |a| a[:attribute] == attribute }
+ .fetch(:returns_current, false)
+ end
subject { model.increment_counter(attribute, increment) }
@@ -61,6 +66,33 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
end
end
+ describe '#increment_amount' do
+ it 'increases the egress in cache' do
+ model.increment_amount(attribute, 3)
+
+ expect(model.counter(attribute).get).to eq(3)
+ end
+ end
+
+ describe '#current_counter' do
+ let(:data_transfer_node) do
+ args = { project: project }
+ args[attribute] = 2
+ create(:project_data_transfer, **args)
+ end
+
+ it 'increases the amount in cache' do
+ if returns_current
+ incremented_by = 4
+ db_state = model.read_attribute(attribute)
+
+ model.send("increment_#{attribute}".to_sym, incremented_by)
+
+ expect(model.send(attribute)).to eq(db_state + incremented_by)
+ end
+ end
+ end
+
context 'when increment amount is 0' do
let(:amount) { 0 }
@@ -155,14 +187,24 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
end
describe '#update_counters_with_lease' do
- let(:increments) { { build_artifacts_size: 1, packages_size: 2 } }
+ let_it_be(:first_attribute) { counter_attributes.first }
+ let_it_be(:second_attribute) { counter_attributes.second }
+
+ let_it_be(:increments) do
+ increments_hash = {}
+
+ increments_hash[first_attribute] = 1
+ increments_hash[second_attribute] = 2
+
+ increments_hash
+ end
subject { model.update_counters_with_lease(increments) }
it 'updates counters of the record' do
expect { subject }
- .to change { model.reload.build_artifacts_size }.by(1)
- .and change { model.reload.packages_size }.by(2)
+ .to change { model.reload.send(first_attribute) }.by(1)
+ .and change { model.reload.send(second_attribute) }.by(2)
end
it_behaves_like 'obtaining lease to update database' do
@@ -193,17 +235,4 @@ RSpec.shared_examples 'obtaining lease to update database' do
expect { subject }.not_to raise_error
end
end
-
- context 'when feature flag counter_attribute_db_lease_for_update is disabled' do
- before do
- stub_feature_flags(counter_attribute_db_lease_for_update: false)
- allow(model).to receive(:in_lock).and_call_original
- end
-
- it 'does not attempt to get a lock' do
- expect(model).not_to receive(:in_lock)
-
- subject
- end
- end
end
diff --git a/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
index 2e528f7996c..2dad35dc46e 100644
--- a/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/integrations/base_slack_notification_shared_examples.rb
@@ -35,7 +35,6 @@ RSpec.shared_examples Integrations::BaseSlackNotification do |factory:|
end
it_behaves_like 'Snowplow event tracking with RedisHLL context' do
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
let(:category) { described_class.to_s }
let(:action) { 'perform_integrations_action' }
let(:namespace) { project.namespace }
diff --git a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
index 0ef9ab25505..28d2d4f1597 100644
--- a/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/integrations/slack_mattermost_notifier_shared_examples.rb
@@ -465,10 +465,13 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
context 'when commit comment event executed' do
let(:commit_note) do
- create(:note_on_commit, author: user,
- project: project,
- commit_id: project.repository.commit.id,
- note: 'a comment on a commit')
+ create(
+ :note_on_commit,
+ author: user,
+ project: project,
+ commit_id: project.repository.commit.id,
+ note: 'a comment on a commit'
+ )
end
let(:data) do
@@ -480,8 +483,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
context 'when merge request comment event executed' do
let(:merge_request_note) do
- create(:note_on_merge_request, project: project,
- note: 'a comment on a merge request')
+ create(:note_on_merge_request, project: project, note: 'a comment on a merge request')
end
let(:data) do
@@ -493,8 +495,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
context 'when issue comment event executed' do
let(:issue_note) do
- create(:note_on_issue, project: project,
- note: 'a comment on an issue')
+ create(:note_on_issue, project: project, note: 'a comment on an issue')
end
let(:data) do
@@ -506,8 +507,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
context 'when snippet comment event executed' do
let(:snippet_note) do
- create(:note_on_project_snippet, project: project,
- note: 'a comment on a snippet')
+ create(:note_on_project_snippet, project: project, note: 'a comment on a snippet')
end
let(:data) do
@@ -522,9 +522,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
let_it_be(:user) { create(:user) }
let_it_be_with_refind(:project) { create(:project, :repository, creator: user) }
let(:pipeline) do
- create(:ci_pipeline,
- project: project, status: status,
- sha: project.commit.sha, ref: project.default_branch)
+ create(:ci_pipeline, project: project, status: status, sha: project.commit.sha, ref: project.default_branch)
end
before do
@@ -557,9 +555,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
context 'with failed pipeline' do
context 'on default branch' do
let(:pipeline) do
- create(:ci_pipeline,
- project: project, status: :failed,
- sha: project.commit.sha, ref: project.default_branch)
+ create(:ci_pipeline, project: project, status: :failed, sha: project.commit.sha, ref: project.default_branch)
end
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
@@ -587,9 +583,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
end
let(:pipeline) do
- create(:ci_pipeline,
- project: project, status: :failed,
- sha: project.commit.sha, ref: 'a-protected-branch')
+ create(:ci_pipeline, project: project, status: :failed, sha: project.commit.sha, ref: 'a-protected-branch')
end
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
@@ -617,9 +611,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
end
let(:pipeline) do
- create(:ci_pipeline,
- project: project, status: :failed,
- sha: project.commit.sha, ref: '1-stable')
+ create(:ci_pipeline, project: project, status: :failed, sha: project.commit.sha, ref: '1-stable')
end
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
@@ -643,9 +635,7 @@ RSpec.shared_examples Integrations::SlackMattermostNotifier do |integration_name
context 'on a neither protected nor default branch' do
let(:pipeline) do
- create(:ci_pipeline,
- project: project, status: :failed,
- sha: project.commit.sha, ref: 'a-random-branch')
+ create(:ci_pipeline, project: project, status: :failed, sha: project.commit.sha, ref: 'a-random-branch')
end
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
diff --git a/spec/support/shared_examples/models/concerns/protected_branch_access_examples.rb b/spec/support/shared_examples/models/concerns/protected_branch_access_examples.rb
new file mode 100644
index 00000000000..dd27ff3844f
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/protected_branch_access_examples.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'protected branch access' do
+ include_examples 'protected ref access', :protected_branch
+
+ it { is_expected.to belong_to(:protected_branch) }
+
+ describe '#project' do
+ before do
+ allow(protected_ref).to receive(:project)
+ end
+
+ it 'delegates project to protected_branch association' do
+ described_class.new(protected_branch: protected_ref).project
+
+ expect(protected_ref).to have_received(:project)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/protected_ref_access_allowed_access_levels_examples.rb b/spec/support/shared_examples/models/concerns/protected_ref_access_allowed_access_levels_examples.rb
new file mode 100644
index 00000000000..8e15720c79a
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/protected_ref_access_allowed_access_levels_examples.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'protected ref access allowed_access_levels' do |excludes: []|
+ describe '::allowed_access_levels' do
+ subject { described_class.allowed_access_levels }
+
+ let(:all_levels) do
+ [
+ Gitlab::Access::DEVELOPER,
+ Gitlab::Access::MAINTAINER,
+ Gitlab::Access::ADMIN,
+ Gitlab::Access::NO_ACCESS
+ ]
+ end
+
+ context 'when running on Gitlab.com?' do
+ let(:levels) { all_levels.excluding(Gitlab::Access::ADMIN, *excludes) }
+
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ end
+
+ it { is_expected.to match_array(levels) }
+ end
+
+ context 'when self hosted?' do
+ let(:levels) { all_levels.excluding(*excludes) }
+
+ before do
+ allow(Gitlab).to receive(:com?).and_return(false)
+ end
+
+ it { is_expected.to match_array(levels) }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/protected_ref_access_examples.rb b/spec/support/shared_examples/models/concerns/protected_ref_access_examples.rb
new file mode 100644
index 00000000000..4753d7a4556
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/protected_ref_access_examples.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'protected ref access' do |association|
+ include ExternalAuthorizationServiceHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:protected_ref) { create(association, project: project) } # rubocop:disable Rails/SaveBang
+
+ it { is_expected.to validate_inclusion_of(:access_level).in_array(described_class.allowed_access_levels) }
+
+ it { is_expected.to validate_presence_of(:access_level) }
+
+ context 'when not role?' do
+ before do
+ allow(subject).to receive(:role?).and_return(false)
+ end
+
+ it { is_expected.not_to validate_presence_of(:access_level) }
+ end
+
+ describe '::human_access_levels' do
+ subject { described_class.human_access_levels }
+
+ let(:levels) do
+ {
+ Gitlab::Access::DEVELOPER => "Developers + Maintainers",
+ Gitlab::Access::MAINTAINER => "Maintainers",
+ Gitlab::Access::ADMIN => 'Instance admins',
+ Gitlab::Access::NO_ACCESS => "No one"
+ }.slice(*described_class.allowed_access_levels)
+ end
+
+ it { is_expected.to eq(levels) }
+ end
+
+ describe '#check_access' do
+ let_it_be(:current_user) { create(:user) }
+
+ let(:access_level) { ::Gitlab::Access::DEVELOPER }
+
+ before_all do
+ project.add_developer(current_user)
+ end
+
+ subject do
+ described_class.new(
+ association => protected_ref,
+ access_level: access_level
+ )
+ end
+
+ context 'when current_user is nil' do
+ it { expect(subject.check_access(nil)).to eq(false) }
+ end
+
+ context 'when access_level is NO_ACCESS' do
+ let(:access_level) { ::Gitlab::Access::NO_ACCESS }
+
+ it { expect(subject.check_access(current_user)).to eq(false) }
+ end
+
+ context 'when instance admin access is configured' do
+ let(:access_level) { Gitlab::Access::ADMIN }
+
+ context 'when current_user is a maintainer' do
+ it { expect(subject.check_access(current_user)).to eq(false) }
+ end
+
+ context 'when current_user is admin' do
+ before do
+ allow(current_user).to receive(:admin?).and_return(true)
+ end
+
+ it { expect(subject.check_access(current_user)).to eq(true) }
+ end
+ end
+
+ context 'when current_user can push_code to project' do
+ context 'and member access is high enough' do
+ it { expect(subject.check_access(current_user)).to eq(true) }
+
+ context 'when external authorization denies access' do
+ before do
+ external_service_deny_access(current_user, project)
+ end
+
+ it { expect(subject.check_access(current_user)).to be_falsey }
+ end
+ end
+
+ context 'and member access is too low' do
+ let(:access_level) { ::Gitlab::Access::MAINTAINER }
+
+ it { expect(subject.check_access(current_user)).to eq(false) }
+ end
+ end
+
+ context 'when current_user cannot push_code to project' do
+ before do
+ allow(current_user).to receive(:can?).with(:push_code, project).and_return(false)
+ end
+
+ it { expect(subject.check_access(current_user)).to eq(false) }
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/protected_tag_access_examples.rb b/spec/support/shared_examples/models/concerns/protected_tag_access_examples.rb
new file mode 100644
index 00000000000..49f616d5a59
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/protected_tag_access_examples.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'protected tag access' do
+ include_examples 'protected ref access', :protected_tag
+
+ let_it_be(:protected_tag) { create(:protected_tag) }
+
+ it { is_expected.to belong_to(:protected_tag) }
+
+ describe '#project' do
+ before do
+ allow(protected_tag).to receive(:project)
+ end
+
+ it 'delegates project to protected_tag association' do
+ described_class.new(protected_tag: protected_tag).project
+
+ expect(protected_tag).to have_received(:project)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb b/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
index e4958779957..b04ac40b309 100644
--- a/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
@@ -84,9 +84,12 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
let(:max_date) { mid_point + 10.days }
def box(from, to)
- create(factory, *timebox_args,
- start_date: from || open_on_left,
- due_date: to || open_on_right)
+ create(
+ factory,
+ *timebox_args,
+ start_date: from || open_on_left,
+ due_date: to || open_on_right
+ )
end
it 'can find overlapping timeboxes' do
diff --git a/spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb b/spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb
index 848840ee297..f98528ffedc 100644
--- a/spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/unstoppable_hooks_shared_examples.rb
@@ -18,8 +18,12 @@ RSpec.shared_examples 'a hook that does not get automatically disabled on failur
[3, nil],
[3, 1.day.ago]
].map do |(recent_failures, disabled_until)|
- create(hook_factory, **default_factory_arguments, recent_failures: recent_failures,
-disabled_until: disabled_until)
+ create(
+ hook_factory,
+ **default_factory_arguments,
+ recent_failures: recent_failures,
+ disabled_until: disabled_until
+ )
end
end
@@ -110,7 +114,7 @@ disabled_until: disabled_until)
context 'when we have exhausted the grace period' do
before do
- hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD)
+ hook.update!(recent_failures: WebHooks::AutoDisabling::FAILURE_THRESHOLD)
end
it 'does not disable the hook' do
@@ -131,7 +135,7 @@ disabled_until: disabled_until)
expect(hook).not_to be_temporarily_disabled
# Backing off
- WebHook::FAILURE_THRESHOLD.times do
+ WebHooks::AutoDisabling::FAILURE_THRESHOLD.times do
hook.backoff!
expect(hook).not_to be_temporarily_disabled
end
@@ -167,7 +171,7 @@ disabled_until: disabled_until)
context 'when hook has been backed off' do
before do
- hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD + 1)
+ hook.update!(recent_failures: WebHooks::AutoDisabling::FAILURE_THRESHOLD + 1)
hook.disabled_until = 1.hour.from_now
end
diff --git a/spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb b/spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb
index cd6eb8c77fa..113dcc266fc 100644
--- a/spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/web_hooks/has_web_hooks_shared_examples.rb
@@ -19,7 +19,7 @@ RSpec.shared_examples 'something that has web-hooks' do
context 'when there is a failed hook' do
before do
hook = create_hook
- hook.update!(recent_failures: WebHook::FAILURE_THRESHOLD + 1)
+ hook.update!(recent_failures: WebHooks::AutoDisabling::FAILURE_THRESHOLD + 1)
end
it { is_expected.to eq(true) }
@@ -83,7 +83,7 @@ RSpec.shared_examples 'something that has web-hooks' do
describe '#fetch_web_hook_failure', :clean_gitlab_redis_shared_state do
context 'when a value has not been stored' do
- it 'does not call #any_hook_failed?' do
+ it 'calls #any_hook_failed?' do
expect(object.get_web_hook_failure).to be_nil
expect(object).to receive(:any_hook_failed?).and_return(true)
diff --git a/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb b/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb
index 5eeefacdeb9..3f532629961 100644
--- a/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb
+++ b/spec/support/shared_examples/models/cycle_analytics_stage_shared_examples.rb
@@ -290,6 +290,7 @@ RSpec.shared_examples 'value stream analytics label based stage' do
context 'when `ProjectLabel is given' do
let_it_be(:label) { create(:label) }
+ let(:expected_error) { s_('CycleAnalyticsStage|is not available for the selected group') }
it 'raises error when `ProjectLabel` is given for `start_event_label`' do
params = {
@@ -300,7 +301,9 @@ RSpec.shared_examples 'value stream analytics label based stage' do
end_event_identifier: :issue_closed
}
- expect { described_class.new(params) }.to raise_error(ActiveRecord::AssociationTypeMismatch)
+ stage = described_class.new(params)
+ expect(stage).to be_invalid
+ expect(stage.errors.messages_for(:start_event_label_id)).to eq([expected_error])
end
it 'raises error when `ProjectLabel` is given for `end_event_label`' do
@@ -312,7 +315,9 @@ RSpec.shared_examples 'value stream analytics label based stage' do
end_event_label: label
}
- expect { described_class.new(params) }.to raise_error(ActiveRecord::AssociationTypeMismatch)
+ stage = described_class.new(params)
+ expect(stage).to be_invalid
+ expect(stage.errors.messages_for(:end_event_label_id)).to eq([expected_error])
end
end
end
diff --git a/spec/support/shared_examples/models/database_event_tracking_shared_examples.rb b/spec/support/shared_examples/models/database_event_tracking_shared_examples.rb
new file mode 100644
index 00000000000..3d98d9136e2
--- /dev/null
+++ b/spec/support/shared_examples/models/database_event_tracking_shared_examples.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'database events tracking' do
+ describe 'events tracking' do
+ # required definitions:
+ # :record, :update_params
+ #
+ # other available attributes:
+ # :project, :namespace
+
+ let(:user) { nil }
+ let(:category) { described_class.to_s }
+ let(:label) { described_class.table_name }
+ let(:action) { "database_event_#{property}" }
+ let(:feature_flag_name) { :product_intelligence_database_event_tracking }
+ let(:record_tracked_attributes) { record.attributes.slice(*described_class::SNOWPLOW_ATTRIBUTES.map(&:to_s)) }
+ let(:base_extra) { record_tracked_attributes.merge(project: try(:project), namespace: try(:namespace)) }
+
+ before do
+ allow(Gitlab::Tracking).to receive(:database_event).and_call_original
+ end
+
+ describe '#create' do
+ it_behaves_like 'Snowplow event tracking', overrides: { tracking_method: :database_event } do
+ subject(:create_record) { record }
+
+ let(:extra) { base_extra }
+ let(:property) { 'create' }
+ end
+ end
+
+ describe '#update', :freeze_time do
+ it_behaves_like 'Snowplow event tracking', overrides: { tracking_method: :database_event } do
+ subject(:update_record) { record.update!(update_params) }
+
+ let(:extra) { base_extra.merge(update_params.stringify_keys) }
+ let(:property) { 'update' }
+ end
+ end
+
+ describe '#destroy' do
+ it_behaves_like 'Snowplow event tracking', overrides: { tracking_method: :database_event } do
+ subject(:delete_record) { record.destroy! }
+
+ let(:extra) { base_extra }
+ let(:property) { 'destroy' }
+ end
+ end
+ end
+end
+
+RSpec.shared_examples 'database events tracking batch 2' do
+ it_behaves_like 'database events tracking' do
+ let(:feature_flag_name) { :product_intelligence_database_event_tracking_batch2 }
+ end
+end
diff --git a/spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb b/spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb
index 64390ccdc25..f1f6d799cf3 100644
--- a/spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb
+++ b/spec/support/shared_examples/models/diff_note_after_commit_shared_examples.rb
@@ -10,10 +10,12 @@ RSpec.shared_examples 'a valid diff note with after commit callback' do
it 'raises an error' do
allow(diff_file_from_repository).to receive(:line_for_position).with(position).and_return(nil)
- expect { subject.save! }.to raise_error(::DiffNote::NoteDiffFileCreationError,
- "Failed to find diff line for: #{diff_file_from_repository.file_path}, "\
- "old_line: #{position.old_line}"\
- ", new_line: #{position.new_line}")
+ expect { subject.save! }.to raise_error(
+ ::DiffNote::NoteDiffFileCreationError,
+ "Failed to find diff line for: #{diff_file_from_repository.file_path}, "\
+ "old_line: #{position.old_line}"\
+ ", new_line: #{position.new_line}"
+ )
end
end
diff --git a/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
index 7dfdd24177e..0cf109ce5c5 100644
--- a/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
+++ b/spec/support/shared_examples/models/integrations/base_slash_commands_shared_examples.rb
@@ -3,7 +3,6 @@
RSpec.shared_examples Integrations::BaseSlashCommands do
describe "Associations" do
it { is_expected.to respond_to :token }
- it { is_expected.to have_many :chat_names }
end
describe 'default values' do
@@ -85,7 +84,7 @@ RSpec.shared_examples Integrations::BaseSlashCommands do
end
context 'when the user is authenticated' do
- let!(:chat_name) { create(:chat_name, integration: subject) }
+ let!(:chat_name) { create(:chat_name) }
let(:params) { { token: 'token', team_id: chat_name.team_id, user_id: chat_name.chat_id } }
subject do
diff --git a/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb b/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
index 6d519e561ee..d438918eb60 100644
--- a/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
+++ b/spec/support/shared_examples/models/issue_tracker_service_shared_examples.rb
@@ -10,19 +10,19 @@ end
RSpec.shared_examples 'allows project key on reference pattern' do |url_attr|
it 'allows underscores in the project name' do
- expect(described_class.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
+ expect(subject.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
end
it 'allows numbers in the project name' do
- expect(described_class.reference_pattern.match('EXT3_EXT-1234')[0]).to eq 'EXT3_EXT-1234'
+ expect(subject.reference_pattern.match('EXT3_EXT-1234')[0]).to eq 'EXT3_EXT-1234'
end
it 'requires the project name to begin with A-Z' do
- expect(described_class.reference_pattern.match('3EXT_EXT-1234')).to eq nil
- expect(described_class.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
+ expect(subject.reference_pattern.match('3EXT_EXT-1234')).to eq nil
+ expect(subject.reference_pattern.match('EXT_EXT-1234')[0]).to eq 'EXT_EXT-1234'
end
it 'does not allow issue number to finish with a letter' do
- expect(described_class.reference_pattern.match('EXT-123A')).to eq(nil)
+ expect(subject.reference_pattern.match('EXT-123A')).to eq(nil)
end
end
diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb
index 7159c55e303..e9e25dee746 100644
--- a/spec/support/shared_examples/models/member_shared_examples.rb
+++ b/spec/support/shared_examples/models/member_shared_examples.rb
@@ -392,6 +392,30 @@ RSpec.shared_examples_for "bulk member creation" do
expect(members.first).to be_invite
end
+ context 'with different source types' do
+ shared_examples 'supports multiple sources' do
+ specify do
+ members = described_class.add_members(sources, [user1, user2], :maintainer)
+
+ expect(members.map(&:user)).to contain_exactly(user1, user2, user1, user2)
+ expect(members).to all(be_a(member_type))
+ expect(members).to all(be_persisted)
+ end
+ end
+
+ context 'with an array of sources' do
+ let_it_be(:sources) { [source, source2] }
+
+ it_behaves_like 'supports multiple sources'
+ end
+
+ context 'with a query producing sources' do
+ let_it_be(:sources) { source_type.id_in([source, source2]) }
+
+ it_behaves_like 'supports multiple sources'
+ end
+ end
+
context 'with de-duplication' do
it 'has the same user by id and user' do
members = described_class.add_members(source, [user1.id, user1, user1.id, user2, user2.id, user2], :maintainer)
@@ -484,11 +508,13 @@ RSpec.shared_examples_for "bulk member creation" do
create(:member_task, member: member, project: task_project, tasks_to_be_done: %w(code ci))
expect do
- described_class.add_members(source,
- [user1.id],
- :developer,
- tasks_to_be_done: %w(issues),
- tasks_project_id: task_project.id)
+ described_class.add_members(
+ source,
+ [user1.id],
+ :developer,
+ tasks_to_be_done: %w(issues),
+ tasks_project_id: task_project.id
+ )
end.not_to change { MemberTask.count }
member.reset
@@ -498,11 +524,13 @@ RSpec.shared_examples_for "bulk member creation" do
it 'adds tasks to be done if they do not exist', :aggregate_failures do
expect do
- described_class.add_members(source,
- [user1.id],
- :developer,
- tasks_to_be_done: %w(issues),
- tasks_project_id: task_project.id)
+ described_class.add_members(
+ source,
+ [user1.id],
+ :developer,
+ tasks_to_be_done: %w(issues),
+ tasks_project_id: task_project.id
+ )
end.to change { MemberTask.count }.by(1)
member = source.members.find_by(user_id: user1.id)
diff --git a/spec/support/shared_examples/models/members_notifications_shared_example.rb b/spec/support/shared_examples/models/members_notifications_shared_example.rb
index e28220334ac..329cb812a08 100644
--- a/spec/support/shared_examples/models/members_notifications_shared_example.rb
+++ b/spec/support/shared_examples/models/members_notifications_shared_example.rb
@@ -69,7 +69,7 @@ RSpec.shared_examples 'members notifications' do |entity_type|
let(:member) { create(:"#{entity_type}_member", :invited) }
it "calls NotificationService.decline_#{entity_type}_invite" do
- expect(notification_service).to receive(:"decline_#{entity_type}_invite").with(member)
+ expect(notification_service).to receive(:decline_invite).with(member)
member.decline_invite!
end
diff --git a/spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb b/spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb
index 5be0f6349ea..c2c123277ee 100644
--- a/spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb
+++ b/spec/support/shared_examples/models/packages/debian/component_file_shared_example.rb
@@ -20,7 +20,6 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
let_it_be(:component_file_other_architecture, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_2) }
let_it_be(:component_file_other_component, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component1_2, architecture: architecture1_1) }
let_it_be(:component_file_other_compression_type, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, compression_type: :xz) }
- let_it_be(:component_file_other_file_md5, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, file_md5: 'other_md5') }
let_it_be(:component_file_other_file_sha256, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component1_1, architecture: architecture1_1, file_sha256: 'other_sha256') }
let_it_be(:component_file_other_container, freeze: can_freeze) { create("debian_#{container_type}_component_file", component: component2_1, architecture: architecture2_1) }
let_it_be_with_refind(:component_file_with_file_type_sources) { create("debian_#{container_type}_component_file", :sources, component: component1_1) }
@@ -100,10 +99,6 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
it { is_expected.to validate_presence_of(:file_store) }
end
- describe '#file_md5' do
- it { is_expected.to validate_presence_of(:file_md5) }
- end
-
describe '#file_sha256' do
it { is_expected.to validate_presence_of(:file_sha256) }
end
@@ -231,4 +226,20 @@ RSpec.shared_examples 'Debian Component File' do |container_type, can_freeze|
it { is_expected.to eq("#{component1_1.name}/binary-#{architecture1_1.name}/Packages.xz") }
end
end
+
+ describe '#empty?' do
+ subject { component_file_with_architecture.empty? }
+
+ context 'with a non-empty component' do
+ it { is_expected.to be_falsey }
+ end
+
+ context 'with an empty component' do
+ before do
+ component_file_with_architecture.update! size: 0
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
end
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
index ac4ad4525aa..3ea2ff4d8f0 100644
--- a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
+++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
@@ -1,32 +1,13 @@
# frozen_string_literal: true
-require 'spec_helper'
-
-RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
- let_it_be(:distribution_with_suite, freeze: can_freeze) { create(factory, :with_suite) }
- let_it_be(:distribution_with_same_container, freeze: can_freeze) { create(factory, container: distribution_with_suite.container ) }
- let_it_be(:distribution_with_same_codename, freeze: can_freeze) { create(factory, codename: distribution_with_suite.codename ) }
- let_it_be(:distribution_with_same_suite, freeze: can_freeze) { create(factory, suite: distribution_with_suite.suite ) }
- let_it_be(:distribution_with_codename_and_suite_flipped, freeze: can_freeze) { create(factory, codename: distribution_with_suite.suite, suite: distribution_with_suite.codename) }
-
- let_it_be_with_refind(:distribution) { create(factory, container: distribution_with_suite.container ) }
-
+RSpec.shared_examples 'Debian Distribution for common behavior' do
subject { distribution }
describe 'relationships' do
- it { is_expected.to belong_to(container) }
it { is_expected.to belong_to(:creator).class_name('User') }
-
- it { is_expected.to have_one(:key).class_name("Packages::Debian::#{container.capitalize}DistributionKey").with_foreign_key(:distribution_id).inverse_of(:distribution) }
- it { is_expected.to have_many(:components).class_name("Packages::Debian::#{container.capitalize}Component").inverse_of(:distribution) }
- it { is_expected.to have_many(:architectures).class_name("Packages::Debian::#{container.capitalize}Architecture").inverse_of(:distribution) }
end
describe 'validations' do
- describe "##{container}" do
- it { is_expected.to validate_presence_of(container) }
- end
-
describe "#creator" do
it { is_expected.not_to validate_presence_of(:creator) }
end
@@ -47,57 +28,6 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
it { is_expected.not_to allow_value('hé').for(:suite) }
end
- describe '#unique_debian_suite_and_codename' do
- using RSpec::Parameterized::TableSyntax
-
- where(:with_existing_suite, :suite, :codename, :errors) do
- false | nil | :keep | nil
- false | 'testing' | :keep | nil
- false | nil | :codename | ["Codename has already been taken"]
- false | :codename | :keep | ["Suite has already been taken as Codename"]
- false | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
- true | nil | :keep | nil
- true | 'testing' | :keep | nil
- true | nil | :codename | ["Codename has already been taken"]
- true | :codename | :keep | ["Suite has already been taken as Codename"]
- true | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
- true | nil | :suite | ["Codename has already been taken as Suite"]
- true | :suite | :keep | ["Suite has already been taken"]
- true | :suite | :suite | ["Suite has already been taken", "Codename has already been taken as Suite"]
- end
-
- with_them do
- context factory do
- let(:new_distribution) { build(factory, container: distribution.container) }
-
- before do
- distribution.update_column(:suite, 'suite-' + distribution.codename) if with_existing_suite
-
- if suite.is_a?(Symbol)
- new_distribution.suite = distribution.send suite unless suite == :keep
- else
- new_distribution.suite = suite
- end
-
- if codename.is_a?(Symbol)
- new_distribution.codename = distribution.send codename unless codename == :keep
- else
- new_distribution.codename = codename
- end
- end
-
- it do
- if errors
- expect(new_distribution).not_to be_valid
- expect(new_distribution.errors.to_a).to eq(errors)
- else
- expect(new_distribution).to be_valid
- end
- end
- end
- end
- end
-
describe '#origin' do
it { is_expected.to allow_value(nil).for(:origin) }
it { is_expected.to allow_value('Debian').for(:origin) }
@@ -179,7 +109,11 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
subject { described_class.with_codename_or_suite(distribution_with_suite.codename) }
it 'does not return other distributions' do
- expect(subject.to_a).to contain_exactly(distribution_with_suite, distribution_with_same_codename, distribution_with_codename_and_suite_flipped)
+ expect(subject.to_a)
+ .to contain_exactly(
+ distribution_with_suite,
+ distribution_with_same_codename,
+ distribution_with_codename_and_suite_flipped)
end
end
@@ -187,54 +121,169 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
subject { described_class.with_codename_or_suite(distribution_with_suite.suite) }
it 'does not return other distributions' do
- expect(subject.to_a).to contain_exactly(distribution_with_suite, distribution_with_same_suite, distribution_with_codename_and_suite_flipped)
+ expect(subject.to_a)
+ .to contain_exactly(
+ distribution_with_suite,
+ distribution_with_same_suite,
+ distribution_with_codename_and_suite_flipped)
end
end
end
end
+end
- if container == :project
- describe 'project distribution specifics' do
- describe 'relationships' do
- it { is_expected.to have_many(:publications).class_name('Packages::Debian::Publication').inverse_of(:distribution).with_foreign_key(:distribution_id) }
- it { is_expected.to have_many(:packages).class_name('Packages::Package').through(:publications) }
- end
+RSpec.shared_examples 'Debian Distribution for specific behavior' do |factory|
+ describe '#unique_debian_suite_and_codename' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:with_existing_suite, :suite, :codename, :errors) do
+ false | nil | :keep | nil
+ false | 'testing' | :keep | nil
+ false | nil | :codename | ["Codename has already been taken"]
+ false | :codename | :keep | ["Suite has already been taken as Codename"]
+ false | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
+ true | nil | :keep | nil
+ true | 'testing' | :keep | nil
+ true | nil | :codename | ["Codename has already been taken"]
+ true | :codename | :keep | ["Suite has already been taken as Codename"]
+ true | :codename | :codename | ["Codename has already been taken", "Suite has already been taken as Codename"]
+ true | nil | :suite | ["Codename has already been taken as Suite"]
+ true | :suite | :keep | ["Suite has already been taken"]
+ true | :suite | :suite | ["Suite has already been taken", "Codename has already been taken as Suite"]
end
- else
- describe 'group distribution specifics' do
- let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container) }
- let_it_be(:public_distribution_with_same_codename) { create(:debian_project_distribution, container: public_project, codename: distribution_with_suite.codename) }
- let_it_be(:public_package_with_same_codename) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename) }
- let_it_be(:public_distribution_with_same_suite) { create(:debian_project_distribution, container: public_project, suite: distribution_with_suite.suite) }
- let_it_be(:public_package_with_same_suite) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite) }
-
- let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container) }
- let_it_be(:private_distribution_with_same_codename) { create(:debian_project_distribution, container: private_project, codename: distribution_with_suite.codename) }
- let_it_be(:private_package_with_same_codename) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename) }
- let_it_be(:private_distribution_with_same_suite) { create(:debian_project_distribution, container: private_project, suite: distribution_with_suite.suite) }
- let_it_be(:private_package_with_same_suite) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename) }
-
- describe '#packages' do
- subject { distribution_with_suite.packages }
-
- it 'returns only public packages with same codename' do
- expect(subject.to_a).to contain_exactly(public_package_with_same_codename)
+
+ with_them do
+ context factory do
+ let(:new_distribution) { build(factory, container: distribution.container) }
+
+ before do
+ distribution.update_column(:suite, "suite-#{distribution.codename}") if with_existing_suite
+
+ if suite.is_a?(Symbol)
+ new_distribution.suite = distribution.send suite unless suite == :keep
+ else
+ new_distribution.suite = suite
+ end
+
+ if codename.is_a?(Symbol)
+ new_distribution.codename = distribution.send codename unless codename == :keep
+ else
+ new_distribution.codename = codename
+ end
+ end
+
+ it do
+ if errors
+ expect(new_distribution).not_to be_valid
+ expect(new_distribution.errors.to_a).to eq(errors)
+ else
+ expect(new_distribution).to be_valid
+ end
end
end
+ end
+ end
+end
- describe '#package_files' do
- subject { distribution_with_suite.package_files }
+RSpec.shared_examples 'Debian Distribution with project container' do
+ it_behaves_like 'Debian Distribution for specific behavior', :debian_project_distribution
- it 'returns only files from public packages with same codename' do
- expect(subject.to_a).to contain_exactly(*public_package_with_same_codename.package_files)
- end
+ describe 'relationships' do
+ it { is_expected.to belong_to(:project) }
- context 'with pending destruction package files' do
- let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: public_package_with_same_codename) }
+ it { is_expected.to have_one(:key).class_name("Packages::Debian::ProjectDistributionKey").with_foreign_key(:distribution_id).inverse_of(:distribution) }
+ it { is_expected.to have_many(:components).class_name("Packages::Debian::ProjectComponent").inverse_of(:distribution) }
+ it { is_expected.to have_many(:architectures).class_name("Packages::Debian::ProjectArchitecture").inverse_of(:distribution) }
+ end
- it 'does not return them' do
- expect(subject.to_a).not_to include(package_file_pending_destruction)
- end
+ describe "#project" do
+ it { is_expected.to validate_presence_of(:project) }
+ end
+
+ describe 'project distribution specifics' do
+ describe 'relationships' do
+ it do
+ is_expected.to have_many(:publications).class_name('Packages::Debian::Publication').inverse_of(:distribution)
+ .with_foreign_key(:distribution_id)
+ end
+
+ it { is_expected.to have_many(:packages).class_name('Packages::Package').through(:publications) }
+ end
+ end
+end
+
+RSpec.shared_examples 'Debian Distribution with group container' do
+ it_behaves_like 'Debian Distribution for specific behavior', :debian_group_distribution
+
+ describe 'relationships' do
+ it { is_expected.to belong_to(:group) }
+
+ it { is_expected.to have_one(:key).class_name("Packages::Debian::GroupDistributionKey").with_foreign_key(:distribution_id).inverse_of(:distribution) }
+ it { is_expected.to have_many(:components).class_name("Packages::Debian::GroupComponent").inverse_of(:distribution) }
+ it { is_expected.to have_many(:architectures).class_name("Packages::Debian::GroupArchitecture").inverse_of(:distribution) }
+ end
+
+ describe "#group" do
+ it { is_expected.to validate_presence_of(:group) }
+ end
+
+ describe 'group distribution specifics' do
+ let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container) }
+ let_it_be(:public_distribution_with_same_codename) do
+ create(:debian_project_distribution, container: public_project, codename: distribution_with_suite.codename)
+ end
+
+ let_it_be(:public_package_with_same_codename) do
+ create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename)
+ end
+
+ let_it_be(:public_distribution_with_same_suite) do
+ create(:debian_project_distribution, container: public_project, suite: distribution_with_suite.suite)
+ end
+
+ let_it_be(:public_package_with_same_suite) do
+ create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite)
+ end
+
+ let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container) }
+ let_it_be(:private_distribution_with_same_codename) do
+ create(:debian_project_distribution, container: private_project, codename: distribution_with_suite.codename)
+ end
+
+ let_it_be(:private_package_with_same_codename) do
+ create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)
+ end
+
+ let_it_be(:private_distribution_with_same_suite) do
+ create(:debian_project_distribution, container: private_project, suite: distribution_with_suite.suite)
+ end
+
+ let_it_be(:private_package_with_same_suite) do
+ create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)
+ end
+
+ describe '#packages' do
+ subject { distribution_with_suite.packages }
+
+ it 'returns only public packages with same codename' do
+ expect(subject.to_a).to contain_exactly(public_package_with_same_codename)
+ end
+ end
+
+ describe '#package_files' do
+ subject { distribution_with_suite.package_files }
+
+ it 'returns only files from public packages with same codename' do
+ expect(subject.to_a).to contain_exactly(*public_package_with_same_codename.package_files)
+ end
+
+ context 'with pending destruction package files' do
+ let_it_be(:package_file_pending_destruction) do
+ create(:package_file, :pending_destruction, package: public_package_with_same_codename)
+ end
+
+ it 'does not return them' do
+ expect(subject.to_a).not_to include(package_file_pending_destruction)
end
end
end
diff --git a/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb b/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
index 3caf58da4d2..f1af1760e8d 100644
--- a/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
+++ b/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb
@@ -19,7 +19,7 @@ RSpec.shared_examples 'ci_cd_settings delegation' do
end
end
-RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: ''|
+RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: '', default: false|
using RSpec::Parameterized::TableSyntax
context 'when ci_cd_settings is nil' do
@@ -28,7 +28,7 @@ RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: ''|
end
it 'returns false' do
- expect(project.send("#{prefix}#{delegated_method}")).to be(false)
+ expect(project.send("#{prefix}#{delegated_method}")).to be(default)
end
end
diff --git a/spec/support/shared_examples/models/resource_event_shared_examples.rb b/spec/support/shared_examples/models/resource_event_shared_examples.rb
index 038ff33c68a..1409f7caea8 100644
--- a/spec/support/shared_examples/models/resource_event_shared_examples.rb
+++ b/spec/support/shared_examples/models/resource_event_shared_examples.rb
@@ -10,6 +10,8 @@ RSpec.shared_examples 'a resource event' do
let_it_be(:issue2) { create(:issue, author: user1) }
let_it_be(:issue3) { create(:issue, author: user2) }
+ let(:resource_event) { described_class.name.demodulize.underscore.to_sym }
+
describe 'importable' do
it { is_expected.to respond_to(:importing?) }
it { is_expected.to respond_to(:imported?) }
@@ -36,9 +38,9 @@ RSpec.shared_examples 'a resource event' do
let!(:created_at2) { 2.days.ago }
let!(:created_at3) { 3.days.ago }
- let!(:event1) { create(described_class.name.underscore.to_sym, issue: issue1, created_at: created_at1) }
- let!(:event2) { create(described_class.name.underscore.to_sym, issue: issue2, created_at: created_at2) }
- let!(:event3) { create(described_class.name.underscore.to_sym, issue: issue2, created_at: created_at3) }
+ let!(:event1) { create(resource_event, issue: issue1, created_at: created_at1) }
+ let!(:event2) { create(resource_event, issue: issue2, created_at: created_at2) }
+ let!(:event3) { create(resource_event, issue: issue2, created_at: created_at3) }
it 'returns the expected events' do
events = described_class.created_after(created_at3)
@@ -62,9 +64,10 @@ RSpec.shared_examples 'a resource event for issues' do
let_it_be(:issue2) { create(:issue, author: user1) }
let_it_be(:issue3) { create(:issue, author: user2) }
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, issue: issue1) }
- let_it_be(:event2) { create(described_class.name.underscore.to_sym, issue: issue2) }
- let_it_be(:event3) { create(described_class.name.underscore.to_sym, issue: issue1) }
+ let_it_be(:resource_event) { described_class.name.demodulize.underscore.to_sym }
+ let_it_be(:event1) { create(resource_event, issue: issue1) }
+ let_it_be(:event2) { create(resource_event, issue: issue2) }
+ let_it_be(:event3) { create(resource_event, issue: issue1) }
describe 'associations' do
it { is_expected.to belong_to(:issue) }
@@ -93,9 +96,9 @@ RSpec.shared_examples 'a resource event for issues' do
end
describe '.by_created_at_earlier_or_equal_to' do
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, issue: issue1, created_at: '2020-03-10') }
- let_it_be(:event2) { create(described_class.name.underscore.to_sym, issue: issue2, created_at: '2020-03-10') }
- let_it_be(:event3) { create(described_class.name.underscore.to_sym, issue: issue1, created_at: '2020-03-12') }
+ let_it_be(:event1) { create(resource_event, issue: issue1, created_at: '2020-03-10') }
+ let_it_be(:event2) { create(resource_event, issue: issue2, created_at: '2020-03-10') }
+ let_it_be(:event3) { create(resource_event, issue: issue1, created_at: '2020-03-12') }
it 'returns the expected events' do
events = described_class.by_created_at_earlier_or_equal_to('2020-03-11 23:59:59')
@@ -112,7 +115,7 @@ RSpec.shared_examples 'a resource event for issues' do
if described_class.method_defined?(:issuable)
describe '#issuable' do
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, issue: issue2) }
+ let_it_be(:event1) { create(resource_event, issue: issue2) }
it 'returns the expected issuable' do
expect(event1.issuable).to eq(issue2)
@@ -125,6 +128,7 @@ RSpec.shared_examples 'a resource event for merge requests' do
let_it_be(:user1) { create(:user) }
let_it_be(:user2) { create(:user) }
+ let_it_be(:resource_event) { described_class.name.demodulize.underscore.to_sym }
let_it_be(:merge_request1) { create(:merge_request, author: user1) }
let_it_be(:merge_request2) { create(:merge_request, author: user1) }
let_it_be(:merge_request3) { create(:merge_request, author: user2) }
@@ -134,9 +138,9 @@ RSpec.shared_examples 'a resource event for merge requests' do
end
describe '.by_merge_request' do
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, merge_request: merge_request1) }
- let_it_be(:event2) { create(described_class.name.underscore.to_sym, merge_request: merge_request2) }
- let_it_be(:event3) { create(described_class.name.underscore.to_sym, merge_request: merge_request1) }
+ let_it_be(:event1) { create(resource_event, merge_request: merge_request1) }
+ let_it_be(:event2) { create(resource_event, merge_request: merge_request2) }
+ let_it_be(:event3) { create(resource_event, merge_request: merge_request1) }
it 'returns the expected records for an issue with events' do
events = described_class.by_merge_request(merge_request1)
@@ -153,7 +157,7 @@ RSpec.shared_examples 'a resource event for merge requests' do
if described_class.method_defined?(:issuable)
describe '#issuable' do
- let_it_be(:event1) { create(described_class.name.underscore.to_sym, merge_request: merge_request2) }
+ let_it_be(:event1) { create(resource_event, merge_request: merge_request2) }
it 'returns the expected issuable' do
expect(event1.issuable).to eq(merge_request2)
@@ -163,7 +167,7 @@ RSpec.shared_examples 'a resource event for merge requests' do
context 'on callbacks' do
it 'does not trigger note created subscription' do
- event = build(described_class.name.underscore.to_sym, merge_request: merge_request1)
+ event = build(resource_event, merge_request: merge_request1)
expect(GraphqlTriggers).not_to receive(:work_item_note_created)
expect(event).not_to receive(:trigger_note_subscription_create)
@@ -177,15 +181,17 @@ RSpec.shared_examples 'a note for work item resource event' do
let_it_be(:project) { create(:project) }
let_it_be(:work_item) { create(:work_item, :task, project: project, author: user) }
+ let(:resource_event) { described_class.name.demodulize.underscore.to_sym }
+
it 'builds synthetic note with correct synthetic_note_class' do
- event = build(described_class.name.underscore.to_sym, issue: work_item)
+ event = build(resource_event, issue: work_item)
expect(event.work_item_synthetic_system_note.class.name).to eq(event.synthetic_note_class.name)
end
context 'on callbacks' do
it 'triggers note created subscription' do
- event = build(described_class.name.underscore.to_sym, issue: work_item)
+ event = build(resource_event, issue: work_item)
expect(GraphqlTriggers).to receive(:work_item_note_created)
expect(event).to receive(:trigger_note_subscription_create).and_call_original
diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb
index 7e69a6663d5..017e51ecd24 100644
--- a/spec/support/shared_examples/models/wiki_shared_examples.rb
+++ b/spec/support/shared_examples/models/wiki_shared_examples.rb
@@ -94,6 +94,40 @@ RSpec.shared_examples 'wiki model' do
end
end
+ describe '#has_home_page?' do
+ context 'when home page exists' do
+ before do
+ wiki.repository.create_file(
+ user,
+ 'home.md',
+ 'home file',
+ branch_name: wiki.default_branch,
+ message: "created home page",
+ author_email: user.email,
+ author_name: user.name
+ )
+ end
+
+ it 'returns true' do
+ expect(wiki.has_home_page?).to eq(true)
+ end
+
+ it 'returns false when #find_page raise an error' do
+ allow(wiki)
+ .to receive(:find_page)
+ .and_raise(StandardError)
+
+ expect(wiki.has_home_page?).to eq(false)
+ end
+ end
+
+ context 'when home page does not exist' do
+ it 'returns false' do
+ expect(wiki.has_home_page?).to eq(false)
+ end
+ end
+ end
+
describe '#to_global_id' do
it 'returns a global ID' do
expect(wiki.to_global_id.to_s).to eq("gid://gitlab/#{wiki.class.name}/#{wiki.id}")
@@ -791,6 +825,21 @@ RSpec.shared_examples 'wiki model' do
end
end
+ context 'when the repository fails to update' do
+ let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') }
+
+ it 'returns false and sets error message', :aggregate_failures do
+ expect(subject.repository)
+ .to receive(:update_file)
+ .and_raise(Gitlab::Git::Index::IndexError.new)
+
+ expect(subject.update_page(page.page, content: 'new content', format: :markdown))
+ .to eq(false)
+ expect(subject.error_message)
+ .to match("Duplicate page: A page with that title already exists")
+ end
+ end
+
context 'when page path does not have a default extension' do
let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') }
diff --git a/spec/support/shared_examples/observability/csp_shared_examples.rb b/spec/support/shared_examples/observability/csp_shared_examples.rb
index 0cd211f69eb..9d6e7e75f4d 100644
--- a/spec/support/shared_examples/observability/csp_shared_examples.rb
+++ b/spec/support/shared_examples/observability/csp_shared_examples.rb
@@ -31,19 +31,19 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe
let(:observability_url) { Gitlab::Observability.observability_url }
let(:signin_url) do
Gitlab::Utils.append_path(Gitlab.config.gitlab.url,
- '/users/sign_in')
+ '/users/sign_in')
end
let(:oauth_url) do
Gitlab::Utils.append_path(Gitlab.config.gitlab.url,
- '/oauth/authorize')
+ '/oauth/authorize')
end
before do
setup_csp_for_controller(controller_class, csp, any_time: true)
group.add_developer(user)
login_as(user)
- allow(Gitlab::Observability).to receive(:observability_enabled?).and_return(true)
+ stub_feature_flags(observability_group_tab: true)
end
subject do
@@ -67,7 +67,7 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe
end
before do
- allow(Gitlab::Observability).to receive(:observability_enabled?).and_return(false)
+ stub_feature_flags(observability_group_tab: false)
end
it 'does not add observability urls to the csp header' do
@@ -76,23 +76,6 @@ RSpec.shared_examples 'observability csp policy' do |controller_class = describe
end
end
- context 'when checking if observability is enabled' do
- let(:csp) do
- ActionDispatch::ContentSecurityPolicy.new do |p|
- p.frame_src 'https://something.test'
- end
- end
-
- it 'check access for a given user and group' do
- allow(Gitlab::Observability).to receive(:observability_enabled?)
-
- get tested_path
-
- expect(Gitlab::Observability).to have_received(:observability_enabled?)
- .with(user, group).at_least(:once)
- end
- end
-
context 'when frame-src exists in the CSP config' do
let(:csp) do
ActionDispatch::ContentSecurityPolicy.new do |p|
diff --git a/spec/support/shared_examples/observability/embed_observabilities_examples.rb b/spec/support/shared_examples/observability/embed_observabilities_examples.rb
new file mode 100644
index 00000000000..c8d4e9e0d7e
--- /dev/null
+++ b/spec/support/shared_examples/observability/embed_observabilities_examples.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'embeds observability' do
+ it 'renders iframe in description' do
+ page.within('.description') do
+ expect_observability_iframe(page.html)
+ end
+ end
+
+ it 'renders iframe in comment' do
+ expect(page).not_to have_css('.note-text')
+
+ page.within('.js-main-target-form') do
+ fill_in('note[note]', with: observable_url)
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ page.within('.note-text') do
+ expect_observability_iframe(page.html)
+ end
+ end
+end
+
+RSpec.shared_examples 'does not embed observability' do
+ it 'does not render iframe in description' do
+ page.within('.description') do
+ expect_observability_iframe(page.html, to_be_nil: true)
+ end
+ end
+
+ it 'does not render iframe in comment' do
+ expect(page).not_to have_css('.note-text')
+
+ page.within('.js-main-target-form') do
+ fill_in('note[note]', with: observable_url)
+ click_button('Comment')
+ end
+
+ wait_for_requests
+
+ page.within('.note-text') do
+ expect_observability_iframe(page.html, to_be_nil: true)
+ end
+ end
+end
+
+def expect_observability_iframe(html, to_be_nil: false)
+ iframe = Nokogiri::HTML.parse(html).at_css('#observability-ui-iframe')
+
+ expect(html).to include(observable_url)
+
+ if to_be_nil
+ expect(iframe).to be_nil
+ else
+ expect(iframe).not_to be_nil
+ iframe_src = "#{expected_observable_url}&theme=light&username=#{user.username}&kiosk=inline-embed"
+ expect(iframe.attributes['src'].value).to eq(iframe_src)
+ end
+end
diff --git a/spec/support/shared_examples/policies/project_policy_shared_examples.rb b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
index 9ec1b8b3f5a..d1f5a01b10c 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -401,3 +401,24 @@ RSpec.shared_examples 'package access with repository disabled' do
it { is_expected.to be_allowed(:read_package) }
end
+
+RSpec.shared_examples 'equivalent project policy abilities' do
+ where(:project_visibility, :user_role_on_project) do
+ project_visibilities = [:public, :internal, :private]
+ user_role_on_project = [:anonymous, :non_member, :guest, :reporter, :developer, :maintainer, :owner, :admin]
+ project_visibilities.product(user_role_on_project)
+ end
+
+ with_them do
+ it 'evaluates the same' do
+ project = public_send("#{project_visibility}_project")
+ current_user = public_send(user_role_on_project)
+ enable_admin_mode!(current_user) if user_role_on_project == :admin
+ policy = ProjectPolicy.new(current_user, project)
+ old_permissions = policy.allowed?(old_policy)
+ new_permissions = policy.allowed?(new_policy)
+
+ expect(old_permissions).to eq new_permissions
+ end
+ end
+end
diff --git a/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
index f70621673d5..f9f8435c211 100644
--- a/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
+++ b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
@@ -7,9 +7,9 @@ RSpec.shared_examples 'when regex matching everything is specified' do
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: delete_expectations
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
context 'with deprecated name_regex param' do
let(:params) do
@@ -17,9 +17,9 @@ RSpec.shared_examples 'when regex matching everything is specified' do
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: delete_expectations
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
end
end
@@ -31,9 +31,9 @@ RSpec.shared_examples 'when regex matching everything is specified and latest is
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: delete_expectations
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
end
RSpec.shared_examples 'when delete regex matching specific tags is used' do
@@ -43,9 +43,9 @@ RSpec.shared_examples 'when delete regex matching specific tags is used' do
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: [%w[C D]]
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: [%w[C D]]
end
RSpec.shared_examples 'when delete regex matching specific tags is used with overriding allow regex' do
@@ -58,9 +58,9 @@ RSpec.shared_examples 'when delete regex matching specific tags is used with ove
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: [%w[D]]
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: [%w[D]]
context 'with name_regex_delete overriding deprecated name_regex' do
let(:params) do
@@ -71,9 +71,9 @@ RSpec.shared_examples 'when delete regex matching specific tags is used with ove
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: [%w[D]]
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: [%w[D]]
end
end
@@ -87,9 +87,9 @@ RSpec.shared_examples 'with allow regex value' do
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: delete_expectations
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
end
RSpec.shared_examples 'when keeping only N tags' do
@@ -135,9 +135,9 @@ RSpec.shared_examples 'when removing keeping only 3' do
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: delete_expectations
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
end
RSpec.shared_examples 'when removing older than 1 day' do
@@ -150,9 +150,9 @@ RSpec.shared_examples 'when removing older than 1 day' do
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: delete_expectations
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
end
RSpec.shared_examples 'when combining all parameters' do
@@ -166,9 +166,9 @@ RSpec.shared_examples 'when combining all parameters' do
end
it_behaves_like 'removing the expected tags',
- service_response_extra: service_response_extra,
- supports_caching: supports_caching,
- delete_expectations: delete_expectations
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
end
RSpec.shared_examples 'when running a container_expiration_policy' do
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/shared_examples/prometheus/additional_metrics_shared_examples.rb
index e589baf0909..d196114b227 100644
--- a/spec/support/prometheus/additional_metrics_shared_examples.rb
+++ b/spec/support/shared_examples/prometheus/additional_metrics_shared_examples.rb
@@ -6,13 +6,13 @@ RSpec.shared_examples 'additional metrics query' do
let(:metric_group_class) { Gitlab::Prometheus::MetricGroup }
let(:metric_class) { Gitlab::Prometheus::Metric }
- let(:metric_names) { %w{metric_a metric_b} }
+ let(:metric_names) { %w[metric_a metric_b] }
let(:query_range_result) do
- [{ 'metric': {}, 'values': [[1488758662.506, '0.00002996364761904785'], [1488758722.506, '0.00003090239047619091']] }]
+ [{ metric: {}, values: [[1488758662.506, '0.00002996364761904785'], [1488758722.506, '0.00003090239047619091']] }]
end
- let(:client) { double('prometheus_client') }
+ let(:client) { instance_double('Gitlab::PrometheusClient') }
let(:query_result) { described_class.new(client).query(*query_params) }
let(:project) { create(:project, :repository) }
let(:environment) { create(:environment, slug: 'environment-slug', project: project) }
@@ -22,12 +22,13 @@ RSpec.shared_examples 'additional metrics query' do
allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group(metrics: [simple_metric])])
end
- context 'metrics query context' do
+ describe 'metrics query context' do
subject! { described_class.new(client) }
shared_examples 'query context containing environment slug and filter' do
it 'contains ci_environment_slug' do
- expect(subject).to receive(:query_metrics).with(project, environment, hash_including(ci_environment_slug: environment.slug))
+ expect(subject)
+ .to receive(:query_metrics).with(project, environment, hash_including(ci_environment_slug: environment.slug))
subject.query(*query_params)
end
@@ -54,7 +55,8 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains kube_namespace' do
- expect(subject).to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: kube_namespace))
+ expect(subject)
+ .to receive(:query_metrics).with(project, environment, hash_including(kube_namespace: kube_namespace))
subject.query(*query_params)
end
@@ -77,7 +79,7 @@ RSpec.shared_examples 'additional metrics query' do
allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group])
end
- context 'some queries return results' do
+ context 'when some queries return results' do
before do
allow(client).to receive(:query_range).with('query_range_a', any_args).and_return(query_range_result)
allow(client).to receive(:query_range).with('query_range_b', any_args).and_return(query_range_result)
@@ -118,7 +120,7 @@ RSpec.shared_examples 'additional metrics query' do
allow(client).to receive(:label_values).and_return(metric_names)
end
- context 'both queries return results' do
+ context 'when both queries return results' do
before do
allow(client).to receive(:query_range).with('query_range_a', any_args).and_return(query_range_result)
allow(client).to receive(:query_range).with('query_range_b', any_args).and_return(query_range_result)
@@ -138,7 +140,7 @@ RSpec.shared_examples 'additional metrics query' do
end
end
- context 'one query returns result' do
+ context 'when one query returns result' do
before do
allow(client).to receive(:query_range).with('query_range_a', any_args).and_return(query_range_result)
allow(client).to receive(:query_range).with('query_range_b', any_args).and_return([])
diff --git a/spec/support/shared_examples/protected_tags/access_control_ce_shared_examples.rb b/spec/support/shared_examples/protected_tags/access_control_ce_shared_examples.rb
new file mode 100644
index 00000000000..f308b4ad372
--- /dev/null
+++ b/spec/support/shared_examples/protected_tags/access_control_ce_shared_examples.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples "protected tags > access control > CE" do
+ ProtectedRef::AccessLevel.human_access_levels.each do |(access_type_id, access_type_name)|
+ it "allows creating protected tags that #{access_type_name} can create" do
+ visit project_protected_tags_path(project)
+
+ set_protected_tag_name('master')
+ set_allowed_to('create', access_type_name)
+ click_on_protect
+
+ expect(ProtectedTag.count).to eq(1)
+ expect(ProtectedTag.last.create_access_levels.map(&:access_level)).to eq([access_type_id])
+ end
+
+ it "allows updating protected tags so that #{access_type_name} can create them" do
+ visit project_protected_tags_path(project)
+
+ set_protected_tag_name('master')
+ set_allowed_to('create', 'No one')
+ click_on_protect
+
+ expect(ProtectedTag.count).to eq(1)
+
+ set_allowed_to('create', access_type_name, form: '.protected-tags-list')
+
+ wait_for_requests
+
+ expect(ProtectedTag.last.create_access_levels.map(&:access_level)).to include(access_type_id)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb
index d8690356f81..7cbaf40721a 100644
--- a/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/close_quick_action_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'close quick action' do |issuable_type|
- include Spec::Support::Helpers::Features::NotesHelpers
+ include Features::NotesHelpers
before do
project.add_maintainer(maintainer)
diff --git a/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb b/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb
index b5704ad8f17..9b03cdbb3bf 100644
--- a/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issuable/max_issuable_examples.rb
@@ -23,11 +23,11 @@ RSpec.shared_examples 'does not exceed the issuable size limit' do
end
note = described_class.new(project, user, opts.merge(
- note: note_text,
- noteable_type: noteable_type,
- noteable_id: issuable.id,
- confidential: false
- )).execute
+ note: note_text,
+ noteable_type: noteable_type,
+ noteable_id: issuable.id,
+ confidential: false
+ )).execute
expect(note.errors[:validation]).to match_array([validation_message])
end
@@ -44,11 +44,11 @@ RSpec.shared_examples 'does not exceed the issuable size limit' do
end
note = described_class.new(project, user, opts.merge(
- note: note_text,
- noteable_type: noteable_type,
- noteable_id: issuable.id,
- confidential: false
- )).execute
+ note: note_text,
+ noteable_type: noteable_type,
+ noteable_id: issuable.id,
+ confidential: false
+ )).execute
expect(note.errors[:validation]).to be_empty
end
diff --git a/spec/support/shared_examples/quick_actions/issue/issue_links_quick_actions_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/issue_links_quick_actions_shared_examples.rb
new file mode 100644
index 00000000000..811b5ee4de2
--- /dev/null
+++ b/spec/support/shared_examples/quick_actions/issue/issue_links_quick_actions_shared_examples.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'issues link quick action' do |command|
+ let_it_be_with_refind(:group) { create(:group) }
+ let_it_be_with_reload(:other_issue) { create(:issue, project: project) }
+ let_it_be_with_reload(:second_issue) { create(:issue, project: project) }
+ let_it_be_with_reload(:third_issue) { create(:issue, project: project) }
+
+ let(:link_type) { command == :relate ? 'relates_to' : 'blocks' }
+ let(:links_query) do
+ if command == :blocked_by
+ IssueLink.where(target: issue, link_type: link_type).map(&:source)
+ else
+ IssueLink.where(source: issue, link_type: link_type).map(&:target)
+ end
+ end
+
+ shared_examples 'link command' do
+ it 'links issues' do
+ service.execute(content, issue)
+
+ expect(links_query).to match_array(issues_linked)
+ end
+ end
+
+ context 'when user is member of group' do
+ before do
+ group.add_developer(user)
+ end
+
+ context 'when linking a single issue' do
+ let(:issues_linked) { [other_issue] }
+ let(:content) { "/#{command} #{other_issue.to_reference}" }
+
+ it_behaves_like 'link command'
+ end
+
+ context 'when linking multiple issues at once' do
+ let(:issues_linked) { [second_issue, third_issue] }
+ let(:content) { "/#{command} #{second_issue.to_reference} #{third_issue.to_reference}" }
+
+ it_behaves_like 'link command'
+ end
+
+ context 'when quick action target is unpersisted' do
+ let(:issue) { build(:issue, project: project) }
+ let(:issues_linked) { [other_issue] }
+ let(:content) { "/#{command} #{other_issue.to_reference}" }
+
+ it 'links the issues after the issue is persisted' do
+ service.execute(content, issue)
+
+ issue.save!
+
+ expect(links_query).to match_array(issues_linked)
+ end
+ end
+
+ context 'with empty link command' do
+ let(:issues_linked) { [] }
+ let(:content) { "/#{command}" }
+
+ it_behaves_like 'link command'
+ end
+
+ context 'with already having linked issues' do
+ let(:issues_linked) { [second_issue, third_issue] }
+ let(:content) { "/#{command} #{third_issue.to_reference(project)}" }
+
+ before do
+ create_existing_link(command)
+ end
+
+ it_behaves_like 'link command'
+ end
+
+ context 'with cross project' do
+ let_it_be_with_reload(:another_group) { create(:group, :public) }
+ let_it_be_with_reload(:other_project) { create(:project, group: another_group) }
+
+ before do
+ another_group.add_developer(user)
+ [other_issue, second_issue, third_issue].map { |i| i.update!(project: other_project) }
+ end
+
+ context 'when linking a cross project issue' do
+ let(:issues_linked) { [other_issue] }
+ let(:content) { "/#{command} #{other_issue.to_reference(project)}" }
+
+ it_behaves_like 'link command'
+ end
+
+ context 'when linking multiple cross projects issues at once' do
+ let(:issues_linked) { [second_issue, third_issue] }
+ let(:content) { "/#{command} #{second_issue.to_reference(project)} #{third_issue.to_reference(project)}" }
+
+ it_behaves_like 'link command'
+ end
+
+ context 'when linking a non-existing issue' do
+ let(:issues_linked) { [] }
+ let(:content) { "/#{command} imaginary##{non_existing_record_iid}" }
+
+ it_behaves_like 'link command'
+ end
+
+ context 'when linking a private issue' do
+ let_it_be(:private_issue) { create(:issue, project: create(:project, :private)) }
+ let(:issues_linked) { [] }
+ let(:content) { "/#{command} #{private_issue.to_reference(project)}" }
+
+ it_behaves_like 'link command'
+ end
+ end
+ end
+
+ def create_existing_link(command)
+ issues = [issue, second_issue]
+ source, target = command == :blocked_by ? issues.reverse : issues
+
+ create(:issue_link, source: source, target: target, link_type: link_type)
+ end
+end
diff --git a/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb
index 3f1a98ca08e..7bd7500d546 100644
--- a/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/issue/promote_to_incident_quick_action_shared_examples.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
RSpec.shared_examples 'promote_to_incident quick action' do
+ include ListboxHelpers
+
describe '/promote_to_incident' do
context 'when issue can be promoted' do
it 'promotes issue to incident' do
@@ -52,9 +54,11 @@ RSpec.shared_examples 'promote_to_incident quick action' do
context 'when incident is selected for issue type' do
it 'promotes issue to incident' do
visit new_project_issue_path(project)
+ wait_for_requests
+
fill_in('Title', with: 'Title')
find('.js-issuable-type-filter-dropdown-wrap').click
- click_link('Incident')
+ select_listbox_item(_('Incident'))
fill_in('Description', with: '/promote_to_incident')
click_button('Create issue')
diff --git a/spec/support/redis/redis_new_instance_shared_examples.rb b/spec/support/shared_examples/redis/redis_new_instance_shared_examples.rb
index 435d342fcca..4a3732efe13 100644
--- a/spec/support/redis/redis_new_instance_shared_examples.rb
+++ b/spec/support/shared_examples/redis/redis_new_instance_shared_examples.rb
@@ -6,7 +6,6 @@ RSpec.shared_examples "redis_new_instance_shared_examples" do |name, fallback_cl
include TmpdirHelper
let(:instance_specific_config_file) { "config/redis.#{name}.yml" }
- let(:environment_config_file_name) { "GITLAB_REDIS_#{name.upcase}_CONFIG_FILE" }
let(:fallback_config_file) { nil }
let(:rails_root) { mktmpdir }
@@ -16,32 +15,6 @@ RSpec.shared_examples "redis_new_instance_shared_examples" do |name, fallback_cl
it_behaves_like "redis_shared_examples"
- describe '.config_file_name' do
- subject { described_class.config_file_name }
-
- before do
- # Undo top-level stub of config_file_name because we are testing that method now.
- allow(described_class).to receive(:config_file_name).and_call_original
-
- allow(described_class).to receive(:rails_root).and_return(rails_root)
- FileUtils.mkdir_p(File.join(rails_root, 'config'))
- end
-
- context 'and there is a global env override' do
- before do
- stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
- end
-
- it { expect(subject).to eq('global override') }
-
- context "and #{fallback_class.name.demodulize} has a different config file" do
- let(:fallback_config_file) { 'fallback config file' }
-
- it { expect(subject).to eq('fallback config file') }
- end
- end
- end
-
describe '#fetch_config' do
subject { described_class.new('test').send(:fetch_config) }
@@ -98,7 +71,7 @@ RSpec.shared_examples "redis_new_instance_shared_examples" do |name, fallback_cl
context 'when resque.yml exists' do
before do
File.write(File.join(rails_root, 'config/resque.yml'), {
- 'test' => { 'foobar' => 123 }
+ 'test' => { 'foobar' => 123 }
}.to_json)
end
diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/shared_examples/redis/redis_shared_examples.rb
index 8c195a9dbeb..9224e01b1fe 100644
--- a/spec/support/redis/redis_shared_examples.rb
+++ b/spec/support/shared_examples/redis/redis_shared_examples.rb
@@ -39,34 +39,6 @@ RSpec.shared_examples "redis_shared_examples" do
context 'when there is no config file anywhere' do
it { expect(subject).to be_nil }
-
- context 'and there is a global env override' do
- before do
- stub_env('GITLAB_REDIS_CONFIG_FILE', 'global override')
- end
-
- it { expect(subject).to eq('global override') }
-
- context 'and there is an instance specific config file' do
- before do
- FileUtils.touch(File.join(rails_root, instance_specific_config_file))
- end
-
- it { expect(subject).to eq("#{rails_root}/#{instance_specific_config_file}") }
-
- it 'returns a path that exists' do
- expect(File.file?(subject)).to eq(true)
- end
-
- context 'and there is a specific env override' do
- before do
- stub_env(environment_config_file_name, 'instance specific override')
- end
-
- it { expect(subject).to eq('instance specific override') }
- end
- end
- end
end
end
@@ -87,7 +59,9 @@ RSpec.shared_examples "redis_shared_examples" do
context 'with the namespace' do
let(:namespace) { 'namespace_name' }
- let(:redis_store_to_s) { "Redis Client connected to #{host} against DB #{redis_database} with namespace #{namespace}" }
+ let(:redis_store_to_s) do
+ "Redis Client connected to #{host} against DB #{redis_database} with namespace #{namespace}"
+ end
subject { described_class.new(rails_env).store(namespace: namespace) }
@@ -188,12 +162,13 @@ RSpec.shared_examples "redis_shared_examples" do
with_them do
it 'returns hash with cluster and password' do
- is_expected.to include(password: 'myclusterpassword',
- cluster: [
- { host: "#{host}1", port: redis_port },
- { host: "#{host}2", port: redis_port }
- ]
- )
+ is_expected.to include(
+ password: 'myclusterpassword',
+ cluster: [
+ { host: "#{host}1", port: redis_port },
+ { host: "#{host}2", port: redis_port }
+ ]
+ )
is_expected.not_to have_key(:url)
end
end
@@ -237,6 +212,7 @@ RSpec.shared_examples "redis_shared_examples" do
before do
clear_pool
end
+
after do
clear_pool
end
@@ -407,12 +383,6 @@ RSpec.shared_examples "redis_shared_examples" do
end
end
- it 'has a value for the legacy default URL' do
- allow(subject).to receive(:fetch_config) { nil }
-
- expect(subject.send(:raw_config_hash)).to include(url: a_string_matching(%r{\Aredis://localhost:638[012]\Z}))
- end
-
context 'when redis.yml exists' do
subject { described_class.new('test').send(:fetch_config) }
@@ -436,11 +406,11 @@ RSpec.shared_examples "redis_shared_examples" do
expect(subject).to eq(nil)
end
- context 'but resque.yml exists' do
+ context 'when resque.yml exists' do
before do
FileUtils.mkdir_p(File.join(rails_root, 'config'))
File.write(File.join(rails_root, 'config/resque.yml'), {
- 'test' => { 'foobar' => 123 }
+ 'test' => { 'foobar' => 123 }
}.to_json)
end
diff --git a/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
index 2170025824f..74dbec063e0 100644
--- a/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
+++ b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
@@ -14,7 +14,7 @@ RSpec.shared_examples 'GET resource access tokens available' do
it 'lists all available scopes' do
get_access_tokens
- expect(assigns(:scopes)).to eq(Gitlab::Auth.resource_bot_scopes)
+ expect(assigns(:scopes)).to eq(Gitlab::Auth.available_scopes_for(resource))
end
it 'returns for json response' do
diff --git a/spec/support/shared_examples/requests/admin_mode_shared_examples.rb b/spec/support/shared_examples/requests/admin_mode_shared_examples.rb
index 07fde7d3f35..4f198dfb740 100644
--- a/spec/support/shared_examples/requests/admin_mode_shared_examples.rb
+++ b/spec/support/shared_examples/requests/admin_mode_shared_examples.rb
@@ -1,98 +1,79 @@
# frozen_string_literal: true
-RSpec.shared_examples 'GET request permissions for admin mode' do
- it_behaves_like 'GET request permissions for admin mode when user'
- it_behaves_like 'GET request permissions for admin mode when admin'
-end
-
-RSpec.shared_examples 'PUT request permissions for admin mode' do |params|
- it_behaves_like 'PUT request permissions for admin mode when user', params
- it_behaves_like 'PUT request permissions for admin mode when admin', params
-end
-
-RSpec.shared_examples 'POST request permissions for admin mode' do |params|
- it_behaves_like 'POST request permissions for admin mode when user', params
- it_behaves_like 'POST request permissions for admin mode when admin', params
-end
RSpec.shared_examples 'DELETE request permissions for admin mode' do
- it_behaves_like 'DELETE request permissions for admin mode when user'
- it_behaves_like 'DELETE request permissions for admin mode when admin'
-end
-
-RSpec.shared_examples 'GET request permissions for admin mode when user' do
- subject { get api(path, current_user, admin_mode: admin_mode) }
+ subject { delete api(path, current_user, admin_mode: admin_mode) }
- let_it_be(:current_user) { create(:user) }
+ let_it_be(:success_status_code) { :no_content }
+ let_it_be(:failed_status_code) { :forbidden }
- it_behaves_like 'admin mode on', true, :forbidden
- it_behaves_like 'admin mode on', false, :forbidden
+ it_behaves_like 'when admin'
+ it_behaves_like 'when user'
end
-RSpec.shared_examples 'GET request permissions for admin mode when admin' do
+RSpec.shared_examples 'GET request permissions for admin mode' do
subject { get api(path, current_user, admin_mode: admin_mode) }
- let_it_be(:current_user) { create(:admin) }
-
- it_behaves_like 'admin mode on', true, :ok
- it_behaves_like 'admin mode on', false, :forbidden
-end
-
-RSpec.shared_examples 'PUT request permissions for admin mode when user' do |params|
- subject { put api(path, current_user, admin_mode: admin_mode), params: params }
-
- let_it_be(:current_user) { create(:user) }
+ let_it_be(:success_status_code) { :ok }
+ let_it_be(:failed_status_code) { :forbidden }
- it_behaves_like 'admin mode on', true, :forbidden
- it_behaves_like 'admin mode on', false, :forbidden
+ it_behaves_like 'when admin'
+ it_behaves_like 'when user'
end
-RSpec.shared_examples 'PUT request permissions for admin mode when admin' do |params|
+RSpec.shared_examples 'PUT request permissions for admin mode' do
subject { put api(path, current_user, admin_mode: admin_mode), params: params }
- let_it_be(:current_user) { create(:admin) }
+ let_it_be(:success_status_code) { :ok }
+ let_it_be(:failed_status_code) { :forbidden }
- it_behaves_like 'admin mode on', true, :ok
- it_behaves_like 'admin mode on', false, :forbidden
+ it_behaves_like 'when admin'
+ it_behaves_like 'when user'
end
-RSpec.shared_examples 'POST request permissions for admin mode when user' do |params|
+RSpec.shared_examples 'POST request permissions for admin mode' do
subject { post api(path, current_user, admin_mode: admin_mode), params: params }
- let_it_be(:current_user) { create(:user) }
+ let_it_be(:success_status_code) { :created }
+ let_it_be(:failed_status_code) { :forbidden }
- it_behaves_like 'admin mode on', true, :forbidden
- it_behaves_like 'admin mode on', false, :forbidden
+ it_behaves_like 'when admin'
+ it_behaves_like 'when user'
end
-RSpec.shared_examples 'POST request permissions for admin mode when admin' do |params|
- subject { post api(path, current_user, admin_mode: admin_mode), params: params }
+RSpec.shared_examples 'when user' do
+ let_it_be(:current_user) { create(:user) }
- let_it_be(:current_user) { create(:admin) }
+ include_examples 'makes request' do
+ let(:status) { failed_status_code }
+ let(:admin_mode) { true }
+ end
- it_behaves_like 'admin mode on', true, :created
- it_behaves_like 'admin mode on', false, :forbidden
+ it_behaves_like 'makes request' do
+ let(:status) { failed_status_code }
+ let(:admin_mode) { false }
+ end
end
-RSpec.shared_examples 'DELETE request permissions for admin mode when user' do
- subject { delete api(path, current_user, admin_mode: admin_mode) }
+RSpec.shared_examples 'when admin' do
+ let_it_be(:current_user) { create(:admin) }
- let_it_be(:current_user) { create(:user) }
+ it_behaves_like 'makes request' do
+ let(:status) { success_status_code }
+ let(:admin_mode) { true }
+ end
- it_behaves_like 'admin mode on', true, :forbidden
- it_behaves_like 'admin mode on', false, :forbidden
+ it_behaves_like 'makes request' do
+ let(:status) { failed_status_code }
+ let(:admin_mode) { false }
+ end
end
-RSpec.shared_examples 'DELETE request permissions for admin mode when admin' do
- subject { delete api(path, current_user, admin_mode: admin_mode) }
-
- let_it_be(:current_user) { create(:admin) }
-
- it_behaves_like 'admin mode on', true, :no_content
- it_behaves_like 'admin mode on', false, :forbidden
-end
+RSpec.shared_examples "makes request" do
+ let_it_be(:status) { nil }
-RSpec.shared_examples "admin mode on" do |admin_mode, status|
- let_it_be(:admin_mode) { admin_mode }
+ it "returns" do
+ subject
- it_behaves_like 'returning response status', status
+ expect(response).to have_gitlab_http_status(status)
+ end
end
diff --git a/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb b/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb
index f5c41416763..3ff52166990 100644
--- a/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/container_repositories_shared_examples.rb
@@ -18,7 +18,7 @@ RSpec.shared_examples 'returns repositories for allowed users' do |user_type, sc
subject
expect(json_response.length).to eq(2)
- expect(json_response.map { |repository| repository['id'] }).to contain_exactly(
+ expect(json_response.pluck('id')).to contain_exactly(
root_repository.id, test_repository.id)
expect(response.body).not_to include('tags')
expect(response.body).not_to include('tags_count')
@@ -47,7 +47,7 @@ RSpec.shared_examples 'returns tags for allowed users' do |user_type, scope|
subject
expect(json_response.length).to eq(2)
- expect(json_response.map { |repository| repository['id'] }).to contain_exactly(
+ expect(json_response.pluck('id')).to contain_exactly(
root_repository.id, test_repository.id)
expect(response.body).to include('tags')
end
diff --git a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
index f31cbcfdec1..804221b7416 100644
--- a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
@@ -4,7 +4,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
let!(:custom_attribute1) { attributable.custom_attributes.create! key: 'foo', value: 'foo' }
let!(:custom_attribute2) { attributable.custom_attributes.create! key: 'bar', value: 'bar' }
- describe "GET /#{attributable_name} with custom attributes filter" do
+ describe "GET /#{attributable_name} with custom attributes filter", :aggregate_failures do
before do
other_attributable
end
@@ -14,13 +14,13 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
get api("/#{attributable_name}", user), params: { custom_attributes: { foo: 'foo', bar: 'bar' } }
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response.map { |r| r['id'] }).to include(attributable.id, other_attributable.id)
+ expect(json_response.pluck('id')).to include(attributable.id, other_attributable.id)
end
end
context 'with an authorized user' do
it 'filters by custom attributes' do
- get api("/#{attributable_name}", admin), params: { custom_attributes: { foo: 'foo', bar: 'bar' } }
+ get api("/#{attributable_name}", admin, admin_mode: true), params: { custom_attributes: { foo: 'foo', bar: 'bar' } }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.size).to be 1
@@ -29,7 +29,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
end
end
- describe "GET /#{attributable_name} with custom attributes" do
+ describe "GET /#{attributable_name} with custom attributes", :aggregate_failures do
before do
other_attributable
end
@@ -46,7 +46,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
context 'with an authorized user' do
it 'does not include custom attributes by default' do
- get api("/#{attributable_name}", admin)
+ get api("/#{attributable_name}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).not_to be_empty
@@ -54,7 +54,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
end
it 'includes custom attributes if requested' do
- get api("/#{attributable_name}", admin), params: { with_custom_attributes: true }
+ get api("/#{attributable_name}", admin, admin_mode: true), params: { with_custom_attributes: true }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).not_to be_empty
@@ -72,7 +72,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
end
end
- describe "GET /#{attributable_name}/:id with custom attributes" do
+ describe "GET /#{attributable_name}/:id with custom attributes", :aggregate_failures do
context 'with an unauthorized user' do
it 'does not include custom attributes' do
get api("/#{attributable_name}/#{attributable.id}", user), params: { with_custom_attributes: true }
@@ -84,14 +84,14 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
context 'with an authorized user' do
it 'does not include custom attributes by default' do
- get api("/#{attributable_name}/#{attributable.id}", admin)
+ get api("/#{attributable_name}/#{attributable.id}", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).not_to include 'custom_attributes'
end
it 'includes custom attributes if requested' do
- get api("/#{attributable_name}/#{attributable.id}", admin), params: { with_custom_attributes: true }
+ get api("/#{attributable_name}/#{attributable.id}", admin, admin_mode: true), params: { with_custom_attributes: true }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['custom_attributes']).to contain_exactly(
@@ -102,7 +102,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
end
end
- describe "GET /#{attributable_name}/:id/custom_attributes" do
+ describe "GET /#{attributable_name}/:id/custom_attributes", :aggregate_failures do
context 'with an unauthorized user' do
subject { get api("/#{attributable_name}/#{attributable.id}/custom_attributes", user) }
@@ -111,7 +111,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
context 'with an authorized user' do
it 'returns all custom attributes' do
- get api("/#{attributable_name}/#{attributable.id}/custom_attributes", admin)
+ get api("/#{attributable_name}/#{attributable.id}/custom_attributes", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to contain_exactly(
@@ -122,7 +122,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
end
end
- describe "GET /#{attributable_name}/:id/custom_attributes/:key" do
+ describe "GET /#{attributable_name}/:id/custom_attributes/:key", :aggregate_failures do
context 'with an unauthorized user' do
subject { get api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", user) }
@@ -131,7 +131,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
context 'with an authorized user' do
it 'returns a single custom attribute' do
- get api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin)
+ get api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin, admin_mode: true)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq({ 'key' => 'foo', 'value' => 'foo' })
@@ -139,7 +139,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
end
end
- describe "PUT /#{attributable_name}/:id/custom_attributes/:key" do
+ describe "PUT /#{attributable_name}/:id/custom_attributes/:key", :aggregate_failures do
context 'with an unauthorized user' do
subject { put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", user), params: { value: 'new' } }
@@ -149,7 +149,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
context 'with an authorized user' do
it 'creates a new custom attribute' do
expect do
- put api("/#{attributable_name}/#{attributable.id}/custom_attributes/new", admin), params: { value: 'new' }
+ put api("/#{attributable_name}/#{attributable.id}/custom_attributes/new", admin, admin_mode: true), params: { value: 'new' }
end.to change { attributable.custom_attributes.count }.by(1)
expect(response).to have_gitlab_http_status(:ok)
@@ -159,7 +159,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
it 'updates an existing custom attribute' do
expect do
- put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin), params: { value: 'new' }
+ put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin, admin_mode: true), params: { value: 'new' }
end.not_to change { attributable.custom_attributes.count }
expect(response).to have_gitlab_http_status(:ok)
@@ -169,7 +169,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
end
end
- describe "DELETE /#{attributable_name}/:id/custom_attributes/:key" do
+ describe "DELETE /#{attributable_name}/:id/custom_attributes/:key", :aggregate_failures do
context 'with an unauthorized user' do
subject { delete api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", user) }
@@ -179,7 +179,7 @@ RSpec.shared_examples 'custom attributes endpoints' do |attributable_name|
context 'with an authorized user' do
it 'deletes an existing custom attribute' do
expect do
- delete api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin)
+ delete api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin, admin_mode: true)
end.to change { attributable.custom_attributes.count }.by(-1)
expect(response).to have_gitlab_http_status(:no_content)
diff --git a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
index 6d29076da0f..bc7ad570441 100644
--- a/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/debian_packages_shared_examples.rb
@@ -165,3 +165,41 @@ RSpec.shared_examples 'Debian packages write endpoint' do |desired_behavior, suc
it_behaves_like 'rejects Debian access with unknown container id', :unauthorized, :basic
end
+
+RSpec.shared_examples 'Debian packages endpoint catching ObjectStorage::RemoteStoreError' do
+ include_context 'Debian repository access', :public, :developer, :basic do
+ it "returns forbidden" do
+ expect(::Packages::Debian::CreatePackageFileService).to receive(:new).and_raise ObjectStorage::RemoteStoreError
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+end
+
+RSpec.shared_examples 'Debian packages index endpoint' do |success_body|
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, success_body
+
+ context 'when no ComponentFile is found' do
+ let(:target_component_name) { component.name + FFaker::Lorem.word }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :no_content, /^$/
+ end
+end
+
+RSpec.shared_examples 'Debian packages index sha256 endpoint' do |success_body|
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, success_body
+
+ context 'with empty checksum' do
+ let(:target_sha256) { 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :no_content, /^$/
+ end
+
+ context 'when ComponentFile is not found' do
+ let(:target_component_name) { component.name + FFaker::Lorem.word }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :not_found, /^{"message":"404 Not Found"}$/
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/discussions_shared_examples.rb b/spec/support/shared_examples/requests/api/discussions_shared_examples.rb
index f577e2ad323..2996c794e52 100644
--- a/spec/support/shared_examples/requests/api/discussions_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/discussions_shared_examples.rb
@@ -123,18 +123,6 @@ RSpec.shared_examples 'discussions API' do |parent_type, noteable_type, id_name,
expect_snowplow_event(category: 'Notes::CreateService', action: 'execute', label: 'note', value: anything)
end
- context 'with notes_create_service_tracking feature flag disabled' do
- before do
- stub_feature_flags(notes_create_service_tracking: false)
- end
-
- it 'does not track Notes::CreateService events', :snowplow do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions"), params: { body: 'hi!' }
-
- expect_no_snowplow_event(category: 'Notes::CreateService', action: 'execute')
- end
- end
-
context 'when an admin or owner makes the request' do
it 'accepts the creation date to be set' do
creation_time = 2.weeks.ago
@@ -243,8 +231,7 @@ RSpec.shared_examples 'discussions API' do |parent_type, noteable_type, id_name,
it 'returns a 404 error when note id not found' do
put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
- "discussions/#{note.discussion_id}/notes/#{non_existing_record_id}", user),
- params: { body: 'Hello!' }
+ "discussions/#{note.discussion_id}/notes/#{non_existing_record_id}", user), params: { body: 'Hello!' }
expect(response).to have_gitlab_http_status(:not_found)
end
diff --git a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
index 6c8b792bf92..930c47dac52 100644
--- a/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/issue_list_shared_examples.rb
@@ -480,6 +480,7 @@ RSpec.shared_examples 'graphql issue list request spec' do
context 'when fetching escalation status' do
let_it_be(:escalation_status) { create(:incident_management_issuable_escalation_status, issue: issue_a) }
+ let_it_be(:incident_type) { WorkItems::Type.default_by_type(:incident) }
let(:fields) do
<<~QUERY
@@ -491,7 +492,7 @@ RSpec.shared_examples 'graphql issue list request spec' do
end
before do
- issue_a.update_columns(issue_type: Issue.issue_types[:incident])
+ issue_a.update_columns(issue_type: WorkItems::Type.base_types[:incident], work_item_type_id: incident_type.id)
end
it 'returns the escalation status values' do
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
index b459e479c91..53329c5caec 100644
--- a/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/snippets_shared_examples.rb
@@ -6,7 +6,7 @@ RSpec.shared_examples 'when the snippet is not found' do
end
it_behaves_like 'a mutation that returns top-level errors',
- errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
+ errors: [Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
end
RSpec.shared_examples 'snippet edit usage data counters' do
diff --git a/spec/support/shared_examples/requests/api/graphql/mutations/subscription_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/mutations/subscription_shared_examples.rb
index 40b88ef370f..4dc0264172f 100644
--- a/spec/support/shared_examples/requests/api/graphql/mutations/subscription_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/mutations/subscription_shared_examples.rb
@@ -36,9 +36,9 @@ RSpec.shared_examples 'a subscribable resource api' do
context 'when the user is not authorized' do
it_behaves_like 'a mutation that returns top-level errors',
- errors: ["The resource that you are attempting to access "\
- "does not exist or you don't have permission to "\
- "perform this action"]
+ errors: ["The resource that you are attempting to access "\
+ "does not exist or you don't have permission to "\
+ "perform this action"]
end
context 'when user is authorized' do
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
index f5835460a77..5e9dfc826d4 100644
--- a/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb
@@ -279,11 +279,11 @@ RSpec.shared_examples 'group and project packages query' do
end
def npm_pipeline_ids
- graphql_data_npm_package.dig('pipelines', 'nodes').map { |pipeline| pipeline['id'] }
+ graphql_data_npm_package.dig('pipelines', 'nodes').pluck('id')
end
def composer_pipeline_ids
- graphql_data_composer_package.dig('pipelines', 'nodes').map { |pipeline| pipeline['id'] }
+ graphql_data_composer_package.dig('pipelines', 'nodes').pluck('id')
end
def graphql_data_npm_package
diff --git a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
index b4019d7c232..161f4a02b8c 100644
--- a/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/packages/package_details_shared_examples.rb
@@ -38,7 +38,7 @@ RSpec.shared_examples 'a package with files' do
context 'with package files pending destruction' do
let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: package) }
- let(:response_package_file_ids) { package_files_response.map { |pf| pf['id'] } }
+ let(:response_package_file_ids) { package_files_response.pluck('id') }
it 'does not return them' do
expect(package.reload.package_files).to include(package_file_pending_destruction)
diff --git a/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb b/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
index 6b4d8cae2ce..6648c18fb70 100644
--- a/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
+++ b/spec/support/shared_examples/requests/api/graphql/projects/branch_protections/access_level_request_examples.rb
@@ -13,13 +13,15 @@ RSpec.shared_examples 'a GraphQL query for access levels' do |access_level_kind|
let(:maintainer_access_level) { access_levels.for_role.first }
let(:maintainer_access_level_data) { access_levels_data.first }
let(:access_levels_data) do
- graphql_data_at('project',
- 'branchRules',
- 'nodes',
- 0,
- 'branchProtection',
- "#{access_level_kind.to_s.camelize(:lower)}AccessLevels",
- 'nodes')
+ graphql_data_at(
+ 'project',
+ 'branchRules',
+ 'nodes',
+ 0,
+ 'branchProtection',
+ "#{access_level_kind.to_s.camelize(:lower)}AccessLevels",
+ 'nodes'
+ )
end
let(:query) do
diff --git a/spec/support/shared_examples/requests/api/hooks_shared_examples.rb b/spec/support/shared_examples/requests/api/hooks_shared_examples.rb
index f2002de4b55..a2c34aa6a54 100644
--- a/spec/support/shared_examples/requests/api/hooks_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/hooks_shared_examples.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
RSpec.shared_examples 'web-hook API endpoints test hook' do |prefix|
- describe "POST #{prefix}/:hook_id" do
+ describe "POST #{prefix}/:hook_id", :aggregate_failures do
it 'tests the hook' do
expect(WebHookService)
.to receive(:new).with(hook, anything, String, force: false)
.and_return(instance_double(WebHookService, execute: nil))
- post api(hook_uri, user)
+ post api(hook_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:created)
end
@@ -17,7 +17,7 @@ end
RSpec.shared_examples 'web-hook API endpoints with branch-filter' do |prefix|
describe "POST #{prefix}/hooks" do
it "returns a 422 error if branch filter is not valid" do
- post api(collection_uri, user),
+ post api(collection_uri, user, admin_mode: user.admin?),
params: { url: "http://example.com", push_events_branch_filter: '~badbranchname/' }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
@@ -58,10 +58,10 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
let(:default_values) { {} }
- describe "GET #{prefix}/hooks" do
+ describe "GET #{prefix}/hooks", :aggregate_failures do
context "authorized user" do
it "returns all hooks" do
- get api(collection_uri, user)
+ get api(collection_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_collection_schema
@@ -70,7 +70,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
context "when user is forbidden" do
it "prevents access to hooks" do
- get api(collection_uri, unauthorized_user)
+ get api(collection_uri, unauthorized_user, admin_mode: true)
expect(response).to have_gitlab_http_status(:forbidden)
end
@@ -90,7 +90,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
end
it 'returns the names of the url variables' do
- get api(collection_uri, user)
+ get api(collection_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to contain_exactly(
@@ -102,10 +102,10 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
end
end
- describe "GET #{prefix}/hooks/:hook_id" do
+ describe "GET #{prefix}/hooks/:hook_id", :aggregate_failures do
context "authorized user" do
it "returns a project hook" do
- get api(hook_uri, user)
+ get api(hook_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_hook_schema
@@ -114,7 +114,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
end
it "returns a 404 error if hook id is not available" do
- get api(hook_uri(non_existing_record_id), user)
+ get api(hook_uri(non_existing_record_id), user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -125,7 +125,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
end
it "has the correct alert status", :aggregate_failures do
- get api(hook_uri, user)
+ get api(hook_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:ok)
@@ -135,12 +135,12 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
context 'the hook is backed-off' do
before do
- WebHook::FAILURE_THRESHOLD.times { hook.backoff! }
+ WebHooks::AutoDisabling::FAILURE_THRESHOLD.times { hook.backoff! }
hook.backoff!
end
it "has the correct alert status", :aggregate_failures do
- get api(hook_uri, user)
+ get api(hook_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:ok)
@@ -156,7 +156,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
context "when user is forbidden" do
it "does not access an existing hook" do
- get api(hook_uri, unauthorized_user)
+ get api(hook_uri, unauthorized_user, admin_mode: true)
expect(response).to have_gitlab_http_status(:forbidden)
end
@@ -171,13 +171,12 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
end
end
- describe "POST #{prefix}/hooks" do
+ describe "POST #{prefix}/hooks", :aggregate_failures do
let(:hook_creation_params) { hook_params }
it "adds hook", :aggregate_failures do
expect do
- post api(collection_uri, user),
- params: hook_creation_params
+ post api(collection_uri, user, admin_mode: user.admin?), params: hook_creation_params
end.to change { hooks_count }.by(1)
expect(response).to have_gitlab_http_status(:created)
@@ -201,8 +200,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
token = "secret token"
expect do
- post api(collection_uri, user),
- params: { url: "http://example.com", token: token }
+ post api(collection_uri, user, admin_mode: user.admin?), params: { url: "http://example.com", token: token }
end.to change { hooks_count }.by(1)
expect(response).to have_gitlab_http_status(:created)
@@ -216,19 +214,19 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
end
it "returns a 400 error if url not given" do
- post api(collection_uri, user), params: { event_names.first => true }
+ post api(collection_uri, user, admin_mode: user.admin?), params: { event_names.first => true }
expect(response).to have_gitlab_http_status(:bad_request)
end
it "returns a 400 error if no parameters are provided" do
- post api(collection_uri, user)
+ post api(collection_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:bad_request)
end
it 'sets default values for events', :aggregate_failures do
- post api(collection_uri, user), params: { url: 'http://mep.mep' }
+ post api(collection_uri, user, admin_mode: user.admin?), params: { url: 'http://mep.mep' }
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_hook_schema
@@ -239,22 +237,22 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
end
it "returns a 422 error if token not valid" do
- post api(collection_uri, user),
+ post api(collection_uri, user, admin_mode: user.admin?),
params: { url: "http://example.com", token: "foo\nbar" }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
it "returns a 422 error if url not valid" do
- post api(collection_uri, user), params: { url: "ftp://example.com" }
+ post api(collection_uri, user, admin_mode: user.admin?), params: { url: "ftp://example.com" }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
- describe "PUT #{prefix}/hooks/:hook_id" do
+ describe "PUT #{prefix}/hooks/:hook_id", :aggregate_failures do
it "updates an existing hook" do
- put api(hook_uri, user), params: update_params
+ put api(hook_uri, user, admin_mode: user.admin?), params: update_params
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_hook_schema
@@ -267,7 +265,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
it 'updates the URL variables' do
hook.update!(url_variables: { 'abc' => 'some value' })
- put api(hook_uri, user),
+ put api(hook_uri, user, admin_mode: user.admin?),
params: { url_variables: [{ key: 'def', value: 'other value' }] }
expect(response).to have_gitlab_http_status(:ok)
@@ -280,7 +278,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
it "adds the token without including it in the response" do
token = "secret token"
- put api(hook_uri, user), params: { url: "http://example.org", token: token }
+ put api(hook_uri, user, admin_mode: user.admin?), params: { url: "http://example.org", token: token }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response["url"]).to eq("http://example.org")
@@ -291,68 +289,68 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
end
it "returns 404 error if hook id not found" do
- put api(hook_uri(non_existing_record_id), user), params: { url: 'http://example.org' }
+ put api(hook_uri(non_existing_record_id), user, admin_mode: user.admin?), params: { url: 'http://example.org' }
expect(response).to have_gitlab_http_status(:not_found)
end
it "returns 400 error if no parameters are provided" do
- put api(hook_uri, user)
+ put api(hook_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:bad_request)
end
it "returns a 422 error if url is not valid" do
- put api(hook_uri, user), params: { url: 'ftp://example.com' }
+ put api(hook_uri, user, admin_mode: user.admin?), params: { url: 'ftp://example.com' }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
it "returns a 422 error if token is not valid" do
- put api(hook_uri, user), params: { token: %w[foo bar].join("\n") }
+ put api(hook_uri, user, admin_mode: user.admin?), params: { token: %w[foo bar].join("\n") }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
- describe "DELETE /projects/:id/hooks/:hook_id" do
+ describe "DELETE /projects/:id/hooks/:hook_id", :aggregate_failures do
it "deletes hook from project" do
expect do
- delete api(hook_uri, user)
+ delete api(hook_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { hooks_count }.by(-1)
end
it "returns a 404 error when deleting non existent hook" do
- delete api(hook_uri(non_existing_record_id), user)
+ delete api(hook_uri(non_existing_record_id), user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
it "returns a 404 error if hook id not given" do
- delete api(collection_uri, user)
+ delete api(collection_uri, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
it "returns forbidden if a user attempts to delete hooks they do not own" do
- delete api(hook_uri, unauthorized_user)
+ delete api(hook_uri, unauthorized_user, admin_mode: true)
expect(response).to have_gitlab_http_status(:forbidden)
expect(WebHook.exists?(hook.id)).to be_truthy
end
it_behaves_like '412 response' do
- let(:request) { api(hook_uri, user) }
+ let(:request) { api(hook_uri, user, admin_mode: user.admin?) }
end
end
describe "PUT #{prefix}/hooks/:hook_id/url_variables/:key", :aggregate_failures do
it 'sets the variable' do
expect do
- put api("#{hook_uri}/url_variables/abc", user),
- params: { value: 'some secret value' }
+ put api("#{hook_uri}/url_variables/abc", user, admin_mode: user.admin?),
+ params: { value: 'some secret value' }
end.to change { hook.reload.url_variables }.to(eq('abc' => 'some secret value'))
expect(response).to have_gitlab_http_status(:no_content)
@@ -361,30 +359,30 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
it 'overwrites existing values' do
hook.update!(url_variables: { 'abc' => 'xyz', 'def' => 'other value' })
- put api("#{hook_uri}/url_variables/abc", user),
- params: { value: 'some secret value' }
+ put api("#{hook_uri}/url_variables/abc", user, admin_mode: user.admin?),
+ params: { value: 'some secret value' }
expect(response).to have_gitlab_http_status(:no_content)
expect(hook.reload.url_variables).to eq('abc' => 'some secret value', 'def' => 'other value')
end
it "returns a 404 error when editing non existent hook" do
- put api("#{hook_uri(non_existing_record_id)}/url_variables/abc", user),
- params: { value: 'xyz' }
+ put api("#{hook_uri(non_existing_record_id)}/url_variables/abc", user, admin_mode: user.admin?),
+ params: { value: 'xyz' }
expect(response).to have_gitlab_http_status(:not_found)
end
it "returns a 422 error when the key is illegal" do
- put api("#{hook_uri}/url_variables/abc%20def", user),
- params: { value: 'xyz' }
+ put api("#{hook_uri}/url_variables/abc%20def", user, admin_mode: user.admin?),
+ params: { value: 'xyz' }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
it "returns a 422 error when the value is illegal" do
- put api("#{hook_uri}/url_variables/abc", user),
- params: { value: '' }
+ put api("#{hook_uri}/url_variables/abc", user, admin_mode: user.admin?),
+ params: { value: '' }
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
@@ -397,7 +395,7 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
it 'unsets the variable' do
expect do
- delete api("#{hook_uri}/url_variables/abc", user)
+ delete api("#{hook_uri}/url_variables/abc", user, admin_mode: user.admin?)
end.to change { hook.reload.url_variables }.to(eq({ 'def' => 'other value' }))
expect(response).to have_gitlab_http_status(:no_content)
@@ -406,13 +404,13 @@ RSpec.shared_examples 'web-hook API endpoints' do |prefix|
it 'returns 404 for keys that do not exist' do
hook.update!(url_variables: { 'def' => 'other value' })
- delete api("#{hook_uri}/url_variables/abc", user)
+ delete api("#{hook_uri}/url_variables/abc", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
it "returns a 404 error when deleting a variable from a non existent hook" do
- delete api(hook_uri(non_existing_record_id) + "/url_variables/abc", user)
+ delete api(hook_uri(non_existing_record_id) + "/url_variables/abc", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
diff --git a/spec/support/shared_examples/requests/api/integrations/github_enterprise_jira_dvcs_end_of_life_shared_examples.rb b/spec/support/shared_examples/requests/api/integrations/github_enterprise_jira_dvcs_end_of_life_shared_examples.rb
new file mode 100644
index 00000000000..6799dec7b80
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/integrations/github_enterprise_jira_dvcs_end_of_life_shared_examples.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'a GitHub Enterprise Jira DVCS reversible end of life endpoint' do
+ it 'is a reachable endpoint' do
+ subject
+
+ expect(response).not_to have_gitlab_http_status(:not_found)
+ end
+
+ context 'when the flag is disabled' do
+ before do
+ stub_feature_flags(jira_dvcs_end_of_life_amnesty: false)
+ end
+
+ it 'presents as an endpoint that does not exist' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/integrations/slack/slack_request_verification_shared_examples.rb b/spec/support/shared_examples/requests/api/integrations/slack/slack_request_verification_shared_examples.rb
new file mode 100644
index 00000000000..ddda9ca6bcc
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/integrations/slack/slack_request_verification_shared_examples.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples 'Slack request verification' do
+ describe 'unauthorized request' do
+ shared_examples 'an unauthorized request' do
+ specify do
+ subject
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ shared_examples 'a successful request that generates a tracked error' do
+ specify do
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).once
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(response.body).to be_empty
+ end
+ end
+
+ context 'when the slack_app_signing_secret setting is not set' do
+ before do
+ stub_application_setting(slack_app_signing_secret: nil)
+ end
+
+ it_behaves_like 'an unauthorized request'
+ end
+
+ context 'when the timestamp header has expired' do
+ before do
+ headers[::API::Integrations::Slack::Request::VERIFICATION_TIMESTAMP_HEADER] = 5.minutes.ago.to_i.to_s
+ end
+
+ it_behaves_like 'an unauthorized request'
+ end
+
+ context 'when the timestamp header is missing' do
+ before do
+ headers.delete(::API::Integrations::Slack::Request::VERIFICATION_TIMESTAMP_HEADER)
+ end
+
+ it_behaves_like 'an unauthorized request'
+ end
+
+ context 'when the signature header is missing' do
+ before do
+ headers.delete(::API::Integrations::Slack::Request::VERIFICATION_SIGNATURE_HEADER)
+ end
+
+ it_behaves_like 'an unauthorized request'
+ end
+
+ context 'when the signature is not verified' do
+ before do
+ headers[::API::Integrations::Slack::Request::VERIFICATION_SIGNATURE_HEADER] = 'unverified_signature'
+ end
+
+ it_behaves_like 'an unauthorized request'
+ end
+
+ context 'when type param is missing' do
+ it_behaves_like 'a successful request that generates a tracked error'
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb b/spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb
index 1045a92f332..e2c9874e7fc 100644
--- a/spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/issuable_update_shared_examples.rb
@@ -34,5 +34,14 @@ RSpec.shared_examples 'issuable update endpoint' do
expect(json_response['labels']).to include '&'
expect(json_response['labels']).to include '?'
end
+
+ it 'clears milestone when milestone_id=0' do
+ entity.update!(milestone: milestone)
+
+ put api(url, user), params: { milestone_id: 0 }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['milestone']).to be_nil
+ end
end
end
diff --git a/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb b/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb
index 41d21490343..fba0533251a 100644
--- a/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb
@@ -9,6 +9,6 @@ RSpec.shared_examples 'fetches labels' do
expect(json_response).to be_an Array
expect(json_response).to all(match_schema('public_api/v4/labels/label'))
expect(json_response.size).to eq(expected_labels.size)
- expect(json_response.map { |r| r['name'] }).to match_array(expected_labels)
+ expect(json_response.pluck('name')).to match_array(expected_labels)
end
end
diff --git a/spec/support/shared_examples/requests/api/milestones_shared_examples.rb b/spec/support/shared_examples/requests/api/milestones_shared_examples.rb
index 1ea11ba3d7c..ee7d0e86771 100644
--- a/spec/support/shared_examples/requests/api/milestones_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/milestones_shared_examples.rb
@@ -52,7 +52,7 @@ RSpec.shared_examples 'group and project milestones' do |route_definition|
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to be_an Array
expect(json_response.length).to eq(2)
- expect(json_response.map { |m| m['id'] }).to match_array([closed_milestone.id, other_milestone.id])
+ expect(json_response.pluck('id')).to match_array([closed_milestone.id, other_milestone.id])
end
it 'does not return any milestone if none found' do
@@ -293,7 +293,7 @@ RSpec.shared_examples 'group and project milestones' do |route_definition|
expect(json_response).to be_an Array
# 2 for projects, 3 for group(which has another project with an issue)
expect(json_response.size).to be_between(2, 3)
- expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id)
+ expect(json_response.pluck('id')).to include(issue.id, confidential_issue.id)
end
it 'does not return confidential issues to team members with guest role' do
@@ -306,7 +306,7 @@ RSpec.shared_examples 'group and project milestones' do |route_definition|
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
- expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
+ expect(json_response.pluck('id')).to include(issue.id)
end
it 'does not return confidential issues to regular users' do
@@ -316,7 +316,7 @@ RSpec.shared_examples 'group and project milestones' do |route_definition|
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.size).to eq(1)
- expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
+ expect(json_response.pluck('id')).to include(issue.id)
end
it 'returns issues ordered by label priority' do
diff --git a/spec/support/shared_examples/requests/api/ml/mlflow/mlflow_shared_examples.rb b/spec/support/shared_examples/requests/api/ml/mlflow/mlflow_shared_examples.rb
new file mode 100644
index 00000000000..2ca62698daf
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/ml/mlflow/mlflow_shared_examples.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'MLflow|Not Found - Resource Does Not Exist' do
+ it "is Resource Does Not Exist", :aggregate_failures do
+ is_expected.to have_gitlab_http_status(:not_found)
+
+ expect(json_response).to include({ "error_code" => 'RESOURCE_DOES_NOT_EXIST' })
+ end
+end
+
+RSpec.shared_examples 'MLflow|Requires api scope' do
+ context 'when user has access but token has wrong scope' do
+ let(:access_token) { tokens[:read] }
+
+ it { is_expected.to have_gitlab_http_status(:forbidden) }
+ end
+end
+
+RSpec.shared_examples 'MLflow|Requires read_api scope' do
+ context 'when user has access but token has wrong scope' do
+ let(:access_token) { tokens[:no_access] }
+
+ it { is_expected.to have_gitlab_http_status(:forbidden) }
+ end
+end
+
+RSpec.shared_examples 'MLflow|Bad Request' do
+ it "is Bad Request" do
+ is_expected.to have_gitlab_http_status(:bad_request)
+ end
+end
+
+RSpec.shared_examples 'MLflow|shared error cases' do
+ context 'when not authenticated' do
+ let(:headers) { {} }
+
+ it "is Unauthorized" do
+ is_expected.to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'when user does not have access' do
+ let(:access_token) { tokens[:different_user] }
+
+ it "is Not Found" do
+ is_expected.to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when ff is disabled' do
+ let(:ff_value) { false }
+
+ it "is Not Found" do
+ is_expected.to have_gitlab_http_status(:not_found)
+ end
+ end
+end
+
+RSpec.shared_examples 'MLflow|Bad Request on missing required' do |keys|
+ keys.each do |key|
+ context "when \"#{key}\" is missing" do
+ let(:params) { default_params.tap { |p| p.delete(key) } }
+
+ it "is Bad Request" do
+ is_expected.to have_gitlab_http_status(:bad_request)
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/notes_shared_examples.rb b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
index efe5ed3bcf9..b44ff952cdf 100644
--- a/spec/support/shared_examples/requests/api/notes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/notes_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
- describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes" do
+ describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes", :aggregate_failures do
context 'sorting' do
before do
params = { noteable: noteable, author: user }
@@ -12,9 +12,9 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
context 'without sort params' do
it 'sorts by created_at in descending order by default' do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?)
- response_dates = json_response.map { |note| note['created_at'] }
+ response_dates = json_response.pluck('created_at')
expect(json_response.length).to eq(4)
expect(response_dates).to eq(response_dates.sort.reverse)
@@ -23,7 +23,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
it 'fetches notes using parent path as id paremeter' do
parent_id = CGI.escape(parent.full_path)
- get api("/#{parent_type}/#{parent_id}/#{noteable_type}/#{noteable[id_name]}/notes", user)
+ get api("/#{parent_type}/#{parent_id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:ok)
end
@@ -40,18 +40,18 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
it 'page breaks first page correctly' do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?per_page=4", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?per_page=4", user, admin_mode: user.admin?)
- response_ids = json_response.map { |note| note['id'] }
+ response_ids = json_response.pluck('id')
expect(response_ids).to include(@note2.id)
expect(response_ids).not_to include(@first_note.id)
end
it 'page breaks second page correctly' do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?per_page=4&page=2", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?per_page=4&page=2", user, admin_mode: user.admin?)
- response_ids = json_response.map { |note| note['id'] }
+ response_ids = json_response.pluck('id')
expect(response_ids).not_to include(@note2.id)
expect(response_ids).to include(@first_note.id)
@@ -60,27 +60,27 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
it 'sorts by ascending order when requested' do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?sort=asc", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?sort=asc", user, admin_mode: user.admin?)
- response_dates = json_response.map { |note| note['created_at'] }
+ response_dates = json_response.pluck('created_at')
expect(json_response.length).to eq(4)
expect(response_dates).to eq(response_dates.sort)
end
it 'sorts by updated_at in descending order when requested' do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?order_by=updated_at", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?order_by=updated_at", user, admin_mode: user.admin?)
- response_dates = json_response.map { |note| note['updated_at'] }
+ response_dates = json_response.pluck('updated_at')
expect(json_response.length).to eq(4)
expect(response_dates).to eq(response_dates.sort.reverse)
end
it 'sorts by updated_at in ascending order when requested' do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?order_by=updated_at&sort=asc", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes?order_by=updated_at&sort=asc", user, admin_mode: user.admin?)
- response_dates = json_response.map { |note| note['updated_at'] }
+ response_dates = json_response.pluck('updated_at')
expect(json_response.length).to eq(4)
expect(response_dates).to eq(response_dates.sort)
@@ -88,7 +88,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
it "returns an array of notes" do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:ok)
expect(response).to include_pagination_headers
@@ -97,7 +97,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
it "returns a 404 error when noteable id not found" do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{non_existing_record_id}/notes", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{non_existing_record_id}/notes", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -105,36 +105,36 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
it "returns 404 when not authorized" do
parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", private_user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", private_user, admin_mode: private_user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
end
- describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id" do
+ describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id", :aggregate_failures do
it "returns a note by id" do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{note.id}", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{note.id}", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['body']).to eq(note.note)
end
it "returns a 404 error if note not found" do
- get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{non_existing_record_id}", user)
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{non_existing_record_id}", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
end
- describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes" do
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes", :aggregate_failures do
let(:params) { { body: 'hi!' } }
subject do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?), params: params
end
it "creates a new note" do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: { body: 'hi!' }
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?), params: { body: 'hi!' }
expect(response).to have_gitlab_http_status(:created)
expect(json_response['body']).to eq('hi!')
@@ -143,7 +143,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
it "returns a 400 bad request error if body not given" do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user)
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -158,7 +158,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
uri = "/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes"
expect do
- post api(uri, user), params: { body: 'hi!' }
+ post api(uri, user, admin_mode: user.admin?), params: { body: 'hi!' }
end.to change { Event.count }.by(1)
end
@@ -169,7 +169,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
context 'by an admin' do
it 'sets the creation time on the new note' do
admin = create(:admin)
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", admin), params: params
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:created)
expect(json_response['body']).to eq('hi!')
@@ -185,7 +185,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
let(:user) { project.first_owner }
it 'sets the creation time on the new note' do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?), params: params
expect(response).to have_gitlab_http_status(:created)
expect(json_response['body']).to eq('hi!')
@@ -215,7 +215,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
when 'groups'
context 'by a group owner' do
it 'sets the creation time on the new note' do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?), params: params
expect(response).to have_gitlab_http_status(:created)
expect(json_response['body']).to eq('hi!')
@@ -253,7 +253,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
context 'when the user is posting an award emoji on their own noteable' do
it 'creates a new note' do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: { body: ':+1:' }
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?), params: { body: ':+1:' }
expect(response).to have_gitlab_http_status(:created)
expect(json_response['body']).to eq(':+1:')
@@ -266,7 +266,7 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
it 'responds with 404' do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", private_user),
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", private_user, admin_mode: private_user.admin?),
params: { body: 'Foo' }
expect(response).to have_gitlab_http_status(:not_found)
@@ -299,11 +299,11 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
end
- describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id" do
+ describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id", :aggregate_failures do
let(:params) { { body: 'Hello!' } }
subject do
- put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{note.id}", user), params: params
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{note.id}", user, admin_mode: user.admin?), params: params
end
context 'when only body param is present' do
@@ -329,40 +329,40 @@ RSpec.shared_examples 'noteable API' do |parent_type, noteable_type, id_name|
end
it 'returns a 404 error when note id not found' do
- put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{non_existing_record_id}", user),
- params: { body: 'Hello!' }
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{non_existing_record_id}", user, admin_mode: user.admin?),
+ params: { body: 'Hello!' }
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns a 400 bad request error if body is empty' do
put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
- "notes/#{note.id}", user), params: { body: '' }
+ "notes/#{note.id}", user, admin_mode: user.admin?), params: { body: '' }
expect(response).to have_gitlab_http_status(:bad_request)
end
end
- describe "DELETE /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id" do
+ describe "DELETE /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes/:note_id", :aggregate_failures do
it 'deletes a note' do
delete api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
- "notes/#{note.id}", user)
+ "notes/#{note.id}", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:no_content)
# Check if note is really deleted
delete api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
- "notes/#{note.id}", user)
+ "notes/#{note.id}", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
it 'returns a 404 error when note id not found' do
- delete api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{non_existing_record_id}", user)
+ delete api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{non_existing_record_id}", user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
it_behaves_like '412 response' do
- let(:request) { api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{note.id}", user) }
+ let(:request) { api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes/#{note.id}", user, admin_mode: user.admin?) }
end
end
end
@@ -370,16 +370,16 @@ end
RSpec.shared_examples 'noteable API with confidential notes' do |parent_type, noteable_type, id_name|
it_behaves_like 'noteable API', parent_type, noteable_type, id_name
- describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes" do
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/notes", :aggregate_failures do
let(:params) { { body: 'hi!' } }
subject do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?), params: params
end
context 'with internal param' do
it "creates a confidential note if internal is set to true" do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params.merge(internal: true)
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?), params: params.merge(internal: true)
expect(response).to have_gitlab_http_status(:created)
expect(json_response['body']).to eq('hi!')
@@ -391,7 +391,7 @@ RSpec.shared_examples 'noteable API with confidential notes' do |parent_type, no
context 'with deprecated confidential param' do
it "creates a confidential note if confidential is set to true" do
- post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user), params: params.merge(confidential: true)
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/notes", user, admin_mode: user.admin?), params: params.merge(confidential: true)
expect(response).to have_gitlab_http_status(:created)
expect(json_response['body']).to eq('hi!')
diff --git a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
index b55639a6b82..f53532d00d7 100644
--- a/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/npm_packages_shared_examples.rb
@@ -507,55 +507,118 @@ RSpec.shared_examples 'handling get dist tags requests' do |scope: :project|
it_behaves_like 'returning response status', status
end
- shared_examples 'handling different package names, visibilities and user roles' do
- where(:package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
- :scoped_naming_convention | :public | :anonymous | :accept | :ok
- :scoped_naming_convention | :public | :guest | :accept | :ok
- :scoped_naming_convention | :public | :reporter | :accept | :ok
- :scoped_no_naming_convention | :public | :anonymous | :accept | :ok
- :scoped_no_naming_convention | :public | :guest | :accept | :ok
- :scoped_no_naming_convention | :public | :reporter | :accept | :ok
- :unscoped | :public | :anonymous | :accept | :ok
- :unscoped | :public | :guest | :accept | :ok
- :unscoped | :public | :reporter | :accept | :ok
- :non_existing | :public | :anonymous | :reject | :not_found
- :non_existing | :public | :guest | :reject | :not_found
- :non_existing | :public | :reporter | :reject | :not_found
-
- :scoped_naming_convention | :private | :anonymous | :reject | :not_found
- :scoped_naming_convention | :private | :guest | :reject | :forbidden
- :scoped_naming_convention | :private | :reporter | :accept | :ok
- :scoped_no_naming_convention | :private | :anonymous | :reject | :not_found
- :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
- :scoped_no_naming_convention | :private | :reporter | :accept | :ok
- :unscoped | :private | :anonymous | :reject | :not_found
- :unscoped | :private | :guest | :reject | :forbidden
- :unscoped | :private | :reporter | :accept | :ok
- :non_existing | :private | :anonymous | :reject | :not_found
- :non_existing | :private | :guest | :reject | :forbidden
- :non_existing | :private | :reporter | :reject | :not_found
-
- :scoped_naming_convention | :internal | :anonymous | :reject | :not_found
- :scoped_naming_convention | :internal | :guest | :accept | :ok
- :scoped_naming_convention | :internal | :reporter | :accept | :ok
- :scoped_no_naming_convention | :internal | :anonymous | :reject | :not_found
- :scoped_no_naming_convention | :internal | :guest | :accept | :ok
- :scoped_no_naming_convention | :internal | :reporter | :accept | :ok
- :unscoped | :internal | :anonymous | :reject | :not_found
- :unscoped | :internal | :guest | :accept | :ok
- :unscoped | :internal | :reporter | :accept | :ok
- :non_existing | :internal | :anonymous | :reject | :not_found
- :non_existing | :internal | :guest | :reject | :not_found
- :non_existing | :internal | :reporter | :reject | :not_found
+ shared_examples 'handling all conditions' do
+ where(:auth, :package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
+ nil | :scoped_naming_convention | :public | nil | :accept | :ok
+ nil | :scoped_no_naming_convention | :public | nil | :accept | :ok
+ nil | :unscoped | :public | nil | :accept | :ok
+ nil | :non_existing | :public | nil | :reject | :not_found
+ nil | :scoped_naming_convention | :private | nil | :reject | :not_found
+ nil | :scoped_no_naming_convention | :private | nil | :reject | :not_found
+ nil | :unscoped | :private | nil | :reject | :not_found
+ nil | :non_existing | :private | nil | :reject | :not_found
+ nil | :scoped_naming_convention | :internal | nil | :reject | :not_found
+ nil | :scoped_no_naming_convention | :internal | nil | :reject | :not_found
+ nil | :unscoped | :internal | nil | :reject | :not_found
+ nil | :non_existing | :internal | nil | :reject | :not_found
+
+ :oauth | :scoped_naming_convention | :public | :guest | :accept | :ok
+ :oauth | :scoped_naming_convention | :public | :reporter | :accept | :ok
+ :oauth | :scoped_no_naming_convention | :public | :guest | :accept | :ok
+ :oauth | :scoped_no_naming_convention | :public | :reporter | :accept | :ok
+ :oauth | :unscoped | :public | :guest | :accept | :ok
+ :oauth | :unscoped | :public | :reporter | :accept | :ok
+ :oauth | :non_existing | :public | :guest | :reject | :not_found
+ :oauth | :non_existing | :public | :reporter | :reject | :not_found
+ :oauth | :scoped_naming_convention | :private | :guest | :reject | :forbidden
+ :oauth | :scoped_naming_convention | :private | :reporter | :accept | :ok
+ :oauth | :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
+ :oauth | :scoped_no_naming_convention | :private | :reporter | :accept | :ok
+ :oauth | :unscoped | :private | :guest | :reject | :forbidden
+ :oauth | :unscoped | :private | :reporter | :accept | :ok
+ :oauth | :non_existing | :private | :guest | :reject | :forbidden
+ :oauth | :non_existing | :private | :reporter | :reject | :not_found
+ :oauth | :scoped_naming_convention | :internal | :guest | :accept | :ok
+ :oauth | :scoped_naming_convention | :internal | :reporter | :accept | :ok
+ :oauth | :scoped_no_naming_convention | :internal | :guest | :accept | :ok
+ :oauth | :scoped_no_naming_convention | :internal | :reporter | :accept | :ok
+ :oauth | :unscoped | :internal | :guest | :accept | :ok
+ :oauth | :unscoped | :internal | :reporter | :accept | :ok
+ :oauth | :non_existing | :internal | :guest | :reject | :not_found
+ :oauth | :non_existing | :internal | :reporter | :reject | :not_found
+
+ :personal_access_token | :scoped_naming_convention | :public | :guest | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | :public | :reporter | :accept | :ok
+ :personal_access_token | :scoped_no_naming_convention | :public | :guest | :accept | :ok
+ :personal_access_token | :scoped_no_naming_convention | :public | :reporter | :accept | :ok
+ :personal_access_token | :unscoped | :public | :guest | :accept | :ok
+ :personal_access_token | :unscoped | :public | :reporter | :accept | :ok
+ :personal_access_token | :non_existing | :public | :guest | :reject | :not_found
+ :personal_access_token | :non_existing | :public | :reporter | :reject | :not_found
+ :personal_access_token | :scoped_naming_convention | :private | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_naming_convention | :private | :reporter | :accept | :ok
+ :personal_access_token | :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_no_naming_convention | :private | :reporter | :accept | :ok
+ :personal_access_token | :unscoped | :private | :guest | :reject | :forbidden
+ :personal_access_token | :unscoped | :private | :reporter | :accept | :ok
+ :personal_access_token | :non_existing | :private | :guest | :reject | :forbidden
+ :personal_access_token | :non_existing | :private | :reporter | :reject | :not_found
+ :personal_access_token | :scoped_naming_convention | :internal | :guest | :accept | :ok
+ :personal_access_token | :scoped_naming_convention | :internal | :reporter | :accept | :ok
+ :personal_access_token | :scoped_no_naming_convention | :internal | :guest | :accept | :ok
+ :personal_access_token | :scoped_no_naming_convention | :internal | :reporter | :accept | :ok
+ :personal_access_token | :unscoped | :internal | :guest | :accept | :ok
+ :personal_access_token | :unscoped | :internal | :reporter | :accept | :ok
+ :personal_access_token | :non_existing | :internal | :guest | :reject | :not_found
+ :personal_access_token | :non_existing | :internal | :reporter | :reject | :not_found
+
+ :job_token | :scoped_naming_convention | :public | :developer | :accept | :ok
+ :job_token | :scoped_no_naming_convention | :public | :developer | :accept | :ok
+ :job_token | :unscoped | :public | :developer | :accept | :ok
+ :job_token | :non_existing | :public | :developer | :reject | :not_found
+ :job_token | :scoped_naming_convention | :private | :developer | :accept | :ok
+ :job_token | :scoped_no_naming_convention | :private | :developer | :accept | :ok
+ :job_token | :unscoped | :private | :developer | :accept | :ok
+ :job_token | :non_existing | :private | :developer | :reject | :not_found
+ :job_token | :scoped_naming_convention | :internal | :developer | :accept | :ok
+ :job_token | :scoped_no_naming_convention | :internal | :developer | :accept | :ok
+ :job_token | :unscoped | :internal | :developer | :accept | :ok
+ :job_token | :non_existing | :internal | :developer | :reject | :not_found
+
+ :deploy_token | :scoped_naming_convention | :public | nil | :accept | :ok
+ :deploy_token | :scoped_no_naming_convention | :public | nil | :accept | :ok
+ :deploy_token | :unscoped | :public | nil | :accept | :ok
+ :deploy_token | :non_existing | :public | nil | :reject | :not_found
+ :deploy_token | :scoped_naming_convention | :private | nil | :accept | :ok
+ :deploy_token | :scoped_no_naming_convention | :private | nil | :accept | :ok
+ :deploy_token | :unscoped | :private | nil | :accept | :ok
+ :deploy_token | :non_existing | :private | nil | :reject | :not_found
+ :deploy_token | :scoped_naming_convention | :internal | nil | :accept | :ok
+ :deploy_token | :scoped_no_naming_convention | :internal | nil | :accept | :ok
+ :deploy_token | :unscoped | :internal | nil | :accept | :ok
+ :deploy_token | :non_existing | :internal | nil | :reject | :not_found
end
with_them do
- let(:anonymous) { user_role == :anonymous }
+ let(:headers) do
+ case auth
+ when :oauth
+ build_token_auth_header(token.plaintext_token)
+ when :personal_access_token
+ build_token_auth_header(personal_access_token.token)
+ when :job_token
+ build_token_auth_header(job.token)
+ when :deploy_token
+ build_token_auth_header(deploy_token.token)
+ else
+ {}
+ end
+ end
- subject { get(url, headers: anonymous ? {} : headers) }
+ subject { get(url, headers: headers) }
before do
- project.send("add_#{user_role}", user) unless anonymous
+ project.send("add_#{user_role}", user) if user_role
project.update!(visibility: visibility.to_s)
end
@@ -571,20 +634,6 @@ RSpec.shared_examples 'handling get dist tags requests' do |scope: :project|
end
end
- shared_examples 'handling all conditions' do
- context 'with oauth token' do
- let(:headers) { build_token_auth_header(token.plaintext_token) }
-
- it_behaves_like 'handling different package names, visibilities and user roles'
- end
-
- context 'with personal access token' do
- let(:headers) { build_token_auth_header(personal_access_token.token) }
-
- it_behaves_like 'handling different package names, visibilities and user roles'
- end
- end
-
context 'with a group namespace' do
it_behaves_like 'handling all conditions'
end
@@ -599,7 +648,6 @@ RSpec.shared_examples 'handling get dist tags requests' do |scope: :project|
end
RSpec.shared_examples 'handling create dist tag requests' do |scope: :project|
- using RSpec::Parameterized::TableSyntax
include_context 'set package name from package name type'
let_it_be(:tag_name) { 'test' }
@@ -617,82 +665,10 @@ RSpec.shared_examples 'handling create dist tag requests' do |scope: :project|
it_behaves_like 'returning response status', status
end
- shared_examples 'handling different package names, visibilities and user roles' do
- where(:package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
- :scoped_naming_convention | :public | :anonymous | :reject | :forbidden
- :scoped_naming_convention | :public | :guest | :reject | :forbidden
- :scoped_naming_convention | :public | :developer | :accept | :ok
- :scoped_no_naming_convention | :public | :anonymous | :reject | :forbidden
- :scoped_no_naming_convention | :public | :guest | :reject | :forbidden
- :scoped_no_naming_convention | :public | :developer | :accept | :ok
- :unscoped | :public | :anonymous | :reject | :forbidden
- :unscoped | :public | :guest | :reject | :forbidden
- :unscoped | :public | :developer | :accept | :ok
- :non_existing | :public | :anonymous | :reject | :forbidden
- :non_existing | :public | :guest | :reject | :forbidden
- :non_existing | :public | :developer | :reject | :not_found
-
- :scoped_naming_convention | :private | :anonymous | :reject | :not_found
- :scoped_naming_convention | :private | :guest | :reject | :forbidden
- :scoped_naming_convention | :private | :developer | :accept | :ok
- :scoped_no_naming_convention | :private | :anonymous | :reject | :not_found
- :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
- :scoped_no_naming_convention | :private | :developer | :accept | :ok
- :unscoped | :private | :anonymous | :reject | :not_found
- :unscoped | :private | :guest | :reject | :forbidden
- :unscoped | :private | :developer | :accept | :ok
- :non_existing | :private | :anonymous | :reject | :not_found
- :non_existing | :private | :guest | :reject | :forbidden
- :non_existing | :private | :developer | :reject | :not_found
-
- :scoped_naming_convention | :internal | :anonymous | :reject | :forbidden
- :scoped_naming_convention | :internal | :guest | :reject | :forbidden
- :scoped_naming_convention | :internal | :developer | :accept | :ok
- :scoped_no_naming_convention | :internal | :anonymous | :reject | :forbidden
- :scoped_no_naming_convention | :internal | :guest | :reject | :forbidden
- :scoped_no_naming_convention | :internal | :developer | :accept | :ok
- :unscoped | :internal | :anonymous | :reject | :forbidden
- :unscoped | :internal | :guest | :reject | :forbidden
- :unscoped | :internal | :developer | :accept | :ok
- :non_existing | :internal | :anonymous | :reject | :forbidden
- :non_existing | :internal | :guest | :reject | :forbidden
- :non_existing | :internal | :developer | :reject | :not_found
- end
-
- with_them do
- let(:anonymous) { user_role == :anonymous }
-
- subject { put(url, env: env, headers: headers) }
-
- before do
- project.send("add_#{user_role}", user) unless anonymous
- project.update!(visibility: visibility.to_s)
- end
-
- example_name = "#{params[:expected_result]} create package tag request"
- status = params[:expected_status]
-
- if scope == :instance && params[:package_name_type] != :scoped_naming_convention
- example_name = 'reject create package tag request'
- status = :not_found
- end
-
- it_behaves_like example_name, status: status
- end
- end
-
shared_examples 'handling all conditions' do
- context 'with oauth token' do
- let(:headers) { build_token_auth_header(token.plaintext_token) }
+ subject { put(url, env: env, headers: headers) }
- it_behaves_like 'handling different package names, visibilities and user roles'
- end
-
- context 'with personal access token' do
- let(:headers) { build_token_auth_header(personal_access_token.token) }
-
- it_behaves_like 'handling different package names, visibilities and user roles'
- end
+ it_behaves_like 'handling different package names, visibilities and user roles for tags create or delete', action: :create, scope: scope
end
context 'with a group namespace' do
@@ -709,7 +685,6 @@ RSpec.shared_examples 'handling create dist tag requests' do |scope: :project|
end
RSpec.shared_examples 'handling delete dist tag requests' do |scope: :project|
- using RSpec::Parameterized::TableSyntax
include_context 'set package name from package name type'
let_it_be(:package_tag) { create(:packages_tag, package: package) }
@@ -725,82 +700,10 @@ RSpec.shared_examples 'handling delete dist tag requests' do |scope: :project|
it_behaves_like 'returning response status', status
end
- shared_examples 'handling different package names, visibilities and user roles' do
- where(:package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
- :scoped_naming_convention | :public | :anonymous | :reject | :forbidden
- :scoped_naming_convention | :public | :guest | :reject | :forbidden
- :scoped_naming_convention | :public | :maintainer | :accept | :ok
- :scoped_no_naming_convention | :public | :anonymous | :reject | :forbidden
- :scoped_no_naming_convention | :public | :guest | :reject | :forbidden
- :scoped_no_naming_convention | :public | :maintainer | :accept | :ok
- :unscoped | :public | :anonymous | :reject | :forbidden
- :unscoped | :public | :guest | :reject | :forbidden
- :unscoped | :public | :maintainer | :accept | :ok
- :non_existing | :public | :anonymous | :reject | :forbidden
- :non_existing | :public | :guest | :reject | :forbidden
- :non_existing | :public | :maintainer | :reject | :not_found
-
- :scoped_naming_convention | :private | :anonymous | :reject | :not_found
- :scoped_naming_convention | :private | :guest | :reject | :forbidden
- :scoped_naming_convention | :private | :maintainer | :accept | :ok
- :scoped_no_naming_convention | :private | :anonymous | :reject | :not_found
- :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
- :scoped_no_naming_convention | :private | :maintainer | :accept | :ok
- :unscoped | :private | :anonymous | :reject | :not_found
- :unscoped | :private | :guest | :reject | :forbidden
- :unscoped | :private | :maintainer | :accept | :ok
- :non_existing | :private | :anonymous | :reject | :not_found
- :non_existing | :private | :guest | :reject | :forbidden
- :non_existing | :private | :maintainer | :reject | :not_found
-
- :scoped_naming_convention | :internal | :anonymous | :reject | :forbidden
- :scoped_naming_convention | :internal | :guest | :reject | :forbidden
- :scoped_naming_convention | :internal | :maintainer | :accept | :ok
- :scoped_no_naming_convention | :internal | :anonymous | :reject | :forbidden
- :scoped_no_naming_convention | :internal | :guest | :reject | :forbidden
- :scoped_no_naming_convention | :internal | :maintainer | :accept | :ok
- :unscoped | :internal | :anonymous | :reject | :forbidden
- :unscoped | :internal | :guest | :reject | :forbidden
- :unscoped | :internal | :maintainer | :accept | :ok
- :non_existing | :internal | :anonymous | :reject | :forbidden
- :non_existing | :internal | :guest | :reject | :forbidden
- :non_existing | :internal | :maintainer | :reject | :not_found
- end
-
- with_them do
- let(:anonymous) { user_role == :anonymous }
-
- subject { delete(url, headers: headers) }
-
- before do
- project.send("add_#{user_role}", user) unless anonymous
- project.update!(visibility: visibility.to_s)
- end
-
- example_name = "#{params[:expected_result]} delete package tag request"
- status = params[:expected_status]
-
- if scope == :instance && params[:package_name_type] != :scoped_naming_convention
- example_name = 'reject delete package tag request'
- status = :not_found
- end
-
- it_behaves_like example_name, status: status
- end
- end
-
shared_examples 'handling all conditions' do
- context 'with oauth token' do
- let(:headers) { build_token_auth_header(token.plaintext_token) }
-
- it_behaves_like 'handling different package names, visibilities and user roles'
- end
-
- context 'with personal access token' do
- let(:headers) { build_token_auth_header(personal_access_token.token) }
+ subject { delete(url, headers: headers) }
- it_behaves_like 'handling different package names, visibilities and user roles'
- end
+ it_behaves_like 'handling different package names, visibilities and user roles for tags create or delete', action: :delete, scope: scope
end
context 'with a group namespace' do
@@ -815,3 +718,134 @@ RSpec.shared_examples 'handling delete dist tag requests' do |scope: :project|
end
end
end
+
+RSpec.shared_examples 'handling different package names, visibilities and user roles for tags create or delete' do |action:, scope: :project|
+ using RSpec::Parameterized::TableSyntax
+
+ role = action == :create ? :developer : :maintainer
+
+ where(:auth, :package_name_type, :visibility, :user_role, :expected_result, :expected_status) do
+ nil | :scoped_naming_convention | :public | nil | :reject | :unauthorized
+ nil | :scoped_no_naming_convention | :public | nil | :reject | :unauthorized
+ nil | :unscoped | :public | nil | :reject | :unauthorized
+ nil | :non_existing | :public | nil | :reject | :unauthorized
+ nil | :scoped_naming_convention | :private | nil | :reject | :unauthorized
+ nil | :scoped_no_naming_convention | :private | nil | :reject | :unauthorized
+ nil | :unscoped | :private | nil | :reject | :unauthorized
+ nil | :non_existing | :private | nil | :reject | :unauthorized
+ nil | :scoped_naming_convention | :internal | nil | :reject | :unauthorized
+ nil | :scoped_no_naming_convention | :internal | nil | :reject | :unauthorized
+ nil | :unscoped | :internal | nil | :reject | :unauthorized
+ nil | :non_existing | :internal | nil | :reject | :unauthorized
+
+ :oauth | :scoped_naming_convention | :public | :guest | :reject | :forbidden
+ :oauth | :scoped_naming_convention | :public | role | :accept | :ok
+ :oauth | :scoped_no_naming_convention | :public | :guest | :reject | :forbidden
+ :oauth | :scoped_no_naming_convention | :public | role | :accept | :ok
+ :oauth | :unscoped | :public | :guest | :reject | :forbidden
+ :oauth | :unscoped | :public | role | :accept | :ok
+ :oauth | :non_existing | :public | :guest | :reject | :forbidden
+ :oauth | :non_existing | :public | role | :reject | :not_found
+ :oauth | :scoped_naming_convention | :private | :guest | :reject | :forbidden
+ :oauth | :scoped_naming_convention | :private | role | :accept | :ok
+ :oauth | :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
+ :oauth | :scoped_no_naming_convention | :private | role | :accept | :ok
+ :oauth | :unscoped | :private | :guest | :reject | :forbidden
+ :oauth | :unscoped | :private | role | :accept | :ok
+ :oauth | :non_existing | :private | :guest | :reject | :forbidden
+ :oauth | :non_existing | :private | role | :reject | :not_found
+ :oauth | :scoped_naming_convention | :internal | :guest | :reject | :forbidden
+ :oauth | :scoped_naming_convention | :internal | role | :accept | :ok
+ :oauth | :scoped_no_naming_convention | :internal | :guest | :reject | :forbidden
+ :oauth | :scoped_no_naming_convention | :internal | role | :accept | :ok
+ :oauth | :unscoped | :internal | :guest | :reject | :forbidden
+ :oauth | :unscoped | :internal | role | :accept | :ok
+ :oauth | :non_existing | :internal | :guest | :reject | :forbidden
+ :oauth | :non_existing | :internal | role | :reject | :not_found
+
+ :personal_access_token | :scoped_naming_convention | :public | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_naming_convention | :public | role | :accept | :ok
+ :personal_access_token | :scoped_no_naming_convention | :public | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_no_naming_convention | :public | role | :accept | :ok
+ :personal_access_token | :unscoped | :public | :guest | :reject | :forbidden
+ :personal_access_token | :unscoped | :public | role | :accept | :ok
+ :personal_access_token | :non_existing | :public | :guest | :reject | :forbidden
+ :personal_access_token | :non_existing | :public | role | :reject | :not_found
+ :personal_access_token | :scoped_naming_convention | :private | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_naming_convention | :private | role | :accept | :ok
+ :personal_access_token | :scoped_no_naming_convention | :private | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_no_naming_convention | :private | role | :accept | :ok
+ :personal_access_token | :unscoped | :private | :guest | :reject | :forbidden
+ :personal_access_token | :unscoped | :private | role | :accept | :ok
+ :personal_access_token | :non_existing | :private | :guest | :reject | :forbidden
+ :personal_access_token | :non_existing | :private | role | :reject | :not_found
+ :personal_access_token | :scoped_naming_convention | :internal | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_naming_convention | :internal | role | :accept | :ok
+ :personal_access_token | :scoped_no_naming_convention | :internal | :guest | :reject | :forbidden
+ :personal_access_token | :scoped_no_naming_convention | :internal | role | :accept | :ok
+ :personal_access_token | :unscoped | :internal | :guest | :reject | :forbidden
+ :personal_access_token | :unscoped | :internal | role | :accept | :ok
+ :personal_access_token | :non_existing | :internal | :guest | :reject | :forbidden
+ :personal_access_token | :non_existing | :internal | role | :reject | :not_found
+
+ :job_token | :scoped_naming_convention | :public | role | :accept | :ok
+ :job_token | :scoped_no_naming_convention | :public | role | :accept | :ok
+ :job_token | :unscoped | :public | role | :accept | :ok
+ :job_token | :non_existing | :public | role | :reject | :not_found
+ :job_token | :scoped_naming_convention | :private | role | :accept | :ok
+ :job_token | :scoped_no_naming_convention | :private | role | :accept | :ok
+ :job_token | :unscoped | :private | role | :accept | :ok
+ :job_token | :non_existing | :private | role | :reject | :not_found
+ :job_token | :scoped_naming_convention | :internal | role | :accept | :ok
+ :job_token | :scoped_no_naming_convention | :internal | role | :accept | :ok
+ :job_token | :unscoped | :internal | role | :accept | :ok
+ :job_token | :non_existing | :internal | role | :reject | :not_found
+
+ :deploy_token | :scoped_naming_convention | :public | nil | :accept | :ok
+ :deploy_token | :scoped_no_naming_convention | :public | nil | :accept | :ok
+ :deploy_token | :unscoped | :public | nil | :accept | :ok
+ :deploy_token | :non_existing | :public | nil | :reject | :not_found
+ :deploy_token | :scoped_naming_convention | :private | nil | :accept | :ok
+ :deploy_token | :scoped_no_naming_convention | :private | nil | :accept | :ok
+ :deploy_token | :unscoped | :private | nil | :accept | :ok
+ :deploy_token | :non_existing | :private | nil | :reject | :not_found
+ :deploy_token | :scoped_naming_convention | :internal | nil | :accept | :ok
+ :deploy_token | :scoped_no_naming_convention | :internal | nil | :accept | :ok
+ :deploy_token | :unscoped | :internal | nil | :accept | :ok
+ :deploy_token | :non_existing | :internal | nil | :reject | :not_found
+ end
+
+ with_them do
+ let(:headers) do
+ case auth
+ when :oauth
+ build_token_auth_header(token.plaintext_token)
+ when :personal_access_token
+ build_token_auth_header(personal_access_token.token)
+ when :job_token
+ build_token_auth_header(job.token)
+ when :deploy_token
+ build_token_auth_header(deploy_token.token)
+ else
+ {}
+ end
+ end
+
+ before do
+ project.send("add_#{user_role}", user) if user_role
+ project.update!(visibility: visibility.to_s)
+ end
+
+ example_name = "#{params[:expected_result]} #{action} package tag request"
+ status = params[:expected_status]
+
+ if scope == :instance && params[:package_name_type] != :scoped_naming_convention
+ example_name = "reject #{action} package tag request"
+ # Due to #authenticate_non_get, anonymous requests on private resources
+ # are rejected with unauthorized status
+ status = params[:auth].nil? ? :unauthorized : :not_found
+ end
+
+ it_behaves_like example_name, status: status
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb b/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb
index 1d79a61fbb0..7c20ea661b5 100644
--- a/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/npm_packages_tags_shared_examples.rb
@@ -1,13 +1,5 @@
# frozen_string_literal: true
-RSpec.shared_examples 'rejects package tags access' do |status:|
- before do
- package.update!(name: package_name) unless package_name == 'non-existing-package'
- end
-
- it_behaves_like 'returning response status', status
-end
-
RSpec.shared_examples 'accept package tags request' do |status:|
using RSpec::Parameterized::TableSyntax
include_context 'dependency proxy helpers context'
@@ -23,6 +15,7 @@ RSpec.shared_examples 'accept package tags request' do |status:|
end
it_behaves_like 'returning response status', status
+ it_behaves_like 'track event', :list_tags
it 'returns a valid json response' do
subject
@@ -63,6 +56,7 @@ RSpec.shared_examples 'accept create package tag request' do |user_type|
end
it_behaves_like 'returning response status', :no_content
+ it_behaves_like 'track event', :create_tag
it 'creates the package tag' do
expect { subject }.to change { Packages::Tag.count }.by(1)
@@ -145,6 +139,7 @@ RSpec.shared_examples 'accept delete package tag request' do |user_type|
end
it_behaves_like 'returning response status', :no_content
+ it_behaves_like 'track event', :delete_tag
it 'returns a valid response' do
subject
@@ -190,3 +185,21 @@ RSpec.shared_examples 'accept delete package tag request' do |user_type|
end
end
end
+
+RSpec.shared_examples 'track event' do |event_name|
+ let(:event_user) do
+ if auth == :deploy_token
+ deploy_token
+ elsif user_role
+ user
+ end
+ end
+
+ let(:snowplow_gitlab_standard_context) do
+ { project: project, namespace: project.namespace, property: 'i_package_npm_user' }.tap do |context|
+ context[:user] = event_user if event_user
+ end
+ end
+
+ it_behaves_like 'a package tracking event', described_class.name, event_name.to_s
+end
diff --git a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
index 17d8b9c7fab..7cafe8bb368 100644
--- a/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/nuget_endpoints_shared_examples.rb
@@ -36,6 +36,7 @@ RSpec.shared_examples 'handling nuget service requests' do |example_names_with_s
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
subject { get api(url), headers: headers }
@@ -72,6 +73,7 @@ RSpec.shared_examples 'handling nuget service requests' do |example_names_with_s
with_them do
let(:job) { user_token ? create(:ci_build, project: project, user: user, status: :running) : double(token: 'wrong') }
let(:headers) { user_role == :anonymous ? {} : job_basic_auth_header(job) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
subject { get api(url), headers: headers }
@@ -140,6 +142,7 @@ RSpec.shared_examples 'handling nuget metadata requests with package name' do |e
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
subject { get api(url), headers: headers }
@@ -207,6 +210,7 @@ RSpec.shared_examples 'handling nuget metadata requests with package name and pa
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
subject { get api(url), headers: headers }
@@ -277,6 +281,7 @@ RSpec.shared_examples 'handling nuget search requests' do |example_names_with_st
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:snowplow_gitlab_standard_context) { snowplow_context(user_role: user_role) }
subject { get api(url), headers: headers }
diff --git a/spec/support/shared_examples/requests/api/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
index 98264baa61d..3168f25e4fa 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -143,37 +143,37 @@ RSpec.shared_examples 'job token for package uploads' do |authorize_endpoint: fa
end
RSpec.shared_examples 'a package tracking event' do |category, action, service_ping_context = true|
- before do
- stub_feature_flags(collect_package_events: true)
- end
-
let(:context) do
- [Gitlab::Tracking::ServicePingContext.new(data_source: :redis_hll,
- event: snowplow_gitlab_standard_context[:property]).to_h]
+ [
+ Gitlab::Tracking::ServicePingContext.new(
+ data_source: :redis_hll,
+ event: snowplow_gitlab_standard_context[:property]
+ ).to_h
+ ]
end
it "creates a gitlab tracking event #{action}", :snowplow, :aggregate_failures do
- expect { subject }.to change { Packages::Event.count }.by(1)
+ subject
if service_ping_context
- expect_snowplow_event(category: category, action: action,
- label: "redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly",
- context: context, **snowplow_gitlab_standard_context)
+ expect_snowplow_event(
+ category: category,
+ action: action,
+ label: "redis_hll_counters.user_packages.user_packages_total_unique_counts_monthly",
+ context: context,
+ **snowplow_gitlab_standard_context
+ )
else
expect_snowplow_event(category: category, action: action, **snowplow_gitlab_standard_context)
end
end
end
-RSpec.shared_examples 'not a package tracking event' do
- before do
- stub_feature_flags(collect_package_events: true)
- end
-
+RSpec.shared_examples 'not a package tracking event' do |category, action|
it 'does not create a gitlab tracking event', :snowplow, :aggregate_failures do
- expect { subject }.not_to change { Packages::Event.count }
+ subject
- expect_no_snowplow_event
+ expect_no_snowplow_event category: category, action: action
end
end
@@ -183,3 +183,15 @@ RSpec.shared_examples 'bumping the package last downloaded at field' do
.to change { package.reload.last_downloaded_at }.from(nil).to(instance_of(ActiveSupport::TimeWithZone))
end
end
+
+RSpec.shared_examples 'a successful package creation' do
+ it 'creates npm package with file' do
+ expect { subject }
+ .to change { project.packages.count }.by(1)
+ .and change { Packages::PackageFile.count }.by(1)
+ .and change { Packages::Tag.count }.by(1)
+ .and change { Packages::Npm::Metadatum.count }.by(1)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/pipelines/visibility_table_shared_examples.rb b/spec/support/shared_examples/requests/api/pipelines/visibility_table_shared_examples.rb
index 8dd2ef6ccc6..9847ea4e1e2 100644
--- a/spec/support/shared_examples/requests/api/pipelines/visibility_table_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/pipelines/visibility_table_shared_examples.rb
@@ -224,10 +224,10 @@ RSpec.shared_examples 'pipelines visibility table' do
project.project_feature.update!(project_feature_attributes)
project.add_role(ci_user, user_role) if user_role && user_role != :non_member
- get api(pipelines_api_path, api_user)
+ get api(pipelines_api_path, api_user, admin_mode: is_admin)
end
- it do
+ specify do
expect(response).to have_gitlab_http_status(response_status)
expect(api_response).to match(expected_response)
end
diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
index 6065b1163c4..9bd430c3b4f 100644
--- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
@@ -254,6 +254,13 @@ RSpec.shared_examples 'pypi simple API endpoint' do
with_them do
let(:token) { user_token ? personal_access_token.token : 'wrong' }
let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+ let(:snowplow_gitlab_standard_context) do
+ if user_role == :anonymous || (visibility_level == :public && !user_token)
+ snowplow_context
+ else
+ snowplow_context.merge(user: user)
+ end
+ end
before do
project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level.to_s))
@@ -269,7 +276,7 @@ RSpec.shared_examples 'pypi simple API endpoint' do
let(:url) { "/projects/#{project.id}/packages/pypi/simple/my-package" }
let(:headers) { basic_auth_header(user.username, personal_access_token.token) }
- let(:snowplow_gitlab_standard_context) { { project: project, namespace: group, property: 'i_package_pypi_user' } }
+ let(:snowplow_gitlab_standard_context) { snowplow_context.merge({ project: project, user: user }) }
it_behaves_like 'PyPI package versions', :developer, :success
end
diff --git a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
index 2154a76d765..3913d29e086 100644
--- a/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb
@@ -9,7 +9,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
let(:repository_storage_move_id) { storage_move.id }
def get_container_repository_storage_move
- get api(url, user)
+ get api(url, user, admin_mode: user.admin?)
end
it 'returns a container repository storage move', :aggregate_failures do
@@ -39,7 +39,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
shared_examples 'get container repository storage move list' do
def get_container_repository_storage_moves
- get api(url, user)
+ get api(url, user, admin_mode: user.admin?)
end
it 'returns container repository storage moves', :aggregate_failures do
@@ -70,7 +70,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
get_container_repository_storage_moves
- json_ids = json_response.map { |storage_move| storage_move['id'] }
+ json_ids = json_response.pluck('id')
expect(json_ids).to eq([storage_move.id, storage_move_middle.id, storage_move_oldest.id])
end
@@ -90,7 +90,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
let(:container_id) { non_existing_record_id }
it 'returns not found' do
- get api(url, user)
+ get api(url, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -108,7 +108,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
let(:repository_storage_move_id) { storage_move.id }
it 'returns not found' do
- get api(url, user)
+ get api(url, user, admin_mode: user.admin?)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -127,20 +127,20 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
end
end
- describe "POST /#{container_type}/:id/repository_storage_moves" do
+ describe "POST /#{container_type}/:id/repository_storage_moves", :aggregate_failures do
let(:container_id) { container.id }
let(:url) { "/#{container_type}/#{container_id}/repository_storage_moves" }
let(:destination_storage_name) { 'test_second_storage' }
def create_container_repository_storage_move
- post api(url, user), params: { destination_storage_name: destination_storage_name }
+ post api(url, user, admin_mode: user.admin?), params: { destination_storage_name: destination_storage_name }
end
before do
stub_storage_settings('test_second_storage' => { 'path' => 'tmp/tests/extra_storage' })
end
- it 'schedules a container repository storage move', :aggregate_failures do
+ it 'schedules a container repository storage move' do
create_container_repository_storage_move
storage_move = container.repository_storage_moves.last
@@ -158,7 +158,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
it { expect { create_container_repository_storage_move }.to be_denied_for(:user) }
end
- context 'destination_storage_name is missing', :aggregate_failures do
+ context 'destination_storage_name is missing' do
let(:destination_storage_name) { nil }
it 'schedules a container repository storage move' do
@@ -192,7 +192,7 @@ RSpec.shared_examples 'repository_storage_moves API' do |container_type|
let(:destination_storage_name) { 'test_second_storage' }
def create_container_repository_storage_moves
- post api(url, user), params: {
+ post api(url, user, admin_mode: user.admin?), params: {
source_storage_name: source_storage_name,
destination_storage_name: destination_storage_name
}
diff --git a/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb b/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb
index b5139bd8c99..2770e293683 100644
--- a/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/resolvable_discussions_shared_examples.rb
@@ -71,8 +71,7 @@ RSpec.shared_examples 'resolvable discussions API' do |parent_type, noteable_typ
it 'returns a 404 error when note id not found' do
put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
- "discussions/#{note.discussion_id}/notes/#{non_existing_record_id}", user),
- params: { body: 'Hello!' }
+ "discussions/#{note.discussion_id}/notes/#{non_existing_record_id}", user), params: { body: 'Hello!' }
expect(response).to have_gitlab_http_status(:not_found)
end
diff --git a/spec/support/shared_examples/requests/api/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/snippets_shared_examples.rb
index 1b92eb56f54..56f2394c005 100644
--- a/spec/support/shared_examples/requests/api/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/snippets_shared_examples.rb
@@ -1,12 +1,19 @@
# frozen_string_literal: true
RSpec.shared_examples 'raw snippet files' do
- let_it_be(:user_token) { create(:personal_access_token, user: snippet.author) }
let(:snippet_id) { snippet.id }
- let(:user) { snippet.author }
+ let_it_be(:user) { snippet.author }
let(:file_path) { '%2Egitattributes' }
let(:ref) { 'master' }
+ let_it_be(:user_token) do
+ if user.admin?
+ create(:personal_access_token, :admin_mode, user: user)
+ else
+ create(:personal_access_token, user: user)
+ end
+ end
+
subject { get api(api_path, personal_access_token: user_token) }
context 'with an invalid snippet ID' do
@@ -15,8 +22,10 @@ RSpec.shared_examples 'raw snippet files' do
it 'returns 404' do
subject
- expect(response).to have_gitlab_http_status(:not_found)
- expect(json_response['message']).to eq('404 Snippet Not Found')
+ aggregate_failures do
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 Snippet Not Found')
+ end
end
end
@@ -185,7 +194,7 @@ RSpec.shared_examples 'snippet individual non-file updates' do
end
RSpec.shared_examples 'invalid snippet updates' do
- it 'returns 404 for invalid snippet id' do
+ it 'returns 404 for invalid snippet id', :aggregate_failures do
update_snippet(snippet_id: non_existing_record_id, params: { title: 'foo' })
expect(response).to have_gitlab_http_status(:not_found)
@@ -204,7 +213,7 @@ RSpec.shared_examples 'invalid snippet updates' do
expect(response).to have_gitlab_http_status(:bad_request)
end
- it 'returns 400 if title is blank' do
+ it 'returns 400 if title is blank', :aggregate_failures do
update_snippet(params: { title: '' })
expect(response).to have_gitlab_http_status(:bad_request)
@@ -236,7 +245,9 @@ RSpec.shared_examples 'snippet access with different users' do
it 'returns the correct response' do
request_user = user_for(requester)
- get api(path, request_user)
+ admin_mode = requester == :admin
+
+ get api(path, request_user, admin_mode: admin_mode)
expect(response).to have_gitlab_http_status(status)
end
@@ -250,8 +261,6 @@ RSpec.shared_examples 'snippet access with different users' do
other_user
when :admin
admin
- else
- nil
end
end
diff --git a/spec/support/shared_examples/requests/api/status_shared_examples.rb b/spec/support/shared_examples/requests/api/status_shared_examples.rb
index 40843ccbd15..ff3947c0e73 100644
--- a/spec/support/shared_examples/requests/api/status_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/status_shared_examples.rb
@@ -21,6 +21,23 @@ RSpec.shared_examples '400 response' do
end
end
+RSpec.shared_examples '401 response' do
+ let(:message) { nil }
+
+ before do
+ # Fires the request
+ request
+ end
+
+ it 'returns 401' do
+ expect(response).to have_gitlab_http_status(:unauthorized)
+
+ if message.present?
+ expect(json_response['message']).to eq(message)
+ end
+ end
+end
+
RSpec.shared_examples '403 response' do
before do
# Fires the request
@@ -54,7 +71,7 @@ RSpec.shared_examples '412 response' do
let(:params) { nil }
let(:success_status) { 204 }
- context 'for a modified ressource' do
+ context 'for a modified resource' do
before do
delete request, params: params, headers: { 'HTTP_IF_UNMODIFIED_SINCE' => '1990-01-12T00:00:48-0600' }
end
@@ -65,7 +82,7 @@ RSpec.shared_examples '412 response' do
end
end
- context 'for an unmodified ressource' do
+ context 'for an unmodified resource' do
before do
delete request, params: params, headers: { 'HTTP_IF_UNMODIFIED_SINCE' => Time.now }
end
diff --git a/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb b/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
index 86a1fd76d09..398421c7a79 100644
--- a/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/time_tracking_shared_examples.rb
@@ -173,8 +173,7 @@ RSpec.shared_examples 'time tracking endpoints' do |issuable_name|
describe "GET /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_stats" do
it "returns the time stats for #{issuable_name}" do
- issuable.update!(spend_time: { duration: 1800, user_id: user.id },
- time_estimate: 3600)
+ issuable.update!(spend_time: { duration: 1800, user_id: user.id }, time_estimate: 3600)
get api("/projects/#{project.id}/#{issuable_collection_name}/#{issuable.iid}/time_stats", user)
diff --git a/spec/support/shared_examples/requests/applications_controller_shared_examples.rb b/spec/support/shared_examples/requests/applications_controller_shared_examples.rb
index 642930dd982..4a7a7492398 100644
--- a/spec/support/shared_examples/requests/applications_controller_shared_examples.rb
+++ b/spec/support/shared_examples/requests/applications_controller_shared_examples.rb
@@ -7,40 +7,14 @@ RSpec.shared_examples 'applications controller - GET #show' do
expect(response).to render_template :show
end
-
- context 'when application is viewed after being created' do
- before do
- create_application
- stub_feature_flags(hash_oauth_secrets: false)
- end
-
- it 'sets `@created` instance variable to `true`' do
- get show_path
-
- expect(assigns[:created]).to eq(true)
- end
- end
-
- context 'when application is reviewed' do
- before do
- stub_feature_flags(hash_oauth_secrets: false)
- end
-
- it 'sets `@created` instance variable to `false`' do
- get show_path
-
- expect(assigns[:created]).to eq(false)
- end
- end
end
end
RSpec.shared_examples 'applications controller - POST #create' do
- it "sets `#{OauthApplications::CREATED_SESSION_KEY}` session key to `true`" do
- stub_feature_flags(hash_oauth_secrets: false)
+ it "sets `@created` instance variable to `true`" do
create_application
- expect(session[OauthApplications::CREATED_SESSION_KEY]).to eq(true)
+ expect(assigns[:created]).to eq(true)
end
end
diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb
index d133c5ea641..2c08f946468 100644
--- a/spec/support/shared_examples/requests/graphql_shared_examples.rb
+++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb
@@ -58,5 +58,5 @@ end
RSpec.shared_examples 'a mutation on an unauthorized resource' do
it_behaves_like 'a mutation that returns top-level errors',
- errors: [::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
+ errors: [::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR]
end
diff --git a/spec/support/shared_examples/requests/projects/aws/aws__ff_examples.rb b/spec/support/shared_examples/requests/projects/aws/aws__ff_examples.rb
new file mode 100644
index 00000000000..2221baf5b90
--- /dev/null
+++ b/spec/support/shared_examples/requests/projects/aws/aws__ff_examples.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'requires feature flag `cloudseed_aws` enabled' do
+ context 'when feature flag is disabled' do
+ before do
+ project.add_maintainer(user)
+ stub_feature_flags(cloudseed_aws: false)
+ end
+
+ it 'renders not found' do
+ sign_in(user)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
index 3f457890f35..dafa324b3c6 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -567,8 +567,8 @@ RSpec.shared_examples 'rate-limited unauthenticated requests' do
it 'does not throttle the requests' do
(1 + requests_per_period).times do
post registry_endpoint,
- params: { events: events }.to_json,
- headers: registry_headers.merge('Authorization' => secret_token)
+ params: { events: events }.to_json,
+ headers: registry_headers.merge('Authorization' => secret_token)
expect(response).to have_gitlab_http_status(:ok)
end
diff --git a/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb b/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb
deleted file mode 100644
index f8a752a5673..00000000000
--- a/spec/support/shared_examples/requests/self_monitoring_shared_examples.rb
+++ /dev/null
@@ -1,130 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'not accessible to non-admin users' do
- context 'with unauthenticated user' do
- it 'redirects to signin page' do
- subject
-
- expect(response).to redirect_to(new_user_session_path)
- end
- end
-
- context 'with authenticated non-admin user' do
- before do
- login_as(create(:user))
- end
-
- it 'returns status not_found' do
- subject
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- context 'with authenticated admin user without admin mode' do
- before do
- login_as(create(:admin))
- end
-
- it 'redirects to enable admin mode' do
- subject
-
- expect(response).to redirect_to(new_admin_session_path)
- end
- end
-end
-
-# Requires subject and worker_class and status_api to be defined
-# let(:worker_class) { SelfMonitoringProjectCreateWorker }
-# let(:status_api) { status_create_self_monitoring_project_admin_application_settings_path }
-# subject { post create_self_monitoring_project_admin_application_settings_path }
-RSpec.shared_examples 'triggers async worker, returns sidekiq job_id with response accepted' do
- before do
- allow(worker_class).to receive(:with_status).and_return(worker_class)
- end
-
- it 'returns sidekiq job_id of expected length' do
- subject
-
- job_id = json_response['job_id']
-
- aggregate_failures do
- expect(job_id).to be_present
- expect(job_id.length).to be <= Admin::ApplicationSettingsController::PARAM_JOB_ID_MAX_SIZE
- end
- end
-
- it 'triggers async worker' do
- expect(worker_class).to receive(:perform_async)
-
- subject
- end
-
- it 'returns accepted response' do
- subject
-
- aggregate_failures do
- expect(response).to have_gitlab_http_status(:accepted)
- expect(json_response.keys).to contain_exactly('job_id', 'monitor_status')
- expect(json_response).to include(
- 'monitor_status' => status_api
- )
- end
- end
-
- it 'returns job_id' do
- fake_job_id = 'b5b28910d97563e58c2fe55f'
- allow(worker_class).to receive(:perform_async).and_return(fake_job_id)
-
- subject
-
- expect(json_response).to include('job_id' => fake_job_id)
- end
-end
-
-# Requires job_id and subject to be defined
-# let(:job_id) { 'job_id' }
-# subject do
-# get status_create_self_monitoring_project_admin_application_settings_path,
-# params: { job_id: job_id }
-# end
-RSpec.shared_examples 'handles invalid job_id' do
- context 'with invalid job_id' do
- let(:job_id) { 'a' * 51 }
-
- it 'returns bad_request if job_id too long' do
- subject
-
- aggregate_failures do
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response).to eq('message' => 'Parameter "job_id" cannot ' \
- "exceed length of #{Admin::ApplicationSettingsController::PARAM_JOB_ID_MAX_SIZE}")
- end
- end
- end
-end
-
-# Requires in_progress_message and subject to be defined
-# let(:in_progress_message) { 'Job to create self-monitoring project is in progress' }
-# subject do
-# get status_create_self_monitoring_project_admin_application_settings_path,
-# params: { job_id: job_id }
-# end
-RSpec.shared_examples 'sets polling header and returns accepted' do
- it 'sets polling header' do
- expect(::Gitlab::PollingInterval).to receive(:set_header)
-
- subject
- end
-
- it 'returns accepted' do
- subject
-
- aggregate_failures do
- expect(response).to have_gitlab_http_status(:accepted)
- expect(json_response).to eq(
- 'message' => in_progress_message
- )
- end
- end
-end
diff --git a/spec/support/shared_examples/requests/user_activity_shared_examples.rb b/spec/support/shared_examples/requests/user_activity_shared_examples.rb
index 37da1ce5c63..9c0165f7150 100644
--- a/spec/support/shared_examples/requests/user_activity_shared_examples.rb
+++ b/spec/support/shared_examples/requests/user_activity_shared_examples.rb
@@ -5,7 +5,7 @@ RSpec.shared_examples 'updating of user activity' do |paths_to_visit|
before do
group = create(:group, name: 'group')
- project = create(:project, :public, namespace: group, name: 'project')
+ project = create(:project, :public, namespace: group, path: 'project')
create(:issue, project: project, iid: 10)
create(:merge_request, source_project: project, iid: 15)
diff --git a/spec/support/shared_examples/security_training_providers_importer.rb b/spec/support/shared_examples/security_training_providers_importer.rb
index 69d92964270..81b3d22ab23 100644
--- a/spec/support/shared_examples/security_training_providers_importer.rb
+++ b/spec/support/shared_examples/security_training_providers_importer.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'security training providers importer' do
end
it 'upserts security training providers' do
- expect { 2.times { subject } }.to change { security_training_providers.count }.from(0).to(2)
- expect(security_training_providers.all.map(&:name)).to match_array(['Kontra', 'Secure Code Warrior'])
+ expect { 3.times { subject } }.to change { security_training_providers.count }.from(0).to(3)
+ expect(security_training_providers.all.map(&:name)).to match_array(['Kontra', 'Secure Code Warrior', 'SecureFlag'])
end
end
diff --git a/spec/support/shared_examples/serializers/diff_file_entity_shared_examples.rb b/spec/support/shared_examples/serializers/diff_file_entity_shared_examples.rb
index 32adf98969c..df01f9a5b0b 100644
--- a/spec/support/shared_examples/serializers/diff_file_entity_shared_examples.rb
+++ b/spec/support/shared_examples/serializers/diff_file_entity_shared_examples.rb
@@ -2,13 +2,15 @@
RSpec.shared_examples 'diff file base entity' do
it 'exposes essential attributes' do
- expect(subject).to include(:content_sha, :submodule, :submodule_link,
- :submodule_tree_url, :old_path_html,
- :new_path_html, :blob, :can_modify_blob,
- :file_hash, :file_path, :old_path, :new_path,
- :viewer, :diff_refs, :stored_externally,
- :external_storage, :renamed_file, :deleted_file,
- :a_mode, :b_mode, :new_file, :file_identifier_hash)
+ expect(subject).to include(
+ :content_sha, :submodule, :submodule_link,
+ :submodule_tree_url, :old_path_html,
+ :new_path_html, :blob, :can_modify_blob,
+ :file_hash, :file_path, :old_path, :new_path,
+ :viewer, :diff_refs, :stored_externally,
+ :external_storage, :renamed_file, :deleted_file,
+ :a_mode, :b_mode, :new_file, :file_identifier_hash
+ )
end
# Converted diff files from GitHub import does not contain blob file
@@ -30,13 +32,70 @@ RSpec.shared_examples 'diff file entity' do
it_behaves_like 'diff file base entity'
it 'exposes correct attributes' do
- expect(subject).to include(:added_lines, :removed_lines,
- :context_lines_path)
+ expect(subject).to include(:added_lines, :removed_lines, :context_lines_path)
end
- it 'includes viewer' do
- expect(subject[:viewer].with_indifferent_access)
+ context 'when a viewer' do
+ let(:collapsed) { false }
+ let(:added_lines) { 1 }
+ let(:removed_lines) { 0 }
+ let(:highlighted_lines) { nil }
+
+ before do
+ allow(diff_file).to receive(:diff_lines_for_serializer)
+ .and_return(highlighted_lines)
+
+ allow(diff_file).to receive(:added_lines)
+ .and_return(added_lines)
+
+ allow(diff_file).to receive(:removed_lines)
+ .and_return(removed_lines)
+
+ allow(diff_file).to receive(:collapsed?)
+ .and_return(collapsed)
+ end
+
+ it 'matches the schema' do
+ expect(subject[:viewer].with_indifferent_access)
.to match_schema('entities/diff_viewer')
+ end
+
+ context 'when it is a whitespace only change' do
+ it 'has whitespace_only true' do
+ expect(subject[:viewer][:whitespace_only])
+ .to eq(true)
+ end
+ end
+
+ context 'when the highlighted lines arent shown' do
+ before do
+ allow(diff_file).to receive(:text?)
+ .and_return(false)
+ end
+
+ it 'has whitespace_only nil' do
+ expect(subject[:viewer][:whitespace_only])
+ .to eq(nil)
+ end
+ end
+
+ context 'when it is a new file' do
+ let(:added_lines) { 0 }
+
+ it 'has whitespace_only false' do
+ expect(subject[:viewer][:whitespace_only])
+ .to eq(false)
+ end
+ end
+
+ context 'when it is a collapsed file' do
+ let(:collapsed) { true }
+
+ it 'has whitespace_only false' do
+ expect(subject[:viewer][:whitespace_only])
+ .to eq(false)
+ end
+ end
end
context 'diff files' do
diff --git a/spec/support/shared_examples/serializers/note_entity_shared_examples.rb b/spec/support/shared_examples/serializers/note_entity_shared_examples.rb
index b5e3a407b53..e8238480ced 100644
--- a/spec/support/shared_examples/serializers/note_entity_shared_examples.rb
+++ b/spec/support/shared_examples/serializers/note_entity_shared_examples.rb
@@ -18,7 +18,8 @@ RSpec.shared_examples 'note entity' do
:noteable_note_url,
:report_abuse_path,
:resolvable,
- :type
+ :type,
+ :external_author
)
end
diff --git a/spec/support/shared_examples/services/base_helm_service_shared_examples.rb b/spec/support/shared_examples/services/base_helm_service_shared_examples.rb
deleted file mode 100644
index c2252c83140..00000000000
--- a/spec/support/shared_examples/services/base_helm_service_shared_examples.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples 'logs kubernetes errors' do
- let(:error_hash) do
- {
- service: service.class.name,
- app_id: application.id,
- project_ids: application.cluster.project_ids,
- group_ids: [],
- error_code: error_code
- }
- end
-
- it 'logs into kubernetes.log and Sentry' do
- expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
- error,
- hash_including(error_hash)
- )
-
- service.execute
- end
-end
diff --git a/spec/support/services/clusters/create_service_shared.rb b/spec/support/shared_examples/services/clusters/create_service_shared_examples.rb
index 80fa7c58515..7cd76e45ecd 100644
--- a/spec/support/services/clusters/create_service_shared.rb
+++ b/spec/support/shared_examples/services/clusters/create_service_shared_examples.rb
@@ -1,41 +1,5 @@
# frozen_string_literal: true
-RSpec.shared_context 'valid cluster create params' do
- let(:clusterable) { Clusters::Instance.new }
- let(:params) do
- {
- name: 'test-cluster',
- provider_type: :gcp,
- provider_gcp_attributes: {
- gcp_project_id: 'gcp-project',
- zone: 'us-central1-a',
- num_nodes: 1,
- machine_type: 'machine_type-a',
- legacy_abac: 'true'
- },
- clusterable: clusterable
- }
- end
-end
-
-RSpec.shared_context 'invalid cluster create params' do
- let(:clusterable) { Clusters::Instance.new }
- let(:params) do
- {
- name: 'test-cluster',
- provider_type: :gcp,
- provider_gcp_attributes: {
- gcp_project_id: '!!!!!!!',
- zone: 'us-central1-a',
- num_nodes: 1,
- machine_type: 'machine_type-a'
- },
- clusterable: clusterable
-
- }
- end
-end
-
RSpec.shared_examples 'create cluster service success' do
it 'creates a cluster object' do
expect { subject }
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index 58659775d8c..493a96b8dae 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -79,7 +79,8 @@ RSpec.shared_examples 'an accessible' do
let(:access) do
[{ 'type' => 'repository',
'name' => project.full_path,
- 'actions' => actions }]
+ 'actions' => actions,
+ 'meta' => { 'project_path' => project.full_path } }]
end
it_behaves_like 'a valid token'
@@ -244,12 +245,14 @@ RSpec.shared_examples 'a container registry auth service' do
{
'type' => 'repository',
'name' => project.full_path,
- 'actions' => ['pull']
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => project.full_path }
},
{
'type' => 'repository',
'name' => "#{project.full_path}/*",
- 'actions' => ['pull']
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => project.full_path }
}
]
end
@@ -822,16 +825,20 @@ RSpec.shared_examples 'a container registry auth service' do
[
{ 'type' => 'repository',
'name' => internal_project.full_path,
- 'actions' => ['pull'] },
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => internal_project.full_path } },
{ 'type' => 'repository',
'name' => private_project.full_path,
- 'actions' => ['pull'] },
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => private_project.full_path } },
{ 'type' => 'repository',
'name' => public_project.full_path,
- 'actions' => ['pull'] },
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => public_project.full_path } },
{ 'type' => 'repository',
'name' => public_project_private_container_registry.full_path,
- 'actions' => ['pull'] }
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => public_project_private_container_registry.full_path } }
]
end
end
@@ -845,10 +852,12 @@ RSpec.shared_examples 'a container registry auth service' do
[
{ 'type' => 'repository',
'name' => internal_project.full_path,
- 'actions' => ['pull'] },
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => internal_project.full_path } },
{ 'type' => 'repository',
'name' => public_project.full_path,
- 'actions' => ['pull'] }
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => public_project.full_path } }
]
end
end
@@ -862,7 +871,8 @@ RSpec.shared_examples 'a container registry auth service' do
[
{ 'type' => 'repository',
'name' => public_project.full_path,
- 'actions' => ['pull'] }
+ 'actions' => ['pull'],
+ 'meta' => { 'project_path' => public_project.full_path } }
]
end
end
@@ -1258,4 +1268,29 @@ RSpec.shared_examples 'a container registry auth service' do
end
end
end
+
+ context 'with a project with a path containing special characters' do
+ let_it_be(:bad_project) { create(:project) }
+
+ before do
+ bad_project.update_attribute(:path, "#{bad_project.path}_")
+ end
+
+ describe '#access_token' do
+ let(:token) { described_class.access_token(['pull'], [bad_project.full_path]) }
+ let(:access) do
+ [{ 'type' => 'repository',
+ 'name' => bad_project.full_path,
+ 'actions' => ['pull'] }]
+ end
+
+ subject { { token: token } }
+
+ it_behaves_like 'a valid token'
+
+ it 'has the correct scope' do
+ expect(payload).to include('access' => access)
+ end
+ end
+ end
end
diff --git a/spec/support/services/deploy_token_shared_examples.rb b/spec/support/shared_examples/services/deploy_token_shared_examples.rb
index d322b3fc81d..814b6565497 100644
--- a/spec/support/services/deploy_token_shared_examples.rb
+++ b/spec/support/shared_examples/services/deploy_token_shared_examples.rb
@@ -50,7 +50,9 @@ RSpec.shared_examples 'a deploy token creation service' do
end
context 'when the deploy token is invalid' do
- let(:deploy_token_params) { attributes_for(:deploy_token, read_repository: false, read_registry: false, write_registry: false) }
+ let(:deploy_token_params) do
+ attributes_for(:deploy_token, read_repository: false, read_registry: false, write_registry: false)
+ end
it 'does not create a new DeployToken' do
expect { subject }.not_to change { DeployToken.count }
@@ -75,7 +77,7 @@ RSpec.shared_examples 'a deploy token deletion service' do
.and change { DeployToken.count }.by(-1)
end
- context 'invalid token id' do
+ context 'with invalid token id' do
let(:deploy_token_params) { { token_id: 9999 } }
it 'raises an error' do
diff --git a/spec/support/shared_examples/services/import_csv_service_shared_examples.rb b/spec/support/shared_examples/services/import_csv_service_shared_examples.rb
new file mode 100644
index 00000000000..1555497ae48
--- /dev/null
+++ b/spec/support/shared_examples/services/import_csv_service_shared_examples.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.shared_examples_for 'importer with email notification' do
+ it 'notifies user of import result' do
+ expect(Notify).to receive_message_chain(email_method, :deliver_later)
+
+ subject
+ end
+end
+
+RSpec.shared_examples 'correctly handles invalid files' do
+ shared_examples_for 'invalid file' do
+ it 'returns invalid file error' do
+ expect(subject[:success]).to eq(0)
+ expect(subject[:parse_error]).to eq(true)
+ end
+ end
+
+ context 'when given file with unsupported extension' do
+ let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }
+
+ it_behaves_like 'invalid file'
+ end
+
+ context 'when given empty file' do
+ let(:file) { fixture_file_upload('spec/fixtures/csv_empty.csv') }
+
+ it_behaves_like 'invalid file'
+ end
+
+ context 'when given file without headers' do
+ let(:file) { fixture_file_upload('spec/fixtures/csv_no_headers.csv') }
+
+ it_behaves_like 'invalid file'
+ end
+end
diff --git a/spec/support/shared_examples/services/incident_shared_examples.rb b/spec/support/shared_examples/services/incident_shared_examples.rb
index a87e7c1f801..db2b448f567 100644
--- a/spec/support/shared_examples/services/incident_shared_examples.rb
+++ b/spec/support/shared_examples/services/incident_shared_examples.rb
@@ -12,7 +12,6 @@
# include_examples 'incident issue'
RSpec.shared_examples 'incident issue' do
it 'has incident as issue type' do
- expect(issue.issue_type).to eq('incident')
expect(issue.work_item_type.base_type).to eq('incident')
end
end
@@ -29,7 +28,6 @@ end
# include_examples 'not an incident issue'
RSpec.shared_examples 'not an incident issue' do
it 'has not incident as issue type' do
- expect(issue.issue_type).not_to eq('incident')
expect(issue.work_item_type.base_type).not_to eq('incident')
end
end
diff --git a/spec/support/services/issuable_description_quick_actions_shared_examples.rb b/spec/support/shared_examples/services/issuable/issuable_description_quick_actions_shared_examples.rb
index 1970301e4c9..1970301e4c9 100644
--- a/spec/support/services/issuable_description_quick_actions_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/issuable_description_quick_actions_shared_examples.rb
diff --git a/spec/support/services/issuable_import_csv_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/issuable_import_csv_service_shared_examples.rb
index 0dea6cfb729..5336e0f4c2f 100644
--- a/spec/support/services/issuable_import_csv_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/issuable_import_csv_service_shared_examples.rb
@@ -18,45 +18,14 @@ RSpec.shared_examples 'issuable import csv service' do |issuable_type|
end
end
- shared_examples_for 'importer with email notification' do
- it 'notifies user of import result' do
- expect(Notify).to receive_message_chain(email_method, :deliver_later)
-
- subject
- end
- end
-
- shared_examples_for 'invalid file' do
- it 'returns invalid file error' do
- expect(subject[:success]).to eq(0)
- expect(subject[:parse_error]).to eq(true)
- end
-
- it_behaves_like 'importer with email notification'
- it_behaves_like 'an issuable importer'
- end
-
describe '#execute' do
before do
project.add_developer(user)
end
- context 'invalid file extension' do
- let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif') }
-
- it_behaves_like 'invalid file'
- end
-
- context 'empty file' do
- let(:file) { fixture_file_upload('spec/fixtures/csv_empty.csv') }
-
- it_behaves_like 'invalid file'
- end
-
- context 'file without headers' do
- let(:file) { fixture_file_upload('spec/fixtures/csv_no_headers.csv') }
-
- it_behaves_like 'invalid file'
+ it_behaves_like 'correctly handles invalid files' do
+ it_behaves_like 'importer with email notification'
+ it_behaves_like 'an issuable importer'
end
context 'with a file generated by Gitlab CSV export' do
@@ -78,7 +47,7 @@ RSpec.shared_examples 'issuable import csv service' do |issuable_type|
it_behaves_like 'an issuable importer'
end
- context 'comma delimited file' do
+ context 'with comma delimited file' do
let(:file) { fixture_file_upload('spec/fixtures/csv_comma.csv') }
it 'imports CSV without errors' do
@@ -97,7 +66,7 @@ RSpec.shared_examples 'issuable import csv service' do |issuable_type|
it_behaves_like 'an issuable importer'
end
- context 'tab delimited file with error row' do
+ context 'with tab delimited file with error row' do
let(:file) { fixture_file_upload('spec/fixtures/csv_tab.csv') }
it 'imports CSV with some error rows' do
@@ -116,7 +85,7 @@ RSpec.shared_examples 'issuable import csv service' do |issuable_type|
it_behaves_like 'an issuable importer'
end
- context 'semicolon delimited file with CRLF' do
+ context 'with semicolon delimited file with CRLF' do
let(:file) { fixture_file_upload('spec/fixtures/csv_semicolon.csv') }
it 'imports CSV with a blank row' do
diff --git a/spec/support/services/issuable_update_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/issuable_update_service_shared_examples.rb
index feea21be428..85a05bbe56d 100644
--- a/spec/support/services/issuable_update_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/issuable_update_service_shared_examples.rb
@@ -5,10 +5,10 @@ RSpec.shared_examples 'issuable update service' do
described_class.new(project, user, opts).execute(open_issuable)
end
- context 'changing state' do
+ describe 'changing state' do
let(:hook_event) { :"#{closed_issuable.class.name.underscore.to_sym}_hooks" }
- context 'to reopened' do
+ describe 'to reopened' do
let(:expected_payload) do
include(
changes: include(
@@ -23,14 +23,19 @@ RSpec.shared_examples 'issuable update service' do
end
it 'executes hooks' do
- expect(project).to receive(:execute_hooks).with(expected_payload, hook_event)
- expect(project).to receive(:execute_integrations).with(expected_payload, hook_event)
-
- described_class.new(**described_class.constructor_container_arg(project), current_user: user, params: { state_event: 'reopen' }).execute(closed_issuable)
+ hooks_container = described_class < Issues::BaseService ? project.project_namespace : project
+ expect(hooks_container).to receive(:execute_hooks).with(expected_payload, hook_event)
+ expect(hooks_container).to receive(:execute_integrations).with(expected_payload, hook_event)
+
+ described_class.new(
+ **described_class.constructor_container_arg(project),
+ current_user: user,
+ params: { state_event: 'reopen' }
+ ).execute(closed_issuable)
end
end
- context 'to closed' do
+ describe 'to closed' do
let(:expected_payload) do
include(
changes: include(
@@ -45,10 +50,15 @@ RSpec.shared_examples 'issuable update service' do
end
it 'executes hooks' do
- expect(project).to receive(:execute_hooks).with(expected_payload, hook_event)
- expect(project).to receive(:execute_integrations).with(expected_payload, hook_event)
-
- described_class.new(**described_class.constructor_container_arg(project), current_user: user, params: { state_event: 'close' }).execute(open_issuable)
+ hooks_container = described_class < Issues::BaseService ? project.project_namespace : project
+ expect(hooks_container).to receive(:execute_hooks).with(expected_payload, hook_event)
+ expect(hooks_container).to receive(:execute_integrations).with(expected_payload, hook_event)
+
+ described_class.new(
+ **described_class.constructor_container_arg(project),
+ current_user: user,
+ params: { state_event: 'close' }
+ ).execute(open_issuable)
end
end
end
@@ -97,3 +107,31 @@ RSpec.shared_examples 'broadcasting issuable labels updates' do
end
end
end
+
+RSpec.shared_examples_for 'issuable update service updating last_edited_at values' do
+ context 'when updating the title of the issuable' do
+ let(:update_params) { { title: 'updated title' } }
+
+ it 'does not update last_edited values' do
+ expect { update_issuable }.to change { issuable.title }.from(issuable.title).to('updated title').and(
+ not_change(issuable, :last_edited_at)
+ ).and(
+ not_change(issuable, :last_edited_by)
+ )
+ end
+ end
+
+ context 'when updating the description of the issuable' do
+ let(:update_params) { { description: 'updated description' } }
+
+ it 'updates last_edited values' do
+ expect do
+ update_issuable
+ end.to change { issuable.description }.from(issuable.description).to('updated description').and(
+ change { issuable.last_edited_at }
+ ).and(
+ change { issuable.last_edited_by }
+ )
+ end
+ end
+end
diff --git a/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
deleted file mode 100644
index ff7acc7e907..00000000000
--- a/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples_for 'issuable update service updating last_edited_at values' do
- context 'when updating the title of the issuable' do
- let(:update_params) { { title: 'updated title' } }
-
- it 'does not update last_edited values' do
- expect { update_issuable }.to change { issuable.title }.from(issuable.title).to('updated title').and(
- not_change(issuable, :last_edited_at)
- ).and(
- not_change(issuable, :last_edited_by)
- )
- end
- end
-
- context 'when updating the description of the issuable' do
- let(:update_params) { { description: 'updated description' } }
-
- it 'updates last_edited values' do
- expect do
- update_issuable
- end.to change { issuable.description }.from(issuable.description).to('updated description').and(
- change { issuable.last_edited_at }
- ).and(
- change { issuable.last_edited_by }
- )
- end
- end
-end
diff --git a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
index e47ff2fcd59..0bf8bc4ff04 100644
--- a/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb
@@ -34,7 +34,11 @@ RSpec.shared_examples 'issuable link creation' do
end
it 'returns error' do
- is_expected.to eq(message: "No matching #{issuable_type} found. Make sure that you are adding a valid #{issuable_type} URL.", status: :error, http_status: 404)
+ if issuable_type == :issue
+ is_expected.to eq(message: "Couldn't link #{issuable_type}. You must have at least the Reporter role in both projects.", status: :error, http_status: 403)
+ else
+ is_expected.to eq(message: "No matching #{issuable_type} found. Make sure that you are adding a valid #{issuable_type} URL.", status: :error, http_status: 404)
+ end
end
it 'no relationship is created' do
diff --git a/spec/support/services/issues/move_and_clone_services_shared_examples.rb b/spec/support/shared_examples/services/issues/move_and_clone_services_shared_examples.rb
index 2b2e90c0461..2b2e90c0461 100644
--- a/spec/support/services/issues/move_and_clone_services_shared_examples.rb
+++ b/spec/support/shared_examples/services/issues/move_and_clone_services_shared_examples.rb
diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/shared_examples/services/migrate_to_ghost_user_service_shared_examples.rb
index ae98ce689e3..e77d73d1c72 100644
--- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/migrate_to_ghost_user_service_shared_examples.rb
@@ -54,10 +54,10 @@ RSpec.shared_examples "migrating a deleted user's associated records to the ghos
end
end
- context "race conditions" do
+ describe "race conditions" do
context "when #{record_class_name} migration fails and is rolled back" do
before do
- allow_any_instance_of(ActiveRecord::Associations::CollectionProxy)
+ allow_next_instance_of(ActiveRecord::Associations::CollectionProxy)
.to receive(:update_all).and_raise(ActiveRecord::StatementTimeout)
end
diff --git a/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb b/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
index a3042ac2e26..cb544f42765 100644
--- a/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
@@ -29,26 +29,76 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
let_it_be(:architecture_amd64) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'amd64') }
let_it_be(:architecture_arm64) { create("debian_#{container_type}_architecture", distribution: distribution, name: 'arm64') }
- let_it_be(:component_file1) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_all, updated_at: '2020-01-24T08:00:00Z', file_sha256: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', file_md5: 'd41d8cd98f00b204e9800998ecf8427e', file_fixture: nil, size: 0) } # updated
- let_it_be(:component_file2) { create("debian_#{container_type}_component_file", component: component_main, architecture: architecture_all, updated_at: '2020-01-24T09:00:00Z', file_sha256: 'a') } # destroyed
- let_it_be(:component_file3) { create("debian_#{container_type}_component_file", component: component_main, architecture: architecture_amd64, updated_at: '2020-01-24T10:54:59Z', file_sha256: 'b') } # destroyed, 1 second before last generation
- let_it_be(:component_file4) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_all, updated_at: '2020-01-24T10:55:00Z', file_sha256: 'c') } # kept, last generation
- let_it_be(:component_file5) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_all, updated_at: '2020-01-24T10:55:00Z', file_sha256: 'd') } # kept, last generation
- let_it_be(:component_file6) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_amd64, updated_at: '2020-01-25T15:17:18Z', file_sha256: 'e') } # kept, less than 1 hour ago
-
- def check_component_file(release_date, component_name, component_file_type, architecture_name, expected_content)
+ let_it_be(:component_file_old_main_amd64) { create("debian_#{container_type}_component_file", component: component_main, architecture: architecture_amd64, updated_at: '2020-01-24T08:00:00Z', file_sha256: 'a') } # destroyed
+
+ let_it_be(:component_file_oldest_kept_contrib_all) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_all, updated_at: '2020-01-24T10:55:00Z', file_sha256: 'b') } # oldest kept
+ let_it_be(:component_file_oldest_kept_contrib_amd64) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_amd64, updated_at: '2020-01-24T10:55:00Z', file_sha256: 'c') } # oldest kept
+ let_it_be(:component_file_recent_contrib_amd64) { create("debian_#{container_type}_component_file", component: component_contrib, architecture: architecture_amd64, updated_at: '2020-01-25T15:17:18Z', file_sha256: 'd') } # kept, less than 1 hour ago
+
+ let_it_be(:component_file_empty_contrib_all_di) { create("debian_#{container_type}_component_file", :di_packages, :empty, component: component_contrib, architecture: architecture_all, updated_at: '2020-01-24T10:55:00Z') } # oldest kept
+ let_it_be(:component_file_empty_contrib_amd64_di) { create("debian_#{container_type}_component_file", :di_packages, :empty, component: component_contrib, architecture: architecture_amd64, updated_at: '2020-01-24T10:55:00Z') } # touched, as last empty
+ let_it_be(:component_file_recent_contrib_amd64_di) { create("debian_#{container_type}_component_file", :di_packages, component: component_contrib, architecture: architecture_amd64, updated_at: '2020-01-25T15:17:18Z', file_sha256: 'f') } # kept, less than 1 hour ago
+
+ let(:pool_prefix) do
+ prefix = "pool/#{distribution.codename}"
+ prefix += "/#{project.id}" if container_type == :group
+ prefix += "/#{package.name[0]}/#{package.name}/#{package.version}"
+ prefix
+ end
+
+ let(:expected_main_amd64_di_content) do
+ <<~MAIN_AMD64_DI_CONTENT
+ Section: misc
+ Priority: extra
+ Filename: #{pool_prefix}/sample-udeb_1.2.3~alpha2_amd64.udeb
+ Size: 409600
+ SHA256: #{package.package_files.with_debian_file_type(:udeb).first.file_sha256}
+ MAIN_AMD64_DI_CONTENT
+ end
+
+ let(:expected_main_amd64_di_sha256) { Digest::SHA256.hexdigest(expected_main_amd64_di_content) }
+ let!(:component_file_old_main_amd64_di) do # touched
+ create("debian_#{container_type}_component_file", :di_packages, component: component_main, architecture: architecture_amd64, updated_at: '2020-01-24T08:00:00Z', file_sha256: expected_main_amd64_di_sha256).tap do |cf|
+ cf.update! file: CarrierWaveStringFile.new(expected_main_amd64_di_content), size: expected_main_amd64_di_content.size
+ end
+ end
+
+ def check_component_file(
+ release_date, component_name, component_file_type, architecture_name, expected_content,
+ updated: true, id_of: nil
+ )
component_file = distribution
.component_files
.with_component_name(component_name)
.with_file_type(component_file_type)
.with_architecture_name(architecture_name)
+ .with_compression_type(nil)
.order_updated_asc
.last
+ if expected_content.nil?
+ expect(component_file).to be_nil
+ return
+ end
+
expect(component_file).not_to be_nil
- expect(component_file.updated_at).to eq(release_date)
- unless expected_content.nil?
+ if id_of
+ expect(component_file&.id).to eq(id_of.id)
+ else
+ # created
+ expect(component_file&.id).to be > component_file_old_main_amd64_di.id
+ end
+
+ if updated
+ expect(component_file.updated_at).to eq(release_date)
+ else
+ expect(component_file.updated_at).not_to eq(release_date)
+ end
+
+ if expected_content == ''
+ expect(component_file.size).to eq(0)
+ else
expect(expected_content).not_to include('MD5')
component_file.file.use_file do |file_path|
expect(File.read(file_path)).to eq(expected_content)
@@ -57,30 +107,23 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
end
it 'generates Debian distribution and component files', :aggregate_failures do
- current_time = Time.utc(2020, 01, 25, 15, 17, 18, 123456)
+ current_time = Time.utc(2020, 1, 25, 15, 17, 19)
travel_to(current_time) do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
- components_count = 2
- architectures_count = 3
-
- initial_count = 6
- destroyed_count = 2
- updated_count = 1
- created_count = components_count * (architectures_count * 2 + 1) - updated_count
+ initial_count = 8
+ destroyed_count = 1
+ created_count = 4 # main_amd64 + main_sources + empty contrib_all + empty contrib_amd64
expect { subject }
.to not_change { Packages::Package.count }
.and not_change { Packages::PackageFile.count }
.and change { distribution.reload.updated_at }.to(current_time.round)
.and change { distribution.component_files.reset.count }.from(initial_count).to(initial_count - destroyed_count + created_count)
- .and change { component_file1.reload.updated_at }.to(current_time.round)
+ .and change { component_file_old_main_amd64_di.reload.updated_at }.to(current_time.round)
package_files = package.package_files.order(id: :asc).preload_debian_file_metadata.to_a
- pool_prefix = "pool/#{distribution.codename}"
- pool_prefix += "/#{project.id}" if container_type == :group
- pool_prefix += "/#{package.name[0]}/#{package.name}/#{package.version}"
expected_main_amd64_content = <<~EOF
Package: libsample0
Source: #{package.name}
@@ -120,17 +163,9 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
SHA256: #{package_files[3].file_sha256}
EOF
- expected_main_amd64_di_content = <<~EOF
- Section: misc
- Priority: extra
- Filename: #{pool_prefix}/sample-udeb_1.2.3~alpha2_amd64.udeb
- Size: 409600
- SHA256: #{package_files[4].file_sha256}
- EOF
-
expected_main_sources_content = <<~EOF
Package: #{package.name}
- Binary: sample-dev, libsample0, sample-udeb
+ Binary: sample-dev, libsample0, sample-udeb, sample-ddeb
Version: #{package.version}
Maintainer: #{package_files[1].debian_fields['Maintainer']}
Build-Depends: debhelper-compat (= 13)
@@ -139,13 +174,13 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
Format: 3.0 (native)
Files:
#{package_files[1].file_md5} #{package_files[1].size} #{package_files[1].file_name}
- d5ca476e4229d135a88f9c729c7606c9 864 sample_1.2.3~alpha2.tar.xz
+ #{package_files[0].file_md5} 964 #{package_files[0].file_name}
Checksums-Sha256:
#{package_files[1].file_sha256} #{package_files[1].size} #{package_files[1].file_name}
- 40e4682bb24a73251ccd7c7798c0094a649091e5625d6a14bcec9b4e7174f3da 864 sample_1.2.3~alpha2.tar.xz
+ #{package_files[0].file_sha256} 964 #{package_files[0].file_name}
Checksums-Sha1:
#{package_files[1].file_sha1} #{package_files[1].size} #{package_files[1].file_name}
- c5cfc111ea924842a89a06d5673f07dfd07de8ca 864 sample_1.2.3~alpha2.tar.xz
+ #{package_files[0].file_sha1} 964 #{package_files[0].file_name}
Homepage: #{package_files[1].debian_fields['Homepage']}
Section: misc
Priority: extra
@@ -157,42 +192,38 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
check_component_file(current_time.round, 'main', :packages, 'arm64', nil)
check_component_file(current_time.round, 'main', :di_packages, 'all', nil)
- check_component_file(current_time.round, 'main', :di_packages, 'amd64', expected_main_amd64_di_content)
+ check_component_file(current_time.round, 'main', :di_packages, 'amd64', expected_main_amd64_di_content, id_of: component_file_old_main_amd64_di)
check_component_file(current_time.round, 'main', :di_packages, 'arm64', nil)
check_component_file(current_time.round, 'main', :sources, nil, expected_main_sources_content)
- check_component_file(current_time.round, 'contrib', :packages, 'all', nil)
- check_component_file(current_time.round, 'contrib', :packages, 'amd64', nil)
+ check_component_file(current_time.round, 'contrib', :packages, 'all', '')
+ check_component_file(current_time.round, 'contrib', :packages, 'amd64', '')
check_component_file(current_time.round, 'contrib', :packages, 'arm64', nil)
- check_component_file(current_time.round, 'contrib', :di_packages, 'all', nil)
- check_component_file(current_time.round, 'contrib', :di_packages, 'amd64', nil)
+ check_component_file(current_time.round, 'contrib', :di_packages, 'all', '', updated: false, id_of: component_file_empty_contrib_all_di)
+ check_component_file(current_time.round, 'contrib', :di_packages, 'amd64', '', id_of: component_file_empty_contrib_amd64_di)
check_component_file(current_time.round, 'contrib', :di_packages, 'arm64', nil)
check_component_file(current_time.round, 'contrib', :sources, nil, nil)
- main_amd64_size = expected_main_amd64_content.length
- main_amd64_sha256 = Digest::SHA256.hexdigest(expected_main_amd64_content)
+ expected_main_amd64_size = expected_main_amd64_content.bytesize
+ expected_main_amd64_sha256 = Digest::SHA256.hexdigest(expected_main_amd64_content)
- contrib_all_size = component_file1.size
- contrib_all_sha256 = component_file1.file_sha256
+ expected_main_amd64_di_size = expected_main_amd64_di_content.length
- main_amd64_di_size = expected_main_amd64_di_content.length
- main_amd64_di_sha256 = Digest::SHA256.hexdigest(expected_main_amd64_di_content)
-
- main_sources_size = expected_main_sources_content.length
- main_sources_sha256 = Digest::SHA256.hexdigest(expected_main_sources_content)
+ expected_main_sources_size = expected_main_sources_content.length
+ expected_main_sources_sha256 = Digest::SHA256.hexdigest(expected_main_sources_content)
expected_release_content = <<~EOF
Codename: #{distribution.codename}
- Date: Sat, 25 Jan 2020 15:17:18 +0000
- Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
+ Date: Sat, 25 Jan 2020 15:17:19 +0000
+ Valid-Until: Mon, 27 Jan 2020 15:17:19 +0000
Acquire-By-Hash: yes
Architectures: all amd64 arm64
Components: contrib main
SHA256:
- #{contrib_all_sha256} #{contrib_all_size.to_s.rjust(8)} contrib/binary-all/Packages
+ e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/binary-all/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-all/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/binary-amd64/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/debian-installer/binary-amd64/Packages
@@ -201,11 +232,11 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 contrib/source/Sources
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-all/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-all/Packages
- #{main_amd64_sha256} #{main_amd64_size.to_s.rjust(8)} main/binary-amd64/Packages
- #{main_amd64_di_sha256} #{main_amd64_di_size.to_s.rjust(8)} main/debian-installer/binary-amd64/Packages
+ #{expected_main_amd64_sha256} #{expected_main_amd64_size.to_s.rjust(8)} main/binary-amd64/Packages
+ #{expected_main_amd64_di_sha256} #{expected_main_amd64_di_size.to_s.rjust(8)} main/debian-installer/binary-amd64/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/binary-arm64/Packages
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 0 main/debian-installer/binary-arm64/Packages
- #{main_sources_sha256} #{main_sources_size.to_s.rjust(8)} main/source/Sources
+ #{expected_main_sources_sha256} #{expected_main_sources_size.to_s.rjust(8)} main/source/Sources
EOF
expected_release_content = "Suite: #{distribution.suite}\n#{expected_release_content}" if distribution.suite
@@ -222,7 +253,7 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
context 'without components and architectures' do
it 'generates minimal distribution', :aggregate_failures do
- travel_to(Time.utc(2020, 01, 25, 15, 17, 18, 123456)) do
+ travel_to(Time.utc(2020, 1, 25, 15, 17, 18, 123456)) do
expect(Gitlab::ErrorTracking).not_to receive(:log_exception)
expect { subject }
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index f63693dbf26..7a4d7f81e96 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -76,7 +76,7 @@ RSpec.shared_examples 'returns packages' do |container_type, user_type|
subject
expect(json_response.length).to eq(2)
- expect(json_response.map { |package| package['id'] }).to contain_exactly(package1.id, package2.id)
+ expect(json_response.pluck('id')).to contain_exactly(package1.id, package2.id)
end
end
end
@@ -123,7 +123,7 @@ RSpec.shared_examples 'returns packages with subgroups' do |container_type, user
subject
expect(json_response.length).to eq(3)
- expect(json_response.map { |package| package['id'] }).to contain_exactly(package1.id, package2.id, package3.id)
+ expect(json_response.pluck('id')).to contain_exactly(package1.id, package2.id, package3.id)
end
end
end
@@ -138,7 +138,7 @@ RSpec.shared_examples 'package sorting' do |order_by|
it 'returns the sorted packages' do
subject
- expect(json_response.map { |package| package['id'] }).to eq(packages.map(&:id))
+ expect(json_response.pluck('id')).to eq(packages.map(&:id))
end
end
@@ -148,7 +148,7 @@ RSpec.shared_examples 'package sorting' do |order_by|
it 'returns the sorted packages' do
subject
- expect(json_response.map { |package| package['id'] }).to eq(packages.reverse.map(&:id))
+ expect(json_response.pluck('id')).to eq(packages.reverse.map(&:id))
end
end
end
@@ -225,7 +225,7 @@ RSpec.shared_examples 'filters on each package_type' do |is_project: false|
subject
expect(json_response.length).to eq(1)
- expect(json_response.map { |package| package['package_type'] }).to contain_exactly(package_type)
+ expect(json_response.pluck('package_type')).to contain_exactly(package_type)
end
end
end
@@ -253,7 +253,7 @@ RSpec.shared_examples 'with versionless packages' do
it 'does not return the package' do
subject
- expect(json_response.map { |package| package['id'] }).not_to include(versionless_package.id)
+ expect(json_response.pluck('id')).not_to include(versionless_package.id)
end
end
@@ -268,7 +268,7 @@ RSpec.shared_examples 'with versionless packages' do
it 'returns the package' do
subject
- expect(json_response.map { |package| package['id'] }).to include(versionless_package.id)
+ expect(json_response.pluck('id')).to include(versionless_package.id)
end
end
end
@@ -295,7 +295,7 @@ RSpec.shared_examples 'with status param' do
it 'does not return the package' do
subject
- expect(json_response.map { |package| package['id'] }).not_to include(hidden_package.id)
+ expect(json_response.pluck('id')).not_to include(hidden_package.id)
end
end
@@ -309,7 +309,7 @@ RSpec.shared_examples 'with status param' do
it 'returns the package' do
subject
- expect(json_response.map { |package| package['id'] }).to include(hidden_package.id)
+ expect(json_response.pluck('id')).to include(hidden_package.id)
end
end
end
diff --git a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
index 9f940d27341..2070cac24b0 100644
--- a/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/projects/update_repository_storage_service_shared_examples.rb
@@ -63,35 +63,6 @@ RSpec.shared_examples 'moves repository to another storage' do |repository_type|
expect(gitlab_shell.repository_exists?('default', old_project_repository_path)).to be(false)
expect(gitlab_shell.repository_exists?('default', old_repository_path)).to be(false)
end
-
- context ':repack_after_shard_migration feature flag disabled' do
- before do
- stub_feature_flags(repack_after_shard_migration: false)
- end
-
- it 'does not enqueue a GC run' do
- expect { subject.execute }
- .not_to change { Projects::GitGarbageCollectWorker.jobs.count }
- end
- end
-
- context ':repack_after_shard_migration feature flag enabled' do
- before do
- stub_feature_flags(repack_after_shard_migration: true)
- end
-
- it 'does not enqueue a GC run if housekeeping is disabled' do
- stub_application_setting(housekeeping_enabled: false)
-
- expect { subject.execute }
- .not_to change { Projects::GitGarbageCollectWorker.jobs.count }
- end
-
- it 'enqueues a GC run' do
- expect { subject.execute }
- .to change { Projects::GitGarbageCollectWorker.jobs.count }.by(1)
- end
- end
end
context 'when the filesystems are the same' do
diff --git a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
index 209be09c807..21dc3c2bf70 100644
--- a/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/security/ci_configuration/create_service_shared_examples.rb
@@ -114,7 +114,8 @@ RSpec.shared_examples_for 'services security ci configuration create service' do
it 'fails with error' do
expect(project).to receive(:ci_config_for).and_return(unsupported_yaml)
- expect { result }.to raise_error(Gitlab::Graphql::Errors::MutationError, '.gitlab-ci.yml with aliases/anchors is not supported. Please change the CI configuration manually.')
+ expect { result }.to raise_error(Gitlab::Graphql::Errors::MutationError, Gitlab::Utils::ErrorMessage.to_user_facing(
+ _(".gitlab-ci.yml with aliases/anchors is not supported. Please change the CI configuration manually.")))
end
end
@@ -145,7 +146,7 @@ RSpec.shared_examples_for 'services security ci configuration create service' do
let_it_be(:repository) { project.repository }
it 'is successful' do
- expect(repository).to receive(:root_ref_sha).and_raise(StandardError)
+ expect(repository).to receive(:commit).and_return(nil)
expect(result.status).to eq(:success)
end
end
@@ -168,7 +169,7 @@ RSpec.shared_examples_for 'services security ci configuration create service' do
it 'returns an error' do
expect { result }.to raise_error { |error|
expect(error).to be_a(Gitlab::Graphql::Errors::MutationError)
- expect(error.message).to eq('You must <a target="_blank" rel="noopener noreferrer" ' \
+ expect(error.message).to eq('UF You must <a target="_blank" rel="noopener noreferrer" ' \
'href="http://localhost/help/user/project/repository/index.md' \
'#add-files-to-a-repository">add at least one file to the repository' \
'</a> before using Security features.')
diff --git a/spec/support/services/service_response_shared_examples.rb b/spec/support/shared_examples/services/service_response_shared_examples.rb
index 186627347fb..e55f16a2994 100644
--- a/spec/support/services/service_response_shared_examples.rb
+++ b/spec/support/shared_examples/services/service_response_shared_examples.rb
@@ -6,9 +6,7 @@ RSpec.shared_examples 'returning an error service response' do |message: nil|
expect(result).to be_error
- if message
- expect(result.message).to eq(message)
- end
+ expect(result.message).to eq(message) if message
end
end
@@ -18,8 +16,6 @@ RSpec.shared_examples 'returning a success service response' do |message: nil|
expect(result).to be_success
- if message
- expect(result.message).to eq(message)
- end
+ expect(result.message).to eq(message) if message
end
end
diff --git a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
index e72e8e79411..d3b3434b339 100644
--- a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
@@ -5,7 +5,6 @@ RSpec.shared_examples 'issue_edit snowplow tracking' do
let(:action) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_ACTION }
let(:label) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_LABEL }
let(:namespace) { project.namespace }
- let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
it_behaves_like 'Snowplow event tracking with RedisHLL context'
end
diff --git a/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb b/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb
deleted file mode 100644
index ac064ed4c33..00000000000
--- a/spec/support/shared_examples/services/work_items/widgets/milestone_service_shared_examples.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.shared_examples "setting work item's milestone" do
- context "when 'milestone' param does not exist" do
- let(:params) { {} }
-
- it "does not set the work item's milestone" do
- expect { execute_callback }.to not_change(work_item, :milestone)
- end
- end
-
- context "when 'milestone' is not in the work item's project's hierarchy" do
- let(:another_group_milestone) { create(:milestone, group: create(:group)) }
- let(:params) { { milestone_id: another_group_milestone.id } }
-
- it "does not set the work item's milestone" do
- expect { execute_callback }.to not_change(work_item, :milestone)
- end
- end
-
- context 'when assigning a group milestone' do
- let(:params) { { milestone_id: group_milestone.id } }
-
- it "sets the work item's milestone" do
- expect { execute_callback }
- .to change { work_item.milestone }
- .from(nil)
- .to(group_milestone)
- end
- end
-
- context 'when assigning a project milestone' do
- let(:params) { { milestone_id: project_milestone.id } }
-
- it "sets the work item's milestone" do
- expect { execute_callback }
- .to change { work_item.milestone }
- .from(nil)
- .to(project_milestone)
- end
- end
-end
diff --git a/spec/support/shared_examples/views/pipeline_status_changes_email.rb b/spec/support/shared_examples/views/pipeline_status_changes_email.rb
index 698f11c2216..fe6cc5e03d2 100644
--- a/spec/support/shared_examples/views/pipeline_status_changes_email.rb
+++ b/spec/support/shared_examples/views/pipeline_status_changes_email.rb
@@ -8,12 +8,14 @@ RSpec.shared_examples 'pipeline status changes email' do
let(:merge_request) { create(:merge_request, :simple, source_project: project) }
let(:pipeline) do
- create(:ci_pipeline,
- project: project,
- user: user,
- ref: project.default_branch,
- sha: project.commit.sha,
- status: status)
+ create(
+ :ci_pipeline,
+ project: project,
+ user: user,
+ ref: project.default_branch,
+ sha: project.commit.sha,
+ status: status
+ )
end
before do
diff --git a/spec/support/shared_examples/work_items/export_and_import_shared_examples.rb b/spec/support/shared_examples/work_items/export_and_import_shared_examples.rb
new file mode 100644
index 00000000000..bbbfacfdf53
--- /dev/null
+++ b/spec/support/shared_examples/work_items/export_and_import_shared_examples.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples_for 'a exported file that can be imported' do
+ before do
+ origin_project.add_reporter(user)
+ target_project.add_reporter(user)
+ end
+
+ def export_work_items_for(project)
+ origin_work_items = WorkItem.where(project: origin_project)
+ export = described_class.new(origin_work_items, project)
+ export.email(user)
+ attachment = ActionMailer::Base.deliveries.last.attachments.first
+ file = Tempfile.new('temp_work_item_export.csv')
+ file.write(attachment.read)
+
+ file
+ end
+
+ def import_file_for(project, file)
+ uploader = FileUploader.new(project)
+ uploader.store!(file)
+ import_service = WorkItems::ImportCsvService.new(user, target_project, uploader)
+
+ import_service.execute
+ end
+
+ it 'imports work item with correct attributes', :aggregate_failures do
+ csv_file = export_work_items_for(origin_project)
+
+ imported_work_items = ::WorkItems::WorkItemsFinder.new(user, project: target_project).execute
+ expect { import_file_for(target_project, csv_file) }.to change { imported_work_items.count }.by 1
+ imported_work_item = imported_work_items.first
+ expect(imported_work_item.author).to eq(user)
+ expected_matching_fields.each do |field|
+ expect(imported_work_item.public_send(field)).to eq(work_item.public_send(field))
+ end
+ end
+end
diff --git a/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
index e224b71da91..095c32c3136 100644
--- a/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
+++ b/spec/support/shared_examples/workers/batched_background_migration_execution_worker_shared_example.rb
@@ -50,14 +50,20 @@ RSpec.shared_examples 'batched background migrations execution worker' do
end
describe '.max_running_jobs' do
- it 'returns MAX_RUNNING_MIGRATIONS' do
- expect(described_class.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
+ it 'returns database_max_running_batched_background_migrations application setting' do
+ stub_application_setting(database_max_running_batched_background_migrations: 3)
+
+ expect(described_class.max_running_jobs)
+ .to eq(Gitlab::CurrentSettings.database_max_running_batched_background_migrations)
end
end
describe '#max_running_jobs' do
- it 'returns MAX_RUNNING_MIGRATIONS' do
- expect(described_class.new.max_running_jobs).to eq(described_class::MAX_RUNNING_MIGRATIONS)
+ it 'returns database_max_running_batched_background_migrations application setting' do
+ stub_application_setting(database_max_running_batched_background_migrations: 3)
+
+ expect(described_class.new.max_running_jobs)
+ .to eq(Gitlab::CurrentSettings.database_max_running_batched_background_migrations)
end
end
diff --git a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
index 8ec955940c0..06877aee565 100644
--- a/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
+++ b/spec/support/shared_examples/workers/batched_background_migration_worker_shared_examples.rb
@@ -88,9 +88,9 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
end
- context 'when the feature flag is disabled' do
+ context 'when the tracking database is shared' do
before do
- stub_feature_flags(execute_batched_migrations_on_schedule: false)
+ skip_if_database_exists(tracking_database)
end
it 'does nothing' do
@@ -101,22 +101,17 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
end
- context 'when the feature flag is enabled' do
- let(:base_model) { Gitlab::Database.database_base_models[tracking_database] }
-
+ context 'when the tracking database is not shared' do
before do
- stub_feature_flags(execute_batched_migrations_on_schedule: true)
-
- allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration)
- .with(connection: base_model.connection)
- .and_return(nil)
+ skip_if_shared_database(tracking_database)
end
- context 'when database config is shared' do
- it 'does nothing' do
- expect(Gitlab::Database).to receive(:db_config_share_with)
- .with(base_model.connection_db_config).and_return('main')
+ context 'when the feature flag is disabled' do
+ before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: false)
+ end
+ it 'does nothing' do
expect(worker).not_to receive(:active_migration)
expect(worker).not_to receive(:run_active_migration)
@@ -124,123 +119,146 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
end
- context 'when no active migrations exist' do
- context 'when parallel execution is disabled' do
- before do
- stub_feature_flags(batched_migrations_parallel_execution: false)
- end
+ context 'when the feature flag is enabled' do
+ let(:base_model) { Gitlab::Database.database_base_models[tracking_database] }
+ let(:connection) { base_model.connection }
+
+ before do
+ stub_feature_flags(execute_batched_migrations_on_schedule: true)
+ allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration)
+ .with(connection: connection)
+ .and_return(nil)
+ end
+
+ context 'when database config is shared' do
it 'does nothing' do
+ expect(Gitlab::Database).to receive(:db_config_share_with)
+ .with(base_model.connection_db_config).and_return('main')
+
+ expect(worker).not_to receive(:active_migration)
expect(worker).not_to receive(:run_active_migration)
worker.perform
end
end
- context 'when parallel execution is enabled' do
- before do
- stub_feature_flags(batched_migrations_parallel_execution: true)
- end
+ context 'when no active migrations exist' do
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
- it 'does nothing' do
- expect(worker).not_to receive(:queue_migrations_for_execution)
+ it 'does nothing' do
+ expect(worker).not_to receive(:run_active_migration)
- worker.perform
+ worker.perform
+ end
end
- end
- end
- context 'when active migrations exist' do
- let(:job_interval) { 5.minutes }
- let(:lease_timeout) { 15.minutes }
- let(:lease_key) { described_class.name.demodulize.underscore }
- let(:migration_id) { 123 }
- let(:migration) do
- build(
- :batched_background_migration, :active,
- id: migration_id, interval: job_interval, table_name: table_name
- )
- end
+ context 'when parallel execution is enabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
+ end
- let(:execution_worker_class) do
- case tracking_database
- when :main
- Database::BatchedBackgroundMigration::MainExecutionWorker
- when :ci
- Database::BatchedBackgroundMigration::CiExecutionWorker
+ it 'does nothing' do
+ expect(worker).not_to receive(:queue_migrations_for_execution)
+
+ worker.perform
+ end
end
end
- before do
- allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration)
- .with(connection: base_model.connection)
- .and_return(migration)
- end
+ context 'when active migrations exist' do
+ let(:job_interval) { 5.minutes }
+ let(:lease_timeout) { 15.minutes }
+ let(:lease_key) { described_class.name.demodulize.underscore }
+ let(:migration_id) { 123 }
+ let(:migration) do
+ build(
+ :batched_background_migration, :active,
+ id: migration_id, interval: job_interval, table_name: table_name
+ )
+ end
+
+ let(:execution_worker_class) do
+ case tracking_database
+ when :main
+ Database::BatchedBackgroundMigration::MainExecutionWorker
+ when :ci
+ Database::BatchedBackgroundMigration::CiExecutionWorker
+ end
+ end
- context 'when parallel execution is disabled' do
before do
- stub_feature_flags(batched_migrations_parallel_execution: false)
+ allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration)
+ .with(connection: connection)
+ .and_return(migration)
end
- let(:execution_worker) { instance_double(execution_worker_class) }
+ context 'when parallel execution is disabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: false)
+ end
- context 'when the calculated timeout is less than the minimum allowed' do
- let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT }
- let(:job_interval) { 2.minutes }
+ let(:execution_worker) { instance_double(execution_worker_class) }
- it 'sets the lease timeout to the minimum value' do
- expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout)
+ context 'when the calculated timeout is less than the minimum allowed' do
+ let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT }
+ let(:job_interval) { 2.minutes }
- expect(execution_worker_class).to receive(:new).and_return(execution_worker)
- expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
+ it 'sets the lease timeout to the minimum value' do
+ expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout)
- expect(worker).to receive(:run_active_migration).and_call_original
+ expect(execution_worker_class).to receive(:new).and_return(execution_worker)
+ expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
- worker.perform
- end
- end
+ expect(worker).to receive(:run_active_migration).and_call_original
- it 'always cleans up the exclusive lease' do
- lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+ worker.perform
+ end
+ end
- expect(lease).to receive(:try_obtain).and_return(true)
+ it 'always cleans up the exclusive lease' do
+ lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
- expect(worker).to receive(:run_active_migration).and_raise(RuntimeError, 'I broke')
- expect(lease).to receive(:cancel)
+ expect(lease).to receive(:try_obtain).and_return(true)
- expect { worker.perform }.to raise_error(RuntimeError, 'I broke')
- end
+ expect(worker).to receive(:run_active_migration).and_raise(RuntimeError, 'I broke')
+ expect(lease).to receive(:cancel)
- it 'delegetes the execution to ExecutionWorker' do
- base_model = Gitlab::Database.database_base_models[tracking_database]
+ expect { worker.perform }.to raise_error(RuntimeError, 'I broke')
+ end
- expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(base_model.connection).and_yield
- expect(execution_worker_class).to receive(:new).and_return(execution_worker)
- expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
+ it 'delegetes the execution to ExecutionWorker' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(connection).and_yield
+ expect(execution_worker_class).to receive(:new).and_return(execution_worker)
+ expect(execution_worker).to receive(:perform_work).with(tracking_database, migration_id)
- worker.perform
+ worker.perform
+ end
end
- end
- context 'when parallel execution is enabled' do
- before do
- stub_feature_flags(batched_migrations_parallel_execution: true)
- end
+ context 'when parallel execution is enabled' do
+ before do
+ stub_feature_flags(batched_migrations_parallel_execution: true)
+ end
- it 'delegetes the execution to ExecutionWorker' do
- expect(Gitlab::Database::BackgroundMigration::BatchedMigration)
- .to receive(:active_migrations_distinct_on_table).with(
- connection: base_model.connection,
- limit: execution_worker_class.max_running_jobs
- ).and_return([migration])
+ it 'delegetes the execution to ExecutionWorker' do
+ expect(Gitlab::Database::BackgroundMigration::BatchedMigration)
+ .to receive(:active_migrations_distinct_on_table).with(
+ connection: base_model.connection,
+ limit: execution_worker_class.max_running_jobs
+ ).and_return([migration])
- expected_arguments = [
- [tracking_database.to_s, migration_id]
- ]
+ expected_arguments = [
+ [tracking_database.to_s, migration_id]
+ ]
- expect(execution_worker_class).to receive(:perform_with_capacity).with(expected_arguments)
+ expect(execution_worker_class).to receive(:perform_with_capacity).with(expected_arguments)
- worker.perform
+ worker.perform
+ end
end
end
end
@@ -248,7 +266,7 @@ RSpec.shared_examples 'it runs batched background migration jobs' do |tracking_d
end
end
- describe 'executing an entire migration', :freeze_time, if: Gitlab::Database.has_config?(tracking_database) do
+ describe 'executing an entire migration', :freeze_time, if: Gitlab::Database.has_database?(tracking_database) do
include Gitlab::Database::DynamicModelHelpers
include Database::DatabaseHelpers
diff --git a/spec/support/shared_examples/workers/self_monitoring_shared_examples.rb b/spec/support/shared_examples/workers/self_monitoring_shared_examples.rb
deleted file mode 100644
index e6da96e12ec..00000000000
--- a/spec/support/shared_examples/workers/self_monitoring_shared_examples.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-# This shared_example requires the following variables:
-# let(:service_class) { Gitlab::DatabaseImporters::SelfMonitoring::Project::DeleteService }
-# let(:service) { instance_double(service_class) }
-RSpec.shared_examples 'executes service' do
- before do
- allow(service_class).to receive(:new) { service }
- end
-
- it 'runs the service' do
- expect(service).to receive(:execute)
-
- subject.perform
- end
-end
-
-RSpec.shared_examples 'returns in_progress based on Sidekiq::Status' do
- it 'returns true when job is enqueued' do
- jid = described_class.with_status.perform_async
-
- expect(described_class.in_progress?(jid)).to eq(true)
- end
-
- it 'returns false when job does not exist' do
- expect(described_class.in_progress?('fake_jid')).to eq(false)
- end
-end
diff --git a/spec/support/stub_dot_com_check.rb b/spec/support/stub_dot_com_check.rb
new file mode 100644
index 00000000000..6934b33d111
--- /dev/null
+++ b/spec/support/stub_dot_com_check.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+RSpec.configure do |config|
+ %i[saas saas_registration].each do |metadata|
+ config.before(:context, metadata) do
+ # Ensure Gitlab.com? returns true during context.
+ # This is needed for let_it_be which is shared across examples,
+ # therefore the value must be changed in before_all,
+ # but RSpec prevent stubbing method calls in before_all,
+ # therefore we have to resort to temporarily swap url value.
+ @_original_gitlab_url = Gitlab.config.gitlab['url']
+ Gitlab.config.gitlab['url'] = Gitlab::Saas.com_url
+ end
+
+ config.after(:context, metadata) do
+ # Swap back original value
+ Gitlab.config.gitlab['url'] = @_original_gitlab_url
+ end
+ end
+end
diff --git a/spec/support/stub_member_access_level.rb b/spec/support/stub_member_access_level.rb
new file mode 100644
index 00000000000..62e932ee1fc
--- /dev/null
+++ b/spec/support/stub_member_access_level.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module StubMemberAccessLevel
+ # Stubs access level of a member of +object+.
+ #
+ # The following types are supported:
+ # * `Project` - stubs `project.team.max_member_access(user.id)`
+ # * `Group` - stubs `group.max_member_access_for_user(user)`
+ #
+ # @example
+ #
+ # stub_member_access_level(project, maintainer: user)
+ # project.team.max_member_access(user.id) # => Gitlab::Access::MAINTAINER
+ #
+ # stub_member_access_level(group, developer: user)
+ # group.max_member_access_for_user(user) # => Gitlab::Access::DEVELOPER
+ #
+ # stub_member_access_level(project, reporter: user, guest: [guest1, guest2])
+ # project.team.max_member_access(user.id) # => Gitlab::Access::REPORTER
+ # project.team.max_member_access(guests.first.id) # => Gitlab::Access::GUEST
+ # project.team.max_member_access(guests.last.id) # => Gitlab::Access::GUEST
+ #
+ # @param object [Project, Group] Object to be stubbed.
+ # @param access_levels [Hash<Symbol, User>, Hash<Symbol, [User]>] Map of access level to users
+ def stub_member_access_level(object, **access_levels)
+ expectation = case object
+ when Project
+ ->(user) { expect(object.team).to receive(:max_member_access).with(user.id) }
+ when Group
+ ->(user) { expect(object).to receive(:max_member_access_for_user).with(user) }
+ else
+ raise ArgumentError,
+ "Stubbing member access level unsupported for #{object.inspect} (#{object.class})"
+ end
+
+ access_levels.each do |access_level, users|
+ access_level = Gitlab::Access.sym_options_with_owner.fetch(access_level) do
+ raise ArgumentError, "Invalid access level #{access_level.inspect}"
+ end
+
+ Array(users).each do |user|
+ expectation.call(user).at_least(1).times.and_return(access_level)
+ end
+ end
+ end
+end
diff --git a/spec/support/tmpdir.rb b/spec/support/tmpdir.rb
index ea8e26d2878..92126ec1522 100644
--- a/spec/support/tmpdir.rb
+++ b/spec/support/tmpdir.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require 'tmpdir'
+
module TmpdirHelper
def mktmpdir
@tmpdir_helper_dirs ||= []