Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-29 15:09:34 +00:00
parent 9bf40d9fdc
commit 11f742d4e7
75 changed files with 1304 additions and 783 deletions

View File

@ -374,7 +374,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/integration/kroki.md @msedlakjakubowski
/doc/administration/integration/mailgun.md @msedlakjakubowski
/doc/administration/integration/plantuml.md @aqualls
/doc/administration/integration/terminal.md @ashrafkhamis
/doc/administration/integration/terminal.md @phillipwells
/doc/administration/invalidate_markdown_cache.md @msedlakjakubowski
/doc/administration/issue_closing_pattern.md @aqualls
/doc/administration/job_artifacts.md @marcel.amirault
@ -386,27 +386,45 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/logs/log_parsing.md @axil
/doc/administration/logs/tracing_correlation_id.md @axil
/doc/administration/maintenance_mode/ @axil
/doc/administration/merge_request_diffs.md @aqualls
/doc/administration/monitoring/ @msedlakjakubowski
/doc/administration/merge_request_diffs.md @ashrafkhamis
/doc/administration/monitoring/github_imports.md @msedlakjakubowski
/doc/administration/monitoring/gitlab_self_monitoring_project/ @msedlakjakubowski
/doc/administration/monitoring/index.md @msedlakjakubowski
/doc/administration/monitoring/ip_allowlist.md @sselhorn
/doc/administration/monitoring/performance/ @msedlakjakubowski
/doc/administration/monitoring/prometheus/ @msedlakjakubowski
/doc/administration/monitoring/prometheus/gitlab_exporter.md @msedlakjakubowski
/doc/administration/monitoring/prometheus/gitlab_metrics.md @msedlakjakubowski
/doc/administration/monitoring/prometheus/index.md @axil
/doc/administration/monitoring/prometheus/node_exporter.md @msedlakjakubowski
/doc/administration/monitoring/prometheus/pgbouncer_exporter.md @msedlakjakubowski
/doc/administration/monitoring/prometheus/postgres_exporter.md @msedlakjakubowski
/doc/administration/monitoring/prometheus/redis_exporter.md @msedlakjakubowski
/doc/administration/monitoring/prometheus/registry_exporter.md @msedlakjakubowski
/doc/administration/monitoring/prometheus/web_exporter.md @sselhorn
/doc/administration/nfs.md @axil
/doc/administration/object_storage.md @axil
/doc/administration/operations/ @axil
/doc/administration/operations/fast_ssh_key_lookup.md @aqualls
/doc/administration/operations/filesystem_benchmarking.md @axil
/doc/administration/operations/index.md @axil
/doc/administration/operations/moving_repositories.md @eread
/doc/administration/operations/puma.md @axil
/doc/administration/operations/rails_console.md @axil
/doc/administration/operations/ssh_certificates.md @axil
/doc/administration/package_information/ @axil
/doc/administration/packages/ @claytoncornell
/doc/administration/pages/ @aqualls
/doc/administration/pages/ @ashrafkhamis
/doc/administration/polling.md @axil
/doc/administration/postgresql/ @aqualls
/doc/administration/raketasks/ @axil
/doc/administration/raketasks/check.md @axil
/doc/administration/raketasks/geo.md @axil
/doc/administration/raketasks/github_import.md @axil
/doc/administration/raketasks/ldap.md @eread
/doc/administration/raketasks/maintenance.md @axil
/doc/administration/raketasks/praefect.md @eread
/doc/administration/raketasks/project_import_export.md @axil
/doc/administration/raketasks/smtp.md @axil
/doc/administration/raketasks/storage.md @axil
/doc/administration/raketasks/uploads/ @axil
/doc/administration/read_only_gitlab.md @axil
/doc/administration/redis/ @axil
/doc/administration/reference_architectures/ @axil
@ -417,18 +435,28 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/repository_storage_types.md @eread
/doc/administration/restart_gitlab.md @axil
/doc/administration/server_hooks.md @eread
/doc/administration/sidekiq/ @axil
/doc/administration/sidekiq/extra_sidekiq_processes.md @axil
/doc/administration/sidekiq/extra_sidekiq_routing.md @axil
/doc/administration/sidekiq/index.md @axil
/doc/administration/sidekiq/sidekiq_health_check.md @axil
/doc/administration/sidekiq/sidekiq_memory_killer.md @sselhorn
/doc/administration/sidekiq/sidekiq_troubleshooting.md @axil
/doc/administration/smime_signing_email.md @axil
/doc/administration/snippets/ @aqualls
/doc/administration/static_objects_external_storage.md @aqualls
/doc/administration/snippets/ @ashrafkhamis
/doc/administration/static_objects_external_storage.md @ashrafkhamis
/doc/administration/system_hooks.md @ashrafkhamis
/doc/administration/terraform_state.md @phillipwells
/doc/administration/timezone.md @axil
/doc/administration/troubleshooting/ @axil
/doc/administration/troubleshooting/diagnostics_tools.md @axil
/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md @axil
/doc/administration/troubleshooting/index.md @axil
/doc/administration/troubleshooting/linux_cheat_sheet.md @axil
/doc/administration/troubleshooting/postgresql.md @aqualls
/doc/administration/troubleshooting/ssl.md @axil
/doc/administration/troubleshooting/test_environments.md @axil
/doc/administration/uploads.md @axil
/doc/administration/user_settings.md @eread
/doc/administration/wikis/ @aqualls
/doc/administration/wikis/ @ashrafkhamis
/doc/api/access_requests.md @eread
/doc/api/admin_sidekiq_queues.md @axil
/doc/api/alert_management_alerts.md @msedlakjakubowski
@ -445,7 +473,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/cluster_agents.md @phillipwells
/doc/api/commits.md @aqualls
/doc/api/container_registry.md @claytoncornell
/doc/api/custom_attributes.md @ashrafkhamis
/doc/api/custom_attributes.md @msedlakjakubowski
/doc/api/dependencies.md @rdickenson
/doc/api/dependency_proxy.md @claytoncornell
/doc/api/deploy_keys.md @rdickenson
@ -466,9 +494,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/features.md @rdickenson
/doc/api/freeze_periods.md @rdickenson
/doc/api/geo_nodes.md @axil
/doc/api/graphql/ @ashrafkhamis
/doc/api/graphql/audit_report.md @eread
/doc/api/graphql/custom_emoji.md @msedlakjakubowski
/doc/api/graphql/getting_started.md @ashrafkhamis
/doc/api/graphql/index.md @ashrafkhamis
/doc/api/graphql/reference/ @ashrafkhamis
/doc/api/graphql/removed_items.md @ashrafkhamis
/doc/api/graphql/sample_issue_boards.md @msedlakjakubowski
/doc/api/graphql/users_example.md @eread
/doc/api/group_access_tokens.md @eread
/doc/api/group_activity_analytics.md @fneill
/doc/api/group_badges.md @fneill
@ -482,8 +515,8 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/group_protected_environments.md @rdickenson
/doc/api/group_relations_export.md @eread
/doc/api/group_releases.md @rdickenson
/doc/api/group_repository_storage_moves.md @aqualls
/doc/api/group_wikis.md @aqualls
/doc/api/group_repository_storage_moves.md @ashrafkhamis
/doc/api/group_wikis.md @ashrafkhamis
/doc/api/groups.md @fneill
/doc/api/import.md @eread
/doc/api/index.md @ashrafkhamis
@ -509,7 +542,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/merge_request_context_commits.md @aqualls
/doc/api/merge_requests.md @aqualls
/doc/api/merge_trains.md @marcel.amirault
/doc/api/metadata.md @ashrafkhamis
/doc/api/metadata.md @phillipwells
/doc/api/metrics_dashboard_annotations.md @msedlakjakubowski
/doc/api/metrics_user_starred_dashboards.md @msedlakjakubowski
/doc/api/milestones.md @msedlakjakubowski
@ -520,8 +553,8 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/openapi/ @ashrafkhamis
/doc/api/packages.md @claytoncornell
/doc/api/packages/ @claytoncornell
/doc/api/pages.md @aqualls
/doc/api/pages_domains.md @aqualls
/doc/api/pages.md @ashrafkhamis
/doc/api/pages_domains.md @ashrafkhamis
/doc/api/personal_access_tokens.md @eread
/doc/api/pipeline_schedules.md @marcel.amirault
/doc/api/pipeline_triggers.md @marcel.amirault
@ -535,7 +568,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/project_level_variables.md @marcel.amirault
/doc/api/project_relations_export.md @eread
/doc/api/project_repository_storage_moves.md @eread
/doc/api/project_snippets.md @aqualls
/doc/api/project_snippets.md @ashrafkhamis
/doc/api/project_statistics.md @aqualls
/doc/api/project_templates.md @aqualls
/doc/api/project_vulnerabilities.md @aqualls
@ -556,12 +589,12 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/resource_weight_events.md @msedlakjakubowski
/doc/api/runners.md @sselhorn
/doc/api/scim.md @eread
/doc/api/search.md @aqualls
/doc/api/search.md @ashrafkhamis
/doc/api/secure_files.md @marcel.amirault
/doc/api/settings.md @eread
/doc/api/sidekiq_metrics.md @axil
/doc/api/snippet_repository_storage_moves.md @aqualls
/doc/api/snippets.md @aqualls
/doc/api/snippet_repository_storage_moves.md @ashrafkhamis
/doc/api/snippets.md @ashrafkhamis
/doc/api/statistics.md @eread
/doc/api/status_checks.md @eread
/doc/api/suggestions.md @aqualls
@ -575,16 +608,18 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/topics.md @fneill
/doc/api/usage_data.md @claytoncornell
/doc/api/users.md @eread
/doc/api/version.md @ashrafkhamis
/doc/api/version.md @phillipwells
/doc/api/visual_review_discussions.md @marcel.amirault
/doc/api/vulnerabilities.md @claytoncornell
/doc/api/vulnerability_exports.md @claytoncornell
/doc/api/vulnerability_findings.md @claytoncornell
/doc/api/wikis.md @aqualls
/doc/api/wikis.md @ashrafkhamis
/doc/architecture/blueprints/ci_pipeline_components/ @marcel.amirault
/doc/architecture/blueprints/container_registry_metadata_database/ @claytoncornell
/doc/architecture/blueprints/database/scalability/patterns/ @aqualls
/doc/architecture/blueprints/database_scaling/ @aqualls
/doc/architecture/blueprints/gitlab_to_kubernetes_communication/ @phillipwells
/doc/architecture/blueprints/work_items/ @msedlakjakubowski
/doc/ci/ @marcel.amirault
/doc/ci/caching/ @marcel.amirault
/doc/ci/chatops/ @phillipwells
@ -626,8 +661,16 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/ci/services/ @sselhorn
/doc/ci/ssh_keys/ @marcel.amirault
/doc/ci/test_cases/ @msedlakjakubowski
/doc/ci/testing/ @marcel.amirault
/doc/ci/testing/accessibility_testing.md @marcel.amirault
/doc/ci/testing/browser_performance_testing.md @marcel.amirault
/doc/ci/testing/code_quality.md @rdickenson
/doc/ci/testing/fail_fast_testing.md @marcel.amirault
/doc/ci/testing/index.md @marcel.amirault
/doc/ci/testing/load_performance_testing.md @marcel.amirault
/doc/ci/testing/metrics_reports.md @marcel.amirault
/doc/ci/testing/test_coverage_visualization.md @marcel.amirault
/doc/ci/testing/unit_test_report_examples.md @marcel.amirault
/doc/ci/testing/unit_test_reports.md @marcel.amirault
/doc/ci/triggers/ @marcel.amirault
/doc/ci/variables/ @marcel.amirault
/doc/ci/yaml/ @marcel.amirault
@ -645,9 +688,57 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/cicd/ @marcel.amirault
/doc/development/code_intelligence/ @aqualls
/doc/development/contributing/ @sselhorn
/doc/development/database/ @aqualls
/doc/development/database/add_foreign_key_to_existing_column.md @aqualls
/doc/development/database/adding_database_indexes.md @aqualls
/doc/development/database/avoiding_downtime_in_migrations.md @aqualls
/doc/development/database/background_migrations.md @aqualls
/doc/development/database/batched_background_migrations.md @aqualls
/doc/development/database/ci_mirrored_tables.md @aqualls
/doc/development/database/client_side_connection_pool.md @aqualls
/doc/development/database/constraint_naming_convention.md @aqualls
/doc/development/database/creating_enums.md @aqualls
/doc/development/database/database_debugging.md @aqualls
/doc/development/database/database_dictionary.md @aqualls
/doc/development/database/database_lab.md @aqualls
/doc/development/database/database_migration_pipeline.md @aqualls
/doc/development/database/database_query_comments.md @aqualls
/doc/development/database/database_reviewer_guidelines.md @aqualls
/doc/development/database/db_dump.md @aqualls
/doc/development/database/dbcheck-migrations-job.md @aqualls
/doc/development/database/deleting_migrations.md @aqualls
/doc/development/database/efficient_in_operator_queries.md @aqualls
/doc/development/database/filtering_by_label.md @msedlakjakubowski
/doc/development/database/foreign_keys.md @aqualls
/doc/development/database/hash_indexes.md @aqualls
/doc/development/database/index.md @aqualls
/doc/development/database/insert_into_tables_in_batches.md @aqualls
/doc/development/database/iterating_tables_in_batches.md @aqualls
/doc/development/database/keyset_pagination.md @aqualls
/doc/development/database/layout_and_access_patterns.md @aqualls
/doc/development/database/loose_foreign_keys.md @aqualls
/doc/development/database/maintenance_operations.md @aqualls
/doc/development/database/migrations_for_multiple_databases.md @aqualls
/doc/development/database/multiple_databases.md @sselhorn
/doc/development/database/not_null_constraints.md @aqualls
/doc/development/database/ordering_table_columns.md @aqualls
/doc/development/database/pagination_guidelines.md @aqualls
/doc/development/database/pagination_performance_guidelines.md @aqualls
/doc/development/database/polymorphic_associations.md @aqualls
/doc/development/database/post_deployment_migrations.md @aqualls
/doc/development/database/query_count_limits.md @aqualls
/doc/development/database/query_performance.md @aqualls
/doc/development/database/query_recorder.md @aqualls
/doc/development/database/rename_database_tables.md @aqualls
/doc/development/database/serializing_data.md @aqualls
/doc/development/database/setting_multiple_values.md @aqualls
/doc/development/database/sha1_as_binary.md @aqualls
/doc/development/database/single_table_inheritance.md @aqualls
/doc/development/database/strings_and_the_text_data_type.md @aqualls
/doc/development/database/swapping_tables.md @aqualls
/doc/development/database/table_partitioning.md @aqualls
/doc/development/database/transaction_guidelines.md @aqualls
/doc/development/database/understanding_explain_plans.md @aqualls
/doc/development/database/verifying_database_capabilities.md @aqualls
/doc/development/database_review.md @aqualls
/doc/development/developing_with_solargraph.md @aqualls
/doc/development/development_processes.md @sselhorn
@ -659,11 +750,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/elasticsearch.md @ashrafkhamis
/doc/development/experiment_guide/ @phillipwells
/doc/development/export_csv.md @eread
/doc/development/fe_guide/content_editor.md @aqualls
/doc/development/fe_guide/content_editor.md @ashrafkhamis
/doc/development/fe_guide/dark_mode.md @sselhorn
/doc/development/fe_guide/graphql.md @sselhorn
/doc/development/fe_guide/merge_request_widget_extensions.md @aqualls
/doc/development/fe_guide/source_editor.md @aqualls
/doc/development/fe_guide/source_editor.md @ashrafkhamis
/doc/development/fe_guide/view_component.md @rdickenson
/doc/development/feature_categorization/ @sselhorn
/doc/development/feature_development.md @sselhorn
@ -673,8 +764,8 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/geo/ @axil
/doc/development/git_object_deduplication.md @eread
/doc/development/gitaly.md @eread
/doc/development/gitlab_flavored_markdown/ @aqualls
/doc/development/gitlab_flavored_markdown/specification_guide/ @aqualls
/doc/development/gitlab_flavored_markdown/ @ashrafkhamis
/doc/development/gitlab_flavored_markdown/specification_guide/ @ashrafkhamis
/doc/development/graphql_guide/batchloader.md @aqualls
/doc/development/graphql_guide/graphql_pro.md @ashrafkhamis
/doc/development/graphql_guide/index.md @ashrafkhamis
@ -684,8 +775,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/image_scaling.md @sselhorn
/doc/development/import_export.md @eread
/doc/development/index.md @sselhorn
/doc/development/integrations/ @ashrafkhamis
/doc/development/integrations/secure.md @claytoncornell
/doc/development/integrations/codesandbox.md @sselhorn
/doc/development/integrations/index.md @ashrafkhamis
/doc/development/integrations/jenkins.md @ashrafkhamis
/doc/development/integrations/jira_connect.md @ashrafkhamis
/doc/development/integrations/secure.md @rdickenson
/doc/development/integrations/secure_partner_integration.md @rdickenson
/doc/development/internal_api/ @aqualls
/doc/development/internal_users.md @sselhorn
@ -698,7 +792,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/merge_request_concepts/ @aqualls
/doc/development/omnibus.md @axil
/doc/development/packages/ @claytoncornell
/doc/development/pages/ @aqualls
/doc/development/pages/ @ashrafkhamis
/doc/development/permissions.md @eread
/doc/development/policies.md @eread
/doc/development/product_qualified_lead_guide/ @phillipwells
@ -716,7 +810,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/development/testing_guide/end_to_end/ @sselhorn
/doc/development/value_stream_analytics.md @fneill
/doc/development/value_stream_analytics/ @fneill
/doc/development/wikis.md @aqualls
/doc/development/wikis.md @ashrafkhamis
/doc/development/work_items.md @msedlakjakubowski
/doc/development/work_items_widgets.md @msedlakjakubowski
/doc/development/workhorse/ @aqualls
@ -725,13 +819,8 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/drawers/ @ashrafkhamis
/doc/gitlab-basics/ @aqualls
/doc/install/ @axil
/doc/install/aws/ @axil
/doc/install/azure/ @axil
/doc/install/cloud_native/ @axil
/doc/install/google_cloud_platform/ @axil
/doc/install/openshift_and_gitlab/ @axil
/doc/integration/advanced_search/ @ashrafkhamis
/doc/integration/akismet.md @ashrafkhamis
/doc/integration/akismet.md @phillipwells
/doc/integration/alicloud.md @eread
/doc/integration/arkose.md @phillipwells
/doc/integration/auth0.md @eread
@ -744,7 +833,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/integration/facebook.md @eread
/doc/integration/github.md @eread
/doc/integration/gitlab.md @eread
/doc/integration/gitpod.md @aqualls
/doc/integration/gitpod.md @ashrafkhamis
/doc/integration/gmail_action_buttons_for_gitlab.md @ashrafkhamis
/doc/integration/google.md @eread
/doc/integration/index.md @ashrafkhamis
@ -756,7 +845,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/integration/oauth_provider.md @eread
/doc/integration/omniauth.md @eread
/doc/integration/openid_connect_provider.md @eread
/doc/integration/recaptcha.md @ashrafkhamis
/doc/integration/recaptcha.md @eread
/doc/integration/salesforce.md @eread
/doc/integration/saml.md @eread
/doc/integration/security_partners/ @rdickenson
@ -774,10 +863,18 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/operations/product_analytics.md @claytoncornell
/doc/operations/tracing.md @msedlakjakubowski
/doc/policy/ @axil
/doc/raketasks/ @axil
/doc/raketasks/backup_gitlab.md @axil
/doc/raketasks/backup_restore.md @axil
/doc/raketasks/cleanup.md @axil
/doc/raketasks/generate_sample_prometheus_data.md @msedlakjakubowski
/doc/raketasks/migrate_snippets.md @aqualls
/doc/raketasks/import.md @axil
/doc/raketasks/index.md @axil
/doc/raketasks/list_repos.md @axil
/doc/raketasks/migrate_snippets.md @ashrafkhamis
/doc/raketasks/restore_gitlab.md @axil
/doc/raketasks/spdx.md @rdickenson
/doc/raketasks/user_management.md @axil
/doc/raketasks/web_hooks.md @axil
/doc/raketasks/x509_signatures.md @aqualls
/doc/security/ @eread
/doc/subscriptions/ @fneill
@ -795,10 +892,17 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/topics/offline/ @axil
/doc/topics/plan_and_track.md @msedlakjakubowski
/doc/tutorials/ @kpaizee
/doc/update/ @axil
/doc/update/index.md @axil
/doc/update/mysql_to_postgresql.md @aqualls
/doc/update/package/ @axil
/doc/update/patch_versions.md @axil
/doc/update/plan_your_upgrade.md @axil
/doc/update/restore_after_failure.md @axil
/doc/update/upgrading_from_ce_to_ee.md @axil
/doc/update/upgrading_from_source.md @axil
/doc/update/upgrading_postgresql_using_slony.md @aqualls
/doc/update/with_downtime.md @axil
/doc/update/zero_downtime.md @axil
/doc/user/admin_area/analytics/ @fneill
/doc/user/admin_area/broadcast_messages.md @phillipwells
/doc/user/admin_area/credentials_inventory.md @eread
@ -838,10 +942,16 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/admin_area/settings/usage_statistics.md @claytoncornell
/doc/user/admin_area/settings/visibility_and_access_controls.md @aqualls
/doc/user/analytics/ci_cd_analytics.md @rdickenson
/doc/user/analytics/ @fneill
/doc/user/analytics/code_review_analytics.md @fneill
/doc/user/analytics/index.md @fneill
/doc/user/analytics/issue_analytics.md @fneill
/doc/user/analytics/merge_request_analytics.md @fneill
/doc/user/analytics/productivity_analytics.md @fneill
/doc/user/analytics/repository_analytics.md @fneill
/doc/user/analytics/value_stream_analytics.md @fneill
/doc/user/application_security/api_fuzzing/ @rdickenson
/doc/user/application_security/configuration/ @rdickenson
/doc/user/application_security/container_scanning/ @claytoncornell
/doc/user/application_security/container_scanning/ @rdickenson
/doc/user/application_security/coverage_fuzzing/ @rdickenson
/doc/user/application_security/cve_id_request.md @claytoncornell
/doc/user/application_security/dast/ @rdickenson
@ -913,6 +1023,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/packages/dependency_proxy/ @claytoncornell
/doc/user/packages/generic_packages/ @claytoncornell
/doc/user/packages/go_proxy/ @claytoncornell
/doc/user/packages/harbor_container_registry/ @claytoncornell
/doc/user/packages/helm_repository/ @claytoncornell
/doc/user/packages/infrastructure_registry/ @phillipwells
/doc/user/packages/maven_repository/ @claytoncornell
@ -929,6 +1040,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/profile/notifications.md @msedlakjakubowski
/doc/user/profile/personal_access_tokens.md @eread
/doc/user/profile/unknown_sign_in_notification.md @eread
/doc/user/profile/wrong_two_factor_authentication_code_notification.md @eread
/doc/user/project/autocomplete_characters.md @aqualls
/doc/user/project/badges.md @aqualls
/doc/user/project/clusters/ @phillipwells
@ -942,48 +1054,133 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/project/file_lock.md @aqualls
/doc/user/project/git_attributes.md @aqualls
/doc/user/project/highlighting.md @aqualls
/doc/user/project/import/ @eread
/doc/user/project/import/bitbucket.md @eread
/doc/user/project/import/bitbucket_server.md @eread
/doc/user/project/import/clearcase.md @eread
/doc/user/project/import/cvs.md @eread
/doc/user/project/import/fogbugz.md @eread
/doc/user/project/import/gitea.md @eread
/doc/user/project/import/github.md @eread
/doc/user/project/import/gitlab_com.md @eread
/doc/user/project/import/index.md @eread
/doc/user/project/import/jira.md @msedlakjakubowski
/doc/user/project/import/manifest.md @eread
/doc/user/project/import/perforce.md @eread
/doc/user/project/import/phabricator.md @eread
/doc/user/project/import/repo_by_url.md @eread
/doc/user/project/import/svn.md @eread
/doc/user/project/import/tfvc.md @eread
/doc/user/project/index.md @fneill
/doc/user/project/integrations/ @ashrafkhamis
/doc/user/project/insights/ @fneill
/doc/user/project/integrations/asana.md @ashrafkhamis
/doc/user/project/integrations/bamboo.md @ashrafkhamis
/doc/user/project/integrations/bugzilla.md @ashrafkhamis
/doc/user/project/integrations/custom_issue_tracker.md @ashrafkhamis
/doc/user/project/integrations/discord_notifications.md @ashrafkhamis
/doc/user/project/integrations/emails_on_push.md @ashrafkhamis
/doc/user/project/integrations/ewm.md @ashrafkhamis
/doc/user/project/integrations/github.md @ashrafkhamis
/doc/user/project/integrations/gitlab_slack_application.md @ashrafkhamis
/doc/user/project/integrations/hangouts_chat.md @ashrafkhamis
/doc/user/project/integrations/harbor.md @ashrafkhamis
/doc/user/project/integrations/index.md @ashrafkhamis
/doc/user/project/integrations/irker.md @ashrafkhamis
/doc/user/project/integrations/mattermost.md @ashrafkhamis
/doc/user/project/integrations/mattermost_slash_commands.md @ashrafkhamis
/doc/user/project/integrations/microsoft_teams.md @ashrafkhamis
/doc/user/project/integrations/mock_ci.md @ashrafkhamis
/doc/user/project/integrations/pipeline_status_emails.md @ashrafkhamis
/doc/user/project/integrations/pivotal_tracker.md @ashrafkhamis
/doc/user/project/integrations/prometheus.md @msedlakjakubowski
/doc/user/project/integrations/prometheus_library/ @msedlakjakubowski
/doc/user/project/integrations/pumble.md @ashrafkhamis
/doc/user/project/integrations/redmine.md @ashrafkhamis
/doc/user/project/integrations/servicenow.md @ashrafkhamis
/doc/user/project/integrations/shimo.md @ashrafkhamis
/doc/user/project/integrations/slack.md @ashrafkhamis
/doc/user/project/integrations/slack_slash_commands.md @ashrafkhamis
/doc/user/project/integrations/unify_circuit.md @ashrafkhamis
/doc/user/project/integrations/webex_teams.md @ashrafkhamis
/doc/user/project/integrations/webhook_events.md @ashrafkhamis
/doc/user/project/integrations/webhooks.md @ashrafkhamis
/doc/user/project/integrations/youtrack.md @ashrafkhamis
/doc/user/project/integrations/zentao.md @ashrafkhamis
/doc/user/project/issue_board.md @msedlakjakubowski
/doc/user/project/issues/ @msedlakjakubowski
/doc/user/project/issues/associate_zoom_meeting.md @msedlakjakubowski
/doc/user/project/issues/confidential_issues.md @msedlakjakubowski
/doc/user/project/issues/crosslinking_issues.md @msedlakjakubowski
/doc/user/project/issues/csv_import.md @eread
/doc/user/project/issues/design_management.md @msedlakjakubowski
/doc/user/project/issues/due_dates.md @msedlakjakubowski
/doc/user/project/issues/index.md @msedlakjakubowski
/doc/user/project/issues/issue_weight.md @msedlakjakubowski
/doc/user/project/issues/managing_issues.md @msedlakjakubowski
/doc/user/project/issues/multiple_assignees_for_issues.md @msedlakjakubowski
/doc/user/project/issues/related_issues.md @msedlakjakubowski
/doc/user/project/issues/sorting_issue_lists.md @msedlakjakubowski
/doc/user/project/labels.md @msedlakjakubowski
/doc/user/project/members/ @fneill
/doc/user/project/merge_requests/ @aqualls
/doc/user/project/merge_requests/allow_collaboration.md @aqualls
/doc/user/project/merge_requests/approvals/ @aqualls
/doc/user/project/merge_requests/authorization_for_merge_requests.md @aqualls
/doc/user/project/merge_requests/changes.md @aqualls
/doc/user/project/merge_requests/cherry_pick_changes.md @aqualls
/doc/user/project/merge_requests/commit_templates.md @aqualls
/doc/user/project/merge_requests/commits.md @aqualls
/doc/user/project/merge_requests/confidential.md @aqualls
/doc/user/project/merge_requests/conflicts.md @aqualls
/doc/user/project/merge_requests/creating_merge_requests.md @aqualls
/doc/user/project/merge_requests/csv_export.md @eread
/doc/user/project/merge_requests/dependencies.md @aqualls
/doc/user/project/merge_requests/drafts.md @aqualls
/doc/user/project/merge_requests/getting_started.md @aqualls
/doc/user/project/merge_requests/index.md @aqualls
/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md @aqualls
/doc/user/project/merge_requests/methods/ @aqualls
/doc/user/project/merge_requests/revert_changes.md @aqualls
/doc/user/project/merge_requests/reviews/ @aqualls
/doc/user/project/merge_requests/squash_and_merge.md @aqualls
/doc/user/project/merge_requests/status_checks.md @eread
/doc/user/project/merge_requests/versions.md @aqualls
/doc/user/project/merge_requests/widgets.md @aqualls
/doc/user/project/milestones/ @msedlakjakubowski
/doc/user/project/pages/ @aqualls
/doc/user/project/pages/custom_domains_ssl_tls_certification/ @aqualls
/doc/user/project/pages/getting_started/ @aqualls
/doc/user/project/pages/ @ashrafkhamis
/doc/user/project/protected_branches.md @aqualls
/doc/user/project/protected_tags.md @aqualls
/doc/user/project/push_options.md @aqualls
/doc/user/project/quick_actions.md @msedlakjakubowski
/doc/user/project/releases/ @rdickenson
/doc/user/project/repository/ @aqualls
/doc/user/project/repository/branches/ @aqualls
/doc/user/project/repository/csv.md @aqualls
/doc/user/project/repository/file_finder.md @ashrafkhamis
/doc/user/project/repository/forking_workflow.md @aqualls
/doc/user/project/repository/git_blame.md @aqualls
/doc/user/project/repository/git_history.md @aqualls
/doc/user/project/repository/gpg_signed_commits/ @aqualls
/doc/user/project/repository/index.md @aqualls
/doc/user/project/repository/jupyter_notebooks/ @aqualls
/doc/user/project/repository/managing_large_repositories.md @axil
/doc/user/project/repository/mirror/ @aqualls
/doc/user/project/repository/push_rules.md @aqualls
/doc/user/project/repository/reducing_the_repo_size_using_git.md @eread
/doc/user/project/repository/vscode.md @aqualls
/doc/user/project/repository/web_editor.md @ashrafkhamis
/doc/user/project/repository/x509_signed_commits/ @aqualls
/doc/user/project/requirements/ @msedlakjakubowski
/doc/user/project/service_desk.md @msedlakjakubowski
/doc/user/project/settings/import_export.md @eread
/doc/user/project/settings/index.md @fneill
/doc/user/project/settings/project_access_tokens.md @eread
/doc/user/project/time_tracking.md @msedlakjakubowski
/doc/user/project/web_ide/ @aqualls
/doc/user/project/wiki/ @aqualls
/doc/user/project/web_ide/ @ashrafkhamis
/doc/user/project/wiki/ @ashrafkhamis
/doc/user/project/working_with_projects.md @fneill
/doc/user/public_access.md @fneill
/doc/user/reserved_names.md @fneill
/doc/user/search/ @ashrafkhamis
/doc/user/search/global_search/ @ashrafkhamis
/doc/user/shortcuts.md @aqualls
/doc/user/snippets.md @aqualls
/doc/user/shortcuts.md @ashrafkhamis
/doc/user/snippets.md @ashrafkhamis
/doc/user/ssh.md @eread
/doc/user/tasks.md @msedlakjakubowski
/doc/user/todos.md @msedlakjakubowski

View File

@ -19,26 +19,6 @@ Layout/FirstArrayElementIndentation:
- 'spec/lib/gitlab/gitaly_client/blob_service_spec.rb'
- 'spec/lib/gitlab/github_import/importer/diff_notes_importer_spec.rb'
- 'spec/lib/gitlab/github_import/importer/issues_importer_spec.rb'
- 'spec/lib/gitlab/github_import/importer/lfs_objects_importer_spec.rb'
- 'spec/lib/gitlab/github_import/importer/notes_importer_spec.rb'
- 'spec/lib/gitlab/github_import/parallel_scheduling_spec.rb'
- 'spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb'
- 'spec/lib/gitlab/hook_data/release_builder_spec.rb'
- 'spec/lib/gitlab/import_export/group/tree_restorer_spec.rb'
- 'spec/lib/gitlab/import_export/group/tree_saver_spec.rb'
- 'spec/lib/gitlab/import_export/project/tree_restorer_spec.rb'
- 'spec/lib/gitlab/kubernetes/rollout_instances_spec.rb'
- 'spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/order_values_loader_strategy_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/iterator_spec.rb'
- 'spec/lib/gitlab/pagination/keyset/order_spec.rb'
- 'spec/lib/gitlab/project_transfer_spec.rb'
- 'spec/lib/gitlab/prometheus_client_spec.rb'
- 'spec/lib/gitlab/push_options_spec.rb'
- 'spec/lib/gitlab/rack_attack/request_spec.rb'
- 'spec/lib/gitlab/search/abuse_detection_spec.rb'
- 'spec/lib/gitlab/search/found_blob_spec.rb'
- 'spec/lib/gitlab/serializer/ci/variables_spec.rb'
- 'spec/lib/gitlab/sidekiq_config_spec.rb'

View File

@ -1,7 +1,6 @@
<script>
import { GlButton } from '@gitlab/ui';
import api from '~/api';
import { __ } from '~/locale';
import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@ -115,9 +114,6 @@ export default {
},
computed: {
collapseText() {
return this.isCollapsed ? __('Expand') : __('Collapse');
},
isLoading() {
return this.status === status.LOADING;
},
@ -172,6 +168,11 @@ export default {
},
methods: {
toggleCollapsed() {
// Because the top-level div is always clickable, we need to check if we can collapse.
if (!this.isCollapsible) {
return;
}
if (this.trackAction) {
api.trackRedisHllUserEvent(this.trackAction);
}
@ -186,9 +187,9 @@ export default {
</script>
<template>
<section class="media-section">
<div class="media">
<div class="media" :class="{ 'gl-cursor-pointer': isCollapsible }" @click="toggleCollapsed">
<status-icon :status="statusIconName" :size="24" class="align-self-center" />
<div class="media-body d-flex flex-align-self-center align-items-center">
<div class="media-body gl-display-flex gl-align-items-flex-start gl-flex-direction-row!">
<div data-testid="report-section-code-text" class="js-code-text code-text">
<div class="gl-display-flex gl-align-items-center">
<p class="gl-line-height-normal gl-m-0">{{ headerText }}</p>
@ -204,14 +205,19 @@ export default {
<slot name="action-buttons" :is-collapsible="isCollapsible"></slot>
<gl-button
<div
v-if="isCollapsible"
data-testid="report-section-expand-button"
data-qa-selector="expand_report_button"
@click="toggleCollapsed"
class="gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3"
>
{{ collapseText }}
</gl-button>
<gl-button
data-testid="report-section-expand-button"
data-qa-selector="expand_report_button"
category="tertiary"
size="small"
:icon="isExpanded ? 'chevron-lg-up' : 'chevron-lg-down'"
@click.stop="toggleCollapsed"
/>
</div>
</div>
</div>

View File

@ -1,7 +1,7 @@
import axios from '~/lib/utils/axios_utils';
import { joinPaths } from '~/lib/utils/url_utility';
import { normalizeData } from 'ee_else_ce/repository/utils/commit';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { COMMIT_BATCH_SIZE, I18N_COMMIT_DATA_FETCH_ERROR } from './constants';
let requestedOffsets = [];
@ -43,7 +43,7 @@ const fetchData = (projectPath, path, ref, offset) => {
return axios
.get(url, { params: { format: 'json', offset } })
.then(({ data }) => normalizeData(data, path))
.catch(() => createFlash({ message: I18N_COMMIT_DATA_FETCH_ERROR }));
.catch(() => createAlert({ message: I18N_COMMIT_DATA_FETCH_ERROR }));
};
export const loadCommits = async (projectPath, path, ref, offset) => {

View File

@ -4,7 +4,7 @@ import { uniqueId } from 'lodash';
import BlobContent from '~/blob/components/blob_content.vue';
import BlobHeader from '~/blob/components/blob_header.vue';
import { SIMPLE_BLOB_VIEWER, RICH_BLOB_VIEWER } from '~/blob/components/constants';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { isLoggedIn, handleLocationHash } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
@ -271,7 +271,7 @@ export default {
.catch(() => this.displayError());
},
displayError() {
createFlash({ message: __('An error occurred while loading the file. Please try again.') });
createAlert({ message: __('An error occurred while loading the file. Please try again.') });
},
switchViewer(newViewer) {
this.activeViewerType = newViewer || SIMPLE_BLOB_VIEWER;

View File

@ -1,7 +1,7 @@
<script>
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import getRefMixin from '~/repository/mixins/get_ref';
import initSourcegraph from '~/sourcegraph';
import ShortcutsBlob from '~/behaviors/shortcuts/shortcuts_blob';
@ -36,7 +36,7 @@ export default {
return !this.filePath;
},
error() {
createFlash({ message: this.$options.i18n.errorMessage });
createAlert({ message: this.$options.i18n.errorMessage });
},
},
},

View File

@ -8,7 +8,7 @@ import {
GlFormTextarea,
GlToggle,
} from '@gitlab/ui';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
@ -140,7 +140,7 @@ export default {
})
.catch(() => {
this.loading = false;
createFlash({ message: ERROR_MESSAGE });
createAlert({ message: ERROR_MESSAGE });
});
},
},

View File

@ -1,6 +1,6 @@
<script>
import paginatedTreeQuery from 'shared_queries/repository/paginated_tree.query.graphql';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __ } from '~/locale';
import {
@ -142,7 +142,7 @@ export default {
}
})
.catch((error) => {
createFlash({
createAlert({
message: __('An error occurred while fetching folder content.'),
});
throw error;

View File

@ -9,7 +9,7 @@ import {
GlButton,
GlAlert,
} from '@gitlab/ui';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { ContentTypeMultipartFormData } from '~/lib/utils/headers';
import { numberToHumanSize } from '~/lib/utils/number_utils';
@ -171,7 +171,7 @@ export default {
})
.catch(() => {
this.loading = false;
createFlash({ message: ERROR_MESSAGE });
createAlert({ message: ERROR_MESSAGE });
});
},
formData() {

View File

@ -1,5 +1,5 @@
import Api from '~/api';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY, SIDEBAR_PARAMS } from './constants';
@ -13,7 +13,7 @@ export const fetchGroups = ({ commit }, search) => {
commit(types.RECEIVE_GROUPS_SUCCESS, data);
})
.catch(() => {
createFlash({ message: __('There was a problem fetching groups.') });
createAlert({ message: __('There was a problem fetching groups.') });
commit(types.RECEIVE_GROUPS_ERROR);
});
};
@ -23,7 +23,7 @@ export const fetchProjects = ({ commit, state }, search) => {
const groupId = state.query?.group_id;
const handleCatch = () => {
createFlash({ message: __('There was an error fetching projects') });
createAlert({ message: __('There was an error fetching projects') });
commit(types.RECEIVE_PROJECTS_ERROR);
};
const handleSuccess = ({ data }) => {
@ -59,7 +59,7 @@ export const loadFrequentGroups = async ({ commit, state }) => {
const inflatedData = mergeById(await Promise.all(promises), storedData);
commit(types.LOAD_FREQUENT_ITEMS, { key: GROUPS_LOCAL_STORAGE_KEY, data: inflatedData });
} catch {
createFlash({ message: __('There was a problem fetching recent groups.') });
createAlert({ message: __('There was a problem fetching recent groups.') });
}
};
@ -70,7 +70,7 @@ export const loadFrequentProjects = async ({ commit, state }) => {
const inflatedData = mergeById(await Promise.all(promises), storedData);
commit(types.LOAD_FREQUENT_ITEMS, { key: PROJECTS_LOCAL_STORAGE_KEY, data: inflatedData });
} catch {
createFlash({ message: __('There was a problem fetching recent projects.') });
createAlert({ message: __('There was a problem fetching recent projects.') });
}
};

View File

@ -1,5 +1,5 @@
import $ from 'jquery';
import createFlash, { hideFlash } from './flash';
import { createAlert, hideFlash } from './flash';
import axios from './lib/utils/axios_utils';
import { parseBoolean } from './lib/utils/common_utils';
import { __ } from './locale';
@ -27,7 +27,7 @@ export default () => {
})
.catch(() => {
hideConsentMessage();
createFlash({
createAlert({
message: __('Something went wrong. Try again later.'),
});
});

View File

@ -1,7 +1,7 @@
<script>
import { GlToast, GlTooltipDirective, GlSafeHtmlDirective, GlModal } from '@gitlab/ui';
import Vue from 'vue';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
import { s__ } from '~/locale';
import { updateUserStatus } from '~/rest_api';
@ -89,7 +89,7 @@ export default {
window.location.reload();
},
onUpdateFail() {
createFlash({
createAlert({
message: s__(
"SetStatusModal|Sorry, we weren't able to set your status. Please try again later.",
),

View File

@ -1,7 +1,6 @@
@import './pages/branches';
@import './pages/colors';
@import './pages/commits';
@import './pages/deploy_keys';
@import './pages/detail_page';
@import './pages/events';
@import './pages/groups';

View File

@ -1,4 +0,0 @@
.deploy-keys-title {
padding-bottom: 2px;
line-height: 2;
}

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
module Resolvers
module Ci
class AllJobsResolver < BaseResolver
type ::Types::Ci::JobType.connection_type, null: true
argument :statuses, [::Types::Ci::JobStatusEnum],
required: false,
description: 'Filter jobs by status.'
def resolve(statuses: nil)
::Ci::JobsFinder.new(current_user: current_user, params: { scope: statuses }).execute
end
end
end
end

View File

@ -4,7 +4,7 @@ module Types
module BranchProtections
class MergeAccessLevelType < BaseAccessLevelType # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'MergeAccessLevel'
description 'Defines which user roles, users, or groups can merge into a protected branch.'
description 'Represents the merge access level of a branch protection.'
accepts ::ProtectedBranch::MergeAccessLevel
end
end

View File

@ -4,7 +4,7 @@ module Types
module BranchProtections
class PushAccessLevelType < BaseAccessLevelType # rubocop:disable Graphql/AuthorizeTypes
graphql_name 'PushAccessLevel'
description 'Defines which user roles, users, or groups can push to a protected branch.'
description 'Represents the push access level of a branch protection.'
accepts ::ProtectedBranch::PushAccessLevel
end
end

View File

@ -154,6 +154,12 @@ module Types
null: true,
description: "Whether Gitpod is enabled in application settings."
field :jobs,
::Types::Ci::JobType.connection_type,
null: true,
description: 'All jobs on this GitLab instance.',
resolver: ::Resolvers::Ci::AllJobsResolver
def design_management
DesignManagementObject.new(nil)
end

View File

@ -49,7 +49,7 @@ module NavHelper
end
def page_has_markdown?
current_path?('merge_requests#show') ||
current_path?('projects/merge_requests#show') ||
current_path?('projects/merge_requests/conflicts#show') ||
current_path?('issues#show') ||
current_path?('milestones#show') ||

View File

@ -225,6 +225,10 @@ class NotifyPreview < ActionMailer::Preview
Notify.request_review_merge_request_email(user.id, merge_request.id, user.id).message
end
def project_was_moved_email
Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab").message
end
private
def project

View File

@ -4,7 +4,7 @@
"properties": {
"top_n": { "type": "number" },
"version": { "type": "string" },
"changes": { "type": "array" }
"reviewers": { "type": "array" }
},
"additionalProperties": true
}

View File

@ -1,24 +1,18 @@
- @can_bulk_update = can?(current_user, :admin_merge_request, @group) && @group.licensed_feature_available?(:group_bulk_edit)
- @can_bulk_update = can?(current_user, :admin_merge_request, @group) && @group.licensed_feature_available?(:group_bulk_edit) && issuables_count_for_state(:merge_requests, :all) > 0
- page_title _("Merge requests")
- if issuables_count_for_state(:merge_requests, :all) == 0
= render 'shared/issuable/search_bar', type: :merge_requests
.top-area
= render 'shared/issuable/nav', type: :merge_requests
- if current_user
.nav-controls
- if @can_bulk_update
= render_if_exists 'projects/merge_requests/bulk_update_button'
= render 'shared/empty_states/merge_requests', project_select_button: true
- else
.top-area
= render 'shared/issuable/nav', type: :merge_requests
- if current_user
.nav-controls
- if @can_bulk_update
= render_if_exists 'projects/merge_requests/bulk_update_button'
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: _("merge request"), type: :merge_requests, with_feature_enabled: 'merge_requests', with_shared: false, include_projects_in_subgroups: true
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: _("merge request"), type: :merge_requests, with_feature_enabled: 'merge_requests', with_shared: false, include_projects_in_subgroups: true
= render 'shared/issuable/search_bar', type: :merge_requests
- if @can_bulk_update
= render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :merge_requests
= render 'shared/issuable/search_bar', type: :merge_requests
- if @can_bulk_update
= render_if_exists 'shared/issuable/group_bulk_update_sidebar', group: @group, type: :merge_requests
= render 'shared/merge_requests'
= render 'shared/merge_requests'

View File

@ -1,15 +1,9 @@
- repo_url_styles = "background: #f5f5f5; padding:10px; border:1px solid #ddd"
- ssh_url_to_repo = content_tag(:p, "git remote set-url origin #{strip_tags(@project.ssh_url_to_repo)}", style: repo_url_styles)
- http_url_to_repo = content_tag(:p, "git remote set-url origin #{strip_tags(@project.http_url_to_repo)}", style: repo_url_styles)
%p
Project #{@old_path_with_namespace} was moved to another location
= s_('Notify|Project %{old_path_with_namespace} was moved to another location.') % { old_path_with_namespace: @old_path_with_namespace }
%p
The project is now located under
= link_to project_url(@project) do
= @project.full_name
%p
To update the remote url in your local repository run (for ssh):
%p{ style: "background: #f5f5f5; padding:10px; border:1px solid #ddd" }
git remote set-url origin #{@project.ssh_url_to_repo}
%p
or for http(s):
%p{ style: "background: #f5f5f5; padding:10px; border:1px solid #ddd" }
git remote set-url origin #{@project.http_url_to_repo}
%br
- project_full_name_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: project_url(@project) }
= html_escape(s_('Notify|The project is now located under %{project_full_name_link_start}%{project_full_name}%{link_end}.')) % { project_full_name_link_start: project_full_name_link_start, link_end: '</a>'.html_safe, project_full_name: @project.full_name }
= html_escape(s_('Notify|%{p_start}To update the remote url in your local repository run (for ssh):%{p_end} %{ssh_url_to_repo} %{p_start}or for http(s):%{p_end} %{http_url_to_repo}')) % { p_start: '<p>'.html_safe, p_end: '</p>'.html_safe, ssh_url_to_repo: ssh_url_to_repo, http_url_to_repo: http_url_to_repo }

View File

@ -24,7 +24,7 @@
%span.gl-ml-3.gl-mb-3
= render 'shared/members/access_request_links', source: @project
= cache_if(cache_enabled, [@project, :buttons, current_user, @notification_setting], expires_in: 1.day) do
= cache_if(cache_enabled, [@project, @project.star_count, @project.forks_count, :buttons, current_user, @notification_setting], expires_in: 1.day) do
.project-repo-buttons.gl-display-flex.gl-justify-content-md-end.gl-align-items-start.gl-flex-wrap.gl-mt-5
- if current_user
- if current_user.admin?

View File

@ -14,5 +14,5 @@
= link_to new_project_fork_path(@project), class: "gl-button btn btn-default btn-sm fork-btn #{button_class}" do
= sprite_icon('fork', css_class: 'icon')
%span= s_('ProjectOverview|Fork')
= link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Forks'), s_('ProjectOverview|Forks'), @project.forks_count), class: "gl-button btn btn-default btn-sm count has-tooltip #{count_class}" do
= link_to project_forks_path(@project), title: n_(s_('ProjectOverview|Forks'), s_('ProjectOverview|Forks'), @project.forks_count), class: "gl-button btn btn-default btn-sm count has-tooltip fork-count #{count_class}" do
= @project.forks_count

View File

@ -1,14 +1,16 @@
- if @project.pages_deployed?
- if can?(current_user, :remove_pages, @project)
.card.border-danger
.card-header.bg-danger.text-white
= render Pajamas::CardComponent.new(card_options: { class: 'border-danger' }, header_options: {class: 'bg-danger text-white'}) do |c|
- c.with_header do
= s_('GitLabPages|Remove pages')
.errors-holder
.card-body
%p.gl-mb-0
= s_('GitLabPages|Removing pages will prevent them from being exposed to the outside world.')
.card-footer
= link_to s_('GitLabPages|Remove pages'), project_pages_path(@project), data: { confirm: s_('GitLabPages|Are you sure?'), 'confirm-btn-variant': 'danger'}, method: :delete, class: "btn gl-button btn-danger", "aria-label": s_('GitLabPages|Remove pages')
- c.with_body do
= s_('GitLabPages|Removing pages will prevent them from being exposed to the outside world.')
- c.with_footer do
= render Pajamas::ButtonComponent.new(href: project_pages_path(@project),
variant: :danger,
method: :delete,
button_options: {data: { confirm: s_('GitLabPages|Are you sure?'), 'confirm-btn-variant': 'danger'}, "aria-label": s_('GitLabPages|Remove pages')}) do
= s_('GitLabPages|Remove pages')
- else
.nothing-here-block
= s_('GitLabPages|Only project maintainers can remove pages')

View File

@ -4,7 +4,7 @@
%section.rspec-merge-request-settings.settings.merge-requests-feature.no-animate#js-merge-request-settings.expanded{ class: [('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)], data: { qa_selector: 'merge_request_settings_content' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Merge requests')
%h4= _('Merge requests')
= render_if_exists 'projects/merge_request_settings_description_text'
.settings-content

View File

@ -26,7 +26,7 @@
- if project.creator && use_creator_avatar
= render Pajamas::AvatarComponent.new(project.creator, size: 48, alt: '', class: 'gl-mr-5')
- else
= project_icon(project, alt: '', class: 'avatar project-avatar s48', width: 48, height: 48)
= render Pajamas::AvatarComponent.new(project, size: 48, alt: '', class: 'gl-mr-5')
.project-details.d-sm-flex.flex-sm-fill.align-items-center{ data: { qa_selector: 'project_content', qa_project_name: project.name } }
.flex-wrapper
.d-flex.align-items-center.flex-wrap.project-title

View File

@ -226,6 +226,22 @@ Returns [`Iteration`](#iteration).
| ---- | ---- | ----------- |
| <a id="queryiterationid"></a>`id` | [`IterationID!`](#iterationid) | Find an iteration by its ID. |
### `Query.jobs`
All jobs on this GitLab instance.
Returns [`CiJobConnection`](#cijobconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="queryjobsstatuses"></a>`statuses` | [`[CiJobStatus!]`](#cijobstatus) | Filter jobs by status. |
### `Query.licenseHistoryEntries`
Fields related to entries in the license history.
@ -9322,29 +9338,6 @@ The edge type for [`TreeEntry`](#treeentry).
| <a id="treeentryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="treeentryedgenode"></a>`node` | [`TreeEntry`](#treeentry) | The item at the end of the edge. |
#### `UnprotectAccessLevelConnection`
The connection type for [`UnprotectAccessLevel`](#unprotectaccesslevel).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="unprotectaccesslevelconnectionedges"></a>`edges` | [`[UnprotectAccessLevelEdge]`](#unprotectaccessleveledge) | A list of edges. |
| <a id="unprotectaccesslevelconnectionnodes"></a>`nodes` | [`[UnprotectAccessLevel]`](#unprotectaccesslevel) | A list of nodes. |
| <a id="unprotectaccesslevelconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
#### `UnprotectAccessLevelEdge`
The edge type for [`UnprotectAccessLevel`](#unprotectaccesslevel).
##### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="unprotectaccessleveledgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="unprotectaccessleveledgenode"></a>`node` | [`UnprotectAccessLevel`](#unprotectaccesslevel) | The item at the end of the edge. |
#### `UploadRegistryConnection`
The connection type for [`UploadRegistry`](#uploadregistry).
@ -10227,7 +10220,6 @@ Branch protection details for a branch rule.
| <a id="branchprotectioncodeownerapprovalrequired"></a>`codeOwnerApprovalRequired` | [`Boolean!`](#boolean) | Enforce code owner approvals before allowing a merge. |
| <a id="branchprotectionmergeaccesslevels"></a>`mergeAccessLevels` | [`MergeAccessLevelConnection`](#mergeaccesslevelconnection) | Details about who can merge when this branch is the source branch. (see [Connections](#connections)) |
| <a id="branchprotectionpushaccesslevels"></a>`pushAccessLevels` | [`PushAccessLevelConnection`](#pushaccesslevelconnection) | Details about who can push when this branch is the source branch. (see [Connections](#connections)) |
| <a id="branchprotectionunprotectaccesslevels"></a>`unprotectAccessLevels` | [`UnprotectAccessLevelConnection`](#unprotectaccesslevelconnection) | Details about who can unprotect this branch. (see [Connections](#connections)) |
### `BranchRule`
@ -13961,7 +13953,7 @@ Maven metadata.
### `MergeAccessLevel`
Defines which user roles, users, or groups can merge into a protected branch.
Represents the merge access level of a branch protection.
#### Fields
@ -17369,7 +17361,7 @@ Which group, user or role is allowed to execute deployments to the environment.
### `PushAccessLevel`
Defines which user roles, users, or groups can push to a protected branch.
Represents the push access level of a branch protection.
#### Fields
@ -18598,19 +18590,6 @@ Represents a directory.
| <a id="treeentrywebpath"></a>`webPath` | [`String`](#string) | Web path for the tree entry (directory). |
| <a id="treeentryweburl"></a>`webUrl` | [`String`](#string) | Web URL for the tree entry (directory). |
### `UnprotectAccessLevel`
Defines which user roles, users, or groups can unprotect a protected branch.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="unprotectaccesslevelaccesslevel"></a>`accessLevel` | [`Int!`](#int) | GitLab::Access level. |
| <a id="unprotectaccesslevelaccessleveldescription"></a>`accessLevelDescription` | [`String!`](#string) | Human readable representation for this access level. |
| <a id="unprotectaccesslevelgroup"></a>`group` | [`Group`](#group) | Group associated with this access level. |
| <a id="unprotectaccessleveluser"></a>`user` | [`UserCore`](#usercore) | User associated with this access level. |
### `UploadRegistry`
Represents the Geo replication and verification state of an upload.

View File

@ -22,14 +22,9 @@ To enable the Bitbucket OmniAuth provider you must register your application
with Bitbucket.org. Bitbucket generates an application ID and secret key for
you to use.
WARNING:
To help prevent an [OAuth 2 covert redirect](https://oauth.net/advisories/2014-1-covert-redirect/)
vulnerability in which users' GitLab accounts could be compromised, append `/users/auth`
to the end of the Bitbucket authorization callback URL.
1. Sign in to [Bitbucket.org](https://bitbucket.org).
1. Navigate to your individual user settings (**Bitbucket settings**) or a team's
settings (**Manage team**), depending on how you want the application registered.
1. Go to your individual user settings (**Bitbucket settings**) or a team's
settings (**Manage team**), depending on how you want to register the application.
It does not matter if the application is registered as an individual or a
team, that is entirely up to you.
1. In the left menu under **Access Management**, select **OAuth**.
@ -44,6 +39,12 @@ to the end of the Bitbucket authorization callback URL.
`https://gitlab.example.com/users/auth`.
Leaving this field empty
[results in an `Invalid redirect_uri` message](https://confluence.atlassian.com/bitbucket/oauth-faq-338365710.html).
WARNING:
To help prevent an [OAuth 2 covert redirect](https://oauth.net/advisories/2014-1-covert-redirect/)
vulnerability in which users' GitLab accounts could be compromised, append `/users/auth`
to the end of the Bitbucket authorization callback URL.
- **URL:** The URL to your GitLab installation, such as `https://gitlab.example.com`.
1. Grant at least the following permissions:
@ -85,8 +86,8 @@ to the end of the Bitbucket authorization callback URL.
{
name: "bitbucket",
# label: "Provider name", # optional label for login button, defaults to "Bitbucket"
app_id: "BITBUCKET_APP_KEY",
app_secret: "BITBUCKET_APP_SECRET",
app_id: "<bitbucket_app_key>",
app_secret: "<bitbucket_app_secret>",
url: "https://bitbucket.org/"
}
]
@ -100,12 +101,12 @@ to the end of the Bitbucket authorization callback URL.
providers:
- { name: 'bitbucket',
# label: 'Provider name', # optional label for login button, defaults to "Bitbucket"
app_id: 'BITBUCKET_APP_KEY',
app_secret: 'BITBUCKET_APP_SECRET',
app_id: '<bitbucket_app_key>',
app_secret: '<bitbucket_app_secret>',
url: 'https://bitbucket.org/' }
```
Where `BITBUCKET_APP_KEY` is the Key and `BITBUCKET_APP_SECRET` the Secret
Where `<bitbucket_app_key>` is the **Key** and `<bitbucket_app_secret>` the **Secret**
from the Bitbucket application page.
1. Save the configuration file.

View File

@ -6,13 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Epics **(PREMIUM)**
> Single-level epics were [moved](https://gitlab.com/gitlab-org/gitlab/-/issues/37081) from GitLab Ultimate to GitLab Premium in 12.8.
When [issues](../../project/issues/index.md) share a theme across projects and
milestones, you can manage them by using epics.
When [issues](../../project/issues/index.md) share a theme across projects and milestones,
you can manage them by using epics.
You can also create child epics, and assign start and end dates,
which creates a visual roadmap for you to view progress.
You can also create child epics and assign start and end dates, which creates
a visual roadmap for you to view progress.
Use epics:

View File

@ -46,6 +46,7 @@ module Gitlab
store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Groups::GroupTransferedEvent
store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Groups::GroupPathChangedEvent
store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::Groups::GroupDeletedEvent
store.subscribe ::Pages::InvalidateDomainCacheWorker, to: ::PagesDomains::PagesDomainDeletedEvent
store.subscribe ::MergeRequests::CreateApprovalEventWorker, to: ::MergeRequests::ApprovedEvent
store.subscribe ::MergeRequests::CreateApprovalNoteWorker, to: ::MergeRequests::ApprovedEvent

View File

@ -27248,6 +27248,9 @@ msgstr ""
msgid "Notify|%{name} requested a new review on %{mr_link}."
msgstr ""
msgid "Notify|%{p_start}To update the remote url in your local repository run (for ssh):%{p_end} %{ssh_url_to_repo} %{p_start}or for http(s):%{p_end} %{http_url_to_repo}"
msgstr ""
msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
msgstr ""
@ -27383,6 +27386,9 @@ msgstr ""
msgid "Notify|Pipeline has been fixed and #%{pipeline_id} has passed!"
msgstr ""
msgid "Notify|Project %{old_path_with_namespace} was moved to another location."
msgstr ""
msgid "Notify|Project %{project_name} was exported successfully."
msgstr ""
@ -27404,6 +27410,9 @@ msgstr ""
msgid "Notify|The download link will expire in 24 hours."
msgstr ""
msgid "Notify|The project is now located under %{project_full_name_link_start}%{project_full_name}%{link_end}."
msgstr ""
msgid "Notify|The push did not contain any new commits, but force pushed to delete the commits and changes below."
msgstr ""

View File

@ -48,8 +48,9 @@ module QA
usage_quota.purchase_more_storage
end
# Purchase checkout opens a new tab
Chemlab.configuration.browser.session.engine.switch_window
# Purchase checkout opens a new tab but buying additional storage does not
session = Chemlab.configuration.browser.session.engine
session.switch_window if session.windows.size == 2
Gitlab::Page::Subscriptions::New.perform do |storage|
storage.quantity = quantity

View File

@ -39,43 +39,23 @@ FactoryBot.define do
end
end
trait :no_one_can_merge do
transient do
default_merge_level { false }
end
after(:build) do |protected_branch|
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
end
end
trait :developers_can_merge do
transient do
default_merge_level { false }
end
after(:build) do |protected_branch|
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
end
end
trait :maintainers_can_merge do
transient do
default_merge_level { false }
end
after(:build) do |protected_branch|
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
trait :no_one_can_push do
trait :maintainers_can_push do
transient do
default_push_level { false }
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
trait :maintainers_can_merge do
transient do
default_push_level { false }
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
@ -89,13 +69,33 @@ FactoryBot.define do
end
end
trait :maintainers_can_push do
trait :developers_can_merge do
transient do
default_merge_level { false }
end
after(:build) do |protected_branch|
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
end
end
trait :no_one_can_push do
transient do
default_push_level { false }
end
after(:build) do |protected_branch|
protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
protected_branch.push_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
end
end
trait :no_one_can_merge do
transient do
default_merge_level { false }
end
after(:build) do |protected_branch|
protected_branch.merge_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
end
end
end

View File

@ -94,9 +94,7 @@ RSpec.describe 'Group empty states' do
end
it "shows a new #{issuable_name} button" do
within '.empty-state' do
expect(page).to have_content("create #{issuable_name}")
end
expect(page).to have_content("create #{issuable_name}")
end
it "the new #{issuable_name} button opens a project dropdown" do

View File

@ -57,6 +57,16 @@ RSpec.describe 'Group merge requests page' do
expect(find('#js-dropdown-assignee .filter-dropdown')).to have_content(user.name)
expect(find('#js-dropdown-assignee .filter-dropdown')).not_to have_content(user2.name)
end
it 'will still show the navbar with no results' do
search_term = 'some-search-term-that-produces-zero-results'
filtered_search.set(search_term)
filtered_search.send_keys(:enter)
expect(page).to have_content('filter produced no results')
expect(page).to have_link('Open', href: "/groups/#{group.name}/-/merge_requests?scope=all&search=#{search_term}&state=opened")
end
end
describe 'new merge request dropdown' do

View File

@ -13,6 +13,10 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
let(:merge_request_in_only_mwps_project) { create(:merge_request, source_project: project_only_mwps) }
def click_expand_button
find('[data-testid="report-section-expand-button"]').click
end
before do
project.add_maintainer(user)
project_only_mwps.add_maintainer(user)
@ -606,7 +610,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
it 'shows test reports summary which includes the new failure' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
expect(page).to have_content('Test summary contained 1 failed out of 2 total tests')
within(".js-report-section-container") do
@ -621,7 +625,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the new failure' do
it 'shows the test report detail' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
within(".js-report-section-container") do
click_button 'addTest'
@ -654,7 +658,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
it 'shows test reports summary which includes the existing failure' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
expect(page).to have_content('Test summary contained 1 failed out of 2 total tests')
within(".js-report-section-container") do
@ -668,7 +672,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the existing failure' do
it 'shows test report detail of it' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
within(".js-report-section-container") do
click_button 'Test#sum when a is 1 and b is 3 returns summary'
@ -701,7 +705,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
it 'shows test reports summary which includes the resolved failure' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
expect(page).to have_content('Test summary contained 1 fixed test result out of 2 total tests')
within(".js-report-section-container") do
@ -715,7 +719,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the resolved failure' do
it 'shows test report detail of it' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
within(".js-report-section-container") do
click_button 'addTest'
@ -747,7 +751,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
it 'shows test reports summary which includes the new error' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
expect(page).to have_content('Test summary contained 1 error out of 2 total tests')
within(".js-report-section-container") do
@ -762,7 +766,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the new error' do
it 'shows the test report detail' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
within(".js-report-section-container") do
click_button 'addTest'
@ -794,7 +798,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
it 'shows test reports summary which includes the existing error' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
expect(page).to have_content('Test summary contained 1 error out of 2 total tests')
within(".js-report-section-container") do
@ -808,7 +812,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the existing error' do
it 'shows test report detail of it' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
within(".js-report-section-container") do
click_button 'Test#sum when a is 4 and b is 4 returns summary'
@ -840,7 +844,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
it 'shows test reports summary which includes the resolved error' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
expect(page).to have_content('Test summary contained 1 fixed test result out of 2 total tests')
within(".js-report-section-container") do
@ -854,7 +858,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the resolved error' do
it 'shows test report detail of it' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
within(".js-report-section-container") do
click_button 'addTest'
@ -894,7 +898,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
it 'shows test reports summary which includes the resolved failure' do
within(".js-reports-container") do
click_button 'Expand'
click_expand_button
expect(page).to have_content('Test summary contained 20 failed out of 20 total tests')
within(".js-report-section-container") do

View File

@ -118,17 +118,28 @@ RSpec.describe 'Project fork' do
end
end
shared_examples "increments the fork counter on the source project's page" do
specify :sidekiq_might_not_need_inline do
create_forks
visit project_path(project)
expect(page).to have_css('.fork-count', text: 2)
end
end
it_behaves_like 'fork button on project page'
it_behaves_like 'create fork page', 'Fork project'
context 'fork form', :js do
let(:group) { create(:group) }
let(:group2) { create(:group) }
let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
def submit_form
def submit_form(group_obj = group)
find('[data-testid="select_namespace_dropdown"]').click
find('[data-testid="select_namespace_dropdown_search_field"]').fill_in(with: group.name)
click_button group.name
find('[data-testid="select_namespace_dropdown_search_field"]').fill_in(with: group_obj.name)
click_button group_obj.name
click_button 'Fork project'
end
@ -166,5 +177,41 @@ RSpec.describe 'Project fork' do
expect(page).to have_content("#{group.name} / #{fork_name}")
end
end
context 'with cache_home_panel feature flag' do
before do
create(:group_member, :maintainer, user: user, group: group2 )
end
context 'when caching is enabled' do
before do
stub_feature_flags(cache_home_panel: project)
end
it_behaves_like "increments the fork counter on the source project's page"
end
context 'when caching is disabled' do
before do
stub_feature_flags(cache_home_panel: false)
end
it_behaves_like "increments the fork counter on the source project's page"
end
end
end
end
private
def create_fork(group_obj = group)
visit project_path(project)
find('.fork-btn').click
submit_form(group_obj)
wait_for_requests
end
def create_forks
create_fork
create_fork(group2)
end

View File

@ -13,6 +13,14 @@ RSpec.describe 'Projects > Show > User interacts with project stars' do
visit(project_path(project))
end
it 'retains the star count even after a page reload' do
star_project
reload_page
expect(page).to have_css('.star-count', text: 1)
end
it 'toggles the star' do
star_project
@ -63,6 +71,10 @@ end
private
def reload_page
visit current_path
end
def star_project
click_button(_('Star'))
wait_for_requests

View File

@ -7,9 +7,13 @@ import ReportSection from '~/reports/components/report_section.vue';
describe('ReportSection component', () => {
let wrapper;
const findButton = () => wrapper.findComponent(GlButton);
const findExpandButton = () => wrapper.findComponent(GlButton);
const findPopover = () => wrapper.findComponent(HelpPopover);
const findReportSection = () => wrapper.find('.js-report-section-container');
const expectExpandButtonOpen = () =>
expect(findExpandButton().props('icon')).toBe('chevron-lg-up');
const expectExpandButtonClosed = () =>
expect(findExpandButton().props('icon')).toBe('chevron-lg-down');
const resolvedIssues = [
{
@ -122,22 +126,22 @@ describe('ReportSection component', () => {
it('toggles issues', async () => {
createComponent({ props: { hasIssues: true } });
await findButton().trigger('click');
await findExpandButton().trigger('click');
expect(findReportSection().isVisible()).toBe(true);
expect(findButton().text()).toBe('Collapse');
expectExpandButtonOpen();
await findButton().trigger('click');
await findExpandButton().trigger('click');
expect(findReportSection().isVisible()).toBe(false);
expect(findButton().text()).toBe('Expand');
expectExpandButtonClosed();
});
it('is always expanded, if always-open is set to true', () => {
createComponent({ props: { hasIssues: true, alwaysOpen: true } });
expect(findReportSection().isVisible()).toBe(true);
expect(findButton().exists()).toBe(false);
expect(findExpandButton().exists()).toBe(false);
});
});
});
@ -148,7 +152,7 @@ describe('ReportSection component', () => {
expect(wrapper.emitted('toggleEvent')).toBeUndefined();
findButton().trigger('click');
findExpandButton().trigger('click');
expect(wrapper.emitted('toggleEvent')).toEqual([[]]);
});
@ -158,7 +162,7 @@ describe('ReportSection component', () => {
expect(wrapper.emitted('toggleEvent')).toBeUndefined();
findButton().trigger('click');
findExpandButton().trigger('click');
expect(wrapper.emitted('toggleEvent')).toBeUndefined();
});
@ -208,7 +212,7 @@ describe('ReportSection component', () => {
});
it('should still render the expand/collapse button', () => {
expect(findButton().text()).toBe('Expand');
expectExpandButtonClosed();
});
});

View File

@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { loadCommits, isRequested, resetRequestedCommits } from '~/repository/commits_service';
import httpStatus from '~/lib/utils/http_status';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { I18N_COMMIT_DATA_FETCH_ERROR } from '~/repository/constants';
jest.mock('~/flash');
@ -65,13 +65,13 @@ describe('commits service', () => {
expect(isRequested(300)).toBe(false);
});
it('calls `createFlash` when the request fails', async () => {
it('calls `createAlert` when the request fails', async () => {
const invalidPath = '/#@ some/path';
const invalidUrl = `${url}${invalidPath}`;
mock.onGet(invalidUrl).replyOnce(httpStatus.INTERNAL_SERVER_ERROR, [], {});
await requestCommits(1, 'my-project', invalidPath);
expect(createFlash).toHaveBeenCalledWith({ message: I18N_COMMIT_DATA_FETCH_ERROR });
expect(createAlert).toHaveBeenCalledWith({ message: I18N_COMMIT_DATA_FETCH_ERROR });
});
});

View File

@ -4,7 +4,7 @@ import { nextTick } from 'vue';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status';
import { visitUrl } from '~/lib/utils/url_utility';
import NewDirectoryModal from '~/repository/components/new_directory_modal.vue';
@ -194,7 +194,7 @@ describe('NewDirectoryModal', () => {
await fillForm({ dirName: 'foo', branchName: 'master', commitMessage: 'foo' });
await submitForm();
expect(createFlash).toHaveBeenCalledWith({
expect(createAlert).toHaveBeenCalledWith({
message: NewDirectoryModal.i18n.ERROR_MESSAGE,
});
});

View File

@ -4,7 +4,7 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import httpStatusCodes from '~/lib/utils/http_status';
import { visitUrl } from '~/lib/utils/url_utility';
import UploadBlobModal from '~/repository/components/upload_blob_modal.vue';
@ -185,7 +185,7 @@ describe('UploadBlobModal', () => {
});
it('creates a flash error', () => {
expect(createFlash).toHaveBeenCalledWith({
expect(createAlert).toHaveBeenCalledWith({
message: 'Error uploading file. Please try again.',
});
});

View File

@ -1,7 +1,7 @@
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import Api from '~/api';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import * as urlUtils from '~/lib/utils/url_utility';
import * as actions from '~/search/store/actions';
@ -37,8 +37,8 @@ describe('Global Search Store Actions', () => {
let state;
const flashCallback = (callCount) => {
expect(createFlash).toHaveBeenCalledTimes(callCount);
createFlash.mockClear();
expect(createAlert).toHaveBeenCalledTimes(callCount);
createAlert.mockClear();
};
beforeEach(() => {

View File

@ -4,7 +4,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import { initEmojiMock, clearEmojiMock } from 'helpers/emoji';
import * as UserApi from '~/api/user_api';
import EmojiPicker from '~/emoji/components/picker.vue';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import stubChildren from 'helpers/stub_children';
import SetStatusModalWrapper from '~/set_status_modal/set_status_modal_wrapper.vue';
import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
@ -253,7 +253,7 @@ describe('SetStatusModalWrapper', () => {
findModal().vm.$emit('primary');
await nextTick();
expect(createFlash).toHaveBeenCalledWith({
expect(createAlert).toHaveBeenCalledWith({
message: "Sorry, we weren't able to set your status. Please try again later.",
});
});

View File

@ -0,0 +1,52 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::Ci::AllJobsResolver do
include GraphqlHelpers
let_it_be(:successful_job) { create(:ci_build, :success, name: 'Job One') }
let_it_be(:successful_job_two) { create(:ci_build, :success, name: 'Job Two') }
let_it_be(:failed_job) { create(:ci_build, :failed, name: 'Job Three') }
let_it_be(:pending_job) { create(:ci_build, :pending, name: 'Job Three') }
let(:args) { {} }
let(:current_user) { create(:admin) }
subject { resolve_jobs(args) }
describe '#resolve' do
context 'with authorized user' do
context 'with statuses argument' do
let(:args) { { statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS')] } }
it { is_expected.to contain_exactly(successful_job, successful_job_two) }
end
context 'with multiple statuses' do
let(:args) do
{ statuses: [Types::Ci::JobStatusEnum.coerce_isolated_input('SUCCESS'),
Types::Ci::JobStatusEnum.coerce_isolated_input('FAILED')] }
end
it { is_expected.to contain_exactly(successful_job, successful_job_two, failed_job) }
end
context 'without statuses argument' do
it { is_expected.to contain_exactly(successful_job, successful_job_two, failed_job, pending_job) }
end
end
context 'with unauthorized user' do
let(:current_user) { nil }
it { is_expected.to be_empty }
end
end
private
def resolve_jobs(args = {}, context = { current_user: current_user })
resolve(described_class, args: args, ctx: context)
end
end

View File

@ -116,7 +116,7 @@ RSpec.describe NavHelper do
using RSpec::Parameterized::TableSyntax
where path: %w(
merge_requests#show
projects/merge_requests#show
projects/merge_requests/conflicts#show
issues#show
milestones#show

View File

@ -118,9 +118,10 @@ RSpec.describe Gitlab::GithubImport::Importer::LfsObjectsImporter do
expect(service).to receive(:execute).and_return([lfs_download_object])
end
expect(Gitlab::GithubImport::ImportLfsObjectWorker).to receive(:bulk_perform_in).with(1.second, [
[project.id, an_instance_of(Hash), an_instance_of(String)]
], batch_size: 1000, batch_delay: 1.minute)
expect(Gitlab::GithubImport::ImportLfsObjectWorker).to receive(:bulk_perform_in)
.with(1.second, [
[project.id, an_instance_of(Hash), an_instance_of(String)]
], batch_size: 1000, batch_delay: 1.minute)
waiter = importer.parallel_import

View File

@ -83,9 +83,10 @@ RSpec.describe Gitlab::GithubImport::Importer::NotesImporter do
.to receive(:each_object_to_import)
.and_yield(github_comment)
expect(Gitlab::GithubImport::ImportNoteWorker).to receive(:bulk_perform_in).with(1.second, [
[project.id, an_instance_of(Hash), an_instance_of(String)]
], batch_size: 1000, batch_delay: 1.minute)
expect(Gitlab::GithubImport::ImportNoteWorker).to receive(:bulk_perform_in)
.with(1.second, [
[project.id, an_instance_of(Hash), an_instance_of(String)]
], batch_size: 1000, batch_delay: 1.minute)
waiter = importer.parallel_import

View File

@ -295,11 +295,12 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
end
it 'imports data in parallel batches with delays' do
expect(worker_class).to receive(:bulk_perform_in).with(1.second, [
[project.id, { title: 'Foo' }, an_instance_of(String)],
[project.id, { title: 'Foo' }, an_instance_of(String)],
[project.id, { title: 'Foo' }, an_instance_of(String)]
], batch_size: batch_size, batch_delay: batch_delay)
expect(worker_class).to receive(:bulk_perform_in)
.with(1.second, [
[project.id, { title: 'Foo' }, an_instance_of(String)],
[project.id, { title: 'Foo' }, an_instance_of(String)],
[project.id, { title: 'Foo' }, an_instance_of(String)]
], batch_size: batch_size, batch_delay: batch_delay)
importer.parallel_import
end

View File

@ -43,10 +43,11 @@ RSpec.describe Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp do
it 're-formats the params hash' do
params = result['params']
expect(params).to eq([
{ 'key' => 'description', 'value' => '[FILTERED]' },
{ 'key' => 'name', 'value' => 'gitlab test' },
{ 'key' => 'int', 'value' => 42 }
])
expect(params).to eq(
[
{ 'key' => 'description', 'value' => '[FILTERED]' },
{ 'key' => 'name', 'value' => 'gitlab test' },
{ 'key' => 'int', 'value' => 42 }
])
end
end

View File

@ -13,12 +13,12 @@ RSpec.describe Gitlab::HookData::ReleaseBuilder do
it 'includes safe attribute' do
%w[
id
created_at
description
name
released_at
tag
id
created_at
description
name
released_at
tag
].each do |key|
expect(data).to include(key)
end

View File

@ -116,15 +116,15 @@ RSpec.describe Gitlab::ImportExport::Group::TreeRestorer do
shared_examples 'excluded attributes' do
excluded_attributes = %w[
id
parent_id
owner_id
created_at
updated_at
runners_token
runners_token_encrypted
saml_discovery_token
]
id
parent_id
owner_id
created_at
updated_at
runners_token
runners_token_encrypted
saml_discovery_token
]
before do
group.add_owner(importer_user)

View File

@ -51,11 +51,12 @@ RSpec.describe Gitlab::ImportExport::Group::TreeSaver do
.map { |line| Integer(line) }
expect(groups_catalog.size).to eq(3)
expect(groups_catalog).to eq([
group.id,
group.descendants.first.id,
group.descendants.first.descendants.first.id
])
expect(groups_catalog).to eq(
[
group.id,
group.descendants.first.id,
group.descendants.first.descendants.first.id
])
end
it 'has a file per group' do

View File

@ -140,13 +140,13 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
it 'restores pipelines based on ascending id order' do
expected_ordered_shas = %w[
2ea1f3dec713d940208fb5ce4a38765ecb5d3f73
ce84140e8b878ce6e7c4d298c7202ff38170e3ac
048721d90c449b244b7b4c53a9186b04330174ec
sha-notes
5f923865dde3436854e9ceb9cdb7815618d4e849
d2d430676773caa88cdaf7c55944073b2fd5561a
2ea1f3dec713d940208fb5ce4a38765ecb5d3f73
2ea1f3dec713d940208fb5ce4a38765ecb5d3f73
ce84140e8b878ce6e7c4d298c7202ff38170e3ac
048721d90c449b244b7b4c53a9186b04330174ec
sha-notes
5f923865dde3436854e9ceb9cdb7815618d4e849
d2d430676773caa88cdaf7c55944073b2fd5561a
2ea1f3dec713d940208fb5ce4a38765ecb5d3f73
]
project = Project.find_by_path('project')

View File

@ -51,13 +51,14 @@ RSpec.describe Gitlab::Kubernetes::RolloutInstances do
end
it 'returns instances when there are two stable deployments' do
deployments, pods = setup([
kube_deployment(name: 'one', track: 'stable', replicas: 1),
kube_deployment(name: 'two', track: 'stable', replicas: 1)
], [
kube_pod(name: 'one', status: 'Running', track: 'stable'),
kube_pod(name: 'two', status: 'Running', track: 'stable')
])
deployments, pods = setup(
[
kube_deployment(name: 'one', track: 'stable', replicas: 1),
kube_deployment(name: 'two', track: 'stable', replicas: 1)
], [
kube_pod(name: 'one', status: 'Running', track: 'stable'),
kube_pod(name: 'two', status: 'Running', track: 'stable')
])
rollout_instances = described_class.new(deployments, pods)
expect(rollout_instances.pod_instances).to eq([{
@ -76,13 +77,14 @@ RSpec.describe Gitlab::Kubernetes::RolloutInstances do
end
it 'returns instances for two deployments with different tracks' do
deployments, pods = setup([
kube_deployment(name: 'one', track: 'mytrack', replicas: 1),
kube_deployment(name: 'two', track: 'othertrack', replicas: 1)
], [
kube_pod(name: 'one', status: 'Running', track: 'mytrack'),
kube_pod(name: 'two', status: 'Running', track: 'othertrack')
])
deployments, pods = setup(
[
kube_deployment(name: 'one', track: 'mytrack', replicas: 1),
kube_deployment(name: 'two', track: 'othertrack', replicas: 1)
], [
kube_pod(name: 'one', status: 'Running', track: 'mytrack'),
kube_pod(name: 'two', status: 'Running', track: 'othertrack')
])
rollout_instances = described_class.new(deployments, pods)
expect(rollout_instances.pod_instances).to eq([{
@ -101,13 +103,14 @@ RSpec.describe Gitlab::Kubernetes::RolloutInstances do
end
it 'sorts stable tracks after canary tracks' do
deployments, pods = setup([
kube_deployment(name: 'one', track: 'stable', replicas: 1),
kube_deployment(name: 'two', track: 'canary', replicas: 1)
], [
kube_pod(name: 'one', status: 'Running', track: 'stable'),
kube_pod(name: 'two', status: 'Running', track: 'canary')
])
deployments, pods = setup(
[
kube_deployment(name: 'one', track: 'stable', replicas: 1),
kube_deployment(name: 'two', track: 'canary', replicas: 1)
], [
kube_pod(name: 'one', status: 'Running', track: 'stable'),
kube_pod(name: 'two', status: 'Running', track: 'canary')
])
rollout_instances = described_class.new(deployments, pods)
expect(rollout_instances.pod_instances).to eq([{

View File

@ -132,11 +132,12 @@ RSpec.describe Gitlab::Middleware::HandleMalformedStrings do
end
it "rejects bad params for arrays containing hashes with string values" do
env = env_for(name: [
{
inner_key: "I am #{problematic_input} bad"
}
])
env = env_for(
name: [
{
inner_key: "I am #{problematic_input} bad"
}
])
expect(subject.call(env)).to eq error_400
end
@ -148,11 +149,12 @@ RSpec.describe Gitlab::Middleware::HandleMalformedStrings do
it_behaves_like 'checks params'
it "gives up and does not reject too deeply nested params" do
env = env_for(name: [
{
inner_key: { deeper_key: [{ hash_inside_array_key: "I am #{problematic_input} bad" }] }
}
])
env = env_for(
name: [
{
inner_key: { deeper_key: [{ hash_inside_array_key: "I am #{problematic_input} bad" }] }
}
])
expect(subject.call(env)).not_to eq error_400
end

View File

@ -23,10 +23,11 @@ RSpec.describe Gitlab::Pagination::Keyset::ColumnOrderDefinition do
let_it_be(:project_calculated_column_expression) do
# COALESCE("projects"."description", 'No Description')
Arel::Nodes::NamedFunction.new('COALESCE', [
Project.arel_table[:description],
Arel.sql("'No Description'")
])
Arel::Nodes::NamedFunction.new('COALESCE',
[
Project.arel_table[:description],
Arel.sql("'No Description'")
])
end
let_it_be(:project_calculated_column) do

View File

@ -117,23 +117,24 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder
let(:order) do
# NULLS LAST ordering requires custom Order object for keyset pagination:
# https://docs.gitlab.com/ee/development/database/keyset_pagination.html#complex-order-configuration
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :relative_position,
column_expression: Issue.arel_table[:relative_position],
order_expression: Issue.arel_table[:relative_position].desc.nulls_last,
reversed_order_expression: Issue.arel_table[:relative_position].asc.nulls_first,
order_direction: :desc,
nullable: :nulls_last,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Issue.arel_table[:id].desc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :relative_position,
column_expression: Issue.arel_table[:relative_position],
order_expression: Issue.arel_table[:relative_position].desc.nulls_last,
reversed_order_expression: Issue.arel_table[:relative_position].asc.nulls_first,
order_direction: :desc,
nullable: :nulls_last,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Issue.arel_table[:id].desc,
nullable: :not_nullable,
distinct: true
)
])
end
let(:in_operator_optimization_options) do
@ -279,17 +280,18 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::QueryBuilder
context 'when ordering by SQL expression' do
let(:order) do
# ORDER BY (id * 10), id
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id_multiplied_by_ten',
order_expression: Arel.sql('(id * 10)').asc,
sql_type: 'integer'
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Issue.arel_table[:id].asc
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id_multiplied_by_ten',
order_expression: Arel.sql('(id * 10)').asc,
sql_type: 'integer'
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Issue.arel_table[:id].asc
)
])
end
let(:scope) { Issue.reorder(order) }

View File

@ -25,22 +25,24 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::Strategies::O
describe '#initializer_columns' do
it 'returns NULLs for each ORDER BY columns' do
expect(strategy.initializer_columns).to eq([
'NULL::timestamp without time zone AS created_at',
'NULL::integer AS id'
])
expect(strategy.initializer_columns).to eq(
[
'NULL::timestamp without time zone AS created_at',
'NULL::integer AS id'
])
end
end
context 'when an SQL expression is given' do
context 'when the sql_type attribute is missing' do
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id_times_ten',
order_expression: Arel.sql('id * 10').asc
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id_times_ten',
order_expression: Arel.sql('id * 10').asc
)
])
end
let(:keyset_scope) { Project.order(order) }
@ -52,13 +54,14 @@ RSpec.describe Gitlab::Pagination::Keyset::InOperatorOptimization::Strategies::O
context 'when the sql_type_attribute is present' do
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id_times_ten',
order_expression: Arel.sql('id * 10').asc,
sql_type: 'integer'
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id_times_ten',
order_expression: Arel.sql('id * 10').asc,
sql_type: 'integer'
)
])
end
let(:keyset_scope) { Project.order(order) }

View File

@ -15,21 +15,22 @@ RSpec.describe Gitlab::Pagination::Keyset::Iterator do
let(:nulls_position) { :nulls_last }
let(:reverse_nulls_position) { ::Gitlab::Pagination::Keyset::ColumnOrderDefinition::REVERSED_NULL_POSITIONS[nulls_position] }
let(:custom_reorder) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: column,
column_expression: klass.arel_table[column],
order_expression: klass.arel_table[column].public_send(direction).public_send(nulls_position), # rubocop:disable GitlabSecurity/PublicSend
reversed_order_expression: klass.arel_table[column].public_send(reverse_direction).public_send(reverse_nulls_position), # rubocop:disable GitlabSecurity/PublicSend
order_direction: direction,
nullable: nulls_position,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: klass.arel_table[:id].send(direction)
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: column,
column_expression: klass.arel_table[column],
order_expression: klass.arel_table[column].public_send(direction).public_send(nulls_position), # rubocop:disable GitlabSecurity/PublicSend
reversed_order_expression: klass.arel_table[column].public_send(reverse_direction).public_send(reverse_nulls_position), # rubocop:disable GitlabSecurity/PublicSend
order_direction: direction,
nullable: nulls_position,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: klass.arel_table[:id].send(direction)
)
])
end
let(:iterator_params) { nil }

View File

@ -148,15 +148,16 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
end
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
end
let(:expected) do
@ -192,29 +193,30 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
end
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table['year'].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'month',
column_expression: table['month'],
order_expression: table['month'].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].asc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table['year'].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'month',
column_expression: table['month'],
order_expression: table['month'].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].asc,
nullable: :not_nullable,
distinct: true
)
])
end
let(:expected) do
@ -258,33 +260,34 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
end
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table[:year].asc.nulls_last,
reversed_order_expression: table[:year].desc.nulls_first,
order_direction: :asc,
nullable: :nulls_last,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'month',
column_expression: table['month'],
order_expression: table[:month].asc.nulls_last,
reversed_order_expression: table[:month].desc.nulls_first,
order_direction: :asc,
nullable: :nulls_last,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].asc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table[:year].asc.nulls_last,
reversed_order_expression: table[:year].desc.nulls_first,
order_direction: :asc,
nullable: :nulls_last,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'month',
column_expression: table['month'],
order_expression: table[:month].asc.nulls_last,
reversed_order_expression: table[:month].desc.nulls_first,
order_direction: :asc,
nullable: :nulls_last,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].asc,
nullable: :not_nullable,
distinct: true
)
])
end
let(:expected) do
@ -324,33 +327,34 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
end
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table[:year].asc.nulls_first,
reversed_order_expression: table[:year].desc.nulls_last,
order_direction: :asc,
nullable: :nulls_first,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'month',
column_expression: table['month'],
order_expression: table[:month].asc.nulls_first,
order_direction: :asc,
reversed_order_expression: table[:month].desc.nulls_last,
nullable: :nulls_first,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].asc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table[:year].asc.nulls_first,
reversed_order_expression: table[:year].desc.nulls_last,
order_direction: :asc,
nullable: :nulls_first,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'month',
column_expression: table['month'],
order_expression: table[:month].asc.nulls_first,
order_direction: :asc,
reversed_order_expression: table[:month].desc.nulls_last,
nullable: :nulls_first,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].asc,
nullable: :not_nullable,
distinct: true
)
])
end
let(:expected) do
@ -390,22 +394,23 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
end
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table['year'].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table['year'].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
end
let(:expected) do
@ -432,33 +437,38 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
reversed = order.reversed_order
before_conditions = reversed.where_values_with_or_query(before_cursor)
query = build_query(order: order, where_conditions: [Arel::Nodes::And.new([after_conditions, before_conditions])], limit: 100)
query = build_query(
order: order,
where_conditions: [Arel::Nodes::And.new([after_conditions, before_conditions])],
limit: 100)
expect(run_query(query)).to eq([
{ "id" => 2, "year" => 2011, "month" => 0 },
{ "id" => 6, "year" => 2012, "month" => 0 }
])
expect(run_query(query)).to eq(
[
{ "id" => 2, "year" => 2011, "month" => 0 },
{ "id" => 6, "year" => 2012, "month" => 0 }
])
end
end
context 'when ordering by the named function LOWER' do
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'title',
column_expression: Arel::Nodes::NamedFunction.new("LOWER", [table['title'].desc]),
order_expression: table['title'].lower.desc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'title',
column_expression: Arel::Nodes::NamedFunction.new("LOWER", [table['title'].desc]),
order_expression: table['title'].lower.desc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
end
let(:table_data) do
@ -484,22 +494,23 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
context 'when the passed cursor values do not match with the order definition' do
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table['year'].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'year',
column_expression: table['year'],
order_expression: table['year'].asc,
nullable: :not_nullable,
distinct: false
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
column_expression: table['id'],
order_expression: table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
end
context 'when values are missing' do
@ -553,14 +564,15 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
context 'when string attribute name is given' do
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: Project.arel_table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: Project.arel_table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
end
it_behaves_like 'cursor attribute examples'
@ -568,14 +580,15 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
context 'when symbol attribute name is given' do
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Project.arel_table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: :id,
order_expression: Project.arel_table['id'].desc,
nullable: :not_nullable,
distinct: true
)
])
end
it_behaves_like 'cursor attribute examples'
@ -593,20 +606,21 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
context 'when there are additional_projections' do
let(:order) do
order = Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'created_at_field',
column_expression: Project.arel_table[:created_at],
order_expression: Project.arel_table[:created_at].desc,
order_direction: :desc,
distinct: false,
add_to_projections: true
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: Project.arel_table[:id].desc
)
])
order = Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'created_at_field',
column_expression: Project.arel_table[:created_at],
order_expression: Project.arel_table[:created_at].desc,
order_direction: :desc,
distinct: false,
add_to_projections: true
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: Project.arel_table[:id].desc
)
])
order
end
@ -684,20 +698,21 @@ RSpec.describe Gitlab::Pagination::Keyset::Order do
describe '#attribute_names' do
let(:expected_attribute_names) { %w(id name) }
let(:order) do
Gitlab::Pagination::Keyset::Order.build([
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: Project.arel_table['id'].desc,
nullable: :not_nullable,
distinct: true
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'name',
order_expression: Project.arel_table['name'].desc,
nullable: :not_nullable,
distinct: true
)
])
Gitlab::Pagination::Keyset::Order.build(
[
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'id',
order_expression: Project.arel_table['id'].desc,
nullable: :not_nullable,
distinct: true
),
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'name',
order_expression: Project.arel_table['name'].desc,
nullable: :not_nullable,
distinct: true
)
])
end
subject { order.attribute_names }

View File

@ -15,10 +15,11 @@ RSpec.describe Gitlab::ProjectTransfer do
end
after do
FileUtils.rm_rf([
File.join(@root_dir, @namespace_path),
File.join(@root_dir, @namespace_path_was)
])
FileUtils.rm_rf(
[
File.join(@root_dir, @namespace_path),
File.join(@root_dir, @namespace_path_was)
])
end
describe '#move_project' do

View File

@ -300,12 +300,13 @@ RSpec.describe Gitlab::PrometheusClient do
it 'returns data from the API call' do
req_stub = stub_prometheus_request(query_url, body: prometheus_values_body('matrix'))
expect(subject.query_range(prometheus_query)).to eq([
{
"metric" => {},
"values" => [[1488758662.506, "0.00002996364761904785"], [1488758722.506, "0.00003090239047619091"]]
}
])
expect(subject.query_range(prometheus_query)).to eq(
[
{
"metric" => {},
"values" => [[1488758662.506, "0.00002996364761904785"], [1488758722.506, "0.00003090239047619091"]]
}
])
expect(req_stub).to have_been_requested
end
end

View File

@ -52,10 +52,11 @@ RSpec.describe Gitlab::PushOptions do
end
it 'can parse multiple push options' do
options = described_class.new([
'merge_request.create',
'merge_request.target=value'
])
options = described_class.new(
[
'merge_request.create',
'merge_request.target=value'
])
expect(options.get(:merge_request)).to include({
create: true,
@ -66,19 +67,21 @@ RSpec.describe Gitlab::PushOptions do
end
it 'stores options internally as a HashWithIndifferentAccess' do
options = described_class.new([
'merge_request.create'
])
options = described_class.new(
[
'merge_request.create'
])
expect(options.get('merge_request', 'create')).to eq(true)
expect(options.get(:merge_request, :create)).to eq(true)
end
it 'selects the last option when options contain duplicate namespace and key pairs' do
options = described_class.new([
'merge_request.target=value1',
'merge_request.target=value2'
])
options = described_class.new(
[
'merge_request.target=value1',
'merge_request.target=value2'
])
expect(options.get(:merge_request, :target)).to eq('value2')
end

View File

@ -217,10 +217,11 @@ RSpec.describe Gitlab::RackAttack::Request do
subject { request.protected_path? }
before do
stub_application_setting(protected_paths: [
'/protected',
'/secure'
])
stub_application_setting(
protected_paths: [
'/protected',
'/secure'
])
end
where(:path, :expected) do

View File

@ -21,16 +21,16 @@ RSpec.describe Gitlab::Search::AbuseDetection do
describe 'abusive character matching' do
refs = %w(
main
тест
maiñ
main123
main-v123
main-v12.3
feature/it_works
really_important!
)
main
тест
maiñ
main123
main-v123
main-v12.3
feature/it_works
really_important!
)
refs.each do |ref|
it "does match refs permitted by git refname: #{ref}" do

View File

@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'getting job information' do
include GraphqlHelpers
let_it_be(:job) { create(:ci_build, :success, name: 'job1') }
let(:query) do
graphql_query_for(:jobs)
end
context 'when user is admin' do
let_it_be(:current_user) { create(:admin) }
it 'has full access to all jobs', :aggregate_failure do
post_graphql(query, current_user: current_user)
expect(graphql_data_at(:jobs, :count)).to eq(1)
expect(graphql_data_at(:jobs, :nodes)).to contain_exactly(a_graphql_entity_for(job))
end
context 'when filtered by status' do
let_it_be(:pending_job) { create(:ci_build, :pending) }
let_it_be(:failed_job) { create(:ci_build, :failed) }
it 'gets pending jobs', :aggregate_failure do
post_graphql(graphql_query_for(:jobs, { statuses: :PENDING }), current_user: current_user)
expect(graphql_data_at(:jobs, :count)).to eq(1)
expect(graphql_data_at(:jobs, :nodes)).to contain_exactly(a_graphql_entity_for(pending_job))
end
it 'gets pending and failed jobs', :aggregate_failure do
post_graphql(graphql_query_for(:jobs, { statuses: [:PENDING, :FAILED] }), current_user: current_user)
expect(graphql_data_at(:jobs, :count)).to eq(2)
expect(graphql_data_at(:jobs, :nodes)).to match_array([a_graphql_entity_for(pending_job),
a_graphql_entity_for(failed_job)])
end
end
end
context 'if the user is not an admin' do
let_it_be(:current_user) { create(:user) }
it 'has no access to the jobs', :aggregate_failure do
post_graphql(query, current_user: current_user)
expect(graphql_data_at(:jobs, :count)).to eq(0)
expect(graphql_data_at(:jobs, :nodes)).to match_array([])
end
end
end

View File

@ -3,5 +3,107 @@
require 'spec_helper'
RSpec.describe 'getting merge access levels for a branch protection' do
include_examples 'perform graphql requests for AccessLevel type objects', :merge
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let(:merge_access_level_data) { merge_access_levels_data[0] }
let(:merge_access_levels_data) do
graphql_data_at('project',
'branchRules',
'nodes',
0,
'branchProtection',
'mergeAccessLevels',
'nodes')
end
let(:project) { protected_branch.project }
let(:merge_access_levels_count) { protected_branch.merge_access_levels.size }
let(:variables) { { path: project.full_path } }
let(:fields) { all_graphql_fields_for('MergeAccessLevel') }
let(:query) do
<<~GQL
query($path: ID!) {
project(fullPath: $path) {
branchRules(first: 1) {
nodes {
branchProtection {
mergeAccessLevels {
nodes {
#{fields}
}
}
}
}
}
}
}
GQL
end
context 'when the user does not have read_protected_branch abilities' do
let_it_be(:protected_branch) { create(:protected_branch) }
before do
project.add_guest(current_user)
post_graphql(query, current_user: current_user, variables: variables)
end
it_behaves_like 'a working graphql query'
it { expect(merge_access_levels_data).not_to be_present }
end
shared_examples 'merge access request' do
let(:merge_access) { protected_branch.merge_access_levels.first }
before do
project.add_maintainer(current_user)
post_graphql(query, current_user: current_user, variables: variables)
end
it_behaves_like 'a working graphql query'
it 'returns all merge access levels' do
expect(merge_access_levels_data.size).to eq(merge_access_levels_count)
end
it 'includes access_level' do
expect(merge_access_level_data['accessLevel'])
.to eq(merge_access.access_level)
end
it 'includes access_level_description' do
expect(merge_access_level_data['accessLevelDescription'])
.to eq(merge_access.humanize)
end
end
context 'when the user does have read_protected_branch abilities' do
let(:merge_access) { protected_branch.merge_access_levels.first }
context 'when no one has access' do
let_it_be(:protected_branch) { create(:protected_branch, :no_one_can_merge) }
it_behaves_like 'merge access request'
end
context 'when developers have access' do
let_it_be(:protected_branch) { create(:protected_branch, :developers_can_merge) }
it_behaves_like 'merge access request'
end
context 'when maintainers have access' do
let_it_be(:protected_branch) { create(:protected_branch, :maintainers_can_merge) }
it_behaves_like 'merge access request'
end
end
end

View File

@ -3,5 +3,107 @@
require 'spec_helper'
RSpec.describe 'getting push access levels for a branch protection' do
include_examples 'perform graphql requests for AccessLevel type objects', :push
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let(:push_access_level_data) { push_access_levels_data[0] }
let(:push_access_levels_data) do
graphql_data_at('project',
'branchRules',
'nodes',
0,
'branchProtection',
'pushAccessLevels',
'nodes')
end
let(:project) { protected_branch.project }
let(:push_access_levels_count) { protected_branch.push_access_levels.size }
let(:variables) { { path: project.full_path } }
let(:fields) { all_graphql_fields_for('PushAccessLevel'.classify) }
let(:query) do
<<~GQL
query($path: ID!) {
project(fullPath: $path) {
branchRules(first: 1) {
nodes {
branchProtection {
pushAccessLevels {
nodes {
#{fields}
}
}
}
}
}
}
}
GQL
end
context 'when the user does not have read_protected_branch abilities' do
let_it_be(:protected_branch) { create(:protected_branch) }
before do
project.add_guest(current_user)
post_graphql(query, current_user: current_user, variables: variables)
end
it_behaves_like 'a working graphql query'
it { expect(push_access_levels_data).not_to be_present }
end
shared_examples 'push access request' do
let(:push_access) { protected_branch.push_access_levels.first }
before do
project.add_maintainer(current_user)
post_graphql(query, current_user: current_user, variables: variables)
end
it_behaves_like 'a working graphql query'
it 'returns all push access levels' do
expect(push_access_levels_data.size).to eq(push_access_levels_count)
end
it 'includes access_level' do
expect(push_access_level_data['accessLevel'])
.to eq(push_access.access_level)
end
it 'includes access_level_description' do
expect(push_access_level_data['accessLevelDescription'])
.to eq(push_access.humanize)
end
end
context 'when the user does have read_protected_branch abilities' do
let(:push_access) { protected_branch.push_access_levels.first }
context 'when no one has access' do
let_it_be(:protected_branch) { create(:protected_branch, :no_one_can_push) }
it_behaves_like 'push access request'
end
context 'when developers have access' do
let_it_be(:protected_branch) { create(:protected_branch, :developers_can_push) }
it_behaves_like 'push access request'
end
context 'when maintainers have access' do
let_it_be(:protected_branch) { create(:protected_branch, :maintainers_can_push) }
it_behaves_like 'push access request'
end
end
end

View File

@ -21,24 +21,27 @@ RSpec.describe 'getting list of branch rules for a project' do
let(:branch_rules_data) { graphql_data_at('project', 'branchRules', 'edges') }
let(:variables) { { path: project.full_path } }
let(:fields) { all_graphql_fields_for('BranchRule', max_depth: 2) }
let(:fields) do
<<~QUERY
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
#{all_graphql_fields_for('branch_rules'.classify)}
}
}
QUERY
end
let(:query) do
<<~GQL
query($path: ID!, $n: Int, $cursor: String) {
project(fullPath: $path) {
branchRules(first: $n, after: $cursor) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
#{fields}
}
}
}
branchRules(first: $n, after: $cursor) { #{fields} }
}
}
GQL
@ -61,12 +64,15 @@ RSpec.describe 'getting list of branch rules for a project' do
post_graphql(query, current_user: current_user, variables: variables)
end
it_behaves_like 'a working graphql query' do
it 'includes all fields', :aggregate_failures do
expect(branch_rules_data.dig(0, 'node', 'name')).to be_present
expect(branch_rules_data.dig(0, 'node', 'createdAt')).to be_present
expect(branch_rules_data.dig(0, 'node', 'updatedAt')).to be_present
end
it_behaves_like 'a working graphql query'
it 'includes a name' do
expect(branch_rules_data.dig(0, 'node', 'name')).to be_present
end
it 'includes created_at and updated_at' do
expect(branch_rules_data.dig(0, 'node', 'createdAt')).to be_present
expect(branch_rules_data.dig(1, 'node', 'updatedAt')).to be_present
end
context 'when limiting the number of results' do

View File

@ -1,106 +0,0 @@
# frozen_string_literal: true
RSpec.shared_examples 'perform graphql requests for AccessLevel type objects' do |access_level_kind|
include GraphqlHelpers
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:variables) { { path: project.full_path } }
let(:fields) { all_graphql_fields_for("#{access_level_kind.to_s.classify}AccessLevel", max_depth: 2) }
let(:access_levels) { protected_branch.public_send("#{access_level_kind}_access_levels") }
let(:access_levels_count) { access_levels.size }
let(:maintainer_access_level) { access_levels.for_role.first }
let(:user_access_level) { access_levels.for_user.first }
let(:group_access_level) { access_levels.for_group.first }
let(:user_access_level_data) { access_levels_data.find { |data| data['user'].present? } }
let(:group_access_level_data) { access_levels_data.find { |data| data['group'].present? } }
let(:maintainer_access_level_data) { access_levels_data.find { |data| data['user'].blank? && data['group'].blank? } }
let(:access_levels_data) do
graphql_data_at('project',
'branchRules',
'nodes',
0,
'branchProtection',
"#{access_level_kind.to_s.camelize(:lower)}AccessLevels",
'nodes')
end
let(:query) do
<<~GQL
query($path: ID!) {
project(fullPath: $path) {
branchRules(first: 1) {
nodes {
branchProtection {
#{access_level_kind.to_s.camelize(:lower)}AccessLevels {
nodes {
#{fields}
}
}
}
}
}
}
}
GQL
end
describe 'requesting AccessLevel type objects as a guest user' do
let_it_be(:protected_branch) { create(:protected_branch, project: project) }
before do
create(:project_member, :guest, user: current_user, source: project)
post_graphql(query, current_user: current_user, variables: variables)
end
it_behaves_like 'a working graphql query'
it "does not return any #{access_level_kind.to_s.classify}AccessLevel objects" do
expect(access_levels_data).not_to be_present
end
end
describe 'requesting AccessLevel type objects as a maintainer' do
let_it_be(:protected_branch) do
create(:protected_branch,
"maintainers_can_#{access_level_kind}",
"user_can_#{access_level_kind}",
"group_can_#{access_level_kind}",
project: project)
end
before do
create(:project_member, :maintainer, user: current_user, source: project)
post_graphql(query, current_user: current_user, variables: variables)
end
it_behaves_like 'a working graphql query'
it "returns all #{access_level_kind} access level fields", :aggregate_failures do
expect(access_levels_count).to eq(3),
"Expected 3 #{access_level_kind.to_s.classify}AccessLevel records present, got #{access_levels_count}"
expect(access_levels_data.size).to eq(access_levels_count)
[:maintainer, :user, :group].each do |access_level_type|
access_level_data = public_send("#{access_level_type}_access_level_data")
access_level = public_send("#{access_level_type}_access_level")
expect(access_level_data['accessLevel']).to eq(access_level.access_level)
expect(access_level_data['accessLevelDescription']).to eq(access_level.humanize)
case access_level_type
when :user
expect(access_level_data.dig('user', 'name')).to eq(access_level.user.name)
expect(access_level_data.dig('group', 'name')).to be_nil
when :group
expect(access_level_data.dig('group', 'name')).to eq(access_level.group.name)
expect(access_level_data.dig('user', 'name')).to be_nil
else
expect(access_level_data.dig('group', 'name')).to be_nil
expect(access_level_data.dig('user', 'name')).to be_nil
end
end
end
end
end

View File

@ -21,6 +21,6 @@ RSpec.describe 'shared/projects/_project.html.haml' do
render 'shared/projects/project', use_creator_avatar: true, project: project
expect(rendered).to have_selector('.project-avatar')
expect(rendered).to have_selector('.gl-avatar-identicon')
end
end

View File

@ -126,6 +126,19 @@ RSpec.describe Pages::InvalidateDomainCacheWorker do
{ type: :namespace, id: 3 }
]
it_behaves_like 'clears caches with',
event_class: PagesDomains::PagesDomainDeletedEvent,
event_data: {
project_id: 1,
namespace_id: 2,
root_namespace_id: 3,
domain: 'somedomain.com'
},
caches: [
{ type: :project, id: 1 },
{ type: :namespace, id: 3 }
]
context 'when namespace based cache keys are duplicated' do
# de-dups namespace cache keys
it_behaves_like 'clears caches with',